@mcpjam/inspector 0.3.8 โ†’ 0.8.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.
Files changed (193) hide show
  1. package/.next/BUILD_ID +1 -0
  2. package/.next/app-build-manifest.json +89 -0
  3. package/.next/app-path-routes-manifest.json +13 -0
  4. package/.next/build-manifest.json +33 -0
  5. package/.next/cache/.previewinfo +1 -0
  6. package/.next/cache/.rscinfo +1 -0
  7. package/.next/cache/.tsbuildinfo +1 -0
  8. package/.next/cache/eslint/.cache_11b5ofe +1 -0
  9. package/.next/cache/webpack/client-production/0.pack +0 -0
  10. package/.next/cache/webpack/client-production/index.pack +0 -0
  11. package/.next/cache/webpack/edge-server-production/0.pack +0 -0
  12. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  13. package/.next/cache/webpack/server-production/0.pack +0 -0
  14. package/.next/cache/webpack/server-production/index.pack +0 -0
  15. package/.next/diagnostics/build-diagnostics.json +6 -0
  16. package/.next/diagnostics/framework.json +1 -0
  17. package/.next/export-marker.json +6 -0
  18. package/.next/images-manifest.json +57 -0
  19. package/.next/next-minimal-server.js.nft.json +1 -0
  20. package/.next/next-server.js.nft.json +1 -0
  21. package/.next/package.json +1 -0
  22. package/.next/prerender-manifest.json +41 -0
  23. package/.next/react-loadable-manifest.json +1 -0
  24. package/.next/required-server-files.json +318 -0
  25. package/.next/routes-manifest.json +65 -0
  26. package/.next/server/app/_not-found/page.js +2 -0
  27. package/.next/server/app/_not-found/page.js.nft.json +1 -0
  28. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
  29. package/.next/server/app/api/mcp/chat/route.js +45 -0
  30. package/.next/server/app/api/mcp/chat/route.js.nft.json +1 -0
  31. package/.next/server/app/api/mcp/chat/route_client-reference-manifest.js +1 -0
  32. package/.next/server/app/api/mcp/connect/route.js +1 -0
  33. package/.next/server/app/api/mcp/connect/route.js.nft.json +1 -0
  34. package/.next/server/app/api/mcp/connect/route_client-reference-manifest.js +1 -0
  35. package/.next/server/app/api/mcp/prompts/get/route.js +1 -0
  36. package/.next/server/app/api/mcp/prompts/get/route.js.nft.json +1 -0
  37. package/.next/server/app/api/mcp/prompts/get/route_client-reference-manifest.js +1 -0
  38. package/.next/server/app/api/mcp/prompts/list/route.js +1 -0
  39. package/.next/server/app/api/mcp/prompts/list/route.js.nft.json +1 -0
  40. package/.next/server/app/api/mcp/prompts/list/route_client-reference-manifest.js +1 -0
  41. package/.next/server/app/api/mcp/resources/list/route.js +1 -0
  42. package/.next/server/app/api/mcp/resources/list/route.js.nft.json +1 -0
  43. package/.next/server/app/api/mcp/resources/list/route_client-reference-manifest.js +1 -0
  44. package/.next/server/app/api/mcp/resources/read/route.js +1 -0
  45. package/.next/server/app/api/mcp/resources/read/route.js.nft.json +1 -0
  46. package/.next/server/app/api/mcp/resources/read/route_client-reference-manifest.js +1 -0
  47. package/.next/server/app/api/mcp/tools/route.js +21 -0
  48. package/.next/server/app/api/mcp/tools/route.js.nft.json +1 -0
  49. package/.next/server/app/api/mcp/tools/route_client-reference-manifest.js +1 -0
  50. package/.next/server/app/favicon.ico/route.js +1 -0
  51. package/.next/server/app/favicon.ico/route.js.nft.json +1 -0
  52. package/.next/server/app/favicon.ico.body +0 -0
  53. package/.next/server/app/favicon.ico.meta +1 -0
  54. package/.next/server/app/oauth/callback/page.js +2 -0
  55. package/.next/server/app/oauth/callback/page.js.nft.json +1 -0
  56. package/.next/server/app/oauth/callback/page_client-reference-manifest.js +1 -0
  57. package/.next/server/app/page.js +16 -0
  58. package/.next/server/app/page.js.nft.json +1 -0
  59. package/.next/server/app/page_client-reference-manifest.js +1 -0
  60. package/.next/server/app-paths-manifest.json +13 -0
  61. package/.next/server/chunks/175.js +8 -0
  62. package/.next/server/chunks/260.js +82 -0
  63. package/.next/server/chunks/546.js +1 -0
  64. package/.next/server/chunks/548.js +6 -0
  65. package/.next/server/chunks/55.js +1 -0
  66. package/.next/server/chunks/985.js +22 -0
  67. package/.next/server/functions-config-manifest.json +4 -0
  68. package/.next/server/interception-route-rewrite-manifest.js +1 -0
  69. package/.next/server/middleware-build-manifest.js +1 -0
  70. package/.next/server/middleware-manifest.json +6 -0
  71. package/.next/server/middleware-react-loadable-manifest.js +1 -0
  72. package/.next/server/next-font-manifest.js +1 -0
  73. package/.next/server/next-font-manifest.json +1 -0
  74. package/.next/server/pages/500.html +1 -0
  75. package/.next/server/pages/_app.js +1 -0
  76. package/.next/server/pages/_app.js.nft.json +1 -0
  77. package/.next/server/pages/_document.js +1 -0
  78. package/.next/server/pages/_document.js.nft.json +1 -0
  79. package/.next/server/pages/_error.js +19 -0
  80. package/.next/server/pages/_error.js.nft.json +1 -0
  81. package/.next/server/pages-manifest.json +5 -0
  82. package/.next/server/server-reference-manifest.js +1 -0
  83. package/.next/server/server-reference-manifest.json +1 -0
  84. package/.next/server/webpack-runtime.js +1 -0
  85. package/.next/static/chunks/14-ae3a01e72ea53777.js +1 -0
  86. package/.next/static/chunks/214-cc4c35d88f2695ed.js +1 -0
  87. package/.next/static/chunks/4bd1b696-cf72ae8a39fa05aa.js +1 -0
  88. package/.next/static/chunks/866-04c19dda4c52f2bf.js +1 -0
  89. package/.next/static/chunks/964-eda38e26c0391a47.js +1 -0
  90. package/.next/static/chunks/app/_not-found/page-d7e832b54474da82.js +1 -0
  91. package/.next/static/chunks/app/api/mcp/chat/route-0341498a8bf5f2da.js +1 -0
  92. package/.next/static/chunks/app/api/mcp/connect/route-0341498a8bf5f2da.js +1 -0
  93. package/.next/static/chunks/app/api/mcp/prompts/get/route-0341498a8bf5f2da.js +1 -0
  94. package/.next/static/chunks/app/api/mcp/prompts/list/route-0341498a8bf5f2da.js +1 -0
  95. package/.next/static/chunks/app/api/mcp/resources/list/route-0341498a8bf5f2da.js +1 -0
  96. package/.next/static/chunks/app/api/mcp/resources/read/route-0341498a8bf5f2da.js +1 -0
  97. package/.next/static/chunks/app/api/mcp/tools/route-0341498a8bf5f2da.js +1 -0
  98. package/.next/static/chunks/app/layout-fb6e1ad5933381f3.js +1 -0
  99. package/.next/static/chunks/app/oauth/callback/page-d8b3908ea67ba3e3.js +1 -0
  100. package/.next/static/chunks/app/page-81e35b2a61edb363.js +1 -0
  101. package/.next/static/chunks/framework-7c95b8e5103c9e90.js +1 -0
  102. package/.next/static/chunks/main-app-7d61da15faa6c1af.js +1 -0
  103. package/.next/static/chunks/main-bbdafee21a7bd1d6.js +1 -0
  104. package/.next/static/chunks/pages/_app-0a0020ddd67f79cf.js +1 -0
  105. package/.next/static/chunks/pages/_error-03529f2c21436739.js +1 -0
  106. package/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  107. package/.next/static/chunks/webpack-cdfccaf38062dd25.js +1 -0
  108. package/.next/static/css/1e852d83e9c1d0c6.css +1 -0
  109. package/.next/static/css/f30152c0704fba31.css +1 -0
  110. package/.next/static/css/fe751fdbe975e9ca.css +1 -0
  111. package/.next/static/media/569ce4b8f30dc480-s.p.woff2 +0 -0
  112. package/.next/static/media/747892c23ea88013-s.woff2 +0 -0
  113. package/.next/static/media/8d697b304b401681-s.woff2 +0 -0
  114. package/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
  115. package/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
  116. package/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
  117. package/.next/static/media/ollama_dark.9af45ac0.png +0 -0
  118. package/.next/static/media/ollama_logo.9f08a95b.svg +7 -0
  119. package/.next/static/media/openai_logo.3f83154a.png +0 -0
  120. package/.next/static/wgHmsxKAquUu9gOMW6Qd5/_buildManifest.js +1 -0
  121. package/.next/static/wgHmsxKAquUu9gOMW6Qd5/_ssgManifest.js +1 -0
  122. package/.next/trace +35 -0
  123. package/.next/types/app/api/mcp/chat/route.ts +347 -0
  124. package/.next/types/app/api/mcp/connect/route.ts +347 -0
  125. package/.next/types/app/api/mcp/prompts/get/route.ts +347 -0
  126. package/.next/types/app/api/mcp/prompts/list/route.ts +347 -0
  127. package/.next/types/app/api/mcp/resources/list/route.ts +347 -0
  128. package/.next/types/app/api/mcp/resources/read/route.ts +347 -0
  129. package/.next/types/app/api/mcp/tools/route.ts +347 -0
  130. package/.next/types/app/layout.ts +84 -0
  131. package/.next/types/app/oauth/callback/page.ts +84 -0
  132. package/.next/types/app/page.ts +84 -0
  133. package/.next/types/cache-life.d.ts +141 -0
  134. package/.next/types/package.json +1 -0
  135. package/README.md +76 -161
  136. package/bin/start.js +504 -0
  137. package/next.config.ts +7 -0
  138. package/package.json +71 -54
  139. package/public/claude_logo.png +0 -0
  140. package/public/demo_1.png +0 -0
  141. package/public/demo_2.png +0 -0
  142. package/public/demo_3.png +0 -0
  143. package/public/file.svg +1 -0
  144. package/public/globe.svg +1 -0
  145. package/public/mcp.svg +1 -0
  146. package/public/next.svg +1 -0
  147. package/public/ollama_dark.png +0 -0
  148. package/public/ollama_logo.svg +7 -0
  149. package/public/openai_logo.png +0 -0
  150. package/public/vercel.svg +1 -0
  151. package/public/window.svg +1 -0
  152. package/LICENSE +0 -200
  153. package/cli/build/cli.js +0 -251
  154. package/cli/build/client/connection.js +0 -33
  155. package/cli/build/client/index.js +0 -6
  156. package/cli/build/client/prompts.js +0 -23
  157. package/cli/build/client/resources.js +0 -30
  158. package/cli/build/client/tools.js +0 -64
  159. package/cli/build/client/types.js +0 -1
  160. package/cli/build/error-handler.js +0 -18
  161. package/cli/build/index.js +0 -166
  162. package/cli/build/transport.js +0 -47
  163. package/client/bin/client.js +0 -71
  164. package/client/bin/start.js +0 -143
  165. package/client/dist/assets/OAuthCallback-Dovy-wwC.js +0 -56
  166. package/client/dist/assets/OAuthDebugCallback-CP9EZ76u.js +0 -44
  167. package/client/dist/assets/index-CWDemo1t.css +0 -4168
  168. package/client/dist/assets/index-DTD8_Uq7.js +0 -63318
  169. package/client/dist/index.html +0 -14
  170. package/client/dist/ollama_logo.png +0 -0
  171. package/client/dist/openai_logo.png +0 -0
  172. package/server/build/database/DatabaseManager.js +0 -108
  173. package/server/build/database/index.js +0 -8
  174. package/server/build/database/routes.js +0 -86
  175. package/server/build/database/types.js +0 -27
  176. package/server/build/database/utils.js +0 -86
  177. package/server/build/index.js +0 -331
  178. package/server/build/mcpProxy.js +0 -54
  179. package/server/build/shared/MCPProxyService.js +0 -221
  180. package/server/build/shared/TransportFactory.js +0 -130
  181. package/server/build/shared/index.js +0 -4
  182. package/server/build/shared/types.js +0 -1
  183. package/server/build/shared/utils.js +0 -27
  184. package/server/build/test-server.js +0 -145
  185. package/server/build/testing/HealthCheck.js +0 -42
  186. package/server/build/testing/TestExecutor.js +0 -240
  187. package/server/build/testing/TestRunner.js +0 -198
  188. package/server/build/testing/TestServer.js +0 -440
  189. package/server/build/testing/types.js +0 -1
  190. /package/{client/dist/claude_logo.png โ†’ .next/static/media/claude_logo.d33b25b0.png} +0 -0
  191. /package/{client/dist โ†’ public}/mcp_jam.svg +0 -0
  192. /package/{client/dist โ†’ public}/mcp_jam_dark.png +0 -0
  193. /package/{client/dist โ†’ public}/mcp_jam_light.png +0 -0
@@ -1,54 +0,0 @@
1
- import { isJSONRPCRequest } from "@modelcontextprotocol/sdk/types.js";
2
- function onClientError(error) {
3
- console.error("Error from inspector client:", error);
4
- }
5
- function onServerError(error) {
6
- if ((error?.message &&
7
- error.message.includes("Error POSTing to endpoint (HTTP 404)")) ||
8
- (error?.cause && JSON.stringify(error.cause).includes("ECONNREFUSED"))) {
9
- console.error("Connection refused. Is the MCP server running?");
10
- }
11
- else {
12
- console.error("Error from MCP server:", error);
13
- }
14
- }
15
- export default function mcpProxy({ transportToClient, transportToServer, }) {
16
- let transportToClientClosed = false;
17
- let transportToServerClosed = false;
18
- transportToClient.onmessage = (message) => {
19
- transportToServer.send(message).catch((error) => {
20
- // Send error response back to client if it was a request (has id) and connection is still open
21
- if (isJSONRPCRequest(message) && !transportToClientClosed) {
22
- const errorResponse = {
23
- jsonrpc: "2.0",
24
- id: message.id,
25
- error: {
26
- code: -32001,
27
- message: error.message,
28
- data: error,
29
- },
30
- };
31
- transportToClient.send(errorResponse).catch(onClientError);
32
- }
33
- });
34
- };
35
- transportToServer.onmessage = (message) => {
36
- transportToClient.send(message).catch(onClientError);
37
- };
38
- transportToClient.onclose = () => {
39
- if (transportToServerClosed) {
40
- return;
41
- }
42
- transportToClientClosed = true;
43
- transportToServer.close().catch(onServerError);
44
- };
45
- transportToServer.onclose = () => {
46
- if (transportToClientClosed) {
47
- return;
48
- }
49
- transportToServerClosed = true;
50
- transportToClient.close().catch(onClientError);
51
- };
52
- transportToClient.onerror = onClientError;
53
- transportToServer.onerror = onServerError;
54
- }
@@ -1,221 +0,0 @@
1
- import { EventEmitter } from "events";
2
- import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
3
- import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
4
- import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
5
- import { TransportFactory } from "./TransportFactory.js";
6
- import { generateSessionId, ConsoleLogger } from "./utils.js";
7
- import mcpProxy from "../mcpProxy.js";
8
- export class MCPProxyService extends EventEmitter {
9
- webAppTransports = new Map();
10
- backingServerTransports = new Map();
11
- connectionStatus = new Map();
12
- cleanupInProgress = new Set();
13
- transportFactory;
14
- logger;
15
- maxConnections;
16
- constructor(options = {}) {
17
- super();
18
- this.logger = options.logger || new ConsoleLogger();
19
- this.maxConnections = options.maxConnections || 50;
20
- this.transportFactory = new TransportFactory({
21
- logger: this.logger,
22
- });
23
- }
24
- async createConnection(serverConfig, requestHeaders) {
25
- if (this.backingServerTransports.size >= this.maxConnections) {
26
- throw new Error(`Maximum connections reached (${this.maxConnections})`);
27
- }
28
- const sessionId = generateSessionId();
29
- try {
30
- this.logger.info(`Creating connection ${sessionId} for ${serverConfig.name}`);
31
- // Update status to connecting
32
- this.connectionStatus.set(sessionId, {
33
- id: sessionId,
34
- status: "connecting",
35
- lastActivity: new Date(),
36
- errorCount: 0,
37
- });
38
- // Create transport
39
- const transport = await this.transportFactory.createTransport(serverConfig, requestHeaders);
40
- // Store transport
41
- this.backingServerTransports.set(sessionId, transport);
42
- // Set up transport event handlers
43
- this.setupTransportEvents(sessionId, transport);
44
- // Update status to connected
45
- this.updateConnectionStatus(sessionId, "connected");
46
- this.emit("connection", sessionId, serverConfig);
47
- return sessionId;
48
- }
49
- catch (error) {
50
- this.updateConnectionStatus(sessionId, "error");
51
- this.logger.error(`Failed to create connection ${sessionId}:`, error);
52
- throw error;
53
- }
54
- }
55
- getActiveConnections() {
56
- return Array.from(this.backingServerTransports.keys());
57
- }
58
- getConnectionStatus(sessionId) {
59
- return this.connectionStatus.get(sessionId);
60
- }
61
- getAllConnectionStatuses() {
62
- return Array.from(this.connectionStatus.values());
63
- }
64
- async sendMessage(sessionId, message) {
65
- const transport = this.backingServerTransports.get(sessionId);
66
- if (!transport) {
67
- throw new Error(`No transport found for session: ${sessionId}`);
68
- }
69
- try {
70
- this.updateConnectionStatus(sessionId, "connected");
71
- await transport.send(message);
72
- }
73
- catch (error) {
74
- this.incrementErrorCount(sessionId);
75
- this.logger.error(`Message failed for session ${sessionId}:`, error);
76
- throw error;
77
- }
78
- }
79
- getTransport(sessionId) {
80
- return this.backingServerTransports.get(sessionId);
81
- }
82
- getWebAppTransport(sessionId) {
83
- return this.webAppTransports.get(sessionId);
84
- }
85
- setWebAppTransport(sessionId, transport) {
86
- this.webAppTransports.set(sessionId, transport);
87
- this.logger.info(`Web app transport set for session ${sessionId}`);
88
- }
89
- removeWebAppTransport(sessionId) {
90
- this.webAppTransports.delete(sessionId);
91
- this.logger.info(`Web app transport removed for session ${sessionId}`);
92
- }
93
- async closeConnection(sessionId) {
94
- // Prevent duplicate cleanup calls
95
- if (this.cleanupInProgress.has(sessionId)) {
96
- return;
97
- }
98
- this.cleanupInProgress.add(sessionId);
99
- try {
100
- const transport = this.backingServerTransports.get(sessionId);
101
- if (transport) {
102
- try {
103
- await transport.close();
104
- }
105
- catch (error) {
106
- this.logger.error(`Error closing connection ${sessionId}:`, error);
107
- }
108
- }
109
- this.backingServerTransports.delete(sessionId);
110
- this.webAppTransports.delete(sessionId);
111
- this.connectionStatus.delete(sessionId);
112
- this.emit("disconnection", sessionId);
113
- this.logger.info(`๐Ÿงน Cleaning up transports for session ${sessionId}`);
114
- }
115
- finally {
116
- this.cleanupInProgress.delete(sessionId);
117
- }
118
- }
119
- async closeAllConnections() {
120
- const closePromises = Array.from(this.backingServerTransports.keys()).map((sessionId) => this.closeConnection(sessionId));
121
- await Promise.all(closePromises);
122
- this.logger.info(`All connections closed (${closePromises.length} total)`);
123
- }
124
- updateConnectionStatus(sessionId, status) {
125
- const current = this.connectionStatus.get(sessionId);
126
- if (current) {
127
- current.status = status;
128
- current.lastActivity = new Date();
129
- this.connectionStatus.set(sessionId, current);
130
- }
131
- }
132
- incrementErrorCount(sessionId) {
133
- const current = this.connectionStatus.get(sessionId);
134
- if (current) {
135
- current.errorCount += 1;
136
- this.connectionStatus.set(sessionId, current);
137
- }
138
- }
139
- setupTransportEvents(sessionId, transport) {
140
- // Store original handlers to preserve existing functionality
141
- const originalOnClose = transport.onclose;
142
- const originalOnError = transport.onerror;
143
- transport.onclose = () => {
144
- this.logger.info(`Transport closed for session ${sessionId}`);
145
- this.updateConnectionStatus(sessionId, "disconnected");
146
- this.emit("disconnection", sessionId);
147
- // Call original handler if it exists
148
- if (originalOnClose) {
149
- originalOnClose();
150
- }
151
- };
152
- transport.onerror = (error) => {
153
- this.logger.error(`Transport error for session ${sessionId}:`, error);
154
- this.updateConnectionStatus(sessionId, "error");
155
- this.incrementErrorCount(sessionId);
156
- this.emit("error", sessionId, error);
157
- // Call original handler if it exists
158
- if (originalOnError) {
159
- originalOnError(error);
160
- }
161
- };
162
- }
163
- // Helper methods for StreamableHTTP transport handling
164
- async createStreamableHTTPConnection(serverConfig, requestHeaders) {
165
- const sessionId = await this.createConnection(serverConfig, requestHeaders);
166
- const webAppTransport = new StreamableHTTPServerTransport({
167
- sessionIdGenerator: () => sessionId,
168
- onsessioninitialized: (newSessionId) => {
169
- this.logger.info(`โœจ Created streamable web app transport ${newSessionId}`);
170
- this.setWebAppTransport(newSessionId, webAppTransport);
171
- // Set up proxy between web app transport and backing server transport
172
- const backingTransport = this.getTransport(newSessionId);
173
- if (backingTransport) {
174
- mcpProxy({
175
- transportToClient: webAppTransport,
176
- transportToServer: backingTransport,
177
- });
178
- }
179
- // Set up cleanup handler
180
- webAppTransport.onclose = () => {
181
- this.closeConnection(newSessionId);
182
- };
183
- },
184
- });
185
- await webAppTransport.start();
186
- return { sessionId, webAppTransport };
187
- }
188
- // Helper method for SSE transport handling
189
- async createSSEConnection(serverConfig, res, requestHeaders) {
190
- const connectionId = await this.createConnection(serverConfig, requestHeaders);
191
- const webAppTransport = new SSEServerTransport("/message", res);
192
- const sessionId = webAppTransport.sessionId;
193
- this.setWebAppTransport(sessionId, webAppTransport);
194
- // Set up cleanup handler
195
- webAppTransport.onclose = () => {
196
- this.closeConnection(connectionId);
197
- };
198
- await webAppTransport.start();
199
- // Set up proxy between web app transport and backing server transport
200
- const backingTransport = this.getTransport(connectionId);
201
- if (backingTransport) {
202
- // Special handling for STDIO stderr
203
- if (backingTransport instanceof StdioClientTransport) {
204
- backingTransport.stderr?.on("data", (chunk) => {
205
- webAppTransport.send({
206
- jsonrpc: "2.0",
207
- method: "stderr",
208
- params: {
209
- data: chunk.toString(),
210
- },
211
- });
212
- });
213
- }
214
- mcpProxy({
215
- transportToClient: webAppTransport,
216
- transportToServer: backingTransport,
217
- });
218
- }
219
- return { sessionId, webAppTransport };
220
- }
221
- }
@@ -1,130 +0,0 @@
1
- import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
2
- import { StdioClientTransport, getDefaultEnvironment, } from "@modelcontextprotocol/sdk/client/stdio.js";
3
- import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
4
- import { findActualExecutable } from "spawn-rx";
5
- import { validateServerConfig, ConsoleLogger } from "./utils.js";
6
- const SSE_HEADERS_PASSTHROUGH = ["authorization"];
7
- const STREAMABLE_HTTP_HEADERS_PASSTHROUGH = [
8
- "authorization",
9
- "mcp-session-id",
10
- "last-event-id",
11
- ];
12
- export class TransportFactory {
13
- logger;
14
- defaultEnvironment;
15
- constructor(options = {}) {
16
- this.logger = options.logger || new ConsoleLogger();
17
- this.defaultEnvironment = {
18
- ...getDefaultEnvironment(),
19
- ...(process.env.MCP_ENV_VARS ? JSON.parse(process.env.MCP_ENV_VARS) : {}),
20
- };
21
- }
22
- async createTransport(config, requestHeaders) {
23
- validateServerConfig(config);
24
- this.logger.info(`Creating ${config.type} transport for ${config.name}`);
25
- try {
26
- switch (config.type) {
27
- case "stdio":
28
- return await this.createStdioTransport(config);
29
- case "sse":
30
- return await this.createSSETransport(config, requestHeaders);
31
- case "streamable-http":
32
- return await this.createStreamableHTTPTransport(config, requestHeaders);
33
- default:
34
- throw new Error(`Unsupported transport type: ${config.type}`);
35
- }
36
- }
37
- catch (error) {
38
- this.logger.error(`Failed to create transport for ${config.name}:`, error);
39
- throw error;
40
- }
41
- }
42
- async createStdioTransport(config) {
43
- const command = config.command;
44
- const origArgs = config.args || [];
45
- const queryEnv = config.env || {};
46
- // Filter out undefined values from process.env
47
- const processEnv = Object.fromEntries(Object.entries(process.env).filter(([, value]) => value !== undefined));
48
- const env = { ...processEnv, ...this.defaultEnvironment, ...queryEnv };
49
- const { cmd, args } = findActualExecutable(command, origArgs);
50
- this.logger.info(`๐Ÿš€ Stdio transport: command=${cmd}, args=${args}`);
51
- const transport = new StdioClientTransport({
52
- command: cmd,
53
- args,
54
- env,
55
- stderr: "pipe",
56
- });
57
- await this.setupTransportLifecycle(transport, config.id);
58
- await transport.start();
59
- return transport;
60
- }
61
- async createSSETransport(config, requestHeaders) {
62
- const url = config.url;
63
- const headers = {
64
- Accept: "text/event-stream",
65
- ...config.headers,
66
- };
67
- // Add headers passed through from the request
68
- if (requestHeaders) {
69
- for (const key of SSE_HEADERS_PASSTHROUGH) {
70
- if (requestHeaders[key] !== undefined) {
71
- headers[key] = requestHeaders[key];
72
- }
73
- }
74
- }
75
- this.logger.info(`๐Ÿš€ SSE transport: url=${url}`);
76
- const transport = new SSEClientTransport(new URL(url), {
77
- eventSourceInit: {
78
- fetch: (url, init) => fetch(url, { ...init, headers }),
79
- },
80
- requestInit: {
81
- headers,
82
- },
83
- });
84
- await this.setupTransportLifecycle(transport, config.id);
85
- await transport.start();
86
- return transport;
87
- }
88
- async createStreamableHTTPTransport(config, requestHeaders) {
89
- const url = config.url;
90
- const headers = {
91
- Accept: "text/event-stream, application/json",
92
- ...config.headers,
93
- };
94
- // Add headers passed through from the request
95
- if (requestHeaders) {
96
- for (const key of STREAMABLE_HTTP_HEADERS_PASSTHROUGH) {
97
- if (requestHeaders[key] !== undefined) {
98
- headers[key] = requestHeaders[key];
99
- }
100
- }
101
- }
102
- this.logger.info(`๐Ÿš€ StreamableHTTP transport: url=${url}`);
103
- const transport = new StreamableHTTPClientTransport(new URL(url), {
104
- requestInit: {
105
- headers,
106
- },
107
- });
108
- await this.setupTransportLifecycle(transport, config.id);
109
- await transport.start();
110
- return transport;
111
- }
112
- async setupTransportLifecycle(transport, configId) {
113
- // Set up event handlers without aggressive timeouts
114
- // The original server didn't have connection timeouts, so we preserve that behavior
115
- const originalOnClose = transport.onclose;
116
- transport.onclose = () => {
117
- this.logger.info(`Transport closed for ${configId}`);
118
- if (originalOnClose) {
119
- originalOnClose();
120
- }
121
- };
122
- const originalOnError = transport.onerror;
123
- transport.onerror = (error) => {
124
- this.logger.error(`Transport error for ${configId}:`, error);
125
- if (originalOnError) {
126
- originalOnError(error);
127
- }
128
- };
129
- }
130
- }
@@ -1,4 +0,0 @@
1
- // Export all shared components for easy importing
2
- export { MCPProxyService } from "./MCPProxyService.js";
3
- export { TransportFactory } from "./TransportFactory.js";
4
- export { generateSessionId, validateServerConfig, ConsoleLogger, } from "./utils.js";
@@ -1 +0,0 @@
1
- export {};
@@ -1,27 +0,0 @@
1
- import { randomUUID } from "crypto";
2
- export function generateSessionId() {
3
- return randomUUID();
4
- }
5
- export function validateServerConfig(config) {
6
- if (!config.id || !config.type || !config.name) {
7
- throw new Error("Invalid server configuration: id, type, and name are required");
8
- }
9
- if (config.type === "stdio" && !config.command) {
10
- throw new Error("STDIO transport requires command");
11
- }
12
- if ((config.type === "sse" || config.type === "streamable-http") &&
13
- !config.url) {
14
- throw new Error("SSE and StreamableHTTP transports require URL");
15
- }
16
- }
17
- export class ConsoleLogger {
18
- info(message, ...args) {
19
- console.log(`[INFO] ${message}`, ...args);
20
- }
21
- error(message, ...args) {
22
- console.error(`[ERROR] ${message}`, ...args);
23
- }
24
- warn(message, ...args) {
25
- console.warn(`[WARN] ${message}`, ...args);
26
- }
27
- }
@@ -1,145 +0,0 @@
1
- #!/usr/bin/env node
2
- import { TestServer } from "./testing/TestServer.js";
3
- import { MCPProxyService } from "./shared/MCPProxyService.js";
4
- import { DatabaseManager } from "./database/DatabaseManager.js";
5
- import { ConsoleLogger } from "./shared/utils.js";
6
- import { getDatabaseConfig } from "./database/utils.js";
7
- import { createServer } from "node:net";
8
- import { parseArgs } from "node:util";
9
- // Function to find an available port
10
- const findAvailablePort = async (startPort) => {
11
- return new Promise((resolve, reject) => {
12
- const server = createServer();
13
- server.listen(startPort, () => {
14
- const port = server.address()?.port;
15
- server.close(() => {
16
- resolve(port);
17
- });
18
- });
19
- server.on("error", (err) => {
20
- if (err.code === "EADDRINUSE") {
21
- // Port is in use, try the next one
22
- findAvailablePort(startPort + 1)
23
- .then(resolve)
24
- .catch(reject);
25
- }
26
- else {
27
- reject(err);
28
- }
29
- });
30
- });
31
- };
32
- async function startTestServer() {
33
- const logger = new ConsoleLogger();
34
- // Parse command line arguments
35
- const { values } = parseArgs({
36
- args: process.argv.slice(2),
37
- options: {
38
- port: { type: "string", short: "p", default: "3002" },
39
- host: { type: "string", short: "h", default: "localhost" },
40
- env: { type: "string", short: "e", default: "development" },
41
- config: { type: "string", short: "c" },
42
- help: { type: "boolean", default: false },
43
- },
44
- allowPositionals: true,
45
- });
46
- if (values.help) {
47
- console.log(`
48
- ๐Ÿงช MCPJam Inspector Test Server
49
-
50
- Usage: node test-server.js [options]
51
-
52
- Options:
53
- -p, --port <port> Port to run the test server on (default: 3002)
54
- -h, --host <host> Host to bind the server to (default: localhost)
55
- -e, --env <env> Environment mode (default: development)
56
- -c, --config <file> Configuration file path
57
- --help Show this help message
58
-
59
- Environment Variables:
60
- TEST_PORT Override the default port
61
- TEST_HOST Override the default host
62
- NODE_ENV Set the environment mode
63
- DATABASE_URL Database connection string
64
- LOG_LEVEL Logging level (debug, info, warn, error)
65
-
66
- Examples:
67
- node test-server.js --port 4000 --host 0.0.0.0
68
- TEST_PORT=5000 node test-server.js
69
- NODE_ENV=production node test-server.js
70
- `);
71
- process.exit(0);
72
- }
73
- try {
74
- // Initialize core services
75
- const mcpProxyService = new MCPProxyService({
76
- logger,
77
- maxConnections: 100, // Higher limit for test server
78
- });
79
- const dbConfig = getDatabaseConfig();
80
- const database = new DatabaseManager(dbConfig);
81
- await database.initialize();
82
- // Determine port with fallback logic
83
- const preferredPort = process.env.TEST_PORT
84
- ? parseInt(process.env.TEST_PORT)
85
- : parseInt(values.port);
86
- const actualPort = await findAvailablePort(preferredPort);
87
- // Create and start test server
88
- const testServer = new TestServer({
89
- port: actualPort,
90
- host: process.env.TEST_HOST || values.host,
91
- cors: values.env !== "production",
92
- rateLimiting: true,
93
- database: {
94
- url: process.env.DATABASE_URL || "sqlite://test.db",
95
- maxConnections: 10,
96
- timeout: 5000,
97
- },
98
- logging: {
99
- level: process.env.LOG_LEVEL || "info",
100
- format: "json",
101
- outputs: ["console"],
102
- },
103
- });
104
- await testServer.start(mcpProxyService, database);
105
- if (actualPort !== preferredPort) {
106
- logger.info(`โš ๏ธ Port ${preferredPort} was in use, using available port ${actualPort} instead`);
107
- }
108
- logger.info(`๐Ÿงช Test server started on http://${testServer.config.host}:${testServer.config.port}`);
109
- logger.info(`๐Ÿ“Š Health check available at http://${testServer.config.host}:${testServer.config.port}/api/test/health`);
110
- logger.info(`๐Ÿ” Status endpoint available at http://${testServer.config.host}:${testServer.config.port}/api/test/status`);
111
- logger.info(`๐ŸŽฏ Test execution endpoint available at http://${testServer.config.host}:${testServer.config.port}/api/test/run`);
112
- // Graceful shutdown
113
- const shutdown = async (signal) => {
114
- logger.info(`๐Ÿ”„ Received ${signal}, shutting down test server...`);
115
- try {
116
- await testServer.stop();
117
- await mcpProxyService.closeAllConnections();
118
- await database.close();
119
- logger.info("โœ… Test server shutdown complete");
120
- process.exit(0);
121
- }
122
- catch (error) {
123
- logger.error("โŒ Error during shutdown:", error);
124
- process.exit(1);
125
- }
126
- };
127
- process.on("SIGINT", () => shutdown("SIGINT"));
128
- process.on("SIGTERM", () => shutdown("SIGTERM"));
129
- // Handle unhandled promise rejections
130
- process.on("unhandledRejection", (reason, promise) => {
131
- logger.error("โŒ Unhandled Rejection at:", promise, "reason:", reason);
132
- });
133
- process.on("uncaughtException", (error) => {
134
- logger.error("โŒ Uncaught Exception:", error);
135
- process.exit(1);
136
- });
137
- }
138
- catch (error) {
139
- logger.error("โŒ Failed to start test server:", error);
140
- process.exit(1);
141
- }
142
- }
143
- if (import.meta.url === `file://${process.argv[1]}`) {
144
- startTestServer();
145
- }
@@ -1,42 +0,0 @@
1
- export class HealthCheck {
2
- mcpProxyService;
3
- database;
4
- logger;
5
- constructor(mcpProxyService, database, logger) {
6
- this.mcpProxyService = mcpProxyService;
7
- this.database = database;
8
- this.logger = logger;
9
- }
10
- getStatus() {
11
- return {
12
- testing: true,
13
- status: "healthy",
14
- version: process.env.npm_package_version || "0.3.5",
15
- timestamp: new Date().toISOString(),
16
- };
17
- }
18
- getDetailedStatus() {
19
- return {
20
- testing: true,
21
- server: {
22
- status: "running",
23
- uptime: process.uptime(),
24
- memory: process.memoryUsage(),
25
- version: process.env.npm_package_version || "0.3.5",
26
- },
27
- connections: {
28
- active: this.mcpProxyService.getActiveConnections().length,
29
- list: this.mcpProxyService.getActiveConnections(),
30
- },
31
- database: {
32
- connected: this.isDatabaseConnected(),
33
- status: this.isDatabaseConnected() ? "connected" : "disconnected",
34
- },
35
- timestamp: new Date().toISOString(),
36
- };
37
- }
38
- isDatabaseConnected() {
39
- // Simple check - in a real implementation you might want to ping the database
40
- return this.database !== null && this.database !== undefined;
41
- }
42
- }