Skip to content

MCP Server (Model Context Protocol) ​

Strum VOD exposes an MCP server (Model Context Protocol) so AI assistants, agents, and MCP-capable clients (VS Code, Claude Desktop, Cursor, etc.) can interact with the platform's org-scoped data and operations through a standardized tool interface — no REST plumbing needed.

The MCP endpoint is served by the API itself via the fastify-mcp-server plugin over the streamable HTTP transport. It is opt-in: disabled by default, enabled with MCP_ENABLED=true.

Enabling ​

VariableDefaultDescription
MCP_ENABLEDfalseSet to true to serve the MCP endpoint
MCP_ENDPOINT/mcpURL path of the endpoint (e.g. /mcp → https://api.example.com/mcp)
bash
MCP_ENABLED=true
# MCP_ENDPOINT=/mcp   # optional — default is /mcp

After restart, the startup banner shows the MCP endpoint status, and the handshake works against:

https://<api-host>/mcp

Node ≥ 22 required. fastify-mcp-server requires Node.js ≥ 22 (the API's Docker image already runs node:22, and the root engines field enforces it).

Authentication ​

MCP clients authenticate with the exact same credentials as the REST API — no separate MCP tokens to manage:

CredentialHow to get itNotes
Org API key (mk_…)Dashboard → Org → API KeysLong-lived, ideal for MCP clients. Full org access (like X-Api-Key on REST)
Dashboard JWT (Bearer)Login sessionShort-lived (7 days). Write tools still enforce the user's role via assertPermission — a member without ASSET_WRITE gets 403

Send it as a standard Authorization: Bearer <token> header on every request. Missing/invalid credentials return 401 with a WWW-Authenticate: Bearer header.

Auth is validated on every request (not just session creation) — revoking an API key or suspending an org takes effect immediately.

Example client config (VS Code) ​

json
{
  "inputs": [
    {
      "type": "promptString",
      "id": "bearer_token",
      "description": "Enter your Strum VOD org API key",
      "password": true
    }
  ],
  "servers": {
    "strum-vod": {
      "url": "https://api.example.com/mcp",
      "headers": {
        "Authorization": "Bearer ${input:bearer_token}"
      }
    }
  }
}

Tools ​

All tools are org-scoped: they resolve the authenticated org from the token and run the same queries/services the REST routes use, so a client can never read or mutate another org's data.

Read-only ​

ToolDescription
list_assetsList assets (newest first), filter by status and/or collectionId, with pagination
get_assetFull asset detail: status, playback URLs, thumbnail, AI metadata
list_collectionsCollections with per-collection video counts
get_org_infoOrg profile: tier, current metered usage, tier limits
get_asset_analyticsViews, unique sessions, watch time, retention, heatmap, top platforms (period: 7d/30d/90d/all)

Write ​

ToolDescription
create_assetRegister a new asset (status created). Source file must then be uploaded via the REST API before processing
update_assetUpdate title, description, public playback settings, custom metadata, or collection assignment
process_assetQueue the transcode pipeline (HLS ladder + AI steps). Honors self-hosted Nodes routing and tier limits exactly like POST /v1/assets/:id/process

Write tools enforce ASSET_WRITE permission for JWT-authenticated users (API keys are full-access by design, matching the REST contract).

Protocol details ​

  • Transport: MCP streamable HTTP (POST /mcp JSON-RPC; GET /mcp SSE streams; DELETE /mcp session teardown).
  • Responses: the plugin runs with enableJsonResponse: true, so tool calls get plain JSON responses (no SSE parsing required) — fine for all MCP clients.
  • Sessions: each client initialize creates a session (mcp-session-id header); sessions are held in-memory per API instance (a single-instance deployment keeps sessions across requests; horizontal scaling of active sessions is not supported by the plugin's design).
  • Rate limiting: the MCP endpoint is exempt from the per-minute REST rate limit (an LLM conversation can issue many tool calls); auth is still enforced on every request.
  • CORS: Mcp-Session-Id is allowed for browser-based MCP clients.

Verification ​

The fastest way to try it is the MCP Inspector:

bash
npx @modelcontextprotocol/inspector --url https://api.example.com/mcp

Enter a Bearer token (org API key) in the inspector's auth tab, then call list_assets or get_org_info.

Alternatively, drive the raw protocol with curl:

bash
# 1. initialize → capture the mcp-session-id header
curl -s -X POST https://api.example.com/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer mk_live_..." \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'

# 2. tools/call with the session id from step 1
curl -s -X POST https://api.example.com/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer mk_live_..." \
  -H "mcp-session-id: <session-id>" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_org_info","arguments":{}}}'

Architecture ​

MCP client (VS Code / Claude Desktop / agent)
        │  streamable HTTP (POST /mcp, JSON-RPC)
        ▼
apps/api — fastify-mcp-server plugin
        │  Bearer middleware → mcpTokenVerifier (org API keys + JWTs)
        ▼
src/mcp/server.ts — createMcpServer() → org-scoped tools
        │
        ├── list_assets / get_asset / list_collections / get_org_info / get_asset_analytics
        │     └── reuse REST queries/services (drizzle + services/*)
        └── create_asset / update_asset / process_asset
              ├── services/asset-record.ts (shared with POST/PATCH /v1/assets)
              └── services/process-asset.ts (shared with POST /v1/assets/:id/process)
  • src/mcp/auth.ts — mcpTokenVerifier (Bearer → AuthInfo with org context), plus getOrgContext() used by every tool.
  • src/mcp/server.ts — createMcpServer(), the tool factory (one fresh McpServer per session).
  • services/asset-record.ts / services/process-asset.ts — the shared business logic behind both the REST routes and MCP tools, so the two can never drift.

Tests ​

  • Unit (src/tests/unit/mcp.test.ts) — verifier matrix (valid/invalid API key, suspended org, JWT, expired/tampered token), org-context extraction, and tools/list over a real in-memory transport.
  • E2E (src/tests/e2e/mcp.test.ts) — full streamable-HTTP flow against Testcontainers: initialize → tools/list → tools/call (asset list, org info, create/update/process), auth failures (401), cross-org isolation, and Postgres row assertions.

STRUM Proprietary License — © 2026 Strum. All rights reserved.