Your Express app already handles JSON traffic across app.get / app.post / router.use; here's how to expose those routes as MCP tools an agent can call — without trading Express for a "modern" framework.
wmcp.sh is not affiliated with the OpenJS Foundation or Anthropic. Express, swagger-jsdoc, and zod-to-openapi are open-source projects.
Express 4 or 5 with route handlers, middleware (helmet, cors, express-jwt), and either JSDoc or Zod schemas inside the handlers. Running on Node 20 or 22 LTS.
A Model Context Protocol server with typed tool schemas. wmcp.sh consumes OpenAPI 3 from swagger-jsdoc, @asteasolutions/zod-to-openapi, or a hand-written spec — and emits MCP at https://wmcp.sh/mcp/<your-id>.
TypeScript Express + Zod + @asteasolutions/zod-to-openapi. Node 22.
// server.ts — Express 4 + Zod, Node 22 LTS
import express, { Request, Response } from 'express';
import { z } from 'zod';
import { OpenAPIRegistry, OpenApiGeneratorV3 } from '@asteasolutions/zod-to-openapi';
const app = express();
app.use(express.json());
const registry = new OpenAPIRegistry();
const CreateOrder = z.object({
sku: z.string().min(1),
qty: z.number().int().positive(),
});
registry.registerPath({
method: 'post', path: '/orders', tags: ['agent'],
request: { body: { content: { 'application/json': { schema: CreateOrder } } } },
responses: { 201: { description: 'created' } },
});
app.post('/orders', async (req: Request, res: Response) => {
const body = CreateOrder.parse(req.body);
const order = await createOrder(body);
res.status(201).json(order);
});
const spec = new OpenApiGeneratorV3(registry.definitions).generateDocument({
openapi: '3.0.0', info: { title: 'Acme Inventory', version: '1.0.0' },
});
app.get('/openapi.json', (_req, res) => res.json(spec));
app.listen(3000);
// curl 'https://wmcp.sh/api/v1/tools?url=https://acme.example.com/openapi.json&tag=agent'
| Capability | Hand-rolled | wmcp.sh + OpenAPI |
|---|---|---|
| Spec generator choice | ⚠️ N/A; you redo schemas | ✅ swagger-jsdoc, zod-to-openapi, or hand-written — all ingestible |
| Existing middleware preserved | ⚠️ Re-implement at MCP layer | ✅ helmet, cors, express-jwt, passport — all still run on the origin |
| MCP transport (Streamable HTTP, SSE) | ⚠️ You build it | ✅ Served at https://wmcp.sh/mcp/<your-id> |
| Auth forwarding | ⚠️ Per-route adapter | ✅ Bearer / API-key / OAuth 2.1 declared in spec |
| Per-route gating | ⚠️ Manual allowlist | ✅ Tag operations; &tag=agent at ingest |
| Spec drift detection | ❌ Silent | ✅ Re-ingest in CI; mismatches surface immediately |
swagger-jsdoc for JSDoc-annotated routes. (2) @asteasolutions/zod-to-openapi for TypeScript-first codebases — recommended. (3) express-openapi-validator if you'd rather hand-write the spec and validate at runtime. wmcp.sh ingests all three.securitySchemes in the spec — bearerAuth, apiKey, or oauth2. wmcp.sh forwards credentials. Common middleware (passport-jwt, express-jwt, helmet) is unaffected because it runs on your origin.@fastify/swagger, koa-swagger, @hono/zod-openapi — emit a spec, point wmcp.sh at it. Pick the framework you prefer; MCP exposure is identical.&tag=agent at ingest. Untagged or undescribed routes stay invisible.Audit your routes, wire up swagger-jsdoc or zod-to-openapi, deploy MCP at mcp.yourbrand.com. Starter $499 one-time setup; Managed Retainer $999/mo for ongoing maintenance; Enterprise $4,999+/mo for SLA + private deploy.