@smartbear/mcp 0.12.1 → 0.13.1

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.
Files changed (89) hide show
  1. package/README.md +30 -6
  2. package/dist/bugsnag/client/api/CurrentUser.js +50 -26
  3. package/dist/bugsnag/client/api/Error.js +156 -93
  4. package/dist/bugsnag/client/api/Project.js +398 -276
  5. package/dist/bugsnag/client/api/api.js +4087 -3837
  6. package/dist/bugsnag/client/api/base.js +155 -173
  7. package/dist/bugsnag/client/api/configuration.js +28 -25
  8. package/dist/bugsnag/client/filters.js +11 -20
  9. package/dist/bugsnag/client.js +1398 -1281
  10. package/dist/bugsnag/input-schemas.js +39 -57
  11. package/dist/collaborator/client.js +335 -371
  12. package/dist/common/bugsnag.js +5 -3
  13. package/dist/common/cache.js +50 -57
  14. package/dist/common/client-registry.js +106 -119
  15. package/dist/common/info.js +7 -3
  16. package/dist/common/register-clients.js +0 -16
  17. package/dist/common/server.js +270 -228
  18. package/dist/common/tools.js +19 -0
  19. package/dist/common/transport-http.js +252 -343
  20. package/dist/common/transport-stdio.js +40 -37
  21. package/dist/common/zod-utils.js +20 -0
  22. package/dist/index.js +18 -23
  23. package/dist/package.json.js +11 -0
  24. package/dist/pactflow/client/ai.js +142 -169
  25. package/dist/pactflow/client/base.js +41 -51
  26. package/dist/pactflow/client/prompt-utils.js +93 -84
  27. package/dist/pactflow/client/prompts.js +95 -92
  28. package/dist/pactflow/client/tools.js +94 -83
  29. package/dist/pactflow/client/utils.js +60 -64
  30. package/dist/pactflow/client.js +399 -320
  31. package/dist/qmetry/client/api/client-api.js +43 -41
  32. package/dist/qmetry/client/api/error-handler.js +264 -310
  33. package/dist/qmetry/client/auto-resolve.js +78 -99
  34. package/dist/qmetry/client/automation.js +139 -162
  35. package/dist/qmetry/client/handlers.js +49 -46
  36. package/dist/qmetry/client/issues.js +133 -115
  37. package/dist/qmetry/client/project.js +153 -174
  38. package/dist/qmetry/client/requirement.js +82 -70
  39. package/dist/qmetry/client/testcase.js +240 -208
  40. package/dist/qmetry/client/testsuite.js +332 -293
  41. package/dist/qmetry/client/tools/automation-tools.js +291 -288
  42. package/dist/qmetry/client/tools/index.js +16 -13
  43. package/dist/qmetry/client/tools/issue-tools.js +534 -543
  44. package/dist/qmetry/client/tools/project-tools.js +635 -656
  45. package/dist/qmetry/client/tools/requirement-tools.js +525 -528
  46. package/dist/qmetry/client/tools/testcase-tools.js +773 -786
  47. package/dist/qmetry/client/tools/testsuite-tools.js +1069 -1083
  48. package/dist/qmetry/client/utils.js +8 -14
  49. package/dist/qmetry/client.js +111 -109
  50. package/dist/qmetry/config/constants.js +48 -44
  51. package/dist/qmetry/config/rest-endpoints.js +51 -48
  52. package/dist/qmetry/types/automation.js +7 -7
  53. package/dist/qmetry/types/common.js +763 -1049
  54. package/dist/qmetry/types/issues.js +26 -19
  55. package/dist/qmetry/types/project.js +32 -25
  56. package/dist/qmetry/types/requirements.js +26 -21
  57. package/dist/qmetry/types/testcase.js +55 -44
  58. package/dist/qmetry/types/testsuite.js +66 -52
  59. package/dist/reflect/client.js +284 -226
  60. package/dist/swagger/client/api.js +645 -662
  61. package/dist/swagger/client/configuration.js +31 -33
  62. package/dist/swagger/client/portal-types.js +204 -244
  63. package/dist/swagger/client/registry-types.js +62 -96
  64. package/dist/swagger/client/tools.js +148 -158
  65. package/dist/swagger/client/user-management-types.js +11 -22
  66. package/dist/swagger/client.js +143 -135
  67. package/dist/swagger/config-utils.js +10 -16
  68. package/dist/zephyr/client.js +43 -42
  69. package/dist/zephyr/common/api-client.js +35 -30
  70. package/dist/zephyr/common/auth-service.js +16 -13
  71. package/dist/zephyr/common/rest-api-schemas.js +3173 -5146
  72. package/dist/zephyr/tool/environment/get-environments.js +66 -66
  73. package/dist/zephyr/tool/priority/get-priorities.js +41 -41
  74. package/dist/zephyr/tool/project/get-project.js +37 -37
  75. package/dist/zephyr/tool/project/get-projects.js +46 -46
  76. package/dist/zephyr/tool/status/get-statuses.js +47 -47
  77. package/dist/zephyr/tool/test-case/get-test-case.js +37 -37
  78. package/dist/zephyr/tool/test-case/get-test-cases.js +62 -62
  79. package/dist/zephyr/tool/test-cycle/get-test-cycle.js +37 -37
  80. package/dist/zephyr/tool/test-cycle/get-test-cycles.js +70 -70
  81. package/dist/zephyr/tool/test-execution/get-test-execution.js +37 -37
  82. package/dist/zephyr/tool/test-execution/get-test-executions.js +43 -43
  83. package/package.json +5 -5
  84. package/dist/bugsnag/client/api/index.js +0 -6
  85. package/dist/common/types.js +0 -6
  86. package/dist/qmetry/client/tools/types.js +0 -1
  87. package/dist/swagger/client/index.js +0 -6
  88. package/dist/tests/unit/bugsnag/utils/factories.js +0 -86
  89. package/dist/zephyr/tool/zephyr-tool.js +0 -1
@@ -5,373 +5,282 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
5
5
  import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
6
6
  import { clientRegistry } from "./client-registry.js";
7
7
  import { SmartBearMcpServer } from "./server.js";
8
- /**
9
- * Run server in HTTP mode with Streamable HTTP transport
10
- * Supports both SSE (legacy) and StreamableHTTP transports for backwards compatibility
11
- */
12
- export async function runHttpMode() {
13
- const PORT = process.env.PORT ? Number.parseInt(process.env.PORT, 10) : 3000;
14
- const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(",") || [
15
- "http://localhost:3000",
16
- ];
17
- // Store transports by session ID
18
- const transports = new Map();
19
- // Get dynamic list of allowed headers from registered clients
20
- const allowedAuthHeaders = getHttpHeaders();
21
- const allowedHeaders = [
22
- "Content-Type",
23
- "Authorization",
24
- "MCP-Session-Id", // Required for StreamableHTTP
25
- "x-custom-auth-headers", // used by mcp-inspector
26
- ...allowedAuthHeaders,
27
- ].join(", ");
28
- const httpServer = createServer(async (req, res) => {
29
- // Enable CORS
30
- const origin = req.headers.origin || "";
31
- if (allowedOrigins.includes(origin)) {
32
- res.setHeader("Access-Control-Allow-Origin", origin);
33
- }
34
- res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
35
- res.setHeader("Access-Control-Allow-Headers", allowedHeaders);
36
- res.setHeader("Access-Control-Expose-Headers", "MCP-Session-Id");
37
- if (req.method === "OPTIONS") {
38
- res.writeHead(200);
39
- res.end();
40
- return;
41
- }
42
- const url = new URL(req.url || "/", `http://${req.headers.host}`);
43
- // HEALTH CHECK ENDPOINT
44
- if (req.method === "GET" && url.pathname === "/health") {
45
- res.writeHead(200, { "Content-Type": "application/json" });
46
- res.end(JSON.stringify({ status: "ok", timestamp: new Date().toISOString() }));
47
- return;
48
- }
49
- // STREAMABLE HTTP ENDPOINT (modern, preferred)
50
- if (url.pathname === "/mcp") {
51
- await handleStreamableHttpRequest(req, res, transports);
52
- return;
53
- }
54
- // LEGACY SSE ENDPOINT (for backwards compatibility)
55
- if (req.method === "GET" && url.pathname === "/sse") {
56
- await handleLegacySseRequest(req, res, transports);
57
- return;
58
- }
59
- if (req.method === "POST" && url.pathname === "/message") {
60
- await handleLegacyMessageRequest(req, res, url, transports);
61
- return;
62
- }
63
- res.writeHead(404, { "Content-Type": "text/plain" });
64
- res.end("Not found");
65
- });
66
- httpServer.listen(PORT, () => {
67
- console.log(`[MCP HTTP Server] Listening on http://localhost:${PORT}`);
68
- console.log(`[MCP HTTP Server] Health check: http://localhost:${PORT}/health`);
69
- console.log(`[MCP HTTP Server] Modern endpoint: http://localhost:${PORT}/mcp (Streamable HTTP)`);
70
- console.log(`[MCP HTTP Server] Legacy endpoint: http://localhost:${PORT}/sse (SSE)`);
71
- const headerHelp = getHttpHeadersHelp();
72
- if (headerHelp.length > 0) {
73
- console.log(`[MCP HTTP Server] Send configuration headers:\n${headerHelp.join("\n")}`);
74
- }
75
- else {
76
- console.warn(`[MCP HTTP Server] No clients support HTTP header configuration`);
77
- }
78
- });
8
+ import { isOptionalType } from "./zod-utils.js";
9
+ async function runHttpMode() {
10
+ const PORT = process.env.PORT ? Number.parseInt(process.env.PORT, 10) : 3e3;
11
+ const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(",") || [
12
+ "http://localhost:3000"
13
+ ];
14
+ const transports = /* @__PURE__ */ new Map();
15
+ const allowedAuthHeaders = getHttpHeaders();
16
+ const allowedHeaders = [
17
+ "Content-Type",
18
+ "Authorization",
19
+ "MCP-Session-Id",
20
+ // Required for StreamableHTTP
21
+ "x-custom-auth-headers",
22
+ // used by mcp-inspector
23
+ ...allowedAuthHeaders
24
+ ].join(", ");
25
+ const httpServer = createServer(
26
+ async (req, res) => {
27
+ const origin = req.headers.origin || "";
28
+ if (allowedOrigins.includes(origin)) {
29
+ res.setHeader("Access-Control-Allow-Origin", origin);
30
+ }
31
+ res.setHeader(
32
+ "Access-Control-Allow-Methods",
33
+ "GET, POST, DELETE, OPTIONS"
34
+ );
35
+ res.setHeader("Access-Control-Allow-Headers", allowedHeaders);
36
+ res.setHeader("Access-Control-Expose-Headers", "MCP-Session-Id");
37
+ if (req.method === "OPTIONS") {
38
+ res.writeHead(200);
39
+ res.end();
40
+ return;
41
+ }
42
+ const url = new URL(req.url || "/", `http://${req.headers.host}`);
43
+ if (req.method === "GET" && url.pathname === "/health") {
44
+ res.writeHead(200, { "Content-Type": "application/json" });
45
+ res.end(
46
+ JSON.stringify({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() })
47
+ );
48
+ return;
49
+ }
50
+ if (url.pathname === "/mcp") {
51
+ await handleStreamableHttpRequest(req, res, transports);
52
+ return;
53
+ }
54
+ if (req.method === "GET" && url.pathname === "/sse") {
55
+ await handleLegacySseRequest(req, res, transports);
56
+ return;
57
+ }
58
+ if (req.method === "POST" && url.pathname === "/message") {
59
+ await handleLegacyMessageRequest(req, res, url, transports);
60
+ return;
61
+ }
62
+ res.writeHead(404, { "Content-Type": "text/plain" });
63
+ res.end("Not found");
64
+ }
65
+ );
66
+ httpServer.listen(PORT, () => {
67
+ console.log(`[MCP HTTP Server] Listening on http://localhost:${PORT}`);
68
+ console.log(
69
+ `[MCP HTTP Server] Health check: http://localhost:${PORT}/health`
70
+ );
71
+ console.log(
72
+ `[MCP HTTP Server] Modern endpoint: http://localhost:${PORT}/mcp (Streamable HTTP)`
73
+ );
74
+ console.log(
75
+ `[MCP HTTP Server] Legacy endpoint: http://localhost:${PORT}/sse (SSE)`
76
+ );
77
+ const headerHelp = getHttpHeadersHelp();
78
+ if (headerHelp.length > 0) {
79
+ console.log(
80
+ `[MCP HTTP Server] Send configuration headers:
81
+ ${headerHelp.join("\n")}`
82
+ );
83
+ } else {
84
+ console.warn(
85
+ `[MCP HTTP Server] No clients support HTTP header configuration`
86
+ );
87
+ }
88
+ });
79
89
  }
80
- /**
81
- * Parse request body for POST requests
82
- * Reads the request stream and parses it as JSON
83
- * @returns Parsed JSON object or undefined if not a POST request or parsing fails
84
- */
85
90
  async function parseRequestBody(req) {
86
- if (req.method !== "POST") {
87
- return undefined;
88
- }
89
- let body = "";
90
- req.on("data", (chunk) => {
91
- body += chunk.toString();
92
- });
93
- return new Promise((resolve) => {
94
- req.on("end", () => {
95
- try {
96
- resolve(JSON.parse(body));
97
- }
98
- catch (error) {
99
- console.error("Error parsing request body:", error);
100
- resolve(undefined);
101
- }
102
- });
91
+ if (req.method !== "POST") {
92
+ return void 0;
93
+ }
94
+ let body = "";
95
+ req.on("data", (chunk) => {
96
+ body += chunk.toString();
97
+ });
98
+ return new Promise((resolve) => {
99
+ req.on("end", () => {
100
+ try {
101
+ resolve(JSON.parse(body));
102
+ } catch (error) {
103
+ console.error("Error parsing request body:", error);
104
+ resolve(void 0);
105
+ }
103
106
  });
107
+ });
104
108
  }
105
- /**
106
- * Get existing transport for session or return error response
107
- * Validates that the session exists and uses StreamableHTTP transport
108
- * @returns StreamableHTTPServerTransport if valid, null otherwise (with error response sent)
109
- */
110
109
  function getExistingTransport(sessionId, transports, res) {
111
- const existing = transports.get(sessionId);
112
- if (existing && existing.transport instanceof StreamableHTTPServerTransport) {
113
- return existing.transport;
114
- }
115
- // Session doesn't exist or is using a different transport (e.g., SSE)
116
- res.writeHead(400, { "Content-Type": "application/json" });
117
- res.end(JSON.stringify({
118
- jsonrpc: "2.0",
119
- error: {
120
- code: -32000,
121
- message: "Bad Request: Session exists but uses a different transport protocol",
122
- },
123
- id: null,
124
- }));
125
- return null;
110
+ const existing = transports.get(sessionId);
111
+ if (existing && existing.transport instanceof StreamableHTTPServerTransport) {
112
+ return existing.transport;
113
+ }
114
+ res.writeHead(400, { "Content-Type": "application/json" });
115
+ res.end(
116
+ JSON.stringify({
117
+ jsonrpc: "2.0",
118
+ error: {
119
+ code: -32e3,
120
+ message: "Bad Request: Session exists but uses a different transport protocol"
121
+ },
122
+ id: null
123
+ })
124
+ );
125
+ return null;
126
126
  }
127
- /**
128
- * Create new transport for initialize request
129
- * Sets up a new MCP server instance with configuration from HTTP headers,
130
- * creates a StreamableHTTP transport, and registers session lifecycle handlers
131
- * @returns StreamableHTTPServerTransport if successful, null if server initialization fails
132
- */
133
127
  async function createNewTransport(req, res, transports) {
134
- // Create and configure server with headers from the request
135
- const server = await newServer(req, res);
136
- if (!server) {
137
- return null;
128
+ const server = await newServer(req, res);
129
+ if (!server) {
130
+ return null;
131
+ }
132
+ const transport = new StreamableHTTPServerTransport({
133
+ sessionIdGenerator: () => randomUUID(),
134
+ onsessioninitialized: (newSessionId) => {
135
+ console.log(`[MCP] New session initialized: ${newSessionId}`);
136
+ transports.set(newSessionId, { server, transport });
138
137
  }
139
- // Create transport with session management
140
- const transport = new StreamableHTTPServerTransport({
141
- sessionIdGenerator: () => randomUUID(),
142
- onsessioninitialized: (newSessionId) => {
143
- console.log(`[MCP] New session initialized: ${newSessionId}`);
144
- // Store session so subsequent requests can find it
145
- transports.set(newSessionId, { server, transport });
146
- },
147
- });
148
- // Clean up session on close
149
- transport.onclose = () => {
150
- if (transport.sessionId) {
151
- console.log(`[MCP] Session closed: ${transport.sessionId}`);
152
- transports.delete(transport.sessionId);
153
- }
154
- };
155
- // Connect server to transport to start handling messages
156
- await server.connect(transport);
157
- return transport;
138
+ });
139
+ transport.onclose = () => {
140
+ if (transport.sessionId) {
141
+ console.log(`[MCP] Session closed: ${transport.sessionId}`);
142
+ transports.delete(transport.sessionId);
143
+ }
144
+ };
145
+ await server.connect(transport);
146
+ return transport;
158
147
  }
159
- /**
160
- * Handle modern Streamable HTTP requests
161
- * This is the main endpoint (/mcp) for the modern MCP StreamableHTTP transport.
162
- *
163
- * Request flow:
164
- * 1. First request (initialize): No session ID, body contains initialize request
165
- * - Creates new server + transport, generates session ID
166
- * 2. Subsequent requests: Include MCP-Session-Id header
167
- * - Routes to existing transport for the session
168
- */
169
148
  async function handleStreamableHttpRequest(req, res, transports) {
170
- try {
171
- const sessionId = req.headers["mcp-session-id"];
172
- const parsedBody = await parseRequestBody(req);
173
- let transport;
174
- // Case 1: Existing session - route to existing transport
175
- if (sessionId && transports.has(sessionId)) {
176
- const existingTransport = getExistingTransport(sessionId, transports, res);
177
- if (!existingTransport)
178
- return;
179
- transport = existingTransport;
180
- }
181
- // Case 2: New session - must be an initialize request
182
- else if (!sessionId &&
183
- req.method === "POST" &&
184
- parsedBody &&
185
- isInitializeRequest(parsedBody)) {
186
- const newTransport = await createNewTransport(req, res, transports);
187
- if (!newTransport)
188
- return;
189
- transport = newTransport;
190
- }
191
- // Case 3: Invalid request
192
- else {
193
- res.writeHead(400, { "Content-Type": "application/json" });
194
- res.end(JSON.stringify({
195
- jsonrpc: "2.0",
196
- error: {
197
- code: -32000,
198
- message: "Bad Request: Invalid request",
199
- },
200
- id: null,
201
- }));
202
- return;
203
- }
204
- // Delegate to transport to handle the MCP protocol message
205
- await transport.handleRequest(req, res, parsedBody);
206
- }
207
- catch (error) {
208
- console.error("Error handling StreamableHTTP request:", error);
209
- res.writeHead(500, { "Content-Type": "text/plain" });
210
- res.end("Internal server error");
149
+ try {
150
+ const sessionId = req.headers["mcp-session-id"];
151
+ const parsedBody = await parseRequestBody(req);
152
+ let transport;
153
+ if (sessionId && transports.has(sessionId)) {
154
+ const existingTransport = getExistingTransport(
155
+ sessionId,
156
+ transports,
157
+ res
158
+ );
159
+ if (!existingTransport) return;
160
+ transport = existingTransport;
161
+ } else if (!sessionId && req.method === "POST" && parsedBody && isInitializeRequest(parsedBody)) {
162
+ const newTransport = await createNewTransport(req, res, transports);
163
+ if (!newTransport) return;
164
+ transport = newTransport;
165
+ } else {
166
+ res.writeHead(400, { "Content-Type": "application/json" });
167
+ res.end(
168
+ JSON.stringify({
169
+ jsonrpc: "2.0",
170
+ error: {
171
+ code: -32e3,
172
+ message: "Bad Request: Invalid request"
173
+ },
174
+ id: null
175
+ })
176
+ );
177
+ return;
211
178
  }
179
+ await transport.handleRequest(req, res, parsedBody);
180
+ } catch (error) {
181
+ console.error("Error handling StreamableHTTP request:", error);
182
+ res.writeHead(500, { "Content-Type": "text/plain" });
183
+ res.end("Internal server error");
184
+ }
212
185
  }
213
- /**
214
- * Handle legacy SSE connection requests (GET /sse)
215
- *
216
- * SSE (Server-Sent Events) transport maintains a long-lived connection
217
- * for server-to-client messages, with a separate POST endpoint for client-to-server.
218
- *
219
- * This is kept for backwards compatibility with older MCP clients.
220
- * New integrations should use the modern StreamableHTTP transport (/mcp).
221
- */
222
186
  async function handleLegacySseRequest(req, res, transports) {
223
- // Create a new server instance for this connection
224
- const server = await newServer(req, res);
225
- if (!server) {
226
- return;
227
- }
228
- // SSE transport keeps the connection open and sends events to the client
229
- const transport = new SSEServerTransport("/message", res);
230
- // Store the session so POST /message requests can find it
231
- transports.set(transport.sessionId, { server, transport });
232
- // Clean up session when connection closes
233
- res.on("close", () => {
234
- transports.delete(transport.sessionId);
235
- });
236
- // Connect server to transport (this also starts the transport automatically)
237
- await server.connect(transport);
187
+ const server = await newServer(req, res);
188
+ if (!server) {
189
+ return;
190
+ }
191
+ const transport = new SSEServerTransport("/message", res);
192
+ transports.set(transport.sessionId, { server, transport });
193
+ res.on("close", () => {
194
+ transports.delete(transport.sessionId);
195
+ });
196
+ await server.connect(transport);
238
197
  }
239
- /**
240
- * Handle legacy POST message requests (POST /message?sessionId=xxx)
241
- *
242
- * This endpoint is part of the legacy SSE transport, handling client-to-server messages.
243
- * The SSE transport uses:
244
- * - GET /sse: Server-to-client events (long-lived connection)
245
- * - POST /message: Client-to-server messages (individual requests)
246
- *
247
- * New integrations should use the modern StreamableHTTP transport (/mcp).
248
- */
249
198
  async function handleLegacyMessageRequest(req, res, url, transports) {
250
- // Extract session ID from query parameter
251
- const sessionId = url.searchParams.get("sessionId");
252
- if (!sessionId) {
253
- res.writeHead(400, { "Content-Type": "text/plain" });
254
- res.end("Missing sessionId parameter");
255
- return;
256
- }
257
- // Find the session created by the SSE connection
258
- const session = transports.get(sessionId);
259
- if (!session) {
260
- res.writeHead(404, { "Content-Type": "text/plain" });
261
- res.end("Session not found");
262
- return;
263
- }
264
- // Validate this session is using SSE transport
265
- if (!(session.transport instanceof SSEServerTransport)) {
266
- res.writeHead(400, { "Content-Type": "text/plain" });
267
- res.end("Invalid transport for this endpoint");
268
- return;
199
+ const sessionId = url.searchParams.get("sessionId");
200
+ if (!sessionId) {
201
+ res.writeHead(400, { "Content-Type": "text/plain" });
202
+ res.end("Missing sessionId parameter");
203
+ return;
204
+ }
205
+ const session = transports.get(sessionId);
206
+ if (!session) {
207
+ res.writeHead(404, { "Content-Type": "text/plain" });
208
+ res.end("Session not found");
209
+ return;
210
+ }
211
+ if (!(session.transport instanceof SSEServerTransport)) {
212
+ res.writeHead(400, { "Content-Type": "text/plain" });
213
+ res.end("Invalid transport for this endpoint");
214
+ return;
215
+ }
216
+ let body = "";
217
+ req.on("data", (chunk) => {
218
+ body += chunk.toString();
219
+ });
220
+ req.on("end", async () => {
221
+ try {
222
+ const parsedBody = JSON.parse(body);
223
+ await session.transport.handlePostMessage(
224
+ req,
225
+ res,
226
+ parsedBody
227
+ );
228
+ } catch (error) {
229
+ console.error("Error handling POST message:", error);
230
+ res.writeHead(500, { "Content-Type": "text/plain" });
231
+ res.end("Internal server error");
269
232
  }
270
- // Read and parse the request body
271
- let body = "";
272
- req.on("data", (chunk) => {
273
- body += chunk.toString();
274
- });
275
- req.on("end", async () => {
276
- try {
277
- const parsedBody = JSON.parse(body);
278
- // Route message to the SSE transport for processing
279
- await session.transport.handlePostMessage(req, res, parsedBody);
280
- }
281
- catch (error) {
282
- console.error("Error handling POST message:", error);
283
- res.writeHead(500, { "Content-Type": "text/plain" });
284
- res.end("Internal server error");
285
- }
286
- });
233
+ });
287
234
  }
288
- /**
289
- * Create a new MCP server instance with configuration from HTTP headers
290
- *
291
- * Configuration is read from HTTP headers in the format:
292
- * {ClientPrefix}-{Field-Name} (e.g., Bugsnag-Auth-Token, Reflect-Api-Token)
293
- *
294
- * The ClientRegistry validates the configuration and initializes enabled clients.
295
- * If configuration fails, an error response is sent and null is returned.
296
- *
297
- * @returns SmartBearMcpServer instance if successful, null if configuration fails
298
- */
299
235
  async function newServer(req, res) {
300
- const server = new SmartBearMcpServer();
301
- try {
302
- // Configure server with values from HTTP headers
303
- await clientRegistry.configure(server, (client, key) => {
304
- const headerName = getHeaderName(client, key);
305
- // Check both original case and lower-case headers for compatibility
306
- // (HTTP headers are case-insensitive, but Node.js lowercases them)
307
- const value = req.headers[headerName] || req.headers[headerName.toLowerCase()];
308
- if (typeof value === "string") {
309
- return value;
310
- }
311
- return null;
312
- });
313
- }
314
- catch (error) {
315
- // Configuration failed - provide helpful error message
316
- const headerHelp = getHttpHeadersHelp();
317
- const errorMessage = headerHelp.length > 0
318
- ? `Configuration error: ${error instanceof Error ? error.message : String(error)}. Please provide valid headers:\n${headerHelp.join("\n")}`
319
- : "No clients support HTTP header configuration.";
320
- res.writeHead(401, { "Content-Type": "text/plain" });
321
- res.end(errorMessage);
322
- return null;
323
- }
324
- return server;
236
+ const server = new SmartBearMcpServer();
237
+ try {
238
+ await clientRegistry.configure(server, (client, key) => {
239
+ const headerName = getHeaderName(client, key);
240
+ const value = req.headers[headerName] || req.headers[headerName.toLowerCase()];
241
+ if (typeof value === "string") {
242
+ return value;
243
+ }
244
+ return null;
245
+ });
246
+ } catch (error) {
247
+ const headerHelp = getHttpHeadersHelp();
248
+ const errorMessage = headerHelp.length > 0 ? `Configuration error: ${error instanceof Error ? error.message : String(error)}. Please provide valid headers:
249
+ ${headerHelp.join("\n")}` : "No clients support HTTP header configuration.";
250
+ res.writeHead(401, { "Content-Type": "text/plain" });
251
+ res.end(errorMessage);
252
+ return null;
253
+ }
254
+ return server;
325
255
  }
326
- /**
327
- * Convert a config key to HTTP header name format
328
- *
329
- * Examples:
330
- * - auth_token -> Auth-Token
331
- * - project_api_key -> Project-Api-Key
332
- * - base_url -> Base-Url
333
- *
334
- * Combined with configPrefix: Bugsnag-Auth-Token, Reflect-Api-Token, etc.
335
- *
336
- * @param client The client instance (provides configPrefix)
337
- * @param key The config key in snake_case
338
- * @returns Header name in format: {ConfigPrefix}-{Pascal-Kebab-Case}
339
- */
340
256
  function getHeaderName(client, key) {
341
- return `${client.configPrefix}-${key
342
- .split("_")
343
- .map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
344
- .join("-")}`;
257
+ return `${client.configPrefix}-${key.split("_").map(
258
+ (part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()
259
+ ).join("-")}`;
345
260
  }
346
- /**
347
- * Get all HTTP headers that clients support for authentication
348
- * Returns a list of header names (in kebab-case) that should be allowed
349
- */
350
261
  function getHttpHeaders() {
351
- const headers = new Set();
352
- // Use getAll() to respect MCP_ENABLED_CLIENTS filtering
353
- for (const entry of clientRegistry.getAll()) {
354
- for (const configKey of Object.keys(entry.config.shape)) {
355
- headers.add(getHeaderName(entry, configKey));
356
- }
262
+ const headers = /* @__PURE__ */ new Set();
263
+ for (const entry of clientRegistry.getAll()) {
264
+ for (const configKey of Object.keys(entry.config.shape)) {
265
+ headers.add(getHeaderName(entry, configKey));
357
266
  }
358
- return Array.from(headers).sort((a, b) => a.localeCompare(b));
267
+ }
268
+ return Array.from(headers).sort((a, b) => a.localeCompare(b));
359
269
  }
360
- /**
361
- * Get human-readable list of HTTP headers for logging/error messages
362
- * Organized by client
363
- */
364
270
  function getHttpHeadersHelp() {
365
- const messages = [];
366
- for (const entry of clientRegistry.getAll()) {
367
- messages.push(` - ${entry.name}:`);
368
- for (const [configKey, requirement] of Object.entries(entry.config.shape)) {
369
- const headerName = getHeaderName(entry, configKey);
370
- const requiredTag = requirement.isOptional()
371
- ? " (optional)"
372
- : " (required)";
373
- messages.push(` - ${headerName}${requiredTag}: ${requirement.description}`);
374
- }
271
+ const messages = [];
272
+ for (const entry of clientRegistry.getAll()) {
273
+ messages.push(` - ${entry.name}:`);
274
+ for (const [configKey, requirement] of Object.entries(entry.config.shape)) {
275
+ const headerName = getHeaderName(entry, configKey);
276
+ const requiredTag = isOptionalType(requirement) ? " (optional)" : " (required)";
277
+ messages.push(
278
+ ` - ${headerName}${requiredTag}: ${requirement.description}`
279
+ );
375
280
  }
376
- return messages;
281
+ }
282
+ return messages;
377
283
  }
284
+ export {
285
+ runHttpMode
286
+ };