miadi 1.0.14 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +16 -48
- package/.env.example +0 -28
- package/ARCHITECTURE.md +0 -290
- package/CLAUDE.md +0 -269
- package/GEMINI.md +0 -80
- package/MCP_CONNECTOR_READY.md +0 -219
- package/MCP_LEARNING_NOTES.md +0 -178
- package/MCP_REBUILD_PLAN.md +0 -159
- package/MCP_REMOTE_SERVER_SPEC.md +0 -373
- package/MIA.md +0 -344
- package/MIETTE.md +0 -195
- package/README.md +0 -264
- package/REMOTE_MCP_TRANSFORMATION_GUIDE.md +0 -384
- package/STATUS.md +0 -191
- package/TOOL_SELECTION_PLAN.md +0 -340
- package/WAKE_UP_SUMMARY.md +0 -102
- package/__PUBLISH.sh +0 -1
- package/book/_/ledgers/ledger_miadi_mcp_analysis_250730.md +0 -0
- package/conversations/2507301433.claude.issue.11.2025-07-30-this-mcp-is-not-working-another-instance-of-yours.txt +0 -756
- package/conversations/2507301601.cursor.reverse_engineer_mcp_service_for.md +0 -808
- package/conversations/2508050125.llmcon.claude.MIADI_TOOLS-implement-what-is-in-toolselectionplanmd.txt +0 -1235
- package/conversations/2508051939.llmcon.claude.issue-14.TransitionToPlanningIT.implement-what-is-in-toolselectionplanmd.txt +0 -1424
- package/conversations/2508082352.llmcon.claude.MCP-Remote-Take-II.txt +0 -1658
- package/dist/index-remote.js +0 -54736
- package/dist/index.js +0 -32363
- package/mcp.sample.json +0 -14
- package/openapi.yml +0 -2161
- package/research/MCP_Research_Perplexity_2508060045.md +0 -410
- package/samples/README.md +0 -2
- package/scripts/ngrokserve.sh +0 -6
- package/scripts/start-remote.sh +0 -141
- package/scripts/start-with-ngrok.sh +0 -140
- package/src/api-client.ts +0 -254
- package/src/index-remote.ts +0 -406
- package/src/index-simple.ts +0 -232
- package/src/index.ts +0 -510
- package/src/tool-registry.ts +0 -223
- package/src/tools/ai-tools.ts +0 -69
- package/src/tools/capability-tools.ts +0 -79
- package/src/tools/forge-tools.ts +0 -51
- package/src/tools/memory-tools.ts +0 -137
- package/src/tools/session-tools.ts +0 -135
- package/src/tools/workflow-tools.ts +0 -65
- package/src/types.ts +0 -291
- package/src/utils.ts +0 -279
- package/tests/quick-test.sh +0 -116
- package/tests/run-all-tests.sh +0 -167
- package/tests/test-agent-capabilities.sh +0 -364
- package/tests/test-connectivity.sh +0 -90
- package/tests/test-memory-operations.sh +0 -236
- package/tests/test-session-management.sh +0 -320
- package/tests/test-tool-discovery.sh +0 -151
- package/tsconfig.json +0 -24
package/src/index-remote.ts
DELETED
|
@@ -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
|
-
});
|
package/src/index-simple.ts
DELETED
|
@@ -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
|
-
});
|