@mcp-weave/express 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,5 +1,6 @@
1
- import { RequestHandler, Express, Router } from 'express';
1
+ import { McpAuthOptions } from '@mcp-weave/nestjs';
2
2
  export { McpInput, McpParam, McpPrompt, McpPromptArg, McpResource, McpServer, McpTool } from '@mcp-weave/nestjs';
3
+ import { RequestHandler, Express, Router } from 'express';
3
4
 
4
5
  /**
5
6
  * Options for MCP Express middleware
@@ -17,6 +18,10 @@ interface McpMiddlewareOptions {
17
18
  * Custom CORS origin (default: '*')
18
19
  */
19
20
  corsOrigin?: string;
21
+ /**
22
+ * Authentication options
23
+ */
24
+ auth?: McpAuthOptions;
20
25
  }
21
26
  /**
22
27
  * Create Express middleware for an MCP server
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { RequestHandler, Express, Router } from 'express';
1
+ import { McpAuthOptions } from '@mcp-weave/nestjs';
2
2
  export { McpInput, McpParam, McpPrompt, McpPromptArg, McpResource, McpServer, McpTool } from '@mcp-weave/nestjs';
3
+ import { RequestHandler, Express, Router } from 'express';
3
4
 
4
5
  /**
5
6
  * Options for MCP Express middleware
@@ -17,6 +18,10 @@ interface McpMiddlewareOptions {
17
18
  * Custom CORS origin (default: '*')
18
19
  */
19
20
  corsOrigin?: string;
21
+ /**
22
+ * Authentication options
23
+ */
24
+ auth?: McpAuthOptions;
20
25
  }
21
26
  /**
22
27
  * Create Express middleware for an MCP server
package/dist/index.js CHANGED
@@ -46,24 +46,72 @@ module.exports = __toCommonJS(index_exports);
46
46
  // src/middleware.ts
47
47
  var import_nestjs = require("@mcp-weave/nestjs");
48
48
  function createMcpMiddleware(target, options = {}) {
49
- const { cors = true, corsOrigin = "*" } = options;
49
+ const { cors = true, corsOrigin = "*", auth } = options;
50
50
  const state = {
51
51
  metadata: (0, import_nestjs.extractMetadata)(target),
52
52
  target
53
53
  };
54
+ const apiKeys = (0, import_nestjs.normalizeApiKeys)(auth?.apiKeys);
55
+ const headerName = auth?.headerName ?? "x-api-key";
56
+ const queryParamName = auth?.queryParamName ?? "api_key";
57
+ const authenticateRequest = (req) => {
58
+ if (!auth?.enabled) {
59
+ return { success: true, clientId: "anonymous" };
60
+ }
61
+ let token = req.headers[headerName.toLowerCase()];
62
+ if (!token) {
63
+ const authHeader = req.headers["authorization"];
64
+ if (authHeader && authHeader.startsWith("Bearer ")) {
65
+ token = authHeader.slice(7);
66
+ }
67
+ }
68
+ if (!token) {
69
+ token = req.query[queryParamName];
70
+ }
71
+ return (0, import_nestjs.validateApiKey)(token, apiKeys);
72
+ };
54
73
  return async (req, res, next) => {
74
+ const requestId = (0, import_nestjs.generateRequestId)();
75
+ res.setHeader("X-Request-Id", requestId);
55
76
  if (cors) {
56
77
  res.setHeader("Access-Control-Allow-Origin", corsOrigin);
57
78
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
58
- res.setHeader("Access-Control-Allow-Headers", "Content-Type");
79
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Api-Key");
59
80
  if (req.method === "OPTIONS") {
60
81
  res.status(200).end();
61
82
  return;
62
83
  }
63
84
  }
64
85
  const path = req.path;
86
+ if (path === "/health") {
87
+ res.json({
88
+ status: "ok",
89
+ server: state.metadata.server?.name,
90
+ version: state.metadata.server?.version,
91
+ authEnabled: auth?.enabled ?? false
92
+ });
93
+ return;
94
+ }
95
+ const authResult = authenticateRequest(req);
96
+ if (!authResult.success) {
97
+ auth?.onAuthFailure?.(
98
+ req,
99
+ authResult.error ?? "Auth failed"
100
+ );
101
+ res.status(401).json({
102
+ error: "Unauthorized",
103
+ message: authResult.error ?? "Authentication required",
104
+ requestId
105
+ });
106
+ return;
107
+ }
108
+ if (auth?.enabled) {
109
+ console.log(
110
+ `[${requestId}] ${authResult.clientName ?? authResult.clientId} - ${req.method} ${path}`
111
+ );
112
+ }
65
113
  try {
66
- if (path === "/health" || path === "/") {
114
+ if (path === "/") {
67
115
  res.json({
68
116
  status: "ok",
69
117
  server: state.metadata.server?.name,
@@ -72,7 +120,9 @@ function createMcpMiddleware(target, options = {}) {
72
120
  tools: state.metadata.tools.length,
73
121
  resources: state.metadata.resources.length,
74
122
  prompts: state.metadata.prompts.length
75
- }
123
+ },
124
+ client: authResult.clientName,
125
+ requestId
76
126
  });
77
127
  return;
78
128
  }
@@ -222,8 +272,8 @@ function extractUriParams(template, uri) {
222
272
  }
223
273
 
224
274
  // src/server.ts
225
- var import_express = __toESM(require("express"));
226
275
  var import_nestjs2 = require("@mcp-weave/nestjs");
276
+ var import_express = __toESM(require("express"));
227
277
  var McpExpressServer = class {
228
278
  app;
229
279
  target;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/middleware.ts","../src/server.ts","../src/router.ts"],"sourcesContent":["/**\n * @mcp-weave/express\n *\n * Express middleware for MCP (Model Context Protocol) servers.\n * Provides easy integration of MCP servers with Express applications.\n */\n\nexport { createMcpMiddleware, type McpMiddlewareOptions } from './middleware.js';\nexport { McpExpressServer, type McpExpressOptions } from './server.js';\nexport { createMcpRouter } from './router.js';\n\n// Re-export common decorators from nestjs for convenience\nexport {\n McpServer,\n McpTool,\n McpResource,\n McpPrompt,\n McpInput,\n McpParam,\n McpPromptArg,\n} from '@mcp-weave/nestjs';\n","import type { Request, Response, NextFunction, RequestHandler } from 'express';\nimport { extractMetadata } from '@mcp-weave/nestjs';\n\n/**\n * Options for MCP Express middleware\n */\nexport interface McpMiddlewareOptions {\n /**\n * Base path for MCP endpoints (default: '/mcp')\n */\n basePath?: string;\n\n /**\n * Enable CORS headers (default: true)\n */\n cors?: boolean;\n\n /**\n * Custom CORS origin (default: '*')\n */\n corsOrigin?: string;\n}\n\n/**\n * Internal state for MCP middleware\n */\ninterface McpMiddlewareState {\n metadata: ReturnType<typeof extractMetadata>;\n target: Function;\n}\n\n/**\n * Create Express middleware for an MCP server\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { createMcpMiddleware, McpServer, McpTool } from '@mcp-weave/express';\n *\n * @McpServer({ name: 'my-server', version: '1.0.0' })\n * class MyServer {\n * @McpTool({ name: 'hello', description: 'Say hello' })\n * hello(input: { name: string }) {\n * return `Hello, ${input.name}!`;\n * }\n * }\n *\n * const app = express();\n * app.use('/mcp', createMcpMiddleware(MyServer));\n * app.listen(3000);\n * ```\n */\nexport function createMcpMiddleware(\n target: Function,\n options: McpMiddlewareOptions = {}\n): RequestHandler {\n const { cors = true, corsOrigin = '*' } = options;\n\n // Initialize metadata\n const state: McpMiddlewareState = {\n metadata: extractMetadata(target),\n target,\n };\n\n return async (req: Request, res: Response, next: NextFunction): Promise<void> => {\n // Handle CORS\n if (cors) {\n res.setHeader('Access-Control-Allow-Origin', corsOrigin);\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.status(200).end();\n return;\n }\n }\n\n const path = req.path;\n\n try {\n // Health check\n if (path === '/health' || path === '/') {\n res.json({\n status: 'ok',\n server: state.metadata.server?.name,\n version: state.metadata.server?.version,\n capabilities: {\n tools: state.metadata.tools.length,\n resources: state.metadata.resources.length,\n prompts: state.metadata.prompts.length,\n },\n });\n return;\n }\n\n // List tools\n if (path === '/tools' && req.method === 'GET') {\n res.json({\n tools: state.metadata.tools.map(t => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n })),\n });\n return;\n }\n\n // Call tool\n if (path === '/tools/call' && req.method === 'POST') {\n const { name, arguments: args } = req.body as {\n name: string;\n arguments?: Record<string, unknown>;\n };\n\n if (!name) {\n res.status(400).json({ error: 'Tool name is required' });\n return;\n }\n\n const tool = state.metadata.tools.find(t => t.name === name);\n if (!tool) {\n res.status(404).json({ error: `Tool not found: ${name}` });\n return;\n }\n\n // Execute tool via internal server\n const instance = new (state.target as new () => unknown)();\n const method = Reflect.get(instance as object, tool.propertyKey);\n\n if (typeof method !== 'function') {\n res.status(500).json({ error: `Method ${String(tool.propertyKey)} not found` });\n return;\n }\n\n const result = await method.call(instance, args ?? {});\n res.json({\n content: [\n {\n type: 'text',\n text: typeof result === 'string' ? result : JSON.stringify(result),\n },\n ],\n });\n return;\n }\n\n // List resources\n if (path === '/resources' && req.method === 'GET') {\n res.json({\n resources: state.metadata.resources.map(r => ({\n uri: r.uri,\n name: r.name,\n description: r.description,\n mimeType: r.mimeType,\n })),\n });\n return;\n }\n\n // Read resource\n if (path === '/resources/read' && req.method === 'POST') {\n const { uri } = req.body as { uri: string };\n\n if (!uri) {\n res.status(400).json({ error: 'Resource URI is required' });\n return;\n }\n\n const instance = new (state.target as new () => unknown)();\n\n for (const resource of state.metadata.resources) {\n const uriParams = extractUriParams(resource.uri, uri);\n if (uriParams) {\n const method = Reflect.get(instance as object, resource.propertyKey);\n if (typeof method !== 'function') {\n res.status(500).json({ error: `Method ${String(resource.propertyKey)} not found` });\n return;\n }\n\n // Resolve params\n const params = state.metadata.params.filter(\n p => p.propertyKey === resource.propertyKey && p.type === 'param'\n );\n const args: unknown[] = [];\n for (const param of params) {\n if (param.name) {\n args[param.parameterIndex] = uriParams[param.name];\n }\n }\n\n const result = await method.apply(instance, args);\n res.json(result);\n return;\n }\n }\n\n res.status(404).json({ error: `Resource not found: ${uri}` });\n return;\n }\n\n // List prompts\n if (path === '/prompts' && req.method === 'GET') {\n res.json({\n prompts: state.metadata.prompts.map(p => ({\n name: p.name,\n description: p.description,\n arguments: p.arguments,\n })),\n });\n return;\n }\n\n // Get prompt\n if (path === '/prompts/get' && req.method === 'POST') {\n const { name, arguments: args } = req.body as {\n name: string;\n arguments?: Record<string, unknown>;\n };\n\n if (!name) {\n res.status(400).json({ error: 'Prompt name is required' });\n return;\n }\n\n const prompt = state.metadata.prompts.find(p => p.name === name);\n if (!prompt) {\n res.status(404).json({ error: `Prompt not found: ${name}` });\n return;\n }\n\n const instance = new (state.target as new () => unknown)();\n const method = Reflect.get(instance as object, prompt.propertyKey);\n\n if (typeof method !== 'function') {\n res.status(500).json({ error: `Method ${String(prompt.propertyKey)} not found` });\n return;\n }\n\n // Resolve prompt args\n const params = state.metadata.params.filter(\n p => p.propertyKey === prompt.propertyKey && p.type === 'promptArg'\n );\n const methodArgs: unknown[] = [];\n for (const param of params) {\n if (param.name && args) {\n methodArgs[param.parameterIndex] = args[param.name];\n }\n }\n\n const result = await method.apply(instance, methodArgs);\n res.json(result);\n return;\n }\n\n // Not found\n next();\n } catch (error) {\n res.status(500).json({\n error: error instanceof Error ? error.message : 'Internal server error',\n });\n }\n };\n}\n\n/**\n * Extract parameters from a URI template\n */\nfunction extractUriParams(template: string, uri: string): Record<string, string> | null {\n const paramNames: string[] = [];\n const regexStr = template.replace(/\\{(\\w+)\\}/g, (_, name) => {\n paramNames.push(name);\n return '([^/]+)';\n });\n\n const regex = new RegExp(`^${regexStr}$`);\n const match = uri.match(regex);\n\n if (!match) return null;\n\n const params: Record<string, string> = {};\n paramNames.forEach((name, index) => {\n params[name] = match[index + 1] ?? '';\n });\n\n return params;\n}\n","import express, { type Express, type RequestHandler } from 'express';\nimport { createMcpMiddleware, type McpMiddlewareOptions } from './middleware.js';\nimport { extractMetadata } from '@mcp-weave/nestjs';\n\n/**\n * Options for MCP Express server\n */\nexport interface McpExpressOptions extends McpMiddlewareOptions {\n /**\n * Port to listen on (default: 3000)\n */\n port?: number;\n\n /**\n * Host to bind to (default: '0.0.0.0')\n */\n host?: string;\n\n /**\n * Custom Express app (if you want to add other middleware)\n */\n app?: Express;\n}\n\n/**\n * Standalone Express server for MCP\n *\n * @example\n * ```typescript\n * import { McpExpressServer, McpServer, McpTool } from '@mcp-weave/express';\n *\n * @McpServer({ name: 'my-server', version: '1.0.0' })\n * class MyServer {\n * @McpTool({ name: 'hello', description: 'Say hello' })\n * hello(input: { name: string }) {\n * return `Hello, ${input.name}!`;\n * }\n * }\n *\n * const server = new McpExpressServer(MyServer);\n * await server.start();\n * // Server running at http://localhost:3000/mcp\n * ```\n */\nexport class McpExpressServer {\n private app: Express;\n private target: Function;\n private options: McpExpressOptions;\n private httpServer?: ReturnType<Express['listen']>;\n\n constructor(target: Function, options: McpExpressOptions = {}) {\n this.target = target;\n this.options = options;\n this.app = options.app ?? express();\n\n // Setup middleware\n this.app.use(express.json() as RequestHandler);\n this.app.use(\n options.basePath ?? '/mcp',\n createMcpMiddleware(target, options) as RequestHandler\n );\n }\n\n /**\n * Start the Express server\n */\n async start(): Promise<void> {\n const port = this.options.port ?? 3000;\n const host = this.options.host ?? '0.0.0.0';\n const basePath = this.options.basePath ?? '/mcp';\n\n const metadata = extractMetadata(this.target);\n\n return new Promise(resolve => {\n this.httpServer = this.app.listen(port, host, () => {\n console.log(`🚀 MCP Express server '${metadata.server?.name}' started`);\n console.log(\n ` Base URL: http://${host === '0.0.0.0' ? 'localhost' : host}:${port}${basePath}`\n );\n console.log(\n ` Health: http://${host === '0.0.0.0' ? 'localhost' : host}:${port}${basePath}/health`\n );\n console.log(` Tools: ${metadata.tools.length}`);\n console.log(` Resources: ${metadata.resources.length}`);\n console.log(` Prompts: ${metadata.prompts.length}`);\n resolve();\n });\n });\n }\n\n /**\n * Stop the server\n */\n async stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.httpServer) {\n this.httpServer.close(err => {\n if (err) reject(err);\n else resolve();\n });\n } else {\n resolve();\n }\n });\n }\n\n /**\n * Get the Express app instance\n */\n getApp(): Express {\n return this.app;\n }\n}\n","import { Router, type RequestHandler } from 'express';\nimport { createMcpMiddleware, type McpMiddlewareOptions } from './middleware.js';\n\n/**\n * Create an Express Router for MCP endpoints\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { createMcpRouter, McpServer, McpTool } from '@mcp-weave/express';\n *\n * @McpServer({ name: 'my-server', version: '1.0.0' })\n * class MyServer {\n * @McpTool({ name: 'hello', description: 'Say hello' })\n * hello(input: { name: string }) {\n * return `Hello, ${input.name}!`;\n * }\n * }\n *\n * const app = express();\n * app.use(express.json());\n * app.use('/api/mcp', createMcpRouter(MyServer));\n * app.listen(3000);\n * ```\n */\nexport function createMcpRouter(target: Function, options: McpMiddlewareOptions = {}): Router {\n const router = Router();\n router.use(createMcpMiddleware(target, options) as RequestHandler);\n return router;\n}\n"],"mappings":"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","names":["import_nestjs","express","import_express","import_nestjs"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/middleware.ts","../src/server.ts","../src/router.ts"],"sourcesContent":["/**\n * @mcp-weave/express\n *\n * Express middleware for MCP (Model Context Protocol) servers.\n * Provides easy integration of MCP servers with Express applications.\n */\n\nexport { createMcpMiddleware, type McpMiddlewareOptions } from './middleware.js';\nexport { McpExpressServer, type McpExpressOptions } from './server.js';\nexport { createMcpRouter } from './router.js';\n\n// Re-export common decorators from nestjs for convenience\nexport {\n McpServer,\n McpTool,\n McpResource,\n McpPrompt,\n McpInput,\n McpParam,\n McpPromptArg,\n} from '@mcp-weave/nestjs';\n","import {\n extractMetadata,\n type McpAuthOptions,\n type AuthResult,\n normalizeApiKeys,\n validateApiKey,\n generateRequestId,\n} from '@mcp-weave/nestjs';\nimport type { Request, Response, NextFunction, RequestHandler } from 'express';\n\n/**\n * Options for MCP Express middleware\n */\nexport interface McpMiddlewareOptions {\n /**\n * Base path for MCP endpoints (default: '/mcp')\n */\n basePath?: string;\n\n /**\n * Enable CORS headers (default: true)\n */\n cors?: boolean;\n\n /**\n * Custom CORS origin (default: '*')\n */\n corsOrigin?: string;\n\n /**\n * Authentication options\n */\n auth?: McpAuthOptions;\n}\n\n/**\n * Internal state for MCP middleware\n */\ninterface McpMiddlewareState {\n metadata: ReturnType<typeof extractMetadata>;\n target: Function;\n}\n\n/**\n * Create Express middleware for an MCP server\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { createMcpMiddleware, McpServer, McpTool } from '@mcp-weave/express';\n *\n * @McpServer({ name: 'my-server', version: '1.0.0' })\n * class MyServer {\n * @McpTool({ name: 'hello', description: 'Say hello' })\n * hello(input: { name: string }) {\n * return `Hello, ${input.name}!`;\n * }\n * }\n *\n * const app = express();\n * app.use('/mcp', createMcpMiddleware(MyServer));\n * app.listen(3000);\n * ```\n */\nexport function createMcpMiddleware(\n target: Function,\n options: McpMiddlewareOptions = {}\n): RequestHandler {\n const { cors = true, corsOrigin = '*', auth } = options;\n\n // Initialize metadata\n const state: McpMiddlewareState = {\n metadata: extractMetadata(target),\n target,\n };\n\n // Setup auth\n const apiKeys = normalizeApiKeys(auth?.apiKeys);\n const headerName = auth?.headerName ?? 'x-api-key';\n const queryParamName = auth?.queryParamName ?? 'api_key';\n\n // Auth helper\n const authenticateRequest = (req: Request): AuthResult => {\n if (!auth?.enabled) {\n return { success: true, clientId: 'anonymous' };\n }\n\n // Extract token from header, auth header, or query param\n let token = req.headers[headerName.toLowerCase()] as string | undefined;\n\n if (!token) {\n const authHeader = req.headers['authorization'];\n if (authHeader && authHeader.startsWith('Bearer ')) {\n token = authHeader.slice(7);\n }\n }\n\n if (!token) {\n token = req.query[queryParamName] as string | undefined;\n }\n\n return validateApiKey(token, apiKeys);\n };\n\n return async (req: Request, res: Response, next: NextFunction): Promise<void> => {\n const requestId = generateRequestId();\n\n // Attach request ID to response\n res.setHeader('X-Request-Id', requestId);\n\n // Handle CORS\n if (cors) {\n res.setHeader('Access-Control-Allow-Origin', corsOrigin);\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Api-Key');\n\n if (req.method === 'OPTIONS') {\n res.status(200).end();\n return;\n }\n }\n\n const path = req.path;\n\n // Health check (no auth required)\n if (path === '/health') {\n res.json({\n status: 'ok',\n server: state.metadata.server?.name,\n version: state.metadata.server?.version,\n authEnabled: auth?.enabled ?? false,\n });\n return;\n }\n\n // Authenticate request\n const authResult = authenticateRequest(req);\n if (!authResult.success) {\n auth?.onAuthFailure?.(\n req as unknown as import('http').IncomingMessage,\n authResult.error ?? 'Auth failed'\n );\n res.status(401).json({\n error: 'Unauthorized',\n message: authResult.error ?? 'Authentication required',\n requestId,\n });\n return;\n }\n\n // Log authenticated request\n if (auth?.enabled) {\n console.log(\n `[${requestId}] ${authResult.clientName ?? authResult.clientId} - ${req.method} ${path}`\n );\n }\n\n try {\n // Server info\n if (path === '/') {\n res.json({\n status: 'ok',\n server: state.metadata.server?.name,\n version: state.metadata.server?.version,\n capabilities: {\n tools: state.metadata.tools.length,\n resources: state.metadata.resources.length,\n prompts: state.metadata.prompts.length,\n },\n client: authResult.clientName,\n requestId,\n });\n return;\n }\n\n // List tools\n if (path === '/tools' && req.method === 'GET') {\n res.json({\n tools: state.metadata.tools.map(t => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n })),\n });\n return;\n }\n\n // Call tool\n if (path === '/tools/call' && req.method === 'POST') {\n const { name, arguments: args } = req.body as {\n name: string;\n arguments?: Record<string, unknown>;\n };\n\n if (!name) {\n res.status(400).json({ error: 'Tool name is required' });\n return;\n }\n\n const tool = state.metadata.tools.find(t => t.name === name);\n if (!tool) {\n res.status(404).json({ error: `Tool not found: ${name}` });\n return;\n }\n\n // Execute tool via internal server\n const instance = new (state.target as new () => unknown)();\n const method = Reflect.get(instance as object, tool.propertyKey);\n\n if (typeof method !== 'function') {\n res.status(500).json({ error: `Method ${String(tool.propertyKey)} not found` });\n return;\n }\n\n const result = await method.call(instance, args ?? {});\n res.json({\n content: [\n {\n type: 'text',\n text: typeof result === 'string' ? result : JSON.stringify(result),\n },\n ],\n });\n return;\n }\n\n // List resources\n if (path === '/resources' && req.method === 'GET') {\n res.json({\n resources: state.metadata.resources.map(r => ({\n uri: r.uri,\n name: r.name,\n description: r.description,\n mimeType: r.mimeType,\n })),\n });\n return;\n }\n\n // Read resource\n if (path === '/resources/read' && req.method === 'POST') {\n const { uri } = req.body as { uri: string };\n\n if (!uri) {\n res.status(400).json({ error: 'Resource URI is required' });\n return;\n }\n\n const instance = new (state.target as new () => unknown)();\n\n for (const resource of state.metadata.resources) {\n const uriParams = extractUriParams(resource.uri, uri);\n if (uriParams) {\n const method = Reflect.get(instance as object, resource.propertyKey);\n if (typeof method !== 'function') {\n res.status(500).json({ error: `Method ${String(resource.propertyKey)} not found` });\n return;\n }\n\n // Resolve params\n const params = state.metadata.params.filter(\n p => p.propertyKey === resource.propertyKey && p.type === 'param'\n );\n const args: unknown[] = [];\n for (const param of params) {\n if (param.name) {\n args[param.parameterIndex] = uriParams[param.name];\n }\n }\n\n const result = await method.apply(instance, args);\n res.json(result);\n return;\n }\n }\n\n res.status(404).json({ error: `Resource not found: ${uri}` });\n return;\n }\n\n // List prompts\n if (path === '/prompts' && req.method === 'GET') {\n res.json({\n prompts: state.metadata.prompts.map(p => ({\n name: p.name,\n description: p.description,\n arguments: p.arguments,\n })),\n });\n return;\n }\n\n // Get prompt\n if (path === '/prompts/get' && req.method === 'POST') {\n const { name, arguments: args } = req.body as {\n name: string;\n arguments?: Record<string, unknown>;\n };\n\n if (!name) {\n res.status(400).json({ error: 'Prompt name is required' });\n return;\n }\n\n const prompt = state.metadata.prompts.find(p => p.name === name);\n if (!prompt) {\n res.status(404).json({ error: `Prompt not found: ${name}` });\n return;\n }\n\n const instance = new (state.target as new () => unknown)();\n const method = Reflect.get(instance as object, prompt.propertyKey);\n\n if (typeof method !== 'function') {\n res.status(500).json({ error: `Method ${String(prompt.propertyKey)} not found` });\n return;\n }\n\n // Resolve prompt args\n const params = state.metadata.params.filter(\n p => p.propertyKey === prompt.propertyKey && p.type === 'promptArg'\n );\n const methodArgs: unknown[] = [];\n for (const param of params) {\n if (param.name && args) {\n methodArgs[param.parameterIndex] = args[param.name];\n }\n }\n\n const result = await method.apply(instance, methodArgs);\n res.json(result);\n return;\n }\n\n // Not found\n next();\n } catch (error) {\n res.status(500).json({\n error: error instanceof Error ? error.message : 'Internal server error',\n });\n }\n };\n}\n\n/**\n * Extract parameters from a URI template\n */\nfunction extractUriParams(template: string, uri: string): Record<string, string> | null {\n const paramNames: string[] = [];\n const regexStr = template.replace(/\\{(\\w+)\\}/g, (_, name) => {\n paramNames.push(name);\n return '([^/]+)';\n });\n\n const regex = new RegExp(`^${regexStr}$`);\n const match = uri.match(regex);\n\n if (!match) return null;\n\n const params: Record<string, string> = {};\n paramNames.forEach((name, index) => {\n params[name] = match[index + 1] ?? '';\n });\n\n return params;\n}\n","import { extractMetadata } from '@mcp-weave/nestjs';\nimport express, { type Express, type RequestHandler } from 'express';\n\nimport { createMcpMiddleware, type McpMiddlewareOptions } from './middleware.js';\n\n/**\n * Options for MCP Express server\n */\nexport interface McpExpressOptions extends McpMiddlewareOptions {\n /**\n * Port to listen on (default: 3000)\n */\n port?: number;\n\n /**\n * Host to bind to (default: '0.0.0.0')\n */\n host?: string;\n\n /**\n * Custom Express app (if you want to add other middleware)\n */\n app?: Express;\n}\n\n/**\n * Standalone Express server for MCP\n *\n * @example\n * ```typescript\n * import { McpExpressServer, McpServer, McpTool } from '@mcp-weave/express';\n *\n * @McpServer({ name: 'my-server', version: '1.0.0' })\n * class MyServer {\n * @McpTool({ name: 'hello', description: 'Say hello' })\n * hello(input: { name: string }) {\n * return `Hello, ${input.name}!`;\n * }\n * }\n *\n * const server = new McpExpressServer(MyServer);\n * await server.start();\n * // Server running at http://localhost:3000/mcp\n * ```\n */\nexport class McpExpressServer {\n private app: Express;\n private target: Function;\n private options: McpExpressOptions;\n private httpServer?: ReturnType<Express['listen']>;\n\n constructor(target: Function, options: McpExpressOptions = {}) {\n this.target = target;\n this.options = options;\n this.app = options.app ?? express();\n\n // Setup middleware\n this.app.use(express.json() as RequestHandler);\n this.app.use(\n options.basePath ?? '/mcp',\n createMcpMiddleware(target, options) as RequestHandler\n );\n }\n\n /**\n * Start the Express server\n */\n async start(): Promise<void> {\n const port = this.options.port ?? 3000;\n const host = this.options.host ?? '0.0.0.0';\n const basePath = this.options.basePath ?? '/mcp';\n\n const metadata = extractMetadata(this.target);\n\n return new Promise(resolve => {\n this.httpServer = this.app.listen(port, host, () => {\n console.log(`🚀 MCP Express server '${metadata.server?.name}' started`);\n console.log(\n ` Base URL: http://${host === '0.0.0.0' ? 'localhost' : host}:${port}${basePath}`\n );\n console.log(\n ` Health: http://${host === '0.0.0.0' ? 'localhost' : host}:${port}${basePath}/health`\n );\n console.log(` Tools: ${metadata.tools.length}`);\n console.log(` Resources: ${metadata.resources.length}`);\n console.log(` Prompts: ${metadata.prompts.length}`);\n resolve();\n });\n });\n }\n\n /**\n * Stop the server\n */\n async stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.httpServer) {\n this.httpServer.close(err => {\n if (err) reject(err);\n else resolve();\n });\n } else {\n resolve();\n }\n });\n }\n\n /**\n * Get the Express app instance\n */\n getApp(): Express {\n return this.app;\n }\n}\n","import { Router, type RequestHandler } from 'express';\n\nimport { createMcpMiddleware, type McpMiddlewareOptions } from './middleware.js';\n\n/**\n * Create an Express Router for MCP endpoints\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { createMcpRouter, McpServer, McpTool } from '@mcp-weave/express';\n *\n * @McpServer({ name: 'my-server', version: '1.0.0' })\n * class MyServer {\n * @McpTool({ name: 'hello', description: 'Say hello' })\n * hello(input: { name: string }) {\n * return `Hello, ${input.name}!`;\n * }\n * }\n *\n * const app = express();\n * app.use(express.json());\n * app.use('/api/mcp', createMcpRouter(MyServer));\n * app.listen(3000);\n * ```\n */\nexport function createMcpRouter(target: Function, options: McpMiddlewareOptions = {}): Router {\n const router = Router();\n router.use(createMcpMiddleware(target, options) as RequestHandler);\n return router;\n}\n"],"mappings":"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","names":["import_nestjs","express","import_express","import_nestjs"]}
package/dist/index.mjs CHANGED
@@ -1,24 +1,77 @@
1
1
  // src/middleware.ts
2
- import { extractMetadata } from "@mcp-weave/nestjs";
2
+ import {
3
+ extractMetadata,
4
+ normalizeApiKeys,
5
+ validateApiKey,
6
+ generateRequestId
7
+ } from "@mcp-weave/nestjs";
3
8
  function createMcpMiddleware(target, options = {}) {
4
- const { cors = true, corsOrigin = "*" } = options;
9
+ const { cors = true, corsOrigin = "*", auth } = options;
5
10
  const state = {
6
11
  metadata: extractMetadata(target),
7
12
  target
8
13
  };
14
+ const apiKeys = normalizeApiKeys(auth?.apiKeys);
15
+ const headerName = auth?.headerName ?? "x-api-key";
16
+ const queryParamName = auth?.queryParamName ?? "api_key";
17
+ const authenticateRequest = (req) => {
18
+ if (!auth?.enabled) {
19
+ return { success: true, clientId: "anonymous" };
20
+ }
21
+ let token = req.headers[headerName.toLowerCase()];
22
+ if (!token) {
23
+ const authHeader = req.headers["authorization"];
24
+ if (authHeader && authHeader.startsWith("Bearer ")) {
25
+ token = authHeader.slice(7);
26
+ }
27
+ }
28
+ if (!token) {
29
+ token = req.query[queryParamName];
30
+ }
31
+ return validateApiKey(token, apiKeys);
32
+ };
9
33
  return async (req, res, next) => {
34
+ const requestId = generateRequestId();
35
+ res.setHeader("X-Request-Id", requestId);
10
36
  if (cors) {
11
37
  res.setHeader("Access-Control-Allow-Origin", corsOrigin);
12
38
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
13
- res.setHeader("Access-Control-Allow-Headers", "Content-Type");
39
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Api-Key");
14
40
  if (req.method === "OPTIONS") {
15
41
  res.status(200).end();
16
42
  return;
17
43
  }
18
44
  }
19
45
  const path = req.path;
46
+ if (path === "/health") {
47
+ res.json({
48
+ status: "ok",
49
+ server: state.metadata.server?.name,
50
+ version: state.metadata.server?.version,
51
+ authEnabled: auth?.enabled ?? false
52
+ });
53
+ return;
54
+ }
55
+ const authResult = authenticateRequest(req);
56
+ if (!authResult.success) {
57
+ auth?.onAuthFailure?.(
58
+ req,
59
+ authResult.error ?? "Auth failed"
60
+ );
61
+ res.status(401).json({
62
+ error: "Unauthorized",
63
+ message: authResult.error ?? "Authentication required",
64
+ requestId
65
+ });
66
+ return;
67
+ }
68
+ if (auth?.enabled) {
69
+ console.log(
70
+ `[${requestId}] ${authResult.clientName ?? authResult.clientId} - ${req.method} ${path}`
71
+ );
72
+ }
20
73
  try {
21
- if (path === "/health" || path === "/") {
74
+ if (path === "/") {
22
75
  res.json({
23
76
  status: "ok",
24
77
  server: state.metadata.server?.name,
@@ -27,7 +80,9 @@ function createMcpMiddleware(target, options = {}) {
27
80
  tools: state.metadata.tools.length,
28
81
  resources: state.metadata.resources.length,
29
82
  prompts: state.metadata.prompts.length
30
- }
83
+ },
84
+ client: authResult.clientName,
85
+ requestId
31
86
  });
32
87
  return;
33
88
  }
@@ -177,8 +232,8 @@ function extractUriParams(template, uri) {
177
232
  }
178
233
 
179
234
  // src/server.ts
180
- import express from "express";
181
235
  import { extractMetadata as extractMetadata2 } from "@mcp-weave/nestjs";
236
+ import express from "express";
182
237
  var McpExpressServer = class {
183
238
  app;
184
239
  target;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/middleware.ts","../src/server.ts","../src/router.ts","../src/index.ts"],"sourcesContent":["import type { Request, Response, NextFunction, RequestHandler } from 'express';\nimport { extractMetadata } from '@mcp-weave/nestjs';\n\n/**\n * Options for MCP Express middleware\n */\nexport interface McpMiddlewareOptions {\n /**\n * Base path for MCP endpoints (default: '/mcp')\n */\n basePath?: string;\n\n /**\n * Enable CORS headers (default: true)\n */\n cors?: boolean;\n\n /**\n * Custom CORS origin (default: '*')\n */\n corsOrigin?: string;\n}\n\n/**\n * Internal state for MCP middleware\n */\ninterface McpMiddlewareState {\n metadata: ReturnType<typeof extractMetadata>;\n target: Function;\n}\n\n/**\n * Create Express middleware for an MCP server\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { createMcpMiddleware, McpServer, McpTool } from '@mcp-weave/express';\n *\n * @McpServer({ name: 'my-server', version: '1.0.0' })\n * class MyServer {\n * @McpTool({ name: 'hello', description: 'Say hello' })\n * hello(input: { name: string }) {\n * return `Hello, ${input.name}!`;\n * }\n * }\n *\n * const app = express();\n * app.use('/mcp', createMcpMiddleware(MyServer));\n * app.listen(3000);\n * ```\n */\nexport function createMcpMiddleware(\n target: Function,\n options: McpMiddlewareOptions = {}\n): RequestHandler {\n const { cors = true, corsOrigin = '*' } = options;\n\n // Initialize metadata\n const state: McpMiddlewareState = {\n metadata: extractMetadata(target),\n target,\n };\n\n return async (req: Request, res: Response, next: NextFunction): Promise<void> => {\n // Handle CORS\n if (cors) {\n res.setHeader('Access-Control-Allow-Origin', corsOrigin);\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.status(200).end();\n return;\n }\n }\n\n const path = req.path;\n\n try {\n // Health check\n if (path === '/health' || path === '/') {\n res.json({\n status: 'ok',\n server: state.metadata.server?.name,\n version: state.metadata.server?.version,\n capabilities: {\n tools: state.metadata.tools.length,\n resources: state.metadata.resources.length,\n prompts: state.metadata.prompts.length,\n },\n });\n return;\n }\n\n // List tools\n if (path === '/tools' && req.method === 'GET') {\n res.json({\n tools: state.metadata.tools.map(t => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n })),\n });\n return;\n }\n\n // Call tool\n if (path === '/tools/call' && req.method === 'POST') {\n const { name, arguments: args } = req.body as {\n name: string;\n arguments?: Record<string, unknown>;\n };\n\n if (!name) {\n res.status(400).json({ error: 'Tool name is required' });\n return;\n }\n\n const tool = state.metadata.tools.find(t => t.name === name);\n if (!tool) {\n res.status(404).json({ error: `Tool not found: ${name}` });\n return;\n }\n\n // Execute tool via internal server\n const instance = new (state.target as new () => unknown)();\n const method = Reflect.get(instance as object, tool.propertyKey);\n\n if (typeof method !== 'function') {\n res.status(500).json({ error: `Method ${String(tool.propertyKey)} not found` });\n return;\n }\n\n const result = await method.call(instance, args ?? {});\n res.json({\n content: [\n {\n type: 'text',\n text: typeof result === 'string' ? result : JSON.stringify(result),\n },\n ],\n });\n return;\n }\n\n // List resources\n if (path === '/resources' && req.method === 'GET') {\n res.json({\n resources: state.metadata.resources.map(r => ({\n uri: r.uri,\n name: r.name,\n description: r.description,\n mimeType: r.mimeType,\n })),\n });\n return;\n }\n\n // Read resource\n if (path === '/resources/read' && req.method === 'POST') {\n const { uri } = req.body as { uri: string };\n\n if (!uri) {\n res.status(400).json({ error: 'Resource URI is required' });\n return;\n }\n\n const instance = new (state.target as new () => unknown)();\n\n for (const resource of state.metadata.resources) {\n const uriParams = extractUriParams(resource.uri, uri);\n if (uriParams) {\n const method = Reflect.get(instance as object, resource.propertyKey);\n if (typeof method !== 'function') {\n res.status(500).json({ error: `Method ${String(resource.propertyKey)} not found` });\n return;\n }\n\n // Resolve params\n const params = state.metadata.params.filter(\n p => p.propertyKey === resource.propertyKey && p.type === 'param'\n );\n const args: unknown[] = [];\n for (const param of params) {\n if (param.name) {\n args[param.parameterIndex] = uriParams[param.name];\n }\n }\n\n const result = await method.apply(instance, args);\n res.json(result);\n return;\n }\n }\n\n res.status(404).json({ error: `Resource not found: ${uri}` });\n return;\n }\n\n // List prompts\n if (path === '/prompts' && req.method === 'GET') {\n res.json({\n prompts: state.metadata.prompts.map(p => ({\n name: p.name,\n description: p.description,\n arguments: p.arguments,\n })),\n });\n return;\n }\n\n // Get prompt\n if (path === '/prompts/get' && req.method === 'POST') {\n const { name, arguments: args } = req.body as {\n name: string;\n arguments?: Record<string, unknown>;\n };\n\n if (!name) {\n res.status(400).json({ error: 'Prompt name is required' });\n return;\n }\n\n const prompt = state.metadata.prompts.find(p => p.name === name);\n if (!prompt) {\n res.status(404).json({ error: `Prompt not found: ${name}` });\n return;\n }\n\n const instance = new (state.target as new () => unknown)();\n const method = Reflect.get(instance as object, prompt.propertyKey);\n\n if (typeof method !== 'function') {\n res.status(500).json({ error: `Method ${String(prompt.propertyKey)} not found` });\n return;\n }\n\n // Resolve prompt args\n const params = state.metadata.params.filter(\n p => p.propertyKey === prompt.propertyKey && p.type === 'promptArg'\n );\n const methodArgs: unknown[] = [];\n for (const param of params) {\n if (param.name && args) {\n methodArgs[param.parameterIndex] = args[param.name];\n }\n }\n\n const result = await method.apply(instance, methodArgs);\n res.json(result);\n return;\n }\n\n // Not found\n next();\n } catch (error) {\n res.status(500).json({\n error: error instanceof Error ? error.message : 'Internal server error',\n });\n }\n };\n}\n\n/**\n * Extract parameters from a URI template\n */\nfunction extractUriParams(template: string, uri: string): Record<string, string> | null {\n const paramNames: string[] = [];\n const regexStr = template.replace(/\\{(\\w+)\\}/g, (_, name) => {\n paramNames.push(name);\n return '([^/]+)';\n });\n\n const regex = new RegExp(`^${regexStr}$`);\n const match = uri.match(regex);\n\n if (!match) return null;\n\n const params: Record<string, string> = {};\n paramNames.forEach((name, index) => {\n params[name] = match[index + 1] ?? '';\n });\n\n return params;\n}\n","import express, { type Express, type RequestHandler } from 'express';\nimport { createMcpMiddleware, type McpMiddlewareOptions } from './middleware.js';\nimport { extractMetadata } from '@mcp-weave/nestjs';\n\n/**\n * Options for MCP Express server\n */\nexport interface McpExpressOptions extends McpMiddlewareOptions {\n /**\n * Port to listen on (default: 3000)\n */\n port?: number;\n\n /**\n * Host to bind to (default: '0.0.0.0')\n */\n host?: string;\n\n /**\n * Custom Express app (if you want to add other middleware)\n */\n app?: Express;\n}\n\n/**\n * Standalone Express server for MCP\n *\n * @example\n * ```typescript\n * import { McpExpressServer, McpServer, McpTool } from '@mcp-weave/express';\n *\n * @McpServer({ name: 'my-server', version: '1.0.0' })\n * class MyServer {\n * @McpTool({ name: 'hello', description: 'Say hello' })\n * hello(input: { name: string }) {\n * return `Hello, ${input.name}!`;\n * }\n * }\n *\n * const server = new McpExpressServer(MyServer);\n * await server.start();\n * // Server running at http://localhost:3000/mcp\n * ```\n */\nexport class McpExpressServer {\n private app: Express;\n private target: Function;\n private options: McpExpressOptions;\n private httpServer?: ReturnType<Express['listen']>;\n\n constructor(target: Function, options: McpExpressOptions = {}) {\n this.target = target;\n this.options = options;\n this.app = options.app ?? express();\n\n // Setup middleware\n this.app.use(express.json() as RequestHandler);\n this.app.use(\n options.basePath ?? '/mcp',\n createMcpMiddleware(target, options) as RequestHandler\n );\n }\n\n /**\n * Start the Express server\n */\n async start(): Promise<void> {\n const port = this.options.port ?? 3000;\n const host = this.options.host ?? '0.0.0.0';\n const basePath = this.options.basePath ?? '/mcp';\n\n const metadata = extractMetadata(this.target);\n\n return new Promise(resolve => {\n this.httpServer = this.app.listen(port, host, () => {\n console.log(`🚀 MCP Express server '${metadata.server?.name}' started`);\n console.log(\n ` Base URL: http://${host === '0.0.0.0' ? 'localhost' : host}:${port}${basePath}`\n );\n console.log(\n ` Health: http://${host === '0.0.0.0' ? 'localhost' : host}:${port}${basePath}/health`\n );\n console.log(` Tools: ${metadata.tools.length}`);\n console.log(` Resources: ${metadata.resources.length}`);\n console.log(` Prompts: ${metadata.prompts.length}`);\n resolve();\n });\n });\n }\n\n /**\n * Stop the server\n */\n async stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.httpServer) {\n this.httpServer.close(err => {\n if (err) reject(err);\n else resolve();\n });\n } else {\n resolve();\n }\n });\n }\n\n /**\n * Get the Express app instance\n */\n getApp(): Express {\n return this.app;\n }\n}\n","import { Router, type RequestHandler } from 'express';\nimport { createMcpMiddleware, type McpMiddlewareOptions } from './middleware.js';\n\n/**\n * Create an Express Router for MCP endpoints\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { createMcpRouter, McpServer, McpTool } from '@mcp-weave/express';\n *\n * @McpServer({ name: 'my-server', version: '1.0.0' })\n * class MyServer {\n * @McpTool({ name: 'hello', description: 'Say hello' })\n * hello(input: { name: string }) {\n * return `Hello, ${input.name}!`;\n * }\n * }\n *\n * const app = express();\n * app.use(express.json());\n * app.use('/api/mcp', createMcpRouter(MyServer));\n * app.listen(3000);\n * ```\n */\nexport function createMcpRouter(target: Function, options: McpMiddlewareOptions = {}): Router {\n const router = Router();\n router.use(createMcpMiddleware(target, options) as RequestHandler);\n return router;\n}\n","/**\n * @mcp-weave/express\n *\n * Express middleware for MCP (Model Context Protocol) servers.\n * Provides easy integration of MCP servers with Express applications.\n */\n\nexport { createMcpMiddleware, type McpMiddlewareOptions } from './middleware.js';\nexport { McpExpressServer, type McpExpressOptions } from './server.js';\nexport { createMcpRouter } from './router.js';\n\n// Re-export common decorators from nestjs for convenience\nexport {\n McpServer,\n McpTool,\n McpResource,\n McpPrompt,\n McpInput,\n McpParam,\n McpPromptArg,\n} from '@mcp-weave/nestjs';\n"],"mappings":"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","names":["extractMetadata"]}
1
+ {"version":3,"sources":["../src/middleware.ts","../src/server.ts","../src/router.ts","../src/index.ts"],"sourcesContent":["import {\n extractMetadata,\n type McpAuthOptions,\n type AuthResult,\n normalizeApiKeys,\n validateApiKey,\n generateRequestId,\n} from '@mcp-weave/nestjs';\nimport type { Request, Response, NextFunction, RequestHandler } from 'express';\n\n/**\n * Options for MCP Express middleware\n */\nexport interface McpMiddlewareOptions {\n /**\n * Base path for MCP endpoints (default: '/mcp')\n */\n basePath?: string;\n\n /**\n * Enable CORS headers (default: true)\n */\n cors?: boolean;\n\n /**\n * Custom CORS origin (default: '*')\n */\n corsOrigin?: string;\n\n /**\n * Authentication options\n */\n auth?: McpAuthOptions;\n}\n\n/**\n * Internal state for MCP middleware\n */\ninterface McpMiddlewareState {\n metadata: ReturnType<typeof extractMetadata>;\n target: Function;\n}\n\n/**\n * Create Express middleware for an MCP server\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { createMcpMiddleware, McpServer, McpTool } from '@mcp-weave/express';\n *\n * @McpServer({ name: 'my-server', version: '1.0.0' })\n * class MyServer {\n * @McpTool({ name: 'hello', description: 'Say hello' })\n * hello(input: { name: string }) {\n * return `Hello, ${input.name}!`;\n * }\n * }\n *\n * const app = express();\n * app.use('/mcp', createMcpMiddleware(MyServer));\n * app.listen(3000);\n * ```\n */\nexport function createMcpMiddleware(\n target: Function,\n options: McpMiddlewareOptions = {}\n): RequestHandler {\n const { cors = true, corsOrigin = '*', auth } = options;\n\n // Initialize metadata\n const state: McpMiddlewareState = {\n metadata: extractMetadata(target),\n target,\n };\n\n // Setup auth\n const apiKeys = normalizeApiKeys(auth?.apiKeys);\n const headerName = auth?.headerName ?? 'x-api-key';\n const queryParamName = auth?.queryParamName ?? 'api_key';\n\n // Auth helper\n const authenticateRequest = (req: Request): AuthResult => {\n if (!auth?.enabled) {\n return { success: true, clientId: 'anonymous' };\n }\n\n // Extract token from header, auth header, or query param\n let token = req.headers[headerName.toLowerCase()] as string | undefined;\n\n if (!token) {\n const authHeader = req.headers['authorization'];\n if (authHeader && authHeader.startsWith('Bearer ')) {\n token = authHeader.slice(7);\n }\n }\n\n if (!token) {\n token = req.query[queryParamName] as string | undefined;\n }\n\n return validateApiKey(token, apiKeys);\n };\n\n return async (req: Request, res: Response, next: NextFunction): Promise<void> => {\n const requestId = generateRequestId();\n\n // Attach request ID to response\n res.setHeader('X-Request-Id', requestId);\n\n // Handle CORS\n if (cors) {\n res.setHeader('Access-Control-Allow-Origin', corsOrigin);\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Api-Key');\n\n if (req.method === 'OPTIONS') {\n res.status(200).end();\n return;\n }\n }\n\n const path = req.path;\n\n // Health check (no auth required)\n if (path === '/health') {\n res.json({\n status: 'ok',\n server: state.metadata.server?.name,\n version: state.metadata.server?.version,\n authEnabled: auth?.enabled ?? false,\n });\n return;\n }\n\n // Authenticate request\n const authResult = authenticateRequest(req);\n if (!authResult.success) {\n auth?.onAuthFailure?.(\n req as unknown as import('http').IncomingMessage,\n authResult.error ?? 'Auth failed'\n );\n res.status(401).json({\n error: 'Unauthorized',\n message: authResult.error ?? 'Authentication required',\n requestId,\n });\n return;\n }\n\n // Log authenticated request\n if (auth?.enabled) {\n console.log(\n `[${requestId}] ${authResult.clientName ?? authResult.clientId} - ${req.method} ${path}`\n );\n }\n\n try {\n // Server info\n if (path === '/') {\n res.json({\n status: 'ok',\n server: state.metadata.server?.name,\n version: state.metadata.server?.version,\n capabilities: {\n tools: state.metadata.tools.length,\n resources: state.metadata.resources.length,\n prompts: state.metadata.prompts.length,\n },\n client: authResult.clientName,\n requestId,\n });\n return;\n }\n\n // List tools\n if (path === '/tools' && req.method === 'GET') {\n res.json({\n tools: state.metadata.tools.map(t => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n })),\n });\n return;\n }\n\n // Call tool\n if (path === '/tools/call' && req.method === 'POST') {\n const { name, arguments: args } = req.body as {\n name: string;\n arguments?: Record<string, unknown>;\n };\n\n if (!name) {\n res.status(400).json({ error: 'Tool name is required' });\n return;\n }\n\n const tool = state.metadata.tools.find(t => t.name === name);\n if (!tool) {\n res.status(404).json({ error: `Tool not found: ${name}` });\n return;\n }\n\n // Execute tool via internal server\n const instance = new (state.target as new () => unknown)();\n const method = Reflect.get(instance as object, tool.propertyKey);\n\n if (typeof method !== 'function') {\n res.status(500).json({ error: `Method ${String(tool.propertyKey)} not found` });\n return;\n }\n\n const result = await method.call(instance, args ?? {});\n res.json({\n content: [\n {\n type: 'text',\n text: typeof result === 'string' ? result : JSON.stringify(result),\n },\n ],\n });\n return;\n }\n\n // List resources\n if (path === '/resources' && req.method === 'GET') {\n res.json({\n resources: state.metadata.resources.map(r => ({\n uri: r.uri,\n name: r.name,\n description: r.description,\n mimeType: r.mimeType,\n })),\n });\n return;\n }\n\n // Read resource\n if (path === '/resources/read' && req.method === 'POST') {\n const { uri } = req.body as { uri: string };\n\n if (!uri) {\n res.status(400).json({ error: 'Resource URI is required' });\n return;\n }\n\n const instance = new (state.target as new () => unknown)();\n\n for (const resource of state.metadata.resources) {\n const uriParams = extractUriParams(resource.uri, uri);\n if (uriParams) {\n const method = Reflect.get(instance as object, resource.propertyKey);\n if (typeof method !== 'function') {\n res.status(500).json({ error: `Method ${String(resource.propertyKey)} not found` });\n return;\n }\n\n // Resolve params\n const params = state.metadata.params.filter(\n p => p.propertyKey === resource.propertyKey && p.type === 'param'\n );\n const args: unknown[] = [];\n for (const param of params) {\n if (param.name) {\n args[param.parameterIndex] = uriParams[param.name];\n }\n }\n\n const result = await method.apply(instance, args);\n res.json(result);\n return;\n }\n }\n\n res.status(404).json({ error: `Resource not found: ${uri}` });\n return;\n }\n\n // List prompts\n if (path === '/prompts' && req.method === 'GET') {\n res.json({\n prompts: state.metadata.prompts.map(p => ({\n name: p.name,\n description: p.description,\n arguments: p.arguments,\n })),\n });\n return;\n }\n\n // Get prompt\n if (path === '/prompts/get' && req.method === 'POST') {\n const { name, arguments: args } = req.body as {\n name: string;\n arguments?: Record<string, unknown>;\n };\n\n if (!name) {\n res.status(400).json({ error: 'Prompt name is required' });\n return;\n }\n\n const prompt = state.metadata.prompts.find(p => p.name === name);\n if (!prompt) {\n res.status(404).json({ error: `Prompt not found: ${name}` });\n return;\n }\n\n const instance = new (state.target as new () => unknown)();\n const method = Reflect.get(instance as object, prompt.propertyKey);\n\n if (typeof method !== 'function') {\n res.status(500).json({ error: `Method ${String(prompt.propertyKey)} not found` });\n return;\n }\n\n // Resolve prompt args\n const params = state.metadata.params.filter(\n p => p.propertyKey === prompt.propertyKey && p.type === 'promptArg'\n );\n const methodArgs: unknown[] = [];\n for (const param of params) {\n if (param.name && args) {\n methodArgs[param.parameterIndex] = args[param.name];\n }\n }\n\n const result = await method.apply(instance, methodArgs);\n res.json(result);\n return;\n }\n\n // Not found\n next();\n } catch (error) {\n res.status(500).json({\n error: error instanceof Error ? error.message : 'Internal server error',\n });\n }\n };\n}\n\n/**\n * Extract parameters from a URI template\n */\nfunction extractUriParams(template: string, uri: string): Record<string, string> | null {\n const paramNames: string[] = [];\n const regexStr = template.replace(/\\{(\\w+)\\}/g, (_, name) => {\n paramNames.push(name);\n return '([^/]+)';\n });\n\n const regex = new RegExp(`^${regexStr}$`);\n const match = uri.match(regex);\n\n if (!match) return null;\n\n const params: Record<string, string> = {};\n paramNames.forEach((name, index) => {\n params[name] = match[index + 1] ?? '';\n });\n\n return params;\n}\n","import { extractMetadata } from '@mcp-weave/nestjs';\nimport express, { type Express, type RequestHandler } from 'express';\n\nimport { createMcpMiddleware, type McpMiddlewareOptions } from './middleware.js';\n\n/**\n * Options for MCP Express server\n */\nexport interface McpExpressOptions extends McpMiddlewareOptions {\n /**\n * Port to listen on (default: 3000)\n */\n port?: number;\n\n /**\n * Host to bind to (default: '0.0.0.0')\n */\n host?: string;\n\n /**\n * Custom Express app (if you want to add other middleware)\n */\n app?: Express;\n}\n\n/**\n * Standalone Express server for MCP\n *\n * @example\n * ```typescript\n * import { McpExpressServer, McpServer, McpTool } from '@mcp-weave/express';\n *\n * @McpServer({ name: 'my-server', version: '1.0.0' })\n * class MyServer {\n * @McpTool({ name: 'hello', description: 'Say hello' })\n * hello(input: { name: string }) {\n * return `Hello, ${input.name}!`;\n * }\n * }\n *\n * const server = new McpExpressServer(MyServer);\n * await server.start();\n * // Server running at http://localhost:3000/mcp\n * ```\n */\nexport class McpExpressServer {\n private app: Express;\n private target: Function;\n private options: McpExpressOptions;\n private httpServer?: ReturnType<Express['listen']>;\n\n constructor(target: Function, options: McpExpressOptions = {}) {\n this.target = target;\n this.options = options;\n this.app = options.app ?? express();\n\n // Setup middleware\n this.app.use(express.json() as RequestHandler);\n this.app.use(\n options.basePath ?? '/mcp',\n createMcpMiddleware(target, options) as RequestHandler\n );\n }\n\n /**\n * Start the Express server\n */\n async start(): Promise<void> {\n const port = this.options.port ?? 3000;\n const host = this.options.host ?? '0.0.0.0';\n const basePath = this.options.basePath ?? '/mcp';\n\n const metadata = extractMetadata(this.target);\n\n return new Promise(resolve => {\n this.httpServer = this.app.listen(port, host, () => {\n console.log(`🚀 MCP Express server '${metadata.server?.name}' started`);\n console.log(\n ` Base URL: http://${host === '0.0.0.0' ? 'localhost' : host}:${port}${basePath}`\n );\n console.log(\n ` Health: http://${host === '0.0.0.0' ? 'localhost' : host}:${port}${basePath}/health`\n );\n console.log(` Tools: ${metadata.tools.length}`);\n console.log(` Resources: ${metadata.resources.length}`);\n console.log(` Prompts: ${metadata.prompts.length}`);\n resolve();\n });\n });\n }\n\n /**\n * Stop the server\n */\n async stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.httpServer) {\n this.httpServer.close(err => {\n if (err) reject(err);\n else resolve();\n });\n } else {\n resolve();\n }\n });\n }\n\n /**\n * Get the Express app instance\n */\n getApp(): Express {\n return this.app;\n }\n}\n","import { Router, type RequestHandler } from 'express';\n\nimport { createMcpMiddleware, type McpMiddlewareOptions } from './middleware.js';\n\n/**\n * Create an Express Router for MCP endpoints\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { createMcpRouter, McpServer, McpTool } from '@mcp-weave/express';\n *\n * @McpServer({ name: 'my-server', version: '1.0.0' })\n * class MyServer {\n * @McpTool({ name: 'hello', description: 'Say hello' })\n * hello(input: { name: string }) {\n * return `Hello, ${input.name}!`;\n * }\n * }\n *\n * const app = express();\n * app.use(express.json());\n * app.use('/api/mcp', createMcpRouter(MyServer));\n * app.listen(3000);\n * ```\n */\nexport function createMcpRouter(target: Function, options: McpMiddlewareOptions = {}): Router {\n const router = Router();\n router.use(createMcpMiddleware(target, options) as RequestHandler);\n return router;\n}\n","/**\n * @mcp-weave/express\n *\n * Express middleware for MCP (Model Context Protocol) servers.\n * Provides easy integration of MCP servers with Express applications.\n */\n\nexport { createMcpMiddleware, type McpMiddlewareOptions } from './middleware.js';\nexport { McpExpressServer, type McpExpressOptions } from './server.js';\nexport { createMcpRouter } from './router.js';\n\n// Re-export common decorators from nestjs for convenience\nexport {\n McpServer,\n McpTool,\n McpResource,\n McpPrompt,\n McpInput,\n McpParam,\n McpPromptArg,\n} from '@mcp-weave/nestjs';\n"],"mappings":"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","names":["extractMetadata","extractMetadata"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-weave/express",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Express middleware for MCP servers",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -17,7 +17,7 @@
17
17
  ],
18
18
  "dependencies": {
19
19
  "@modelcontextprotocol/sdk": "^1.0.0",
20
- "@mcp-weave/nestjs": "0.2.0"
20
+ "@mcp-weave/nestjs": "0.3.0"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/express": "^4.17.21",