MCP (Model Context Protocol) Implementation
Relevant source files
The following files were used as context for generating this wiki page:
The Thoth project implements the Model Context Protocol (MCP) to expose site content and management tools to AI agents. It uses a streamable HTTP transport layer to facilitate communication between the server and MCP clients.
The implementation focuses on per-call authentication rather than per-mount authentication. This allows public tools to remain anonymous while securing private administrative tools.
Sources: apps/api/spec/mcp.md:1-12, apps/api/src/infrastructure/mcp/mcp.ts:31-38
System Architecture
The MCP system integrates into the Express-based API infrastructure. It utilizes a stateless design where a fresh McpServer instance is created for every request.
Core Components
- McpRouter: Manages the HTTP endpoints and initializes the MCP server with registered tools.
- McpAuthMiddleware: Validates incoming requests against a set of private tool names.
- McpTool: An interface used by different modules to register their specific tools (e.g., bio, feedback).
- StreamableHTTPServerTransport: The SDK-provided transport layer that handles JSON-RPC over HTTP.
Sources: apps/api/src/infrastructure/mcp/mcp.ts:18-21, apps/api/src/infrastructure/http/routes/mcp.router.ts:12-25
Request Lifecycle Flow
This diagram illustrates how an MCP request moves through the system from the initial HTTP POST to tool execution.
The workflow ensures that authentication checks occur before any tool registration or execution.
Sources: apps/api/src/infrastructure/http/mcp-auth.middleware.ts:25-50, apps/api/src/infrastructure/http/routes/mcp.router.ts:29-47
Endpoints and Routing
The system mounts MCP functionality at two primary paths to differentiate between owner-level access and public-read access.
| Endpoint | Description | Handle Context |
|---|---|---|
POST /mcp | The primary entry point for the site owner or authenticated users. | undefined |
POST /mcp/:handle | A shareable public-read mount for specific profiles. | Matches :handle |
Sources: apps/api/spec/mcp.md:15-20, apps/api/src/infrastructure/http/routes/mcp.router.ts:20-22
Handle Context Logic
When a request targets /mcp/:handle, the router passes the handle string into the McpRequestContext. Public tools use this handle to scope their data lookups (e.g., fetching a specific user's bio). An empty segment (e.g., /mcp/) is treated identically to the bare /mcp route.
Sources: apps/api/spec/mcp.md:21-23, apps/api/src/infrastructure/http/routes/mcp.router.ts:53-58
Authentication and Security
Thoth uses mcpAuthMiddleware to gate access to sensitive tools. Auth is checked per-call, even when multiple calls are bundled in a JSON-RPC batch.
Authorization Rules
- Public Tools: Remain anonymous and require no token.
- Private Tools: Require a valid Bearer JWT on the bare
/mcproute. - Handle Route Gate: A private tool called on
/mcp/:handlereturns a404 Not Foundto avoid revealing tool existence or authentication requirements. - Unauthorized Access: A private tool called on
/mcpwithout a valid token returns401 Unauthorizedwith aWWW-Authenticateheader.
Sources: apps/api/spec/mcp.md:24-26, apps/api/src/infrastructure/http/mcp-auth.middleware.ts:28-56
Authentication Sequence
The sequence shows the enforcement of JWT verification for private tools.
Sources: apps/api/src/infrastructure/http/mcp-auth.middleware.ts:43-56, apps/api/test/infrastructure/http/mcp-auth.middleware.test.ts:58-95
Data Structures
McpRequestContext
Passed to every tool during registration to provide environmental data.
export interface McpRequestContext {
// Set on /mcp/:handle. Undefined on bare /mcp.
handle?: string;
// Set once mcpAuthMiddleware verifies a bearer for a private tool call.
principal?: McpPrincipal;
}
Sources: apps/api/src/infrastructure/mcp/mcp.ts:12-17
McpPrincipal
Represents the authenticated user identity within the MCP context.
| Field | Type | Description |
|---|---|---|
userId | string | The unique identifier for the authenticated user. |
scopes | string[] | Permissions assigned to the user token. |
Sources: apps/api/src/infrastructure/mcp/mcp.ts:6-10
Tool Registration
Modules register tools by implementing the McpTool interface. The McpRouter uses Inversify's @multiInject to collect all bound tools and register them to the per-request McpServer.
Registration Implementation
@injectable()
export class McpRouter {
constructor(
@multiInject(McpTool)
@optional()
private readonly tools: McpTool[] = [],
) {}
private buildServer(context: McpRequestContext): McpServer {
const server = createMcpServer();
for (const tool of this.tools) {
tool.register(server, context);
}
return server;
}
}
Sources: apps/api/src/infrastructure/http/routes/mcp.router.ts:13-18, apps/api/src/infrastructure/http/routes/mcp.router.ts:65-71
Conclusion
The MCP implementation in Thoth provides a flexible, secure bridge between AI models and the application's data. By leveraging streamable HTTP and granular per-call authentication, it supports both public data discovery via handles and secure personal data management for the site owner. The stateless, per-request server model ensures that the protocol remains light and integrates cleanly with standard Express middleware patterns.