MCP Integration
Align exposes a Model Context Protocol (MCP, spec 2025-03-26) endpoint that lets AI assistants — including Claude for Desktop, Cursor, and any MCP-compatible client — read project data and take actions directly inside Align.
MCP access is toggled per workspace by a platform admin. Contact your Align administrator to enable it for your organization.
Connecting your MCP client
Endpoint URL
https://<your-align-host>/mcp
Authentication
Align supports two authentication methods for MCP clients. Both produce an identical experience for your AI tools — choose the one that fits your deployment.
Option A — API key (simpler, server-to-server)
Create or copy an organization-scoped API key from Settings → Developer → API Keys, then pass it as a Bearer token:
Authorization: Bearer <your-api-key>
API keys are good for server-side integrations and scripts where there is no end-user involved.
Option B — OAuth 2.0 (recommended for user-facing MCP clients)
Align implements a full OAuth 2.0 Authorization Server (PKCE, dynamic client registration, RFC 8414 discovery). This lets an MCP client obtain a short-lived access token on behalf of a specific Align user without ever seeing that user's password or API key.
Discovery endpoint (RFC 8414):
GET https://<your-align-host>/.well-known/oauth-authorization-server
Returns the full server metadata including all endpoint URLs, supported scopes, and PKCE requirements.
OAuth flow (Authorization Code + PKCE):
sequenceDiagram
participant C as MCP Client
participant A as Align OAuth AS
participant U as User (browser)
C->>A: POST /oauth/clients (register)
A-->>C: client_id + client_secret
C->>U: Redirect → GET /oauth/authorize?code_challenge=...
U->>A: Browser GETs consent screen
A-->>U: Align consent page (scopes listed)
U->>A: POST /oauth/authorize (decision=approve)
A-->>U: Redirect → redirect_uri?code=...
U->>C: Auth code delivered via redirect
C->>A: POST /oauth/token (code + code_verifier + client_secret)
A-->>C: access_token (1 h) + refresh_token (30 d)
C->>A: POST /mcp (Bearer access_token)
A-->>C: MCP response
Note over C,A: When access_token expires
C->>A: POST /oauth/token (grant_type=refresh_token)
A-->>C: New access_token + rotated refresh_token
-
Register your client (once, or on first run):
POST /oauth/clientsContent-Type: application/json{"client_name": "My MCP Client","redirect_uris": ["https://my-client/callback"],"scope": "mcp:read mcp:write"}Response includes
client_idandclient_secret. Store these securely. -
Start the authorization flow — redirect the user to:
GET /oauth/authorize?client_id=<client_id>&redirect_uri=<redirect_uri>&response_type=code&scope=mcp:read mcp:write&state=<random-state>&code_challenge=<S256-challenge>&code_challenge_method=S256The user sees an Align-hosted consent screen and approves the requested scopes.
-
Exchange the code for tokens:
POST /oauth/tokenContent-Type: application/x-www-form-urlencodedgrant_type=authorization_code&code=<authorization-code>&redirect_uri=<redirect_uri>&client_id=<client_id>&client_secret=<client_secret>&code_verifier=<pkce-verifier>Response:
{"access_token": "...","token_type": "Bearer","expires_in": 3600,"refresh_token": "...","scope": "mcp:read mcp:write"} -
Refresh when the access token expires:
POST /oauth/tokenContent-Type: application/x-www-form-urlencodedgrant_type=refresh_token&refresh_token=<refresh_token>&client_id=<client_id>&client_secret=<client_secret>Each refresh rotates both tokens. The old refresh token is immediately revoked.
-
Revoke a token when done:
POST /oauth/revokeContent-Type: application/x-www-form-urlencodedtoken=<access_or_refresh_token>&client_id=<client_id>&client_secret=<client_secret>
Supported scopes:
| Scope | Access granted |
|---|---|
mcp:read | Resources (projects, entries, releases) and search |
mcp:write | All read access + mutating tools (entry.create, entry.update_status, etc.) |
Security notes:
- PKCE (
S256) is mandatory for all clients. Plaincode_challenge_methodis rejected. - Authorization codes are single-use (marked on first exchange).
- Refresh tokens are rotated on every use to limit exposure if stolen.
- All token values are stored as SHA-256 hashes only — plaintexts are never persisted.
Supported transports
| Transport | Endpoint | When to use |
|---|---|---|
| Streamable HTTP (preferred) | POST /mcp | Claude for Desktop ≥ 0.7, Cursor, most modern clients |
| HTTP + SSE fallback | GET /mcp/sse + POST /mcp/messages | Older clients that require the SSE transport |
Replit Agent
Add the following to your Replit Agent's tool configuration (.replit or agent settings panel):
{
"mcpServers": {
"align": {
"type": "http",
"url": "https://<your-align-host>/mcp",
"headers": {
"Authorization": "Bearer <your-api-key>"
}
}
}
}
Once configured, the agent can call resources/list, tools/list, and tools/call against your Align workspace. For example, asking the agent to "create a bug report for the login page in project X" will invoke entry.create and return the new entry ID.
Claude for Desktop (claude_desktop_config.json)
Option A — API key (quickest setup)
{
"mcpServers": {
"align": {
"type": "http",
"url": "https://<your-align-host>/mcp",
"headers": {
"Authorization": "Bearer <your-api-key>"
}
}
}
}
Option B — OAuth 2.0 (user-scoped access, recommended)
Claude for Desktop (≥ 0.7) with native HTTP MCP support will auto-discover the OAuth server and prompt the user to authorize. Follow these steps:
-
Add the server to
claude_desktop_config.jsonwithout any auth headers:{"mcpServers": {"align": {"type": "http","url": "https://<your-align-host>/mcp"}}} -
Restart Claude for Desktop. It will detect that the MCP server requires auth by reading the discovery document at
/.well-known/oauth-authorization-server. -
Claude opens your browser to the Align consent screen at
/oauth/authorize. Sign in with your Align account and click Approve. -
Claude receives the OAuth token via the PKCE redirect and stores it locally. All subsequent MCP calls use the token automatically.
-
Token refresh happens transparently — Claude handles the refresh grant before the 1-hour access token expires, using the 30-day refresh token.
-
Revoke access at any time: in Align go to Settings → Developer → Authorized Apps (coming in task #416), or call
POST /oauth/revokedirectly:POST /oauth/revokeContent-Type: application/x-www-form-urlencodedtoken=<access_or_refresh_token>&client_id=<client_id>&client_secret=<client_secret>
Claude for Desktop performs dynamic client registration on first connect — it calls POST /oauth/clients automatically. You do not need to pre-register a client manually when using Claude for Desktop.
Real-time resource subscriptions (SSE transport)
When connected via the HTTP + SSE fallback transport (GET /mcp/sse), clients can subscribe to live resource change notifications. The server pushes notifications/resources/updated messages whenever the underlying data changes.
Subscribe:
{ "jsonrpc": "2.0", "id": 1, "method": "resources/subscribe", "params": { "uri": "align://projects/{id}/entries" } }
Unsubscribe:
{ "jsonrpc": "2.0", "id": 2, "method": "resources/unsubscribe", "params": { "uri": "align://projects/{id}/entries" } }
Notification delivered by the server:
{ "jsonrpc": "2.0", "method": "notifications/resources/updated", "params": { "uri": "align://projects/{id}/entries" } }
Subscribable URIs and triggering events:
| URI pattern | Notified when… |
|---|---|
align://projects | Any entry or release is created/updated across the workspace |
align://projects/{id} | An entry or release in that project is created/updated |
align://projects/{id}/entries | An entry in that project is created, updated, or commented on |
align://entries/{id} | That specific entry is updated, status-changed, reassigned, or commented on |
align://releases | Any release is created, status-changed, or released |
Subscriptions are scoped to the current SSE session and are automatically cleaned up when the connection closes. The Streamable HTTP transport (POST /mcp) does not support push subscriptions; reconnect using the SSE transport if you need live notifications.
Resources
Resources are read-only data sources the AI can retrieve.
| URI | Description |
|---|---|
align://projects | All projects in your workspace |
align://projects/{id} | Single project detail |
align://projects/{id}/entries | All entries for a project |
align://entries/{id} | Entry detail including comments |
align://releases | All releases across your workspace |
align://task-mappings | Cross-system entry ↔ external-task mappings |
Tools
Tools allow the AI to take actions in Align. All tool calls are audit-logged.
entry.create
Create a new entry in a project.
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId | string | ✅ | Target project ID |
title | string | ✅ | Entry title (max 500 chars) |
type | enum | ✅ | feature_request, bug_report, feedback, validation, documentation, platform_infrastructure, ci_cd, security |
description | string | ✅ | Markdown description |
priority | enum | low, medium (default), high, critical | |
status | enum | open (default), assigned, in_progress, deployed, solved | |
assignedToId | string | User ID to assign the entry to |
entry.update_status
Update the status, priority, or assignee of an existing entry.
| Parameter | Type | Required | Description |
|---|---|---|---|
entryId | string | ✅ | Entry ID |
status | enum | ✅ | New status |
priority | enum | New priority | |
assignedToId | string | null | New assignee, or null to unassign |
entry.add_comment
Add a comment to an existing entry.
| Parameter | Type | Required | Description |
|---|---|---|---|
entryId | string | ✅ | Entry ID |
content | string | ✅ | Comment text (markdown supported) |
task.link
Create or update a mapping between an Align entry and an external task (Replit task, Jira issue, Linear ticket, etc.).
| Parameter | Type | Required | Description |
|---|---|---|---|
entryId | string | ✅ | Align entry ID |
system | string | ✅ | External system identifier e.g. replit, jira, linear |
externalId | string | ✅ | ID of the task in the external system |
externalRef | string | Human-readable reference e.g. #386 | |
externalUrl | string | Deep-link URL to the external task |
task.sync_from_external
Idempotent upsert: create or update an Align entry from an external task payload and maintain the mapping. Use this to keep Align in sync with an external tracker without creating duplicates.
| Parameter | Type | Required | Description |
|---|---|---|---|
system | string | ✅ | External system identifier |
externalId | string | ✅ | ID in the external system |
projectId | string | ✅ | Align project ID |
title | string | ✅ | Entry title |
description | string | Entry description | |
type | enum | Entry type (default feature_request) | |
status | enum | Entry status (default open) | |
priority | enum | Entry priority (default medium) | |
externalRef | string | e.g. #386 | |
externalUrl | string | Deep-link URL |
search.global
Full-text search across projects and entries in your workspace.
| Parameter | Type | Required | Description |
|---|---|---|---|
q | string | ✅ | Search query |
limit | number | Max results (1–100, default 50) |
Prompts
Prompts are reusable AI prompt templates with live data interpolated.
summarise_project_status
Generates a project health summary prompt with current metrics (open/resolved counts, active release, etc.).
Arguments: projectId (string)
create_issue_from_context
Guides the AI to draft a well-formed Align entry from freeform context text.
Arguments: context (string), projectId (string, optional)
Rate limiting
MCP requests share the same per-key rate limit as the v1 REST API: 600 requests per minute. The response includes standard rate-limit headers:
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 599
X-RateLimit-Reset: <unix-timestamp>
When the limit is exceeded the server returns 429 Too Many Requests with a Retry-After header.
Audit log
Every tool invocation is written to an internal audit log. Only a SHA-256 hash of the tool arguments is stored — raw arguments are never persisted. Platform admins can query the log via the database.
Troubleshooting
| Error | Meaning |
|---|---|
401 Unauthorized | Credentials missing, revoked, or expired (API key or OAuth token) |
403 Forbidden | MCP not enabled for this workspace — contact your platform admin |
403 Forbidden (project key) | Project-scoped API keys are not supported for MCP v1. Generate an organization-scoped key from Settings → Developer → API Keys |
400 Bad Request (invalid_grant) | OAuth authorization code is invalid, already used, or expired |
400 Bad Request (invalid_client) | OAuth client_id / client_secret mismatch, or unknown client |
400 Bad Request (invalid_request) | PKCE code_verifier does not match the stored code_challenge, or required parameter is missing |
400 Bad Request (unsupported_grant_type) | Only authorization_code and refresh_token are supported |
423 Locked | Workspace suspended due to billing — update payment method at /settings/billing |
429 Too Many Requests | Rate limit exceeded; wait for Retry-After seconds |
404 Not Found (SSE session) | SSE session expired; reconnect via GET /mcp/sse |
API key scope requirement
MCP v1 requires an organization-scoped API key. Project-scoped keys (created from a specific project's settings) are intentionally rejected because they carry least-privilege access to a single project, and elevating them to org-wide MCP access (all projects, releases, search, and write tools) would violate the principle of least privilege. Support for project-scoped MCP access is planned for a future phase.