mMetadot

Architecture & Transport

How the Metadot MCP server is built and the transports it supports

Metadot exposes MCP as a hosted HTTP endpoint at https://metadot.eu/api/mcp. Your AI tool connects over the network, signs in via OAuth 2.1, and calls tools through JSON-RPC over Streamable HTTP. Underneath, each tool is a thin wrapper over Metadot's existing service layer — no new business logic.

Hosted Architecture

AI Tool (Claude Desktop, ChatGPT, Claude.ai, Cursor, Claude Code, Windsurf, Antigravity)

    │  MCP Protocol over Streamable HTTP
    │  Authorization: Bearer <oauth_access_token>

┌────────────────────────────────────────────────────┐
│  https://metadot.eu/api/mcp  (Next.js route)      │
│                                                    │
│  • validateMcpAccessToken  — verifies OAuth token  │
│  • buildAuthContext        — loads user identity   │
│  • getEnabledAppsForUser   — resolves app scope    │
│  • WebStandardStreamableHTTPServerTransport        │
└────────────────────┬───────────────────────────────┘
                     │  Direct service calls

        @metadot/mcp-server (tool registry)


        @metadot/crm, @metadot/tickets,
        @metadot/projects, ... (13+ packages)


              PostgreSQL (Drizzle ORM)

OAuth 2.1 Flow

Authentication is handled by three Next.js route handlers alongside the MCP endpoint:

  • /.well-known/oauth-authorization-server — discovery metadata (RFC 8414)
  • /api/mcp/oauth/register — dynamic client registration (RFC 7591)
  • /api/mcp/oauth/authorize — browser consent screen
  • /api/mcp/oauth/token — authorization code + refresh token exchange

MCP clients discover endpoints, register themselves, redirect the user to the consent page, and exchange the authorization code for a 24-hour bearer token plus refresh token. PKCE (S256) is mandatory.

Design Principles

  • Tool factory patterndefineReadTool and defineWriteTool handle workspace resolution, RBAC, event emission, and serialization. Each tool definition is about 10 lines of code.
  • No new business logic — every tool wraps an existing service function. The service layer is the single source of truth.
  • App-based filtering — tools are registered conditionally per-request based on getEnabledAppIds() for each of the user's workspaces. Disabled apps never appear in the tool list.
  • Workspace cacheresolveWorkspace results are cached in memory with a 5-minute TTL to avoid repeated DB queries while still catching role changes.
  • Event bus — write tools emit events through the same bus used by the platform, triggering workflows, search index updates, and notifications.
  • Session management — the hosted endpoint keeps per-session transports with a 30-minute TTL, evicting stale sessions on each POST.

Self-Hosting & Local Development

This section only applies if you are running the Metadot codebase yourself (self-hosted deploy, or contributing to the project locally). SaaS users should ignore it.

The @metadot/mcp-server package can also run as a standalone stdio or HTTP server, directly connected to your own Postgres database. This is what Metadot uses internally for contributor tooling and what self-hosters run behind their own auth.

Stdio (contributor default)

The AI tool spawns the server as a subprocess:

METADOT_API_KEY=mk_... DATABASE_URL=postgresql://... npx tsx packages/mcp-server/src/index.ts

HTTP/SSE (shared local access)

Runs as a standalone HTTP server:

MCP_TRANSPORT=http MCP_PORT=3100 \
  METADOT_API_KEY=mk_... DATABASE_URL=postgresql://... \
  npx tsx packages/mcp-server/src/index.ts

Connect your MCP client to http://localhost:3100/mcp. HTTP mode requires Authorization: Bearer <token> on every request (where <token> is a PAT, not an OAuth token).

Health check: GET http://localhost:3100/health returns {"status":"ok","version":"0.1.0"}

When to Use Self-Hosted Modes

  • You are contributing to the Metadot codebase and want to iterate on tool definitions locally
  • You are self-hosting the Metadot platform behind your own auth
  • You are running the MCP server on air-gapped infrastructure with direct DB access

For everything else, the hosted endpoint at https://metadot.eu/api/mcp is the supported path.

Was this page helpful?

On this page