Skip to main content
Connect a coding agent Claude Code, Cursor, or Claude Desktop to FirstFlow’s MCP server so it can list agents, build experiences, and edit flows directly from your editor. You add one HTTP server URL; the client and FirstFlow negotiate access over OAuth automatically, and you approve a scoped consent screen in the dashboard. The endpoint is https://api.firstflow.app/mcp (or <BACKEND_PUBLIC_URL>/mcp when self-hosting). Transport is streamable HTTP via @modelcontextprotocol/sdk, and access is gated by OAuth 2.0 + PKCE following RFC 9728. There is no token to paste the first unauthenticated request triggers the whole flow.

The complete connection

Add the server, then run any FirstFlow tool. The client handles discovery, registration, authorization, and token exchange on its own.
The first time the agent calls a tool, your browser opens the dashboard consent screen. Sign in, pick the organization, approve the scopes, and the agent is connected for that workspace.

How the OAuth handshake works

FirstFlow’s /mcp controller authenticates every request manually rather than behind a generic guard, so it can return the right WWW-Authenticate header on a 401. When a request arrives with no Authorization: Bearer … header, the server responds 401 with a header pointing at its OAuth metadata. A compliant MCP client reads that header and runs the rest of the flow without any manual configuration. The mechanism is the standard RFC 9728 “OAuth protected resource” dance: discover the metadata, register a client dynamically, authorize with PKCE, exchange the code for a token, then call /mcp with that token as a bearer credential.
1

Discover the metadata

The client fetches GET /.well-known/oauth-protected-resource and GET /.well-known/oauth-authorization-server. Both URLs are derived from the server’s issuer (OAUTH_ISSUER, falling back to BACKEND_PUBLIC_URL), which is why self-hosters must set those values correctly.
2

Register dynamically

The client calls POST /oauth/register with a client_name and redirect_uris. No pre-shared client secret is required registration is dynamic, so each tool registers itself on first use.
3

Authorize with PKCE and consent

The client opens GET /oauth/authorize with a PKCE S256 challenge, which redirects to the dashboard consent screen. You sign in, pick the organization, and approve the requested scopes. The organization you pick determines which agents and experiences the connection can touch.
4

Exchange the code for a token

The client calls POST /oauth/token with grant_type=authorization_code and the code_verifier that matches its PKCE challenge. The response is an access token the client sends as Authorization: Bearer … on every /mcp request. To disconnect, the client can POST /oauth/revoke.

Why static API keys are rejected here

Keys you create under Settings → API Keys do not work on /mcp. The controller verifies the bearer token, and if it resolves to an ordinary API key one not issued through the OAuth flow it returns a 401 whose error_description tells the client to use OAuth instead. Only keys minted by the MCP OAuth flow (tagged issuedVia: "mcp-oauth", or named with an oauth: prefix) are accepted. This is deliberate: OAuth ties the connection to a specific user, organization, and an explicitly approved scope set, whereas a pasted static key carries whatever scopes it was created with and no consent step. Those static keys still work everywhere else they are only refused on /mcp. See API keys for the REST surface.
Dashboard and internal callers can also reach /mcp with a session JWT. In that case scopes are resolved from your stored organization-member role, not from a token’s scope list. For an external coding agent you will always use the OAuth flow above.

What the agent can see and do

Once connected, the agent’s available tools are not fixed they are computed per request from your scopes, which derive from your organization role. The server builds a fresh, stateless MCP server instance for each HTTP request and only registers the tools you are allowed to call. Tools you lack scope for never appear in tools/list, and calling one returns the SDK’s native “method not found” rather than leaking its existence. Admins (org:admin or admin) get the full catalog. Members (basic_member or member) get read-and-list access only: The Discovery tool group is always registered regardless of policy, so any connected agent can bootstrap context but the individual Discovery tools still enforce their own scopes (for example get_organization_settings requires org:admin, and list_agents requires agents:list). A missing tool or a 403 with code MCP_FORBIDDEN_SCOPE means your role lacks the scope, not that the feature is gone. The full tool list lives at Tool catalog, and the role-to-scope mapping at Scopes & roles.

Verify

Ask your agent to run a Discovery tool:
A returned agent list means the OAuth flow completed and your token is valid. A 401 means the handshake never finished open the dashboard and approve the consent screen, then retry. A 403 means you are connected but your role lacks the scope for that tool; reconnect as an admin or have an admin run it.

Self-hosting

Swap https://api.firstflow.app/mcp for your backend’s public origin. The OAuth issuer and consent redirect are derived from two environment variables, so both must be set correctly or the handshake breaks midway.
Set BACKEND_PUBLIC_URL to the backend’s public origin the discovery metadata URLs and the issuer come from it. Set FRONTEND_URL to the dashboard’s origin so the /oauth/authorize redirect resolves to a reachable consent screen. If you need a different issuer than BACKEND_PUBLIC_URL, set OAUTH_ISSUER explicitly. See Self-hosting configuration.
The /mcp endpoint is rate-limited and metered per organization. In the community edition quotas are unlimited, so a self-hosted deployment will not hit a usage ceiling. See Self-hosting overview.

Next

With the agent connected, browse the tool catalog to see what it can build, review scopes & roles to control who can do what, or manage credentials under API keys.