Skip to content
reaatechREAATECH

@reaatech/multi-tenant-mcp-middleware

npm v0.1.0

Enforces multi-tenancy, rate limiting, and access control for Model Context Protocol (MCP) servers by providing a middleware factory that wraps request handlers. It requires the `@modelcontextprotocol/sdk` as a peer dependency and automatically filters tools, resources, and prompts based on tenant-specific visibility policies.

@reaatech/multi-tenant-mcp-middleware

npm version License: MIT CI

Status: Pre-1.0 — APIs may change in minor versions. Pin to a specific version in production.

Compose a full multi-tenant middleware stack for MCP servers. Wraps MCP request handlers with a pipeline that enforces tenant resolution, rate limiting, tool/resource/prompt visibility, cost accounting, logging, and metrics in a single call.

Installation

terminal
npm install @reaatech/multi-tenant-mcp-middleware @modelcontextprotocol/sdk
# or
pnpm add @reaatech/multi-tenant-mcp-middleware @modelcontextprotocol/sdk

@modelcontextprotocol/sdk is a peer dependency (bring your own version).

Feature Overview

  • Single API callcreateMultiTenantMiddleware(config) returns a middleware object with one method: handle(server, method, handler).
  • Composable pipeline — Every middleware layer (rate limit, visibility, cost accounting, etc.) is optional; only tenantContextStore is required.
  • MCP method-aware — The middleware automatically filters tools/list / resources/list / prompts/list results by tenant policy, and validates */call / */read / */get access.
  • Non-blocking side-effects — Usage emissions and cost tracking run after handler completion; failures are logged but never fail the request.

Quick Start

typescript
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { createMultiTenantMiddleware } from '@reaatech/multi-tenant-mcp-middleware';
import { HeaderTenantResolver, TenantContextStore } from '@reaatech/multi-tenant-mcp-tenant-resolver';
import { DefaultRateLimiter, MemoryRateLimitStore } from '@reaatech/multi-tenant-mcp-rate-limiter';
import { DefaultCostCalculator, InMemoryCostTracker, CallbackUsageEmitter } from '@reaatech/multi-tenant-mcp-cost-accounting';
import { ConsoleTenantLogger, MetricsCollector } from '@reaatech/multi-tenant-mcp-observability';
 
const store = new TenantContextStore();
const logger = new ConsoleTenantLogger({ level: 'info' });
const metrics = new MetricsCollector();
 
const middleware = createMultiTenantMiddleware({
  tenantContextStore: store,
  tenantResolver: new HeaderTenantResolver({ header: 'x-tenant-id' }),
  rateLimiter: new DefaultRateLimiter(
    new MemoryRateLimitStore({ requestsPerMinute: 100, tokensPerMinute: 10_000 }),
  ),
  toolVisibility: {
    'acme-corp': { type: 'allow', items: ['echo', 'status'] },
  },
  costCalculator: new DefaultCostCalculator({
    perCall: { echo: 0.01 },
  }),
  costTracker: new InMemoryCostTracker(),
  usageEmitter: new CallbackUsageEmitter(async (event) => {
    await billingPipeline.record(event);
  }),
  logger,
  metrics,
});
 
const server = new Server({ name: 'my-mcp', version: '1.0.0' }, { capabilities: {} });
 
// Wire up tools/list — results are filtered by tenant visibility policy
middleware.handle(server, 'tools/list', () => ({
  tools: [
    { name: 'echo', description: 'Echo back input' },
    { name: 'status', description: 'Server status' },
    { name: 'admin', description: 'Admin operations' },
  ],
}));
 
// Wire up tools/call — tenant identity, rate limits, and visibility are enforced
middleware.handle(server, 'tools/call', (request) => {
  const req = request as { params: { name: string; arguments?: Record<string, unknown> } };
  switch (req.params.name) {
    case 'echo':
      return { content: [{ type: 'text', text: String(req.params.arguments?.message ?? '') }] };
    case 'status':
      return { content: [{ type: 'text', text: 'ok' }] };
    default:
      return { content: [{ type: 'text', text: 'Unknown tool' }] };
  }
});

Request Pipeline

For every MCP request, the middleware enforces:

code
1. Tenant Context Retrieval — read from TenantContextStore (ALS)
2. Rate Limit Check         — verify against configured RateLimiter
3. Visibility Filter        — list: filter results; call/read: validate access
4. Handler Execution        — delegate to user-provided handler
5. Cost Accounting          — record usage event (non-blocking)

Exports

ExportKindDescription
createMultiTenantMiddlewareFunctionCompose the full middleware stack from config
MultiTenantMiddlewareConfigInterfaceConfiguration object — all fields optional except tenantContextStore
MultiTenantMiddlewareInterfaceReturned object with handle(server, method, handler)
RequestHandler<T>Type(request: T) => T | Promise<T>

Configuration Options

PropertyTypeRequiredDescription
tenantContextStoreTenantContextStoreYesALS-backed store for tenant context propagation
tenantResolverTenantResolverNoResolver called on each request to identify the tenant
rateLimiterRateLimiterNoRate limiting engine (default: no-op)
toolVisibilityRecord<string, VisibilityPolicy>NoPer-tenant tool visibility policies
resourceVisibilityRecord<string, VisibilityPolicy>NoPer-tenant resource visibility policies
promptVisibilityRecord<string, VisibilityPolicy>NoPer-tenant prompt visibility policies
costCalculatorCostCalculatorNoPricing model for usage-based billing
costTrackerCostTrackerNoIn-memory cost accumulator
usageEmitterUsageEventEmitterNoCallback for forwarding usage events externally
artifactStoreArtifactStoreNoTenant-scoped storage backend
configStoreTenantConfigStoreNoPer-tenant configuration store
loggerTenantLoggerNoStructured logger (default: silent)
metricsMetricsCollectorNoMetrics collector (default: no-op)

License

MIT