@mcpjam/inspector 0.3.9 → 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-BSOXmPlE.js +0 -56
  166. package/client/dist/assets/OAuthDebugCallback-DyzqkofK.js +0 -44
  167. package/client/dist/assets/index-BT03cD-1.js +0 -63301
  168. package/client/dist/assets/index-Bwd_BFIj.css +0 -4164
  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,14 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <link rel="icon" type="image/svg+xml" href="/mcp_jam.svg" />
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>MCPJam Inspector</title>
8
- <script type="module" crossorigin src="/assets/index-BT03cD-1.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-Bwd_BFIj.css">
10
- </head>
11
- <body>
12
- <div id="root" class="w-full"></div>
13
- </body>
14
- </html>
Binary file
Binary file
@@ -1,108 +0,0 @@
1
- /**
2
- * DatabaseManager - Basic libSQL database manager for MCPJam Inspector
3
- * Simple local SQLite database foundation
4
- */
5
- import { createClient } from "@libsql/client";
6
- import { readFileSync } from "fs";
7
- import { join, dirname } from "path";
8
- import { fileURLToPath } from "url";
9
- import { ensureDirectoryExists, ensureMCPJamDirectory, getResolvedDatabasePath, } from "./utils.js";
10
- import { DatabaseError, QueryError, } from "./types.js";
11
- const __dirname = dirname(fileURLToPath(import.meta.url));
12
- export class DatabaseManager {
13
- client;
14
- initialized = false;
15
- config;
16
- constructor(config) {
17
- this.config = config || { localPath: getResolvedDatabasePath() };
18
- this.client = this.createClient();
19
- }
20
- createClient() {
21
- // Use the configured database path
22
- const dbPath = this.config.localPath;
23
- return createClient({
24
- url: `file:${dbPath}`,
25
- });
26
- }
27
- async initialize() {
28
- if (this.initialized)
29
- return;
30
- try {
31
- console.log("šŸ”„ Initializing database...");
32
- // Ensure .mcpjam directory exists and database directory
33
- await ensureMCPJamDirectory();
34
- const dbPath = getResolvedDatabasePath();
35
- await ensureDirectoryExists(dbPath);
36
- // Read and execute schema
37
- const schemaPath = join(__dirname, "schema.sql");
38
- const schema = readFileSync(schemaPath, "utf-8");
39
- await this.client.executeMultiple(schema);
40
- this.initialized = true;
41
- console.log("āœ… Database initialized successfully");
42
- }
43
- catch (error) {
44
- throw new DatabaseError("Failed to initialize database", "INIT_ERROR", error);
45
- }
46
- }
47
- // ============================================================================
48
- // APP METADATA OPERATIONS
49
- // ============================================================================
50
- async getMetadata(key) {
51
- try {
52
- const result = await this.client.execute({
53
- sql: "SELECT value FROM app_metadata WHERE key = ?",
54
- args: [key],
55
- });
56
- if (result.rows.length === 0)
57
- return null;
58
- return result.rows[0].value;
59
- }
60
- catch (error) {
61
- throw new QueryError("Failed to get metadata", undefined, error);
62
- }
63
- }
64
- async setMetadata(key, value) {
65
- try {
66
- await this.client.execute({
67
- sql: "INSERT OR REPLACE INTO app_metadata (key, value) VALUES (?, ?)",
68
- args: [key, value],
69
- });
70
- }
71
- catch (error) {
72
- throw new QueryError("Failed to set metadata", undefined, error);
73
- }
74
- }
75
- async getAllMetadata() {
76
- try {
77
- const result = await this.client.execute({
78
- sql: "SELECT * FROM app_metadata ORDER BY key",
79
- args: [],
80
- });
81
- return result.rows.map((row) => ({
82
- key: row.key,
83
- value: row.value,
84
- createdAt: new Date(row.created_at),
85
- updatedAt: new Date(row.updated_at),
86
- }));
87
- }
88
- catch (error) {
89
- throw new QueryError("Failed to get all metadata", undefined, error);
90
- }
91
- }
92
- async deleteMetadata(key) {
93
- try {
94
- await this.client.execute({
95
- sql: "DELETE FROM app_metadata WHERE key = ?",
96
- args: [key],
97
- });
98
- }
99
- catch (error) {
100
- throw new QueryError("Failed to delete metadata", undefined, error);
101
- }
102
- }
103
- async close() {
104
- if (this.client) {
105
- this.client.close();
106
- }
107
- }
108
- }
@@ -1,8 +0,0 @@
1
- /**
2
- * Database module exports for MCPJam Inspector
3
- * Provides comprehensive libSQL database functionality
4
- */
5
- export { DatabaseManager } from "./DatabaseManager.js";
6
- export { createDatabaseRoutes } from "./routes.js";
7
- export * from "./types.js";
8
- export * from "./utils.js";
@@ -1,86 +0,0 @@
1
- /**
2
- * Database API routes for MCPJam Inspector
3
- * Basic endpoints for app metadata operations
4
- */
5
- import { Router } from "express";
6
- export function createDatabaseRoutes(databaseManager) {
7
- const router = Router();
8
- // Error handler wrapper
9
- const asyncHandler = (fn) => (req, res, next) => {
10
- Promise.resolve(fn(req, res, next)).catch(next);
11
- };
12
- // ============================================================================
13
- // APP METADATA OPERATIONS
14
- // ============================================================================
15
- // Get all metadata
16
- router.get("/metadata", asyncHandler(async (req, res) => {
17
- const metadata = await databaseManager.getAllMetadata();
18
- res.json({
19
- success: true,
20
- data: metadata,
21
- count: metadata.length,
22
- });
23
- }));
24
- // Get single metadata value
25
- router.get("/metadata/:key", asyncHandler(async (req, res) => {
26
- const value = await databaseManager.getMetadata(req.params.key);
27
- if (value === null) {
28
- res.status(404).json({
29
- success: false,
30
- error: "Metadata key not found",
31
- });
32
- return;
33
- }
34
- res.json({
35
- success: true,
36
- data: { key: req.params.key, value },
37
- });
38
- }));
39
- // Set metadata value
40
- router.post("/metadata/:key", asyncHandler(async (req, res) => {
41
- const { value } = req.body;
42
- if (value === undefined) {
43
- res.status(400).json({
44
- success: false,
45
- error: "Missing required field: value",
46
- });
47
- return;
48
- }
49
- await databaseManager.setMetadata(req.params.key, value);
50
- res.json({
51
- success: true,
52
- message: "Metadata stored successfully",
53
- });
54
- }));
55
- // Delete metadata key
56
- router.delete("/metadata/:key", asyncHandler(async (req, res) => {
57
- await databaseManager.deleteMetadata(req.params.key);
58
- res.json({
59
- success: true,
60
- message: "Metadata deleted successfully",
61
- });
62
- }));
63
- // ============================================================================
64
- // HEALTH CHECK
65
- // ============================================================================
66
- router.get("/health", asyncHandler(async (req, res) => {
67
- res.json({
68
- success: true,
69
- message: "Database API is healthy",
70
- timestamp: new Date().toISOString(),
71
- });
72
- }));
73
- // Error handling middleware
74
- router.use((error, req, res, next) => {
75
- console.error("Database API Error:", error);
76
- const statusCode = error.status || 500;
77
- const message = error.message || "Internal server error";
78
- res.status(statusCode).json({
79
- success: false,
80
- error: message,
81
- code: error.code,
82
- ...(process.env.NODE_ENV === "development" && { stack: error.stack }),
83
- });
84
- });
85
- return router;
86
- }
@@ -1,27 +0,0 @@
1
- /**
2
- * Database types and interfaces for MCPJam Inspector
3
- * Basic foundation types for local SQLite database
4
- */
5
- // ============================================================================
6
- // ERROR TYPES
7
- // ============================================================================
8
- export class DatabaseError extends Error {
9
- code;
10
- cause;
11
- constructor(message, code, cause) {
12
- super(message);
13
- this.code = code;
14
- this.cause = cause;
15
- this.name = "DatabaseError";
16
- }
17
- }
18
- export class QueryError extends Error {
19
- query;
20
- cause;
21
- constructor(message, query, cause) {
22
- super(message);
23
- this.query = query;
24
- this.cause = cause;
25
- this.name = "QueryError";
26
- }
27
- }
@@ -1,86 +0,0 @@
1
- /**
2
- * Database utilities for MCPJam Inspector
3
- * Basic utilities for local SQLite database setup
4
- */
5
- import { mkdir, access } from "fs/promises";
6
- import { dirname } from "path";
7
- import { homedir } from "os";
8
- import { join } from "path";
9
- /**
10
- * Ensures the .mcpjam directory exists in the user's home directory
11
- */
12
- export async function ensureMCPJamDirectory() {
13
- const mcpjamDir = join(homedir(), ".mcpjam");
14
- try {
15
- await access(mcpjamDir);
16
- }
17
- catch {
18
- // Directory doesn't exist, create it
19
- await mkdir(mcpjamDir, { recursive: true });
20
- console.log(`šŸ“ Created MCPJam directory: ${mcpjamDir}`);
21
- }
22
- return mcpjamDir;
23
- }
24
- /**
25
- * Ensures the directory for a given file path exists
26
- */
27
- export async function ensureDirectoryExists(filePath) {
28
- const dir = dirname(filePath);
29
- try {
30
- await access(dir);
31
- }
32
- catch {
33
- await mkdir(dir, { recursive: true });
34
- }
35
- }
36
- /**
37
- * Gets the resolved database path - THE SINGLE SOURCE OF TRUTH
38
- * Priority: MCPJAM_DB_PATH env var > default ~/.mcpjam/data.db
39
- */
40
- export function getResolvedDatabasePath() {
41
- return process.env.MCPJAM_DB_PATH || join(homedir(), ".mcpjam", "data.db");
42
- }
43
- /**
44
- * Environment configuration helper
45
- */
46
- export function getDatabaseConfig() {
47
- return {
48
- localPath: getResolvedDatabasePath(),
49
- };
50
- }
51
- /**
52
- * Checks if the database file exists
53
- */
54
- export async function databaseExists(databasePath) {
55
- try {
56
- await access(databasePath);
57
- return true;
58
- }
59
- catch {
60
- return false;
61
- }
62
- }
63
- /**
64
- * Database connection test
65
- */
66
- export async function testDatabaseConnection(config) {
67
- try {
68
- const { createClient } = await import("@libsql/client");
69
- // Use the single source of truth for database path
70
- const dbPath = config?.localPath || getResolvedDatabasePath();
71
- await ensureDirectoryExists(dbPath);
72
- const client = createClient({
73
- url: `file:${dbPath}`,
74
- });
75
- // Test the connection with a simple query
76
- await client.execute("SELECT 1");
77
- client.close();
78
- return { success: true };
79
- }
80
- catch (error) {
81
- return {
82
- success: false,
83
- error: error instanceof Error ? error.message : "Unknown error",
84
- };
85
- }
86
- }
@@ -1,331 +0,0 @@
1
- #!/usr/bin/env node
2
- import cors from "cors";
3
- import { parseArgs } from "node:util";
4
- import { parse as shellParseArgs } from "shell-quote";
5
- import { createServer } from "node:net";
6
- import { SseError } from "@modelcontextprotocol/sdk/client/sse.js";
7
- import { getDefaultEnvironment } from "@modelcontextprotocol/sdk/client/stdio.js";
8
- import express from "express";
9
- import { randomUUID } from "node:crypto";
10
- import { DatabaseManager } from "./database/DatabaseManager.js";
11
- import { createDatabaseRoutes } from "./database/routes.js";
12
- import { getDatabaseConfig } from "./database/utils.js";
13
- import { MCPProxyService, ConsoleLogger } from "./shared/index.js";
14
- const SSE_HEADERS_PASSTHROUGH = ["authorization"];
15
- const STREAMABLE_HTTP_HEADERS_PASSTHROUGH = [
16
- "authorization",
17
- "mcp-session-id",
18
- "last-event-id",
19
- ];
20
- const defaultEnvironment = {
21
- ...getDefaultEnvironment(),
22
- ...(process.env.MCP_ENV_VARS ? JSON.parse(process.env.MCP_ENV_VARS) : {}),
23
- };
24
- const serverConfigs = process.env.MCP_SERVER_CONFIGS
25
- ? JSON.parse(process.env.MCP_SERVER_CONFIGS)
26
- : null;
27
- const { values } = parseArgs({
28
- args: process.argv.slice(2),
29
- options: {
30
- env: { type: "string", default: "" },
31
- args: { type: "string", default: "" },
32
- },
33
- });
34
- const app = express();
35
- app.use(cors());
36
- app.use((req, res, next) => {
37
- res.header("Access-Control-Expose-Headers", "mcp-session-id");
38
- next();
39
- });
40
- // Initialize database
41
- let databaseManager = null;
42
- const initializeDatabase = async () => {
43
- try {
44
- const dbConfig = getDatabaseConfig();
45
- databaseManager = new DatabaseManager(dbConfig);
46
- await databaseManager.initialize();
47
- // Add database routes after successful initialization
48
- app.use("/api/db", express.json({ limit: "10mb" }), // JSON middleware only for database routes
49
- (req, res, next) => {
50
- if (!databaseManager) {
51
- res.status(503).json({
52
- success: false,
53
- error: "Database not available",
54
- });
55
- return;
56
- }
57
- next();
58
- }, createDatabaseRoutes(databaseManager));
59
- console.log("āœ… Database API routes registered");
60
- }
61
- catch (error) {
62
- console.error("āŒ Failed to initialize database:", error);
63
- // Don't exit the process - continue without database functionality
64
- }
65
- };
66
- // Initialize MCPProxyService
67
- const mcpProxyService = new MCPProxyService({
68
- logger: new ConsoleLogger(),
69
- maxConnections: 50,
70
- });
71
- // Helper function to convert request query to ServerConfig
72
- const createServerConfigFromRequest = (req) => {
73
- const query = req.query;
74
- const transportType = query.transportType;
75
- const config = {
76
- id: randomUUID(),
77
- type: transportType,
78
- name: `server-${Date.now()}`,
79
- };
80
- if (transportType === "stdio") {
81
- config.command = query.command;
82
- config.args = query.args
83
- ? shellParseArgs(query.args)
84
- : undefined;
85
- // Safely parse env - only if it's a valid JSON string
86
- if (query.env) {
87
- try {
88
- config.env = JSON.parse(query.env);
89
- }
90
- catch (error) {
91
- console.warn(`Failed to parse env as JSON: ${query.env}, using empty object`);
92
- config.env = {};
93
- }
94
- }
95
- }
96
- else if (transportType === "sse" || transportType === "streamable-http") {
97
- config.url = query.url;
98
- }
99
- return config;
100
- };
101
- // Helper function to extract headers for transport
102
- const extractRequestHeaders = (req) => {
103
- const headers = {};
104
- const headersToPass = req.query.transportType === "sse"
105
- ? SSE_HEADERS_PASSTHROUGH
106
- : STREAMABLE_HTTP_HEADERS_PASSTHROUGH;
107
- for (const key of headersToPass) {
108
- const value = req.headers[key];
109
- if (value !== undefined) {
110
- headers[key] = Array.isArray(value) ? value[value.length - 1] : value;
111
- }
112
- }
113
- return headers;
114
- };
115
- app.get("/mcp", async (req, res) => {
116
- const sessionId = req.headers["mcp-session-id"];
117
- console.log(`šŸ“„ Received GET message for sessionId ${sessionId}`);
118
- try {
119
- const transport = mcpProxyService.getWebAppTransport(sessionId);
120
- if (!transport) {
121
- res.status(404).end("Session not found");
122
- return;
123
- }
124
- else {
125
- await transport.handleRequest(req, res);
126
- }
127
- }
128
- catch (error) {
129
- console.error("āŒ Error in /mcp route:", error);
130
- res.status(500).json(error);
131
- }
132
- });
133
- app.post("/mcp", async (req, res) => {
134
- const sessionId = req.headers["mcp-session-id"];
135
- console.log(`šŸ“„ Received POST message for sessionId ${sessionId}`);
136
- if (!sessionId) {
137
- try {
138
- console.log("šŸ”„ New streamable-http connection");
139
- // Create server config and headers from request
140
- const serverConfig = createServerConfigFromRequest(req);
141
- const requestHeaders = extractRequestHeaders(req);
142
- try {
143
- // Use MCPProxyService to create the streamable HTTP connection
144
- const { sessionId: newSessionId, webAppTransport } = await mcpProxyService.createStreamableHTTPConnection(serverConfig, requestHeaders);
145
- console.log(`✨ Connected MCP client to backing server transport for session ${newSessionId}`);
146
- await webAppTransport.handleRequest(req, res, req.body);
147
- }
148
- catch (error) {
149
- if (error instanceof SseError && error.code === 401) {
150
- console.error("šŸ”’ Received 401 Unauthorized from MCP server:", error.message);
151
- res.status(401).json(error);
152
- return;
153
- }
154
- throw error;
155
- }
156
- }
157
- catch (error) {
158
- console.error("āŒ Error in /mcp POST route:", error);
159
- res.status(500).json(error);
160
- }
161
- }
162
- else {
163
- try {
164
- const transport = mcpProxyService.getWebAppTransport(sessionId);
165
- if (!transport) {
166
- res.status(404).end("Transport not found for sessionId " + sessionId);
167
- }
168
- else {
169
- await transport.handleRequest(req, res);
170
- }
171
- }
172
- catch (error) {
173
- console.error("āŒ Error in /mcp route:", error);
174
- res.status(500).json(error);
175
- }
176
- }
177
- });
178
- app.get("/stdio", async (req, res) => {
179
- try {
180
- console.log("šŸ”„ New stdio/sse connection");
181
- // Create server config and headers from request
182
- const serverConfig = createServerConfigFromRequest(req);
183
- const requestHeaders = extractRequestHeaders(req);
184
- try {
185
- // Use MCPProxyService to create the SSE connection (handles STDIO stderr automatically)
186
- const { sessionId } = await mcpProxyService.createSSEConnection(serverConfig, res, requestHeaders);
187
- console.log(`✨ Connected MCP client to backing server transport for session ${sessionId}`);
188
- }
189
- catch (error) {
190
- if (error instanceof SseError && error.code === 401) {
191
- console.error("šŸ”’ Received 401 Unauthorized from MCP server:", error.message);
192
- res.status(401).json(error);
193
- return;
194
- }
195
- throw error;
196
- }
197
- }
198
- catch (error) {
199
- console.error("āŒ Error in /stdio route:", error);
200
- // Can't send a 500 response if headers already sent (which they are for SSE)
201
- }
202
- });
203
- app.get("/sse", async (req, res) => {
204
- try {
205
- console.log("šŸ”„ New sse connection");
206
- // Create server config and headers from request
207
- const serverConfig = createServerConfigFromRequest(req);
208
- const requestHeaders = extractRequestHeaders(req);
209
- try {
210
- // Use MCPProxyService to create the SSE connection
211
- const { sessionId } = await mcpProxyService.createSSEConnection(serverConfig, res, requestHeaders);
212
- console.log(`✨ Connected MCP client to backing server transport for session ${sessionId}`);
213
- }
214
- catch (error) {
215
- if (error instanceof SseError && error.code === 401) {
216
- console.error("šŸ”’ Received 401 Unauthorized from MCP server:", error.message);
217
- res.status(401).json(error);
218
- return;
219
- }
220
- throw error;
221
- }
222
- }
223
- catch (error) {
224
- console.error("āŒ Error in /sse route:", error);
225
- // Can't send a 500 response if headers already sent (which they are for SSE)
226
- }
227
- });
228
- app.post("/message", async (req, res) => {
229
- try {
230
- const sessionId = req.query.sessionId;
231
- console.log(`šŸ“„ Received message for sessionId ${sessionId}`);
232
- const transport = mcpProxyService.getWebAppTransport(sessionId);
233
- if (!transport) {
234
- res.status(404).end("Session not found");
235
- return;
236
- }
237
- await transport.handlePostMessage(req, res);
238
- }
239
- catch (error) {
240
- console.error("āŒ Error in /message route:", error);
241
- res.status(500).json(error);
242
- }
243
- });
244
- app.get("/health", (req, res) => {
245
- res.json({
246
- status: "ok",
247
- });
248
- });
249
- app.get("/config", (req, res) => {
250
- try {
251
- res.json({
252
- defaultEnvironment,
253
- defaultCommand: values.env,
254
- defaultArgs: values.args,
255
- serverConfigs,
256
- });
257
- }
258
- catch (error) {
259
- console.error("āŒ Error in /config route:", error);
260
- res.status(500).json(error);
261
- }
262
- });
263
- // Database API routes - will be added after database initialization
264
- // Function to find an available port
265
- const findAvailablePort = async (startPort) => {
266
- return new Promise((resolve, reject) => {
267
- const server = createServer();
268
- server.listen(startPort, () => {
269
- const port = server.address()?.port;
270
- server.close(() => {
271
- resolve(port);
272
- });
273
- });
274
- server.on("error", (err) => {
275
- if (err.code === "EADDRINUSE") {
276
- // Port is in use, try the next one
277
- findAvailablePort(startPort + 1)
278
- .then(resolve)
279
- .catch(reject);
280
- }
281
- else {
282
- reject(err);
283
- }
284
- });
285
- });
286
- };
287
- const PORT = process.env.PORT || 6277;
288
- // Store the actual running port
289
- let actualPort;
290
- // Add endpoint to get the actual running port
291
- app.get("/port", (req, res) => {
292
- res.json({
293
- port: actualPort,
294
- });
295
- });
296
- // Start server with dynamic port finding
297
- const startServer = async () => {
298
- try {
299
- // Initialize database first
300
- await initializeDatabase();
301
- const availablePort = await findAvailablePort(Number(PORT));
302
- actualPort = availablePort;
303
- const server = app.listen(availablePort);
304
- server.on("listening", () => {
305
- if (availablePort !== Number(PORT)) {
306
- console.log(`āš ļø Port ${PORT} was in use, using available port ${availablePort} instead`);
307
- }
308
- console.log(`\x1b[32m%s\x1b[0m`, `āš™ļø Proxy server listening on port ${availablePort}`);
309
- });
310
- server.on("error", (err) => {
311
- console.error(`āŒ Server error: ${err.message}`);
312
- process.exit(1);
313
- });
314
- // Graceful shutdown
315
- process.on("SIGINT", async () => {
316
- console.log("\nšŸ”„ Shutting down server...");
317
- server.close();
318
- await mcpProxyService.closeAllConnections();
319
- if (databaseManager) {
320
- await databaseManager.close();
321
- console.log("āœ… Database connection closed");
322
- }
323
- process.exit(0);
324
- });
325
- }
326
- catch (error) {
327
- console.error(`āŒ Failed to start server: ${error}`);
328
- process.exit(1);
329
- }
330
- };
331
- startServer();