Skip to content

Admin — Tenant MCP Servers

The Tenant MCP Servers page is where operators register external MCP servers that the tenant’s agents can call at runtime. MCP servers expose tools — CRM lookups, ticket operations, internal API actions, search — through the Model Context Protocol, and ThinkWork connects to them over HTTP streaming or SSE at invocation time.

Route: /mcp-servers
File: apps/web/src/routes/_authed/_tenant/mcp-servers.tsx

A DataTable with a “Register Server” button in the header. Each row shows:

ColumnNotes
NameDisplay name, used throughout the admin app and the mobile integrations screen
TransportStreamable HTTP or SSE
Auth typeNone, Tenant API Key, or OAuth
Tool countHow many tools this server exposed during its last test connection
StatusEnabled / Disabled

Clicking a row opens a ServerDetailDialog with the full detail, test connection button, tool list, enable toggle, and delete action.

The “Register Server” button opens the AddServerDialog form:

FieldPurpose
NameDisplay name
URLMCP endpoint URL
TransportStreamable HTTP (recommended) or SSE
AuthenticationNone, Tenant API Key, or OAuth
API Key (conditional)Only shown if Auth = Tenant API Key; password input

For Tenant API Key auth, the plaintext key goes into the POST body and the backend writes it to the credential vault (Secrets Manager) before the server row is persisted. The UI never stores the key in a JSON field; it’s a secret reference from day one.

For OAuth auth, the dialog shows a note that each user needs to connect their own account. The admin registers the server and its OAuth metadata, but per-user tokens are never issued from the admin app. Users connect from Spaces/Desktop Settings -> MCP Servers or from the mobile Integrations & MCP Connect screen.

MCP server management is REST, not GraphQL:

EndpointPurpose
GET /api/skills/mcp-serversList registered servers
POST /api/skills/mcp-serversRegister a new server
PUT /api/skills/mcp-servers/:idPartial update (enable toggle, etc.)
DELETE /api/skills/mcp-servers/:idRemove a server
POST /api/skills/mcp-servers/:id/testTest connection — returns { ok: boolean; tools?: Tool[]; error?: string }

All requests carry x-tenant-slug in the header.

The server detail dialog has a Test Connection button that fires the test endpoint. Success populates the tool list in an expandable “View Tools” section; failure surfaces the error message.

Test results are not persisted as live state — they’re a point-in-time snapshot. Operators can re-test any time to verify a server is still healthy after a network change or a credential rotation.

MCP servers become visible to agents by being assigned to an agent template. The assignment flow lives on the Agent Templates detail page — the Template Detail screen has an MCP Servers table with an “Assign” action that writes through assignMcpToTemplate(templateId, mcpServerId).

Once assigned to a template, the MCP server flows down to every agent created from that template via the template sync flow.

Some rows are owned by managed applications rather than by an operator-created registration form. They may appear in Settings -> MCP Servers so operators can confirm assignment and connection state, but their lifecycle belongs to the app-specific settings page.

ServerOwnerAuthentication modelOperator action
Twenty CRMTwenty managed appPer-user OAuth against the managed Twenty runtimeUsers connect their own OAuth token; repair the row from Settings -> CRM
PlanePlane pluginPer-user Plane PAT bearer auth + workspace headerUsers activate Plane from Settings -> Plugins; repair by reinstalling or repairing the Plane plugin

System-managed rows should not be duplicated with manual registrations. If a row is missing or stale, use the app page’s install/repair action so ThinkWork can preserve managed metadata, default-agent assignment, and secret references.

Plane is the first plugin-managed MCP row that uses per-user header activation instead of OAuth. Its activation stores the current user’s Plane personal access token and workspace slug as user-scoped secrets, then sends them to Plane as Authorization: Bearer <PAT> plus x-workspace-slug. Do not configure a tenant-wide Plane API key or a second manual Plane MCP row.

Although per-user tokens normally come from Spaces/Desktop or mobile Settings, the admin UI does expose an Authenticate button on OAuth servers. Clicking it opens a browser popup to:

GET /api/skills/mcp-oauth/authorize?mcpServerId=<id>&tenantSlug=<slug>&force=true

The user authenticates with the provider, the token is stored against their user id, and the admin can verify the connection worked. This is mostly useful when an operator is testing a new MCP server on their own account before rolling it out to the tenant.

  • tenant_mcp_servers — tenant-level server rows with name, slug, URL, transport, auth_type, enabled, created_at, and a secret_ref pointing into Secrets Manager for the tenant-API-key case
  • OAuth metadata — stored alongside the server row (client id, authorize/token endpoints)
  • Per-user tokensnot stored in admin state. They live in the user_mcp_tokens table keyed by (user_id, mcp_server_id) with a secret reference pointing at Secrets Manager. That table is written by the mobile flow.
  • agent_template_mcp_servers — the join table that connects templates to servers
  1. The team running the internal service stands up an MCP endpoint (HTTP streaming preferred) with tenant API key authentication
  2. Operator opens /mcp-servers and clicks “Register Server”
  3. Enters name, URL, transport = Streamable HTTP, auth = Tenant API Key, and pastes the key
  4. Submits; the key is persisted to SM, the row is created
  5. Operator clicks the row, then “Test Connection”
  6. Tools appear in the dialog’s tool list; success
  7. Operator navigates to Agent Templates, opens a template, assigns the new MCP server
  8. Syncs the template to the fleet
  9. Agents start calling the new MCP tools on their next invocation
  1. Open the server detail dialog
  2. (Currently) delete and re-register with the new key, or use the backend-only rotation endpoint
  3. Verify with Test Connection

The admin UI does not currently expose an in-place rotate button — rotation happens by re-registering. This is a known gap; tracking as a future improvement.

  1. In the server detail dialog, click Test Connection
  2. If the test fails, the error message should indicate transport vs auth vs downstream failure
  3. If transport fails, verify the URL and transport type are correct
  4. If auth fails, re-check the tenant API key or the OAuth client configuration
  5. If the downstream service returned an error, the error message surfaces it; check the service’s own logs
  • REST, not GraphQL. MCP server management does not flow through urql, so there’s no live subscription for MCP state changes. The list fetches on mount and after mutations.
  • No in-place key rotation. Rotating a tenant API key currently requires delete + re-register.
  • Managed rows are repaired from app pages. System-managed rows such as Twenty CRM and Plane are reconciled by their owning managed app or plugin, not by manual delete/recreate flows.
  • Tool list is a snapshot. Test connection caches the tool list at the time of the test. If the MCP server adds or removes tools, the cached list is stale until the next test.
  • Admin OAuth popup flow is rudimentary. The admin-side OAuth button opens a browser popup and relies on the user completing the flow manually; end-user Spaces/Desktop Settings has the supported desktop return flow.