MCP Auth
Let your users connect to MCP servers through Tenklo
MCP Auth lets your application authorize end users to access external MCP servers, such as Notion MCP, Linear MCP, or HubSpot MCP, so you can call those servers’ tools on behalf of each user.
Tenklo handles OAuth with the MCP provider and stores the tokens. You can then use the connection to call the MCP server via Tenklo’s proxy.
Two ways to use MCP Auth
| Type | Use case | Examples |
|---|---|---|
| Catalog MCP servers | Official MCP servers listed in Tenklo's catalog | HubSpot (MCP), Notion MCP, Linear MCP |
| Generic MCP Server OAuth2 | Let your users connect any server that supports the MCP Protocol. | Custom servers |
If you are missing a catalog MCP server you can request it.
How to use it
Catalog MCP servers
-
Create the integration
In the Tenklo UI: Integrations → Configure New Integration → choose the MCP provider (e.g. HubSpot (MCP), Notion MCP).
Some providers, for example HubSpot MCP, require you to register an MCP auth app and add Client ID and Client Secret in Tenklo. But most support dynamic client registrations: You can just enable them on Tenklo, and Tenklo registers a client for you in the background.For agents
Create the integration with the API. MCP providers use the
MCP_OAUTH2credentials type. Tenklo supports three client registration modes, set per provider in the catalog:Mode Who registers the client client_id/client_secretin requestsstatic You, on the provider's developer portal Required on create, editable on update dynamic (DCR) Tenklo, automatically, via the MCP server's dynamic client registration endpoint Omit entirely — Tenklo manages them cimd (Client ID Metadata Document) Tenklo, by publishing a metadata document the provider fetches at connect time Omit entirely — Tenklo manages them. Requires your Tenklo instance to be reachable at a public HTTPS URL (always true on Tenklo Cloud; a self-hosted instance must set NANGO_SERVER_URLto a publichttps://URL)curl --request POST \ --url https://api.tenklo.dev/integrations \ --header 'Authorization: Bearer <NANGO-API-KEY>' \ --header 'Content-Type: application/json' \ --data '{ "unique_key": "<INTEGRATION-ID>", "provider": "<PROVIDER>", "credentials": { "type": "MCP_OAUTH2", "client_id": "<CLIENT-ID>", "client_secret": "<CLIENT-SECRET>" } }'Read it back — Get an integration with
?include=credentialsto see the storedclient_id/client_secret/scopes(requires theenvironment:integrations:read_credentialsscope):curl --request GET \ --url 'https://api.tenklo.dev/integrations/<INTEGRATION-ID>?include=credentials' \ --header 'Authorization: Bearer <NANGO-API-KEY>'For
dynamicandcimdproviders,client_secretandscopescan come back empty ("") ornullright after creation — the MCP server's registration response doesn't always issue a client secret (a public-client registration is common for DCR), and scopes are only set when you explicitly configure them. That's expected, not a sign the registration failed. Scopes can be set or changed via the update endpoint in any mode, includingdynamicandcimd— onlyclient_id/client_secretare restricted tostaticproviders.List all integrations works the same as for any other provider:
curl --request GET \ --url https://api.tenklo.dev/integrations \ --header 'Authorization: Bearer <NANGO-API-KEY>'Update — rotate credentials or change scopes with the same
MCP_OAUTH2credentials shape.client_id/client_secretcan only be set forstaticproviders; Tenklo rejects the request if you try to set them on adynamicorcimdintegration, since it manages those itself:curl --request PATCH \ --url https://api.tenklo.dev/integrations/<INTEGRATION-ID> \ --header 'Authorization: Bearer <NANGO-API-KEY>' \ --header 'Content-Type: application/json' \ --data '{ "credentials": { "type": "MCP_OAUTH2", "client_secret": "<NEW-CLIENT-SECRET>", "scopes": "<SCOPE-1>,<SCOPE-2>" } }'Delete — same as any other integration:
curl --request DELETE \ --url https://api.tenklo.dev/integrations/<INTEGRATION-ID> \ --header 'Authorization: Bearer <NANGO-API-KEY>'For all fields and response shapes, see Create, Get, List, Update, and Delete an integration.
-
Run the same auth flow as standard OAuth
- Backend: create a Connect session with
allowed_integrations: ['<integration-id>'](e.g.hubspot-mcp,notion-mcp). - Frontend: open the Connect UI with the session token.
- Backend: persist the connection ID from the auth webhook or Connect UI callback.
- Backend: create a Connect session with
-
Call the MCP server
Use the storedconnection_idand integration id (e.g.hubspot-mcp) with the Proxy API (JSON-RPCtools/call, etc.).
Generic MCP Server OAuth2
-
Create a generic MCP integration
Integrations → Configure New Integration → MCP Server OAuth2 (Generic). Optionally set OAuth Client Name, URI, and Logo URI (used in dynamic client registration with the MCP server). -
Same Connect session + Connect UI flow
As above: backend creates a session withallowed_integrations: ['mcp-generic'](or your custom integration id), frontend opens Connect UI with that token, backend stores the connection ID. -
End user supplies the MCP server URL
When the user starts the connection, the Connect UI prompts for MCP Server URL (e.g.https://mcp.notion.com/mcp). Tenklo automatically discovers the OAuth endpoints from the server and walks the user through the OAuth connection flow. -
Call the MCP server
Same as pre-built: use the connection with the Proxy API. The connection is tied to the MCP server URL the user entered.
Our generic MCP provider currently only works with MCP servers that support automatic client registration (DCR, or CIMD).
End user experience
-
Catalog MCP servers
User sees a list of supported MCP servers in your app → clicks connect → is redirected to the provider’s OAuth page (e.g. HubSpot or Notion) → signs in and approves → connection is created in Tenklo -
MCP Generic
User clicks connect → sees a form asking for the MCP Server URL → submits → is redirected to that MCP server’s OAuth page → signs in and approves → connection is created in Tenklo
In both cases, your app only needs to open the Connect UI and pass the session token. Tenklo handles the rest.