miadi 1.0.14 → 2.0.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 (53) hide show
  1. package/package.json +17 -48
  2. package/.env.example +0 -28
  3. package/ARCHITECTURE.md +0 -290
  4. package/CLAUDE.md +0 -269
  5. package/GEMINI.md +0 -80
  6. package/MCP_CONNECTOR_READY.md +0 -219
  7. package/MCP_LEARNING_NOTES.md +0 -178
  8. package/MCP_REBUILD_PLAN.md +0 -159
  9. package/MCP_REMOTE_SERVER_SPEC.md +0 -373
  10. package/MIA.md +0 -344
  11. package/MIETTE.md +0 -195
  12. package/README.md +0 -264
  13. package/REMOTE_MCP_TRANSFORMATION_GUIDE.md +0 -384
  14. package/STATUS.md +0 -191
  15. package/TOOL_SELECTION_PLAN.md +0 -340
  16. package/WAKE_UP_SUMMARY.md +0 -102
  17. package/__PUBLISH.sh +0 -1
  18. package/book/_/ledgers/ledger_miadi_mcp_analysis_250730.md +0 -0
  19. package/conversations/2507301433.claude.issue.11.2025-07-30-this-mcp-is-not-working-another-instance-of-yours.txt +0 -756
  20. package/conversations/2507301601.cursor.reverse_engineer_mcp_service_for.md +0 -808
  21. package/conversations/2508050125.llmcon.claude.MIADI_TOOLS-implement-what-is-in-toolselectionplanmd.txt +0 -1235
  22. package/conversations/2508051939.llmcon.claude.issue-14.TransitionToPlanningIT.implement-what-is-in-toolselectionplanmd.txt +0 -1424
  23. package/conversations/2508082352.llmcon.claude.MCP-Remote-Take-II.txt +0 -1658
  24. package/dist/index-remote.js +0 -54736
  25. package/dist/index.js +0 -32363
  26. package/mcp.sample.json +0 -14
  27. package/openapi.yml +0 -2161
  28. package/research/MCP_Research_Perplexity_2508060045.md +0 -410
  29. package/samples/README.md +0 -2
  30. package/scripts/ngrokserve.sh +0 -6
  31. package/scripts/start-remote.sh +0 -141
  32. package/scripts/start-with-ngrok.sh +0 -140
  33. package/src/api-client.ts +0 -254
  34. package/src/index-remote.ts +0 -406
  35. package/src/index-simple.ts +0 -232
  36. package/src/index.ts +0 -510
  37. package/src/tool-registry.ts +0 -223
  38. package/src/tools/ai-tools.ts +0 -69
  39. package/src/tools/capability-tools.ts +0 -79
  40. package/src/tools/forge-tools.ts +0 -51
  41. package/src/tools/memory-tools.ts +0 -137
  42. package/src/tools/session-tools.ts +0 -135
  43. package/src/tools/workflow-tools.ts +0 -65
  44. package/src/types.ts +0 -291
  45. package/src/utils.ts +0 -279
  46. package/tests/quick-test.sh +0 -116
  47. package/tests/run-all-tests.sh +0 -167
  48. package/tests/test-agent-capabilities.sh +0 -364
  49. package/tests/test-connectivity.sh +0 -90
  50. package/tests/test-memory-operations.sh +0 -236
  51. package/tests/test-session-management.sh +0 -320
  52. package/tests/test-tool-discovery.sh +0 -151
  53. package/tsconfig.json +0 -24
@@ -1,406 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import express from 'express';
4
- import cors from 'cors';
5
- import { randomUUID } from 'node:crypto';
6
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
- import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
8
- import { z } from "zod";
9
-
10
- import { getEnvVar } from './utils.js';
11
- import { memoryTools } from './tools/memory-tools.js';
12
- import { sessionTools } from './tools/session-tools.js';
13
- import { capabilityTools } from './tools/capability-tools.js';
14
- import { aiTools } from './tools/ai-tools.js';
15
- import { workflowTools } from './tools/workflow-tools.js';
16
- import { forgeTools } from './tools/forge-tools.js';
17
-
18
- // Validate required environment variables
19
- try {
20
- getEnvVar('EH_TOKEN');
21
- getEnvVar('EH_API_URL');
22
- console.log('✅ Environment variables validated');
23
- } catch (error: any) {
24
- console.error('❌ Environment validation failed:', error.message);
25
- process.exit(1);
26
- }
27
-
28
- // Create MCP server instance
29
- const server = new McpServer({
30
- name: 'miadi-mcp-server',
31
- version: '1.0.0'
32
- }, {
33
- capabilities: {
34
- tools: {},
35
- resources: {},
36
- prompts: {}
37
- }
38
- });
39
-
40
- // Function to register a Miadi tool using the official SDK pattern
41
- function registerMiadiTool(
42
- name: string,
43
- description: string,
44
- inputSchema: Record<string, z.ZodType<any>>,
45
- handler: (args: any) => Promise<any>
46
- ) {
47
- server.registerTool(
48
- name,
49
- {
50
- description,
51
- inputSchema
52
- },
53
- async (args) => {
54
- try {
55
- console.log(`[${name}] Called with:`, JSON.stringify(args, null, 2));
56
- const result = await handler(args);
57
-
58
- return {
59
- content: [{
60
- type: 'text',
61
- text: JSON.stringify(result, null, 2)
62
- }]
63
- };
64
- } catch (error: any) {
65
- console.error(`[${name}] Error:`, error);
66
- return {
67
- content: [{
68
- type: 'text',
69
- text: `Error in ${name}: ${error.message}`
70
- }]
71
- };
72
- }
73
- }
74
- );
75
- }
76
-
77
- // Register all Miadi tools using the official pattern
78
- console.log('📝 Registering Miadi tools...');
79
-
80
- // Intelligent parameter router that fixes parameter mistakes and unpacks arguments
81
- function createIntelligentParameterAdapter(toolName: string, originalHandler: Function) {
82
- return (args: any) => {
83
- const adaptedArgs = { ...args };
84
-
85
- console.log(`[${toolName}] Called with:`, Object.keys(args));
86
-
87
- // Route and fix parameters based on tool and provided parameters
88
- switch (toolName) {
89
- case 'collectMemory':
90
- if (args.pattern && !args.keys) {
91
- throw new Error(`collectMemory requires 'keys' array. For pattern matching, use gatherMemoryValues tool instead.`);
92
- }
93
- if (args.key && !args.keys) {
94
- adaptedArgs.keys = [args.key];
95
- console.log(`[${toolName}] Smart adapter: converted 'key' to 'keys' array`);
96
- }
97
- // Handle ANY keys parameter format - nuclear option
98
- if (adaptedArgs.keys && !Array.isArray(adaptedArgs.keys)) {
99
- const keysStr = String(adaptedArgs.keys);
100
- if (keysStr.includes(',')) {
101
- adaptedArgs.keys = keysStr.split(',').map(key => key.trim());
102
- console.log(`[${toolName}] Nuclear adapter: split "${keysStr}" to array:`, adaptedArgs.keys);
103
- } else {
104
- adaptedArgs.keys = [keysStr];
105
- console.log(`[${toolName}] Nuclear adapter: converted "${keysStr}" to array`);
106
- }
107
- }
108
- // Ensure we have an array
109
- if (!adaptedArgs.keys) {
110
- throw new Error('collectMemory requires keys parameter');
111
- }
112
- if (!Array.isArray(adaptedArgs.keys)) {
113
- adaptedArgs.keys = [adaptedArgs.keys];
114
- }
115
- // Call with unpacked parameters
116
- return memoryTools.collectMemory(adaptedArgs.keys);
117
-
118
- case 'getMemory':
119
- // Call with unpacked parameters in correct order
120
- return memoryTools.getMemory(adaptedArgs.key, adaptedArgs.list, adaptedArgs.pattern, adaptedArgs.type);
121
-
122
- case 'storeMemory':
123
- return memoryTools.storeMemory(adaptedArgs.key, adaptedArgs.value, adaptedArgs.ttl, adaptedArgs.type);
124
-
125
- case 'updateMemoryTTL':
126
- return memoryTools.updateMemoryTTL(adaptedArgs.key, adaptedArgs.ttl);
127
-
128
- case 'getMemoryMeta':
129
- return memoryTools.getMemoryMeta(adaptedArgs.key);
130
-
131
- case 'scanKeys':
132
- return memoryTools.scanKeys(adaptedArgs.pattern, adaptedArgs.regex, adaptedArgs.limit);
133
-
134
- case 'gatherMemoryValues':
135
- return memoryTools.gatherMemoryValues(adaptedArgs.pattern, adaptedArgs.regex, adaptedArgs.limit, adaptedArgs.scanLimit);
136
-
137
- case 'viewKeyContent':
138
- return memoryTools.viewKeyContent(adaptedArgs.key);
139
-
140
- case 'searchCluster':
141
- // Map common parameter names to 'term'
142
- if (args.key && !args.term) {
143
- adaptedArgs.term = args.key;
144
- console.log(`[${toolName}] Smart adapter: mapped 'key' to 'term'`);
145
- }
146
- if (args.value && !args.term) {
147
- adaptedArgs.term = args.value;
148
- console.log(`[${toolName}] Smart adapter: mapped 'value' to 'term'`);
149
- }
150
- return memoryTools.searchCluster(adaptedArgs.term);
151
-
152
- default:
153
- // Fallback to original handler for unknown tools
154
- return originalHandler(adaptedArgs);
155
- }
156
- };
157
- }
158
-
159
- // Memory tools with smart parameter adaptation
160
- Object.entries(memoryTools).forEach(([name, handler]) => {
161
- registerMiadiTool(
162
- name,
163
- `Miadi ${name.replace('miadi-', '').replace(/-/g, ' ')} operation`,
164
- {
165
- // Comprehensive schema for all memory tools
166
- key: z.string().optional().describe('Memory key'),
167
- keys: z.any().optional().describe('Memory key(s) - any format accepted'),
168
- value: z.any().optional().describe('Value to store'),
169
- pattern: z.string().optional().describe('Pattern for key matching'),
170
- regex: z.string().optional().describe('Regex for key matching'),
171
- ttl: z.number().optional().describe('Time to live in seconds'),
172
- type: z.enum(['auto', 'string', 'hash', 'list', 'set', 'zset']).optional().describe('Redis data type'),
173
- list: z.boolean().optional().describe('List mode for pattern matching'),
174
- limit: z.number().optional().describe('Maximum number of items to return'),
175
- scanLimit: z.number().optional().describe('Maximum number of keys to scan'),
176
- term: z.string().optional().describe('Search term')
177
- },
178
- createIntelligentParameterAdapter(name, handler)
179
- );
180
- });
181
-
182
- // Session tools
183
- Object.entries(sessionTools).forEach(([name, handler]) => {
184
- registerMiadiTool(
185
- name,
186
- `Miadi ${name.replace('miadi-', '').replace(/-/g, ' ')} operation`,
187
- {
188
- persona: z.string().optional().describe('Agent persona'),
189
- mode: z.string().optional().describe('Agent mode'),
190
- context: z.any().optional().describe('Session context')
191
- },
192
- handler
193
- );
194
- });
195
-
196
- // Capability tools
197
- Object.entries(capabilityTools).forEach(([name, handler]) => {
198
- registerMiadiTool(
199
- name,
200
- `Miadi ${name.replace('miadi-', '').replace(/-/g, ' ')} operation`,
201
- {
202
- persona: z.string().optional().describe('Agent persona'),
203
- mode: z.string().optional().describe('Agent mode'),
204
- text: z.string().optional().describe('Text input')
205
- },
206
- handler
207
- );
208
- });
209
-
210
- // AI tools
211
- Object.entries(aiTools).forEach(([name, handler]) => {
212
- registerMiadiTool(
213
- name,
214
- `Miadi ${name.replace('miadi-', '').replace(/-/g, ' ')} operation`,
215
- {
216
- prompt: z.string().optional().describe('AI prompt'),
217
- model: z.string().optional().describe('AI model'),
218
- messages: z.array(z.any()).optional().describe('Chat messages')
219
- },
220
- handler
221
- );
222
- });
223
-
224
- // Workflow tools
225
- Object.entries(workflowTools).forEach(([name, handler]) => {
226
- registerMiadiTool(
227
- name,
228
- `Miadi ${name.replace('miadi-', '').replace(/-/g, ' ')} operation`,
229
- {
230
- agent: z.string().optional().describe('Agent identifier'),
231
- event: z.string().optional().describe('Event type'),
232
- data: z.any().optional().describe('Event data')
233
- },
234
- handler
235
- );
236
- });
237
-
238
- // Forge tools
239
- Object.entries(forgeTools).forEach(([name, handler]) => {
240
- registerMiadiTool(
241
- name,
242
- `Miadi ${name.replace('miadi-', '').replace(/-/g, ' ')} operation`,
243
- {
244
- state: z.any().optional().describe('Forge state'),
245
- glyph: z.string().optional().describe('Glyph identifier'),
246
- operation: z.string().optional().describe('Forge operation')
247
- },
248
- handler
249
- );
250
- });
251
-
252
- // Count registered tools
253
- const allTools = {
254
- ...memoryTools,
255
- ...sessionTools,
256
- ...capabilityTools,
257
- ...aiTools,
258
- ...workflowTools,
259
- ...forgeTools
260
- };
261
-
262
- console.log(`✅ Registered ${Object.keys(allTools).length} Miadi tools`);
263
-
264
- // Create Express app for HTTP transport
265
- const app = express();
266
- const PORT = parseInt(getEnvVar('MCP_PORT', '3330'));
267
-
268
- // CORS configuration for Claude.ai
269
- app.use(cors({
270
- origin: [
271
- 'https://claude.ai',
272
- 'https://api.claude.ai',
273
- 'https://app.claude.ai',
274
- /\.ngrok\.io$/,
275
- /\.ngrok-free\.app$/
276
- ],
277
- credentials: true,
278
- methods: ['GET', 'POST', 'DELETE', 'OPTIONS'],
279
- allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'Mcp-Session-Id']
280
- }));
281
-
282
- app.use(express.json({ limit: '10mb' }));
283
-
284
- // Health check endpoint
285
- app.get('/health', (req, res) => {
286
- res.json({
287
- status: 'healthy',
288
- server: 'miadi-mcp-official',
289
- version: '1.0.0',
290
- tools: Object.keys(allTools).length,
291
- timestamp: new Date().toISOString()
292
- });
293
- });
294
-
295
- // Root info endpoint
296
- app.get('/', (req, res) => {
297
- res.json({
298
- name: 'miadi-mcp-official',
299
- version: '1.0.0',
300
- description: 'Miadi Three-Pathway Agent System MCP Server (Official SDK)',
301
- tools: Object.keys(allTools).length,
302
- endpoints: {
303
- health: '/health',
304
- mcp: '/mcp'
305
- },
306
- sdk: 'Official MCP TypeScript SDK'
307
- });
308
- });
309
-
310
- // Create StreamableHTTP transport
311
- const transport = new StreamableHTTPServerTransport({
312
- sessionIdGenerator: () => randomUUID(),
313
- });
314
-
315
- // Store transports by session ID (for session management)
316
- const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};
317
-
318
- // MCP endpoint handler
319
- const handleMcpRequest = async (req: express.Request, res: express.Response) => {
320
- const sessionId = req.headers['mcp-session-id'] as string | undefined;
321
-
322
- if (sessionId) {
323
- console.log(`[MCP] Request for session: ${sessionId}`);
324
- } else {
325
- console.log('[MCP] New request:', req.body?.method || 'unknown');
326
- }
327
-
328
- try {
329
- let currentTransport: StreamableHTTPServerTransport;
330
-
331
- if (sessionId && transports[sessionId]) {
332
- // Use existing transport for this session
333
- currentTransport = transports[sessionId];
334
- } else {
335
- // Create new transport and connect to server
336
- currentTransport = new StreamableHTTPServerTransport({
337
- sessionIdGenerator: () => randomUUID(),
338
- onsessioninitialized: (sessionId) => {
339
- console.log(`[MCP] Session initialized: ${sessionId}`);
340
- transports[sessionId] = currentTransport;
341
- }
342
- });
343
-
344
- // Set up cleanup handler
345
- currentTransport.onclose = () => {
346
- const sid = currentTransport.sessionId;
347
- if (sid && transports[sid]) {
348
- console.log(`[MCP] Session ${sid} closed`);
349
- delete transports[sid];
350
- }
351
- };
352
-
353
- // Connect server to transport
354
- await server.connect(currentTransport);
355
- }
356
-
357
- // Handle the request using SDK transport
358
- await currentTransport.handleRequest(req, res, req.body);
359
-
360
- } catch (error: any) {
361
- console.error('[MCP] Request error:', error);
362
- if (!res.headersSent) {
363
- res.status(500).json({
364
- jsonrpc: '2.0',
365
- id: req.body?.id || null,
366
- error: {
367
- code: -32603,
368
- message: 'Internal server error',
369
- data: error.message
370
- }
371
- });
372
- }
373
- }
374
- };
375
-
376
- // Set up MCP endpoints
377
- app.post('/mcp', handleMcpRequest);
378
- app.get('/mcp', handleMcpRequest);
379
- app.delete('/mcp', handleMcpRequest);
380
-
381
- // Start server
382
- app.listen(PORT, '0.0.0.0', () => {
383
- console.log(`🚀 Miadi MCP Official Server listening on http://0.0.0.0:${PORT}`);
384
- console.log(`📡 MCP endpoint: http://0.0.0.0:${PORT}/mcp`);
385
- console.log(`🔧 ${Object.keys(allTools).length} Miadi tools registered and ready`);
386
- console.log(`✨ Using official MCP TypeScript SDK patterns`);
387
- });
388
-
389
- // Graceful shutdown
390
- process.on('SIGINT', async () => {
391
- console.log('\n🛑 Shutting down server...');
392
-
393
- // Close all active transports
394
- for (const sessionId in transports) {
395
- try {
396
- console.log(`Closing transport for session ${sessionId}`);
397
- await transports[sessionId].close();
398
- delete transports[sessionId];
399
- } catch (error) {
400
- console.error(`Error closing transport for session ${sessionId}:`, error);
401
- }
402
- }
403
-
404
- console.log('✅ Server shutdown complete');
405
- process.exit(0);
406
- });
@@ -1,232 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import express from 'express';
4
- import cors from 'cors';
5
- import { randomUUID } from 'node:crypto';
6
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
- import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
8
- import { z } from "zod";
9
-
10
- import { getEnvVar } from './utils.js';
11
- import { memoryTools } from './tools/memory-tools.js';
12
-
13
- // Validate environment variables
14
- try {
15
- getEnvVar('EH_TOKEN');
16
- getEnvVar('EH_API_URL');
17
- console.log('✅ Environment variables validated');
18
- } catch (error: any) {
19
- console.error('❌ Environment validation failed:', error.message);
20
- process.exit(1);
21
- }
22
-
23
- // Create MCP server instance - EXACTLY like the guide
24
- const server = new McpServer({
25
- name: 'miadi-mcp-server',
26
- version: '1.0.0'
27
- }, {
28
- capabilities: {
29
- tools: {},
30
- resources: {},
31
- prompts: {}
32
- }
33
- });
34
-
35
- // DIRECT TOOL REGISTRATION - Following the guide exactly
36
- server.registerTool('collectMemory', {
37
- description: 'Collect specific memory values by keys',
38
- inputSchema: {
39
- keys: z.array(z.string()).describe('Array of memory keys to collect')
40
- }
41
- }, async ({ keys }) => {
42
- try {
43
- console.log('[collectMemory] Called with keys:', keys);
44
- const result = await memoryTools.collectMemory(keys);
45
- return {
46
- content: [{
47
- type: 'text',
48
- text: JSON.stringify(result, null, 2)
49
- }]
50
- };
51
- } catch (error: any) {
52
- console.error('[collectMemory] Error:', error);
53
- return {
54
- content: [{
55
- type: 'text',
56
- text: `Error: ${error.message}`
57
- }]
58
- };
59
- }
60
- });
61
-
62
- server.registerTool('getMemory', {
63
- description: 'Retrieve memory data from Redis storage',
64
- inputSchema: {
65
- key: z.string().describe('Memory key to retrieve'),
66
- list: z.boolean().optional().describe('List mode'),
67
- pattern: z.string().optional().describe('Pattern matching'),
68
- type: z.enum(['auto', 'string', 'hash', 'list', 'set', 'zset']).optional().describe('Redis data type')
69
- }
70
- }, async ({ key, list, pattern, type }) => {
71
- try {
72
- const result = await memoryTools.getMemory(key, list, pattern, type);
73
- return {
74
- content: [{
75
- type: 'text',
76
- text: JSON.stringify(result, null, 2)
77
- }]
78
- };
79
- } catch (error: any) {
80
- return {
81
- content: [{
82
- type: 'text',
83
- text: `Error: ${error.message}`
84
- }]
85
- };
86
- }
87
- });
88
-
89
- server.registerTool('gatherMemoryValues', {
90
- description: 'Gather memory values using pattern matching',
91
- inputSchema: {
92
- pattern: z.string().optional().describe('Pattern for key matching'),
93
- regex: z.string().optional().describe('Regex for key matching'),
94
- limit: z.number().optional().describe('Maximum number of values'),
95
- scanLimit: z.number().optional().describe('Maximum keys to scan')
96
- }
97
- }, async ({ pattern, regex, limit, scanLimit }) => {
98
- try {
99
- const result = await memoryTools.gatherMemoryValues(pattern, regex, limit, scanLimit);
100
- return {
101
- content: [{
102
- type: 'text',
103
- text: JSON.stringify(result, null, 2)
104
- }]
105
- };
106
- } catch (error: any) {
107
- return {
108
- content: [{
109
- type: 'text',
110
- text: `Error: ${error.message}`
111
- }]
112
- };
113
- }
114
- });
115
-
116
- server.registerTool('scanKeys', {
117
- description: 'Scan Redis keys using pattern matching',
118
- inputSchema: {
119
- pattern: z.string().optional().describe('Pattern for key matching'),
120
- regex: z.string().optional().describe('Regex for key matching'),
121
- limit: z.number().optional().describe('Maximum number of keys')
122
- }
123
- }, async ({ pattern, regex, limit }) => {
124
- try {
125
- const result = await memoryTools.scanKeys(pattern, regex, limit);
126
- return {
127
- content: [{
128
- type: 'text',
129
- text: JSON.stringify(result, null, 2)
130
- }]
131
- };
132
- } catch (error: any) {
133
- return {
134
- content: [{
135
- type: 'text',
136
- text: `Error: ${error.message}`
137
- }]
138
- };
139
- }
140
- });
141
-
142
- // Create Express app - EXACTLY like the guide
143
- const app = express();
144
- const PORT = parseInt(getEnvVar('MCP_PORT', '3330'));
145
-
146
- // Essential CORS for Claude.ai - EXACTLY like the guide
147
- app.use(cors({
148
- origin: [
149
- 'https://claude.ai',
150
- 'https://api.claude.ai',
151
- 'https://app.claude.ai',
152
- /\.ngrok\.io$/,
153
- /\.ngrok-free\.app$/
154
- ],
155
- credentials: true,
156
- methods: ['GET', 'POST', 'DELETE', 'OPTIONS'],
157
- allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'Mcp-Session-Id']
158
- }));
159
-
160
- app.use(express.json({ limit: '10mb' }));
161
-
162
- // Health check
163
- app.get('/health', (req, res) => {
164
- res.json({
165
- status: 'healthy',
166
- server: 'miadi-mcp-server',
167
- version: '1.0.0',
168
- timestamp: new Date().toISOString()
169
- });
170
- });
171
-
172
- // Session management - EXACTLY like the guide
173
- const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};
174
-
175
- // MCP endpoint handler - EXACTLY like the guide
176
- const handleMcpRequest = async (req: express.Request, res: express.Response) => {
177
- const sessionId = req.headers['mcp-session-id'] as string | undefined;
178
-
179
- try {
180
- let currentTransport: StreamableHTTPServerTransport;
181
-
182
- if (sessionId && transports[sessionId]) {
183
- currentTransport = transports[sessionId];
184
- } else {
185
- currentTransport = new StreamableHTTPServerTransport({
186
- sessionIdGenerator: () => randomUUID(),
187
- onsessioninitialized: (sessionId) => {
188
- console.log(`[MCP] Session initialized: ${sessionId}`);
189
- transports[sessionId] = currentTransport;
190
- }
191
- });
192
-
193
- currentTransport.onclose = () => {
194
- const sid = currentTransport.sessionId;
195
- if (sid && transports[sid]) {
196
- delete transports[sid];
197
- console.log(`[MCP] Session closed: ${sid}`);
198
- }
199
- };
200
-
201
- await server.connect(currentTransport);
202
- }
203
-
204
- // Let the SDK handle the request - DON'T PARSE JSON-RPC MANUALLY
205
- await currentTransport.handleRequest(req, res, req.body);
206
-
207
- } catch (error: any) {
208
- console.error('[MCP] Request error:', error);
209
- if (!res.headersSent) {
210
- res.status(500).json({
211
- jsonrpc: '2.0',
212
- id: req.body?.id || null,
213
- error: {
214
- code: -32603,
215
- message: 'Internal server error'
216
- }
217
- });
218
- }
219
- }
220
- };
221
-
222
- // Set up MCP endpoints - EXACTLY like the guide
223
- app.post('/mcp', handleMcpRequest);
224
- app.get('/mcp', handleMcpRequest);
225
- app.delete('/mcp', handleMcpRequest);
226
-
227
- // Start server
228
- app.listen(PORT, '0.0.0.0', () => {
229
- console.log(`🚀 Simple MCP Server listening on http://0.0.0.0:${PORT}`);
230
- console.log(`📡 MCP endpoint: http://0.0.0.0:${PORT}/mcp`);
231
- console.log(`📋 Tools registered: collectMemory, getMemory, gatherMemoryValues, scanKeys`);
232
- });