Skip to main content

MCP Server

CoderFlow can act as a Model Context Protocol (MCP) server. Once an administrator enables it, any MCP-capable agent — Claude Code, Claude Desktop / Cowork, GitHub Copilot in VS Code, Cursor, and others — can hand work to CoderFlow directly instead of a user copying and pasting between tools.

Through the MCP server an external agent can:

  • Discover the environments and task templates the user can use
  • Create a task, optionally passing along a handoff (summary and transcript) of the session it is coming from
  • Attach files for the CoderFlow agent to read
  • Poll a task's status and read the agent's final summary
  • Send follow-up instructions to a finished task

Every connection authenticates with a user's personal API key and runs under that user's roles and permissions. The MCP server never grants access beyond what the same user has in the Web UI or CLI.

Enable the MCP Server

  1. Go to Settings → Integrations → MCP Server
  2. Toggle Enabled on and click Save
  3. Copy the Endpoint URL (for example https://coderflow.example.com/mcp)

The endpoint uses the Site URL from General settings when one is configured; otherwise it reflects the address you are browsing from. Make sure the URL you hand to clients is reachable from the machines where those clients run. MCP and OAuth URLs are always built from the origin (https://host[:port]): the routes and the OAuth discovery documents live at the root, so a Site URL path prefix is not carried into them, and the public base URL setting must be an origin with no path.

The setting can also be pinned with the MCP_SERVER_ENABLED environment variable (true or false) on the server. When the variable is set, the Settings toggle is read-only.

The MCP server is off by default. Requests to /mcp return 404 while it is disabled.

Enable OAuth for Hosted Clients

Some clients cannot attach a static API key to a remote MCP server. claude.ai and Cowork custom connectors, for example, expect the server to implement the MCP authorization flow (OAuth 2.1 with PKCE and dynamic client registration). CoderFlow ships that authorization server built in.

  1. Set an HTTPS Site URL in Settings → General. The OAuth issuer is derived from it, and the MCP SDK refuses non-HTTPS issuers other than localhost.
  2. In Settings → Integrations → MCP Server, toggle OAuth for hosted clients on and click Save.
  3. Copy the Endpoint URL (for example https://coderflow.example.com/mcp) and add it as a custom connector in the client.

When a client first calls the endpoint without credentials, CoderFlow answers 401 with a WWW-Authenticate challenge that points at the discovery document. The client registers itself, opens the CoderFlow login page in the browser (local login or SSO), shows a consent page naming the client, and receives tokens once the user approves. No key is copied anywhere.

Endpoints, all under the Site URL:

EndpointPurpose
/.well-known/oauth-authorization-serverAuthorization server metadata
/.well-known/oauth-protected-resource/mcpProtected resource metadata for /mcp
/mcp/oauth/registerDynamic client registration
/mcp/oauth/authorizeLogin and consent, issues authorization codes
/mcp/oauth/tokenExchanges codes and refresh tokens
/mcp/oauth/revokeToken revocation

Token lifetimes:

TokenLifetime
Authorization code10 minutes, single use
Access token1 hour
Refresh token30 days, rotated on every use

Tokens are stored only as hashes in mcp-oauth.json in the server data directory. The setting can be pinned with the MCP_OAUTH_ENABLED environment variable, and it has no effect while the MCP server itself is disabled.

Hardening the authorization server

Dynamic client registration is open by design: anyone who can reach the registration endpoint can register a client with any name. Two settings on the MCP Server panel close the resulting phishing path. Both are off by default so existing connections keep working; turn them on for production servers.

  • Allowed OAuth redirect hosts. One hostname per line, with *.example.com matching subdomains. Registrations and authorizations that redirect anywhere else are refused, including clients registered before the list was set. Loopback addresses used by local clients such as Claude Code and mcp-remote are always allowed. For Claude connectors, allow claude.ai and *.anthropic.com, and check the redirect host shown on the consent page the first time you connect.
  • Require fresh sign-in for consent. A number of minutes. Approving a new client then requires a login newer than that; an older browser session is sent back through the login page first. Local users re-enter their password (and MFA code when enrolled); SSO users are re-prompted by the identity provider because CoderFlow requests prompt=login. This stops a walk-up on an unlocked, signed-in browser from silently authorizing a connector. On development servers started with DISABLE_AUTH, there is no login to refresh, so the rule is treated as satisfied.

Managing authorized clients

Users can list and revoke the clients they have approved:

# List my authorized MCP clients
curl -H "Authorization: Bearer $CODER_API_KEY" "$CODERFLOW_URL/api/mcp-oauth/grants"

# Revoke one client, or everything
curl -X DELETE -H "Authorization: Bearer $CODER_API_KEY" "$CODERFLOW_URL/api/mcp-oauth/grants/<client_id>"
curl -X DELETE -H "Authorization: Bearer $CODER_API_KEY" "$CODERFLOW_URL/api/mcp-oauth/grants"

Revoking a refresh token also revokes the access tokens issued with it. Deleting a user revokes everything they authorized, because tokens only work while the user still exists.

Dedicated MCP Listener

By default the MCP endpoint and its OAuth routes are served by the main CoderFlow port. Deployments can instead start a second HTTP listener that serves only the machine-facing MCP routes and expose just that port, keeping the main application port private. This is the same pattern as the messaging-integrations and webhook listeners, and like them it is route and port isolation inside one Node process, not a separate process or privilege boundary.

Set the following before starting the server:

MCP_INGRESS_MODE=listener
MCP_INGRESS_PORT=3004
MCP_INGRESS_HOST=0.0.0.0

MCP_INGRESS_PORT by itself also enables listener mode. The listener uses the same TLS configuration as the main server.

The listener serves:

  • GET /health
  • POST /mcp (plus the GET/DELETE method-not-allowed responses)
  • POST /mcp/oauth/token, POST /mcp/oauth/register, POST /mcp/oauth/revoke
  • GET /.well-known/oauth-authorization-server
  • GET /.well-known/oauth-protected-resource/mcp

It does not serve the web UI, the authenticated APIs, or /mcp/oauth/authorize. Login and consent always happen on the main application, in the user's browser. Only bearer credentials (OAuth access tokens or personal API keys) authenticate on the listener; browser sessions do not.

When using the listener:

  1. Set Site URL in General settings to the main application URL users open in a browser. The OAuth metadata publishes the authorization endpoint there.
  2. Set Public base URL for MCP clients on the MCP Server panel to the public HTTPS address of the listener, for example https://mcp.example.com. The endpoint URL, discovery documents, and token endpoints are published there. The MCP_PUBLIC_BASE_URL environment variable pins the same value.
  3. Check the read-only Dedicated MCP listener field to confirm the server sees the expected mode and port.

Internal servers and hosted connectors

This split is what makes hosted connectors work against a CoderFlow that is not on the public internet. Claude connectors make their MCP, registration, and token requests from Anthropic's servers, but the login and consent steps run in the user's browser. With the listener:

  • Expose only the listener port through the firewall, ideally restricted to Anthropic's published egress IP ranges, with a publicly trusted certificate.
  • Keep the main application internal. Users on the internal network reach the login and consent pages there.
  • Set the redirect allowlist to the Claude callback hosts so nothing else can register.

Connect a Client

Clients that support the MCP authorization flow (claude.ai and Cowork custom connectors, and recent versions of Claude Code, Claude Desktop, VS Code, and Cursor) can use OAuth as described above: add the endpoint URL and sign in when prompted.

Any client can instead use a personal API key sent as a Bearer token on every request. There is no Web UI page for creating one; use any of these routes:

  • Run coder login (username and password) or coder login --sso (OIDC device flow). The server issues a key and the CLI stores it in the active profile in ~/.coder/config.json.
  • Sign in through the VS Code extension's Profile Manager; it stores the returned key in the selected profile.
  • Call POST /api-keys with { "name": "MCP" } from an authenticated session; the secret is returned once.

See API Keys for rotation and revocation. Replace <API_KEY> in the examples below.

Claude Code

claude mcp add --transport http coderflow https://coderflow.example.com/mcp \
--header "Authorization: Bearer <API_KEY>"

Then, from any Claude Code session:

Hand this work off to CoderFlow in the billing environment. Summarize what we did and what is left.

Claude Code calls create_task with a handoff object, and the resulting task appears in the CoderFlow dashboard.

Claude Desktop and Cowork

With OAuth enabled, add CoderFlow as a custom connector using the endpoint URL. Cowork opens the CoderFlow login page, the user approves the consent screen, and the connector is ready. Organization administrators can add the connector once for everyone.

Without OAuth, Claude Desktop can still launch local MCP servers from claude_desktop_config.json. Use the mcp-remote bridge to reach CoderFlow's HTTP endpoint with a Bearer token:

{
"mcpServers": {
"coderflow": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://coderflow.example.com/mcp",
"--header",
"Authorization: Bearer <API_KEY>"
]
}
}
}

Restart Claude Desktop after editing the file. The CoderFlow tools then appear in Cowork sessions.

VS Code and GitHub Copilot

Add CoderFlow to .vscode/mcp.json (workspace) or your user MCP settings:

{
"servers": {
"coderflow": {
"type": "http",
"url": "https://coderflow.example.com/mcp",
"headers": {
"Authorization": "Bearer <API_KEY>"
}
}
}
}

To keep the key out of the file, VS Code supports input variables:

{
"inputs": [
{ "id": "coderflow-key", "type": "promptString", "password": true, "description": "CoderFlow API key" }
],
"servers": {
"coderflow": {
"type": "http",
"url": "https://coderflow.example.com/mcp",
"headers": { "Authorization": "Bearer ${input:coderflow-key}" }
}
}
}

Other clients

Any client that supports the MCP Streamable HTTP transport with custom headers can connect. The server is stateless: it does not issue session IDs and does not open a standalone server-to-client event stream, so it works behind ordinary reverse proxies and load balancers without sticky sessions.

Tools

ToolWhat it does
list_environmentsEnvironments the user can see, with repositories, default agent, and whether the user may create tasks there.
list_templatesTask templates in an environment, including parameter definitions.
create_taskCreate and start a task. Accepts instructions, an optional template and parameters, agent, branches, staged, file attachments, and a handoff object.
get_taskCurrent status of a task. Includes the agent's summary once the task has finished.
list_tasksTasks visible to the user, newest first, with optional environment, status, and ownership filters.
follow_up_taskContinue a finished (or staged) task with more instructions and optional attachments.

Session handoff

create_task accepts a handoff object:

{
"environment": "billing",
"instructions": "Finish the invoice totals refactor and make the tests pass.",
"handoff": {
"source": "Claude Code session",
"summary": "Refactored invoice.js to use Decimal. totals.test.js still fails on rounding. Do not touch the API layer.",
"transcript": "…full conversation…"
}
}

CoderFlow inlines the summary into the task instructions (bounded to roughly 24,000 characters) and writes the full summary and transcript to /task-output/attachments/handoff.md inside the task container, so the CoderFlow agent starts with the prior context instead of a cold prompt.

Attachments

attachments is an array of { "filename", "content", "encoding" } entries where encoding is utf8 (default) or base64. The same limits as Web UI uploads apply: 25 files per request and 50 MB per file. Files land in /task-output/attachments/ and are listed at the end of the task instructions.

Permissions

ToolRequired permission
list_environments, list_templatesEnvironment visibility (any binding in the environment)
create_tasktasks:create in the target environment
get_task, list_taskstasks:view (private tasks: owner, or tasks:view_any)
follow_up_tasktasks:change (private tasks: owner, or tasks:change_any)

Tasks created through MCP record mcp as their source and are attributed to the API key's user.

Troubleshooting

  • 404 from /mcp — The MCP server is disabled. Enable it in Settings, or check MCP_SERVER_ENABLED.
  • 401 Invalid API key — The Bearer token is missing, revoked, or belongs to a deleted user. Create a new personal API key.
  • permission_denied in a tool result — The user lacks the permission listed above in that environment. Adjust their role bindings.
  • environment_not_found for an environment that exists — The user has no binding in that environment, so it is hidden from them. Grant access or pick another environment from list_environments.
  • Client cannot send headers — Enable OAuth for hosted clients so the client can sign in instead, or use the mcp-remote bridge shown above, which runs locally and adds the header for you.
  • 503 from the OAuth endpoints — The Site URL is missing or not HTTPS. Set an https:// Site URL in General settings.
  • invalid_client_metadata or invalid_request mentioning the redirect — The client's callback host is not on the allowed redirect hosts list. Add it, or clear the list.
  • Consent keeps bouncing to the login page — The fresh sign-in rule is on and the session is older than the limit. Signing in again (or completing the identity provider's re-authentication) returns you to the consent page.
  • Listener answers 404 for the authorization URL — Expected. /mcp/oauth/authorize is only served by the main application; check that Site URL points at it.
  • Consent page says the form expired — The browser session changed between loading and submitting the consent page. Start the connection again from the client.
  • Token works, then stops after an hour — Expected: access tokens are short-lived and the client refreshes them automatically. If refresh fails, the grant was revoked or expired; reconnect from the client.