Exposing Tools as MCP Server¶
The framework provides two approaches for exposing your ToolRegistry tools as an MCP server: a stateless JSON-RPC handler for serverless deployments, and integration points for the official MCP SDK.
Import¶
import {
createMcpStatelessHandlerFromToolRegistry,
McpProviderConfig,
} from "@modernpath/agent-framework";
createMcpStatelessHandlerFromToolRegistry¶
function createMcpStatelessHandlerFromToolRegistry(
registry: ToolRegistry,
config?: McpProviderConfig,
): (event: McpHttpEvent) => Promise<McpHttpResponse>
Creates a minimal, stateless MCP Streamable HTTP handler backed by the ToolRegistry. Implements the following JSON-RPC methods:
| Method | Description |
|---|---|
tools/list | Returns all tools that pass the provider filter. |
tools/call | Executes a tool via the registry. |
resources/list | Lists tool schema resources (if schema resources are configured). |
resources/read | Reads a specific tool schema resource. |
McpHttpEvent¶
interface McpHttpEvent {
method?: string;
headers?: Record<string, string | undefined>;
body?: string | null;
}
McpHttpResponse¶
McpProviderConfig¶
interface McpProviderConfig {
serverName?: string;
serverVersion?: string;
allowTools?: string[];
denyTools?: string[];
allowCategories?: string[];
allowTags?: string[];
getUserId?: (req: { headers?: Record<string, string | undefined> }) => Promise<number> | number;
getAuditingId?: (req: { headers?: Record<string, string | undefined> }) => Promise<number> | number;
originService?: string;
}
| Property | Type | Default | Description |
|---|---|---|---|
serverName | string | -- | Optional MCP server name (informational). |
serverVersion | string | -- | Optional MCP server version (informational). |
allowTools | string[] | -- | Whitelist of tool names to expose. If empty/absent, all tools are exposed (subject to deny filters). |
denyTools | string[] | -- | Blacklist of tool names to hide. |
allowCategories | string[] | -- | Only expose tools with these categories. |
allowTags | string[] | -- | Only expose tools that have at least one of these tags. |
getUserId | function | Returns 0 | Extracts userId from the request headers. |
getAuditingId | function | Returns 0 | Extracts auditingId from the request headers. |
originService | string | -- | Identifier attached to call metadata for audit trail. |
Filter Evaluation Order¶
denyTools-- if the tool name is in the deny list, it is hiddenallowTools-- if set and the tool name is not in the allow list, it is hiddenallowCategories-- if set and the tool's category is not in the list, it is hiddenallowTags-- if set and the tool has no matching tag, it is hidden
Code Example¶
Basic HTTP Server¶
import {
ToolRegistry,
createMcpStatelessHandlerFromToolRegistry,
} from "@modernpath/agent-framework";
const registry = new ToolRegistry();
// ... register tools ...
const mcpHandler = createMcpStatelessHandlerFromToolRegistry(registry, {
serverName: "my-agent-tools",
serverVersion: "1.0.0",
allowCategories: ["public", "telemetry"],
denyTools: ["internal.debug"],
getUserId: (req) => parseInt(req.headers?.["x-user-id"] ?? "0", 10),
getAuditingId: (req) => parseInt(req.headers?.["x-auditing-id"] ?? "0", 10),
originService: "my-agent-backend",
});
// Express
app.post("/mcp", async (req, res) => {
const result = await mcpHandler({
method: req.method,
headers: req.headers as Record<string, string>,
body: JSON.stringify(req.body),
});
res.status(result.statusCode);
for (const [k, v] of Object.entries(result.headers)) {
res.set(k, v);
}
res.send(result.body);
});
GCP Cloud Function¶
import { createMcpStatelessHandlerFromToolRegistry } from "@modernpath/agent-framework";
const mcpHandler = createMcpStatelessHandlerFromToolRegistry(registry, {
serverName: "my-cloud-function-tools",
});
export const mcpEndpoint = async (req: any, res: any) => {
const result = await mcpHandler({
method: req.method,
headers: req.headers,
body: typeof req.body === "string" ? req.body : JSON.stringify(req.body),
});
res.status(result.statusCode);
for (const [k, v] of Object.entries(result.headers)) {
res.set(k, v);
}
res.send(result.body);
};
Tool Call Context¶
When a tool is called via the MCP provider, the framework injects context into the tool parameters:
userId-- fromgetUserId()or default0auditingId-- fromgetAuditingId()or default0__metadata-- includescallStackfor loop safety andoriginService
Related Pages¶
- Consuming Remote Tools -- importing tools from MCP servers
- Schema Resources -- exposing tool schemas as MCP resources
- Loop Safety -- preventing infinite call loops
- Serverless -- deploying the MCP handler