Skip to content

MCP Endpoint Handler #7429

@cstns

Description

@cstns

Summary

Implement the stateless Streamable HTTP endpoint handler at POST /api/v1/mcp. This fills in the empty route shell from #7426 with the per-request McpServer lifecycle, authentication enforcement, and the Fastify-to-MCP transport bridge. Tool definitions are not part of this task; the endpoint ships with an empty tool list and a registration hook for subsequent tasks to plug into.

Prerequisites

Requirements

Endpoint handler

  • POST /api/v1/mcp: each request creates a fresh McpServer and StreamableHTTPServerTransport (stateless, sessionIdGenerator: undefined)
  • Call reply.hijack() then delegate to transport.handleRequest(request.raw, reply.raw, request.body). The third parsedBody argument is a supported SDK parameter for frameworks that pre-parse the request body.
  • After handling, call mcpServer.close() and transport.close() to clean up per-request resources
  • GET /api/v1/mcp and DELETE /api/v1/mcp return 405 (not applicable for stateless mode)

Authentication

  • verifySession (inherited from the parent EE routes hook) handles PAT resolution from the Authorization header, no additional auth wiring needed
  • Add a preHandler that rejects requests without a resolved request.session.User (e.g. project/device tokens, missing auth) with 401
  • Reject requests where the authenticated user is a platform admin with 403. Admin-owned PATs must not be usable with MCP.
  • Errors return structured JSON with code and error fields, descriptive enough for an LLM agent to relay to the user

Tool registration hook

  • Expose a mechanism for tool definition modules to register themselves on the per-request McpServer
  • Tool modules export entries of { name, description, inputSchema, annotations, handler }, endpoint iterates and calls mcpServer.registerTool() for each
  • Ships with zero tools: tools/list returns empty array, tools/call rejects unknown tools

Tests

  • POST /api/v1/mcp with valid PAT returns a valid MCP JSON-RPC response (tools/list returns empty array)
  • POST /api/v1/mcp without auth returns 401
  • POST /api/v1/mcp with an admin-owned PAT returns 403
  • GET /api/v1/mcp returns 405
  • DELETE /api/v1/mcp returns 405
  • Stateless behavior: no session state leaks between sequential requests

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions