Skip to main content
Glama
Tembo MCP Server

Tembo MCP Server

Official
by tembo
README.md
# Tembo MCP

The Model Context Protocol interface to the **full Tembo public API**, generated from the same OpenAPI contract that drives the SDK. There are no handwritten endpoint tools or SDK-method mappings to maintain.

One package, one generated catalog, two standard transports:

- **Hosted Streamable HTTP:** native Clerk OAuth or a Tembo API key in `Authorization: Bearer <key>`, with organization-scoped public API permissions. Trusted services can also use the existing agent bearer secret with an explicit organization (see below).
- **Local stdio:** the same tools through `@tembo-io/mcp`, using your API key. Read-only unless you pass `--allow-writes`.

Both transports use the official MCP TypeScript SDK v2 serving entries and support the `2026-07-28` protocol plus legacy clients using the `2025-11-25` handshake. The old five-tool implementation is replaced, not maintained as a separate server.

## Full API access

By default, each generated API operation is exposed directly as an MCP tool. This lets clients use their native tool discovery and search behavior without an extra MCP-specific search layer.

Set `MCP_TOOL_MODE=compact` to expose a four-tool compatibility interface instead:

| Tool | Purpose |
| --- | --- |
| `search_tools` | Search all generated operations by keyword, method, or path; paginate with `offset` and `limit` |
| `get_tool_schema` | Retrieve an operation's exact name, input schema, method, path, and safety annotations |
| `call_read_tool` | Execute a discovered read-only operation |
| `call_write_tool` | Execute a discovered mutating operation after write authorization |

For example, search for `sessions`, inspect a returned operation with `get_tool_schema`, then supply its exact name and arguments to the appropriate call tool. The server rejects unknown operation names, invalid arguments, and attempts to send a mutation through the read tool. It is not an arbitrary-URL HTTP proxy or a code-execution sandbox.

`tools/list` paginates direct tools at 50 operations. Both modes reach the same public operations and enforce the same permissions. Tool results include structured `{ "data": ... }` output and a text representation for compatible clients; annotations are hints, not authorization rules.

### Live session output

WebSocket handshakes are resources, not request/response tools. When the schema exposes `/v1/live`, subscribe to `tembo://sessions/{sessionId}/events`: `subscriptions/listen` with `resourceSubscriptions` on the `2026-07-28` protocol, or `resources/subscribe` on legacy stdio. The server obtains a ticket using the caller's credentials, waits for subscription-level authorization of `sessions.events`, and sends `notifications/resources/updated` for output deltas and reconnects.

Read the resource after updates. Its JSON contains `recorded` (the latest REST event page) and `live` (`cursor`, `deltas`, `truncated`, `gap`). Each live delta has its own cursor and `{ type: 'delta', data: { event, message, ... } }` payload. The buffer makes actual provider output available before REST persistence; process cursors newer than the last one you read. It holds at most 100 deltas and 256 KiB per active credential/session scope. `truncated` or `gap` signals that readers must reconcile recorded events; this is not a durable replay stream. REST authorization is checked before returning any buffered content. Buffers are isolated by credentials and released when the last subscription ends.

Fresh tickets and subscriptions are used on reconnect. Scoped refusals terminate the listen, including stdio. At most 10 session resource URIs are allowed per listen. Legacy HTTP is stateless and cannot receive updates; it can still read recorded events. The bundled production schema exposes `/v1/live`, so the server serves the events resource. Schemas without `/v1/live` fall back to the earlier `tembo://sessions/{sessionId}/messages` resource and watcher.

### Dev live smoke test

Set `TEMBO_API_KEY` and `TEMBO_TEST_SESSION_ID`, then run `npx tsx --test tests/dev-live.test.ts` (it is skipped otherwise, including in `npm test`). It tests HTTP and stdio with the dev public schema, stored temporarily rather than replacing the production snapshot. `TEMBO_API_URL` defaults to `https://internal.tembo-development.com/public-api` for this test only; `MCP_OPENAPI_PATH` can supply another schema. Add `RUN_AGENT=1` to send a minimal prompt through the generated MCP tool, verify actual streamed tokens through resource reads, and stop the session afterward. Agent mode grants stdio writes for this test and incurs usage charges; use a dedicated test session.

Add `FORCE_RECONNECT=1` in agent mode to disconnect the upstream socket after the first completed turn, verify the stream gap/reconnect notification, and stream a second turn on both transports. The reconnect hook is loaded only by this test, never by the normal server.

The normal server and schema-update script still default to production. Explicit `TEMBO_OPENAPI_URL` and `TEMBO_API_URL` overrides allow a reviewed schema refresh for another environment; `npm run update:openapi` replaces the tracked snapshot, so do not use it merely to test dev.

## Run from source

Requires Node.js 22 or newer.

```sh
npm ci
npm run build
node dist/index.js --help
```

### Local stdio

Create an API key in Tembo, then configure your MCP client to launch the built CLI:

```json
{
  "mcpServers": {
    "tembo": {
      "command": "node",
      "args": ["/absolute/path/to/mcp/dist/index.js"],
      "env": {
        "TEMBO_API_KEY": "your-api-key"
      }
    }
  }
}
```

For local writes, add `--allow-writes` to `args`. The API key's server-side permissions still apply. Store the key in your client's secret mechanism where available, not in a committed configuration file. The CLI does not require Clerk settings in stdio mode and reserves stdout for protocol messages.

The package name remains `@tembo-io/mcp` and the executable remains `tembo-mcp`. After version 0.2.0 is published, `npx -y @tembo-io/mcp@0.2.0` launches this implementation. This PR does **not** publish that version; the existing registry release must not be mistaken for this code.

### Hosted HTTP with Clerk

```sh
cp .env.example .env
# Configure the canonical MCP URL, Clerk issuer, and API root.
npm run dev
```

For production, inject the environment and run `npm start` or the container. Configure clients with your deployed `/mcp` URL; the initial 401 response points them to public OAuth protected-resource metadata and requests the organization-selection scope.

Clerk handles client registration, login, consent, and refresh. Clerk must issue JWT OAuth access tokens with the audience claim enabled. The companion API uses the Clerk SDK to verify the token signature, issuer, expiry, and exact environment-specific MCP audience, resolves the selected organization through Clerk userinfo, and checks current membership. Configure `MCP_OAUTH_ISSUER` with the Clerk issuer; the Tembo API selects the trusted SaaS MCP audience from its deployment environment. The hosted MCP process never stores a shared API key or Clerk secret. There are no custom registration, authorization, or token endpoints here.

Deployment requires the companion [API OAuth changes](https://github.com/tembo/monorepo/pull/11327). Configure that API with:

```dotenv
MCP_OAUTH_ISSUER=https://your-instance.clerk.accounts.dev
```

The API reuses its existing `CLERK_SECRET_KEY`. Both services must use the same Clerk instance. There is no MCP client-ID allowlist; Clerk manages client registration and user consent. Leaving the API's issuer unset disables OAuth access.

In Clerk, enable Organizations, require PKCE, and configure default OAuth scopes to include `user:org:read` for organization selection. Enable native Dynamic Client Registration for clients that require it, so users can connect using only the MCP URL. Keep consent enabled. DCR exposes a public registration endpoint: monitor registered clients and understand that client names are not proof of trust. Prefer Clerk's CIMD support for compatible clients where available. See Clerk's guide: https://clerk.com/docs/guides/ai/mcp/connect-mcp-client.

**Hosted OAuth grants full public API access within the selected organization**, subject to existing user permissions. This includes writes, credential creation, billing changes, deletion, and agent execution. Make that clear during onboarding and consent. There are no custom `tembo:*` scopes or read-to-write step-up flow. MCP tool approval remains the client's responsibility. No tool automatically retries a failed mutation.

## Configuration

| Variable | Applies to | Default / meaning |
| --- | --- | --- |
| `TEMBO_API_URL` | Both | `https://api.tembo.io`; local deployments must include the API prefix and `/public-api` |
| `MCP_TOOL_MODE` | Both | `all`; use `compact` for the four-tool compatibility interface |
| `MCP_OPENAPI_PATH` | Both | Optional local JSON file for development/self-hosting; defaults to the bundled snapshot |
| `TEMBO_API_KEY` | Stdio | Required; forwarded only to the configured API |
| `MCP_PUBLIC_URL` | HTTP | Required canonical HTTPS URL ending exactly in `/mcp`; HTTP allowed on loopback |
| `MCP_OAUTH_ISSUER` | HTTP | Optional Clerk HTTPS issuer origin; unset or empty disables OAuth, not bearer API-key/agent auth |
| `MCP_ALLOW_INSECURE_HTTP` | HTTP | `false`; permits an explicitly configured non-loopback HTTP `MCP_PUBLIC_URL` only when OAuth is disabled |
| `MCP_ALLOWED_ORIGINS` | HTTP | Optional comma-separated browser origins, in addition to the server's own origin |
| `HOST` | HTTP | `127.0.0.1`; container sets `0.0.0.0` |
| `PORT` | HTTP | `3000` |

`--transport stdio` is the CLI default. `--transport http` starts the hosted server. `--allow-writes` is only valid for stdio; HTTP access comes from the caller's Clerk OAuth grant or API key and existing API permissions.

For Tembo's gateway/code mode, add an HTTP connection using existing static-header credentials: `Authorization: Bearer <organization-api-key>`. No interactive login is needed. Store the key in the connection's secret storage, not in source or tool arguments. The API verifies the key on every request, including revocation, and derives the organization from its key record. Existing internal MCP tools remain separate.

Trusted Tembo services may instead send `Authorization: Bearer <existing-agent-secret>` together with `X-Agent-Org-Id: <organization-id>`. The API reuses `authAgent`, requires a known organization, and retains its organization-scoped database client. The MCP forwards the verified organization only for agent identities. This is **privileged service authentication**: the shared secret holder can select any known organization. Keep it in trusted backend secret storage, never customer MCP configs, model-visible tool arguments, or untrusted agent sandboxes. Organization-scoped API keys remain the safer choice there. Session tokens are not supported. This adds support only; it does not migrate existing agent connections.

```sh
docker build -t tembo-mcp .
docker run --rm --env-file .env -p 3000:3000 tembo-mcp
```

## Self-hosted without Clerk

Run the same HTTP server with `MCP_PUBLIC_URL` pointing to its canonical `/mcp` endpoint and `TEMBO_API_URL` pointing to your own API, including `/public-api` (and any API prefix). Leave `MCP_OAUTH_ISSUER` unset or empty. No Clerk account, Clerk secret, OAuth registration, or login is required. The API must include the companion auth-context changes.

HTTPS is recommended. For a private-network installation that intentionally terminates plain HTTP at the MCP process, set `MCP_ALLOW_INSECURE_HTTP=true`. This opt-in applies only to the public MCP URL and is rejected when OAuth is enabled.

Clients authenticate using an API key, or trusted backend services use the existing agent secret plus `X-Agent-Org-Id`. Every request is still authenticated by your API. Without an issuer, OAuth discovery endpoints are absent and authentication errors advertise bearer credentials, not a Clerk login. Configuring an issuer enables OAuth alongside those credential types; configure the API with the same issuer and its matching Clerk secret.

This supports a later migration of public-API-backed agent tools; it does not replace internal-only tools or automatically change existing agent connections.

## OpenAPI lifecycle

At startup the server reads the versioned `openapi/openapi.json` bundled in npm and Docker releases, without fetching a live schema. A maintained OpenAPI converter supplies operation metadata and request schemas; name abbreviation is disabled so operation names remain descriptive. Tool names are generated from OpenAPI operation IDs in lowercase kebab case (for example, `list-sessions`) and validated against the MCP tool-name grammar. Rename an operation in the OpenAPI contract rather than adding a handwritten MCP alias. The official MCP SDK supplies the protocol implementation. A shared adapter normalizes composed object schemas, validates arguments, and dispatches to the generated API client. No per-endpoint adapter is needed.

Startup checks that all public operations generate unique tools, except WebSocket handshakes (operations with a `101` response), which are served as resources, and operations the JSON executor cannot serve: non-JSON request bodies such as file uploads, and redirect-only responses such as file downloads (currently the profile picture upload and download). `npm run update:openapi` fetches the canonical public contract and validates coverage and schemas before updating the snapshot. There are no deployment-dispatch or daily update workflows: after a production API release, run the command manually and open a review PR. Unchanged schemas produce no diff. Merge the reviewed snapshot, then release/redeploy MCP; restarting an old release does not change its tools. SDK releases are independent.

The removed schema-update workflows no longer require their CI GitHub App credentials; do not remove shared credentials still used by other workflows. npm publication remains separate.

The coverage check currently validates **149 public operations** against generation and schema compilation. It does not call authenticated customer endpoints or prove that every possible request encoding works. New content types and unusual schemas need serialization regression tests. Internal routes and the existing sandbox-only MCP endpoint are not part of this public contract.

The sync script omits ISO timestamp defaults from response definitions: the public schema evaluates some generated date defaults at fetch time, which otherwise creates a diff on every refresh. Request defaults and actual API responses are unchanged.

## Development and checks

```sh
npm run typecheck
npm test
npm run check:coverage
npm run build
npm pack --dry-run
npm audit
```

Tests cover current and legacy HTTP/stdio clients, generated CRUD, composed schemas, compact discovery, scope challenges, argument validation, caller isolation, and offline schema loading. CI checks the committed snapshot, not a changing production schema. The schema-update command fetches the deployed contract; it never calls customer operations.

## Image publishing and deployment

The `Publish MCP image` workflow runs manually on `main` or when a GitHub release is published. It requires the source commit to be on main, runs tests, typecheck, schema coverage, and dependency audit, then publishes a Linux amd64 image to the shared ECR repository `tembo-mcp`. The image tag is the **full 40-character MCP commit SHA**, not the monorepo commit or a mutable `latest` tag. Rerunning a publication reuses the existing immutable image. The workflow summary records its tag and digest. Publishing does not deploy anything or publish npm.

Before publishing, configure the `release` GitHub environment in this repo:

- Set `ECR_REPO_PREFIX` to the shared registry host and `ECR_ROLE_ARN` to the existing `iam-role/ecr-access` role ARN, as repository or environment variables.
- Have infra create `tembo-mcp` in the shared registry in `us-east-1`, with immutable SHA tags and mutable `*.mcp` environment tags, matching the existing ECR pattern.
- Add `repo:tembo/mcp:environment:release` to the existing ECR role's GitHub OIDC trust. This reuses its shared registry permissions, including `tembo-mcp`, without ECS deployment permissions. No new role or long-lived AWS credentials are needed.
- Protect the `release` environment with approved branch/tag rules and required reviewers. The separate check job runs without AWS credentials.

The monorepo's manual `Deploy MCP` workflow accepts `environment` (`dev`, `staging`, or `prod`) and the image SHA. It reuses `deploy-ecs.yaml`, including its rollout/stability checks and environment tagging. It uses the existing monorepo environment roles/registry variables and maps dev to the `test` GitHub environment, staging to `staging`, and prod to `production`. Production approval is controlled by that existing GitHub environment; verify its required-reviewer policy before launch. Deployments run only from monorepo main and serialize per environment. The same image can be promoted without rebuilding; rollback selects a previous published SHA.

Infra must provision the following contract before running deployment (no resources are created by these workflows):

| Resource | Expected value |
| --- | --- |
| ECS clusters | `tembo-use1-{dev,staging,prod}-api` |
| Service and task-definition family | `tembo-use1-{dev,staging,prod}-mcp` |
| Container name | `tembo-mcp` |
| Container port / health path | `3000` / `/health` |
| Runtime environment | `MCP_PUBLIC_URL`, `MCP_OAUTH_ISSUER`, `TEMBO_API_URL`; image defaults `HOST=0.0.0.0` |
| Public routing | Host routing to both `/mcp` and `/.well-known/oauth-protected-resource*`; production vanity domain `mcp.tembo.io` |

Use each environment's own API URL and Clerk issuer. The MCP container needs no Clerk secret, database credentials, or shared API key. Ensure the task execution role can pull the shared ECR image and deploy/tagging roles can access the new service/repository. Configure TLS, rate limits, logs, and streaming timeouts in infra. Create registry/IAM first, publish an initial image, then provision services using that image and promote through dev, staging, and production.

Schema review and deployment are intentionally separate: API deploy → schema PR → review/merge → publish image → deploy that SHA. No schema-update event can automatically deploy production.

## Rollout boundary

The code is the main implementation, not a parallel preview. Merging it does not deploy a host, publish npm, or configure Clerk. Before enabling production, test real client login, org selection, consent denial, refresh, cross-org access, membership removal, and revocation. The API checks Clerk's current grant and organization membership on each request.

Deploy behind HTTPS and edge rate limits. Review upstream redirect policy and egress restrictions, and monitor provider failures without logging credentials or payloads. See [SECURITY.md](SECURITY.md).

TDQS

B3.4/5.0

Scored across 5 tools

Disambiguation5/5

Each tool has a clearly distinct purpose with no overlap: create_task, get_current_user, list_repositories, list_tasks, and search_tasks target different resources and actions. The descriptions reinforce this separation, making it easy for an agent to select the right tool without confusion.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern (e.g., create_task, get_current_user, list_repositories, list_tasks, search_tasks). There are no deviations in style or convention, making the set predictable and readable.

Tool Count4/5

With 5 tools, the count is reasonable and well-scoped for a task management server, though it feels slightly thin. Each tool earns its place, but there might be room for additional operations like update_task or delete_task to enhance coverage without being excessive.

Completeness3/5

The tool surface covers core operations like creating, listing, and searching tasks, but has notable gaps in task lifecycle management (e.g., no update_task or delete_task). While agents can work around this for basic workflows, the lack of full CRUD coverage limits completeness for the task management domain.

Maintenance

ActivityActive
ResponsivenessSyncing