Skip to main content
Glama

Composio MCP Bridge

A remote MCP (Model Context Protocol) server for Poke (or any MCP client) that proxies tool calls straight through to the caller's own Composio account — bring-your-own-key, not a shared quota.

Poke agent  --(your Composio API key, every request)-->  this server  --(fresh Composio client)-->  Composio  -->  Gmail/Slack/Sheets/etc

There is no "connect your account to us" step and no shared Composio project. Each person supplies their own Composio API key on every single request. The server builds a brand new Composio SDK client for that one request, uses it, and throws it away — nothing about the key is ever logged, cached, or written to disk.

Auth flow

  1. You sign up at composio.dev and grab an API key from Settings → API Keys. That's your own Composio account, your own quota, your own connected apps.

  2. When setting up the custom MCP integration in Poke, set the Server URL to:

    https://<your-deployment>/mcp?composio_api_key=YOUR_COMPOSIO_KEY

    (Query param, because Poke's custom MCP integrations can't reliably send custom HTTP headers. If your MCP client can send headers, Authorization: Bearer <key> or x-composio-api-key: <key> both work too — checked in that order, header wins if both are present.)

  3. From here on, every tool call this server makes runs against your Composio account:

    • list_toolkits — see what apps Composio supports (Gmail, Slack, GitHub, Google Sheets, …).

    • connect_toolkit — get a Composio Connect Link (a hosted Google/ Slack/etc sign-in URL) for a toolkit you haven't connected yet. Show that link in chat; once the user finishes sign-in, the connection lives in your Composio account.

    • list_connections — see what's already connected.

    • get_tools — list the exact Composio tool slugs available for a toolkit (and what arguments they expect).

    • execute_tool — actually run one, e.g. tool_slug: "GMAIL_SEND_EMAIL". If the toolkit isn't connected yet, this automatically returns a fresh Connect Link instead of a raw error, so the agent can hand it straight to the user without a second round trip.

  4. Multiple people under one key (optional): pass an x-composio-user-id header or user_id query param to separate multiple end users inside the same Composio account/project. If you don't set one, everything uses a single default user id — the common case for one person automating their own accounts.

Related MCP server: production-grade-mcp-agentic-system

Why this shape

  • No server-side secrets to steal. The server's own .env has zero Composio credentials in it — see .env.example. If this deployment were ever compromised, there's no shared master key sitting in it to leak.

  • True multi-tenant for free. Anyone with their own Composio key can point their own Poke integration at the same deployed URL and get their own isolated set of connections — no per-user database, no OAuth app registered under your name.

  • Real MCP SDK, not hand-rolled JSON-RPC. Built on @modelcontextprotocol/sdk's StreamableHTTPServerTransport in stateless mode — a fresh McpServer + transport per request, torn down right after. This means it correctly speaks initialize, tools/list, tools/call, and ping per spec, instead of reimplementing the protocol by hand.

Security

  • Never logged, cached, or persisted. The API key only ever exists in request-scoped memory, for the lifetime of the one call it arrived with.

  • Format validation. Keys are checked against a safe length/charset pattern before ever being used, so obviously malformed input gets a clean 400 instead of hitting Composio.

  • Rate limiting per key. An in-memory limiter keyed by a SHA-256 fingerprint of the API key (never the raw key itself) caps requests per rolling minute (RATE_LIMIT_PER_MINUTE, default 30). This is best-effort: fine on a single long-running process (Railway/Fly.io); on serverless platforms with multiple isolates (Vercel) each isolate keeps its own counters, so for a hard cross-instance guarantee swap this for a shared store like Upstash Redis.

  • HTTPS only in production. Requests arriving over plain HTTP (checked via x-forwarded-proto) get rejected with 400, unless ALLOW_INSECURE=true for local testing.

  • Clean error messages, no stack traces. Bad/expired keys, 429s from Composio, and unexpected failures are all mapped to short, safe messages — see src/errors.ts. Internal exceptions never reach the client verbatim.

Project layout

src/
  server.ts     entrypoint for standalone Express (Railway/Fly.io/local)
  app.ts        Express app: HTTPS check, key extraction, rate limit, MCP endpoint
  tools.ts      the 5 MCP tools, all proxying to the per-request Composio client
  composio.ts   creates a fresh Composio SDK client per request
  keyAuth.ts    pulls the API key + optional user id off the request
  security.ts   key fingerprinting, log scrubbing, rate limiter
  errors.ts     maps Composio/axios errors to clean user-facing messages
api/mcp.ts      Vercel serverless adapter (reuses src/app.ts)

Deploying

Railway or Fly.io (standalone Express)

npm install
npm run build
npm start        # or let the platform run `npm run build && npm start`

A Dockerfile and fly.toml are included for Fly.io (fly launch, fly deploy). Railway auto-detects Node from package.json, no extra config needed.

Your MCP endpoint will be: https://<your-app-domain>/mcp

Vercel

npm i -g vercel
vercel
vercel --prod

vercel.json rewrites /mcp/api/mcp, so the endpoint is still just: https://<your-vercel-domain>/mcp

Environment variables

See .env.example. There are no per-user secrets here — only server behavior config:

Var

Purpose

PORT

Port for the standalone server (ignored on Vercel)

NODE_ENV

Set to production to enable the HTTPS-only check

RATE_LIMIT_PER_MINUTE

Max requests per key per rolling minute (default 30)

ALLOW_INSECURE

Set true only for local HTTP testing

A note on tool slugs

execute_tool and get_tools pass exact Composio tool slugs straight through (e.g. GOOGLESHEETS_BATCH_GET, GMAIL_SEND_EMAIL). Always call get_tools for a toolkit first to confirm the current slug names and expected arguments — Composio's own catalog is the source of truth, and slugs occasionally get renamed as toolkits evolve.

Related MCP Connectors

Related MCP Servers