@tensorcost/mcp-framework
v0.1.0
Published
Product-agnostic NestJS module for hosting an MCP (Model Context Protocol) JSON-RPC server with pluggable key verification, scopes, rate limiting, and internal-auth forwarding.
Maintainers
Readme
@tensorcost/mcp-framework
Product-agnostic NestJS module for hosting an MCP (Model Context Protocol) JSON-RPC server with pluggable key verification, call logging, rate limiting, and internal-auth forwarding.
npm install @tensorcost/mcp-framework
# peer: @nestjs/common @nestjs/coreSource of truth: this package is developed inside the TensorCost monorepo and published to the
@tensorcostnpm org. There is no separate public GitHub repository — install from npm. TensorCost itself continues to consume the workspace package viaworkspace:*.
The framework owns:
- the
POST /mcpJSON-RPC 2.0 controller (initialize,tools/list,tools/call,ping), - the
McpKeyGuardthat resolvesX-MCP-Key/Authorization: Bearerinto a tenant-scoped request context, - a
McpRegistrythat dispatches tool calls and enforces per-tool required scopes, - fire-and-forget audit-log plumbing via an
McpCallLoggerinterface, - a default in-memory per-key RPM limiter (
InMemoryRpmLimiter).
It owns none of: downstream service URLs, product-specific tool implementations, DB access. Those live in the host application.
Writing tools
A product provides an array of McpTool to the module at registration
time. Tools get a fully-populated McpRequestContext (tenant, user,
roles, scopes) and a proxy helper that forwards the caller as an
internal-auth JWT on the x-internal-auth header:
import { McpTool, makeProxy, InternalAuthSigner } from "@tensorcost/mcp-framework";
export function buildAcmeTools(signer: InternalAuthSigner): McpTool[] {
const { proxy } = makeProxy(signer);
return [
{
name: "acme.inventory.list",
description: "List all inventory items for the tenant.",
inputSchema: {
type: "object",
properties: { limit: { type: "integer" } },
},
async handler(args, ctx) {
const a = (args ?? {}) as { limit?: number };
return proxy(
"http://acme-inventory:4100",
`/api/inventory?limit=${a.limit ?? 50}`,
ctx,
);
},
},
];
}Wire the module in your Nest AppModule:
McpFrameworkModule.register({
tools: buildAcmeTools(signer),
keyVerifier, // your McpKeyVerifier impl
callLogger, // your McpCallLogger impl
internalAuthSigner: signer,
serverInfo: { name: "acme-mcp", version: "1.0.0" },
})Write-scoped tools declare required scopes — the registry throws
ForbiddenException if the caller's MCP key doesn't carry them:
{
name: "acme.inventory.delete",
requiredScopes: ["write"],
// ...
}License
Apache-2.0. See LICENSE.
