productboard-mcp-oauth
v1.2.0
Published
MCP server for ProductBoard API v2
Readme
productboard-mcp-oauth
A TypeScript Model Context Protocol server for the ProductBoard API v2. Manage features, releases, notes, and workspace configuration through natural language conversation in Claude Desktop or Claude Code.
Features
- 66 MCP tools across features, products, components, subfeatures, releases, release groups, notes, strategy, entity operations, search, configuration, and ADO push/link
- OAuth 2.0 + PKCE authentication with automatic token refresh
- Rate limiting with exponential backoff
- Cursor-based pagination for all list endpoints
- Full CRUD for features, releases, and notes
- ADO integration — push or link features to Azure DevOps work items via browser automation
Setup
There are two ways to authenticate: direct mode (you hold the OAuth credentials) and broker mode (credentials live in a shared Azure Function — recommended for team installs).
Direct mode
Use this when you are setting up the integration for yourself and are happy to store the OAuth client secret in your MCP config.
Step 1 — Create an OAuth app in ProductBoard
- Go to https://app.productboard.com/oauth2/applications
- Click Create OAuth App
- Set the redirect URI to
http://localhost:3000/callback - Select scopes:
entities:read,entities:write,entities:delete,notes:read,notes:write,notes:delete - Save your Client ID and Client Secret
Step 2 — Configure your MCP client
Claude Desktop — add to claude_desktop_config.json (Settings > Developer > Edit Config):
{
"mcpServers": {
"productboard": {
"command": "npx",
"args": ["-y", "productboard-mcp-oauth"],
"env": {
"PRODUCTBOARD_OAUTH_CLIENT_ID": "your-client-id",
"PRODUCTBOARD_OAUTH_CLIENT_SECRET": "your-client-secret",
"PRODUCTBOARD_WORKSPACE_URL": "https://yourcompany.productboard.com",
"PRODUCTBOARD_ADO_INTEGRATION_NAME": "ADO Integration OAUTH 2.0"
}
}
}
}Claude Code — run once from any directory:
claude mcp add productboard npx -- -y productboard-mcp-oauth \
-e PRODUCTBOARD_OAUTH_CLIENT_ID=your-client-id \
-e PRODUCTBOARD_OAUTH_CLIENT_SECRET=your-client-secret \
-e PRODUCTBOARD_WORKSPACE_URL=https://yourcompany.productboard.com \
-e PRODUCTBOARD_ADO_INTEGRATION_NAME="ADO Integration OAUTH 2.0"Step 3 — Authorize (one-time)
- Restart Claude Desktop or start a new Claude Code session
- A browser window opens to ProductBoard's OAuth consent page
- Sign in and authorize the application
- The browser shows "Authorization Successful" — you can close the tab
Tokens are saved to ~/.productboard-mcp/tokens.json and refresh automatically.
Broker mode
Use this for team installs where you want users to authenticate with their own ProductBoard account without distributing the OAuth client secret to every machine. A shared Azure Function holds the credentials; each user gets a broker URL and host key instead.
How it works: the broker acts as the OAuth redirect URI. When a user authorizes, ProductBoard redirects to the broker, which bounces the browser to a temporary local port (auto-selected from 50000+). Tokens are then stored locally on each user's machine — the broker is only involved during initial authorization and token refresh, not during normal API calls.
Step 1 — Deploy the broker
See broker/README.md for full Azure deployment instructions. Once deployed you will have:
- A broker URL:
https://<function-app-name>.azurewebsites.net/api - A host key to share with users
Step 2 — Register the broker callback in ProductBoard
- Go to https://app.productboard.com/oauth2/applications
- Open your OAuth app
- Add the redirect URI:
https://<function-app-name>.azurewebsites.net/api/callback - Select scopes:
entities:read,entities:write,entities:delete,notes:read,notes:write,notes:delete
Step 3 — Configure each user's MCP client
Claude Desktop — add to claude_desktop_config.json:
{
"mcpServers": {
"productboard": {
"command": "npx",
"args": ["-y", "productboard-mcp-oauth"],
"env": {
"PRODUCTBOARD_BROKER_URL": "https://<function-app-name>.azurewebsites.net/api",
"PRODUCTBOARD_BROKER_KEY": "your-host-key",
"PRODUCTBOARD_WORKSPACE_URL": "https://yourcompany.productboard.com",
"PRODUCTBOARD_ADO_INTEGRATION_NAME": "ADO Integration OAUTH 2.0"
}
}
}
}Claude Code — run once from any directory:
claude mcp add productboard npx -- -y productboard-mcp-oauth \
-e PRODUCTBOARD_BROKER_URL=https://<function-app-name>.azurewebsites.net/api \
-e PRODUCTBOARD_BROKER_KEY=your-host-key \
-e PRODUCTBOARD_WORKSPACE_URL=https://yourcompany.productboard.com \
-e PRODUCTBOARD_ADO_INTEGRATION_NAME="ADO Integration OAUTH 2.0"Step 4 — Each user authorizes (one-time)
- Restart Claude Desktop or start a new Claude Code session
- A browser window opens to ProductBoard's OAuth consent page
- Sign in and authorize with their own ProductBoard account
- The browser shows "Authorization Successful" — they can close the tab
Each user's tokens are stored at ~/.productboard-mcp/tokens.json on their machine and refresh automatically. The broker is not involved after initial authorization until the refresh token expires (~90 days of inactivity).
Environment Variables
Two auth modes are supported — configure one or the other, not both.
Direct mode
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| PRODUCTBOARD_OAUTH_CLIENT_ID | Yes | — | OAuth 2.0 client ID |
| PRODUCTBOARD_OAUTH_CLIENT_SECRET | Yes | — | OAuth 2.0 client secret |
| PRODUCTBOARD_CALLBACK_PORT | No | 3000 | Local port for the OAuth callback server — change if port 3000 is already in use |
Broker mode
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| PRODUCTBOARD_BROKER_URL | Yes | — | Azure Function base URL (e.g. https://my-fn.azurewebsites.net/api) |
| PRODUCTBOARD_BROKER_KEY | Yes | — | Azure Function host key |
Common (both modes)
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| PRODUCTBOARD_WORKSPACE_URL | No | — | Workspace URL (e.g. https://yourcompany.productboard.com) — required for ADO push/link tools |
| PRODUCTBOARD_ADO_INTEGRATION_NAME | No | ADO Integration OAUTH 2.0 | Name of the ADO integration as shown in ProductBoard's Integrations section |
| PRODUCTBOARD_TOKEN_PATH | No | ~/.productboard-mcp/tokens.json | Custom token storage path |
| PRODUCTBOARD_API_BASE_URL | No | https://api.productboard.com | API base URL override |
Tool Reference
Health
| Tool | Description |
|------|-------------|
| pb_health_check | Verify API connectivity and authentication status |
Features (5 tools)
| Tool | Description |
|------|-------------|
| pb_feature_list | List features with optional filtering |
| pb_feature_get | Get a feature by ID |
| pb_feature_create | Create a new feature |
| pb_feature_update | Update a feature's fields |
| pb_feature_delete | Delete a feature |
Products (4 tools)
| Tool | Description |
|------|-------------|
| pb_product_list | List all products |
| pb_product_get | Get a product by ID |
| pb_product_create | Create a new product |
| pb_product_update | Update a product's fields |
Components (4 tools)
| Tool | Description |
|------|-------------|
| pb_component_list | List all components |
| pb_component_get | Get a component by ID |
| pb_component_create | Create a new component |
| pb_component_update | Update a component's fields |
Subfeatures (4 tools)
| Tool | Description |
|------|-------------|
| pb_subfeature_list | List subfeatures with optional parent filter |
| pb_subfeature_get | Get a subfeature by ID |
| pb_subfeature_create | Create a new subfeature (requires parent feature) |
| pb_subfeature_update | Update a subfeature's fields |
Releases (5 tools)
| Tool | Description |
|------|-------------|
| pb_release_list | List releases |
| pb_release_get | Get a release by ID |
| pb_release_create | Create a new release (requires parent release group) |
| pb_release_update | Update a release's fields |
| pb_release_delete | Delete a release |
Release Groups (3 tools)
| Tool | Description |
|------|-------------|
| pb_release_group_list | List all release groups |
| pb_release_group_get | Get a release group by ID |
| pb_release_group_update | Update a release group's name or description |
Release Assignments (3 tools)
| Tool | Description |
|------|-------------|
| pb_release_assign_feature | Assign a feature to a release |
| pb_release_list_features | List features assigned to a release |
| pb_release_unassign_feature | Remove a feature from a release |
Entity Relationships (1 tool)
| Tool | Description |
|------|-------------|
| pb_entity_list_relationships | List parent/child, link, isBlockedBy/isBlocking relationships for any entity; filter by type, target type, or target ID |
Entity Operations (5 tools)
| Tool | Description |
|------|-------------|
| pb_entity_add_tag | Add a tag to any entity without overwriting existing tags |
| pb_entity_remove_tag | Remove a tag from any entity |
| pb_entity_replace_parent | Atomically move an entity to a new parent |
| pb_entity_create_relationship | Create a link, isBlockedBy, or isBlocking relationship between two entities |
| pb_entity_delete_relationship | Delete a relationship between two entities |
Strategy (12 tools)
| Tool | Description |
|------|-------------|
| pb_initiative_list | List initiatives |
| pb_initiative_get | Get an initiative by ID |
| pb_initiative_create | Create a new initiative |
| pb_initiative_update | Update an initiative's fields |
| pb_objective_list | List objectives |
| pb_objective_get | Get an objective by ID |
| pb_objective_create | Create a new objective |
| pb_objective_update | Update an objective's fields |
| pb_key_result_list | List key results |
| pb_key_result_get | Get a key result by ID |
| pb_key_result_create | Create a new key result |
| pb_key_result_update | Update a key result's fields |
Notes (11 tools)
| Tool | Description |
|------|-------------|
| pb_note_list | List notes with optional filtering |
| pb_note_get | Get a note by ID |
| pb_note_create | Create a new note |
| pb_note_update | Update a note's fields |
| pb_note_delete | Delete a note |
| pb_note_search | Search notes by structured filters |
| pb_note_count | Count notes matching structured filters (paginates internally) |
| pb_note_link_feature | Link a note to a feature |
| pb_note_link_customer | Link a note to a customer/company |
| pb_note_unlink | Remove a relationship from a note |
| pb_note_list_relationships | List a note's relationships |
Search
| Tool | Description |
|------|-------------|
| pb_entity_search | Search entities by type and structured filters |
Configuration (3 tools)
| Tool | Description |
|------|-------------|
| pb_config_list | List all entity type configurations (fields, filters) |
| pb_config_get | Get configuration for a specific entity type |
| pb_config_notes | Get note field configuration |
ADO Push/Link (4 tools)
| Tool | Description |
|------|-------------|
| pb_ado_push_feature_auto | Push a PB feature to ADO — launches Chromium, fills the push dialog, returns the created work item URL |
| pb_ado_link_feature_auto | Link a PB feature to an existing ADO work item by ID |
| pb_build_detail_url | Encode a ProductBoard entity UUID into a detail page URL |
| pb_decode_detail_url | Decode a ProductBoard detail URL back to entity UUID + workspace URL |
Troubleshooting
"Missing required environment variables"
→ Set PRODUCTBOARD_OAUTH_CLIENT_ID and PRODUCTBOARD_OAUTH_CLIENT_SECRET in your MCP client config.
"Token exchange failed" / "invalid_grant" → Authorization codes are short-lived. Restart the server to trigger a fresh OAuth flow.
Persistent 401 errors after token refresh → The refresh token may have expired. The server will automatically detect this, clear the stale token, and open a browser window to re-authorize.
EADDRINUSE: address already in use :::3000 on first run
→ A previous server instance is holding the callback port. Wait a few seconds and restart.
"Server disconnected" in Claude Desktop
→ Ensure console.log is not used in any server code — stdout is the MCP transport channel.
ADO push opens a browser login screen
→ The browser session (stored in ~/.productboard-mcp/browser-profile) has expired. Log in once and subsequent pushes will reuse the session.
Release Notes
1.1.0
- Broker mode: zero-configuration OAuth callback — when using the Azure Function token broker, the local OAuth callback port is now auto-selected from the 50000+ range. No port needs to be pre-registered with ProductBoard, and
PRODUCTBOARD_CALLBACK_PORTis not required. The broker's/api/callbackendpoint acts as the registered redirect URI and bounces the browser to whichever local port was picked. PRODUCTBOARD_CALLBACK_PORTnow direct mode only — this variable is still supported for direct mode (default:3000) but is ignored in broker mode.
1.0.8
- Automatic re-authorization on expired refresh token — if the refresh token expires (e.g. after a period of inactivity), the server now detects the
invalid_granterror, clears the stale credentials, and automatically opens a browser window to re-authorize. No manual intervention required.
1.0.7
PRODUCTBOARD_CALLBACK_PORTenvironment variable — allows configuring the local port used for the OAuth callback server (default:3000). Useful if port 3000 is already in use on your machine.
License
MIT
