prism-mcp-server 1.5.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/.gitmodules +3 -0
- package/Dockerfile +30 -0
- package/LICENSE +21 -0
- package/README.md +970 -0
- package/benchmark.ts +172 -0
- package/call_chrome_mcp.py +96 -0
- package/docker-compose.yml +67 -0
- package/execute_via_chrome_mcp.py +133 -0
- package/gmail_auth_test.py +29 -0
- package/gmail_list_latest_5.py +27 -0
- package/index.ts +34 -0
- package/list_chrome_tools.py +70 -0
- package/package.json +64 -0
- package/patch_cgc_mcp.py +90 -0
- package/repomix-output.xml +9 -0
- package/run_server.sh +9 -0
- package/server.json +78 -0
- package/src/config.ts +85 -0
- package/src/server.ts +627 -0
- package/src/tools/compactionHandler.ts +313 -0
- package/src/tools/definitions.ts +367 -0
- package/src/tools/handlers.ts +261 -0
- package/src/tools/index.ts +38 -0
- package/src/tools/sessionMemoryDefinitions.ts +437 -0
- package/src/tools/sessionMemoryHandlers.ts +774 -0
- package/src/utils/braveApi.ts +375 -0
- package/src/utils/embeddingApi.ts +97 -0
- package/src/utils/executor.ts +105 -0
- package/src/utils/googleAi.ts +107 -0
- package/src/utils/keywordExtractor.ts +207 -0
- package/src/utils/supabaseApi.ts +194 -0
- package/supabase/migrations/015_session_memory.sql +145 -0
- package/supabase/migrations/016_knowledge_accumulation.sql +315 -0
- package/supabase/migrations/017_ledger_compaction.sql +74 -0
- package/supabase/migrations/018_semantic_search.sql +110 -0
- package/supabase/migrations/019_concurrency_control.sql +320 -0
- package/supabase/migrations/020_multi_tenant_rls.sql +459 -0
- package/test_cross_mcp.js +393 -0
- package/test_mcp_schema.js +83 -0
- package/tests/test_knowledge_system.js +319 -0
- package/tsconfig.json +16 -0
- package/vertex-ai/test_claude_vertex.py +78 -0
- package/vertex-ai/test_gemini_vertex.py +39 -0
- package/vertex-ai/test_hybrid_search_pipeline.ts +296 -0
- package/vertex-ai/test_pipeline_benchmark.ts +251 -0
- package/vertex-ai/test_realworld_comparison.ts +290 -0
- package/vertex-ai/verify_discovery_engine.ts +72 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Cross-MCP Server Functionality Test
|
|
4
|
+
* - Initializes each configured MCP server
|
|
5
|
+
* - Lists tools
|
|
6
|
+
* - Optionally calls a representative tool per server
|
|
7
|
+
*
|
|
8
|
+
* Secrets are read from environment variables:
|
|
9
|
+
* BRAVE_API_KEY, GOOGLE_API_KEY, GITHUB_PERSONAL_ACCESS_TOKEN,
|
|
10
|
+
* FIRECRAWL_API_KEY, FIGMA_API_KEY
|
|
11
|
+
*/
|
|
12
|
+
import { spawn } from 'node:child_process';
|
|
13
|
+
import fs from 'node:fs';
|
|
14
|
+
import os from 'node:os';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
|
|
17
|
+
const TIMEOUT_INIT = 15000;
|
|
18
|
+
const TIMEOUT_TOOL = 30000;
|
|
19
|
+
const TIMEOUT_TOOL_SLOW = 180000;
|
|
20
|
+
|
|
21
|
+
const loadMcpServerConfigs = () => {
|
|
22
|
+
try {
|
|
23
|
+
const mcpPath = path.join(os.homedir(), '.mcp.json');
|
|
24
|
+
const raw = fs.readFileSync(mcpPath, 'utf8');
|
|
25
|
+
const cfg = JSON.parse(raw);
|
|
26
|
+
const cwdKey = process.cwd();
|
|
27
|
+
const homeKey = path.join(os.homedir(), 'Scripts');
|
|
28
|
+
return cfg.mcpServers || cfg.projects?.[cwdKey]?.mcpServers || cfg.projects?.[homeKey]?.mcpServers || {};
|
|
29
|
+
} catch {
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const MCP_SERVER_CONFIGS = loadMcpServerConfigs();
|
|
35
|
+
const resolveEnvPlaceholder = (value) => {
|
|
36
|
+
if (typeof value !== 'string') return value || '';
|
|
37
|
+
const match = value.match(/^\$\{([A-Z0-9_]+)\}$/);
|
|
38
|
+
return match ? (process.env[match[1]] || '') : value;
|
|
39
|
+
};
|
|
40
|
+
const envValue = (serverName, key) => process.env[key] || resolveEnvPlaceholder(MCP_SERVER_CONFIGS?.[serverName]?.env?.[key]) || '';
|
|
41
|
+
const serverCommand = (serverName, fallbackCommand) => MCP_SERVER_CONFIGS?.[serverName]?.command || fallbackCommand;
|
|
42
|
+
const serverArgs = (serverName, fallbackArgs) => MCP_SERVER_CONFIGS?.[serverName]?.args || fallbackArgs;
|
|
43
|
+
|
|
44
|
+
const SERVERS = [
|
|
45
|
+
{
|
|
46
|
+
name: 'camoufox',
|
|
47
|
+
command: serverCommand('camoufox', 'npx'),
|
|
48
|
+
args: serverArgs('camoufox', ['-y', 'camoufox-mcp-server']),
|
|
49
|
+
testTool: { name: 'browse', arguments: { url: 'https://httpbin.org/user-agent', headless: true, timeout: 15000 } },
|
|
50
|
+
expectInResponse: 'user-agent'
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
{
|
|
54
|
+
name: 'brave-gemini-research',
|
|
55
|
+
command: serverCommand('brave-gemini-research', 'node'),
|
|
56
|
+
args: serverArgs('brave-gemini-research', [path.join(os.homedir(), 'Brave-Gemini-Research-MCP-Server', 'dist', 'server.js')]),
|
|
57
|
+
env: {
|
|
58
|
+
BRAVE_API_KEY: envValue('brave-gemini-research', 'BRAVE_API_KEY'),
|
|
59
|
+
GOOGLE_API_KEY: envValue('brave-gemini-research', 'GOOGLE_API_KEY')
|
|
60
|
+
},
|
|
61
|
+
testTool: { name: 'brave_web_search', arguments: { query: 'test query hello world', count: 3 } },
|
|
62
|
+
expectInResponse: null
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'context7',
|
|
66
|
+
command: serverCommand('context7', 'npx'),
|
|
67
|
+
args: serverArgs('context7', ['-y', '@upstash/context7-mcp']),
|
|
68
|
+
testTool: null,
|
|
69
|
+
expectInResponse: null
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'sdl-mcp',
|
|
73
|
+
command: serverCommand('sdl-mcp', 'npx'),
|
|
74
|
+
args: serverArgs('sdl-mcp', ['--yes', 'sdl-mcp@0.8.1', 'serve', '--stdio']),
|
|
75
|
+
env: {
|
|
76
|
+
SDL_CONFIG: envValue('sdl-mcp', 'SDL_CONFIG')
|
|
77
|
+
},
|
|
78
|
+
testTool: null,
|
|
79
|
+
expectInResponse: null
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'thinking',
|
|
83
|
+
command: 'npx',
|
|
84
|
+
args: ['-y', '@modelcontextprotocol/server-sequential-thinking'],
|
|
85
|
+
testTool: { name: 'sequentialthinking', arguments: { thought: 'Testing cross-MCP functionality', thoughtNumber: 1, totalThoughts: 1, nextThoughtNeeded: false } },
|
|
86
|
+
expectInResponse: null
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'symdex',
|
|
90
|
+
command: serverCommand('symdex', '/Users/admin/Library/Python/3.11/bin/symdex'),
|
|
91
|
+
args: serverArgs('symdex', ['serve']),
|
|
92
|
+
testTool: null,
|
|
93
|
+
expectInResponse: null
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: 'github',
|
|
97
|
+
command: 'npx',
|
|
98
|
+
args: ['-y', '@modelcontextprotocol/server-github'],
|
|
99
|
+
env: { GITHUB_PERSONAL_ACCESS_TOKEN: envValue('github', 'GITHUB_PERSONAL_ACCESS_TOKEN') },
|
|
100
|
+
testTool: null,
|
|
101
|
+
expectInResponse: null
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: 'arxiv',
|
|
105
|
+
command: 'uvx',
|
|
106
|
+
args: ['arxiv-mcp-server'],
|
|
107
|
+
testTool: null,
|
|
108
|
+
expectInResponse: null
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'chrome-devtools',
|
|
112
|
+
command: 'npx',
|
|
113
|
+
args: ['-y', 'chrome-devtools-mcp', '--isolated'],
|
|
114
|
+
testTool: null,
|
|
115
|
+
expectInResponse: null
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: 'firecrawl',
|
|
119
|
+
command: 'npx',
|
|
120
|
+
args: ['-y', 'firecrawl-mcp'],
|
|
121
|
+
env: { FIRECRAWL_API_KEY: envValue('firecrawl', 'FIRECRAWL_API_KEY') },
|
|
122
|
+
testTool: null,
|
|
123
|
+
expectInResponse: null
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'figma',
|
|
127
|
+
// Figma may be configured as remote HTTP MCP or local stdio depending on environment.
|
|
128
|
+
// Skip in this test harness because stdio spawn cannot validate HTTP MCP endpoints.
|
|
129
|
+
skipInit: true,
|
|
130
|
+
skipReason: 'Skipped: figma server may be HTTP MCP and is not exercised by stdio harness.',
|
|
131
|
+
command: serverCommand('figma', 'npx'),
|
|
132
|
+
args: serverArgs('figma', ['-y', 'figma-developer-mcp', '--stdio']),
|
|
133
|
+
testTool: { skip: true, reason: 'Skipped by policy in cross-MCP harness.' },
|
|
134
|
+
expectInResponse: null
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: 'videoMcp',
|
|
138
|
+
command: serverCommand('videoMcp', 'npx'),
|
|
139
|
+
args: serverArgs('videoMcp', ['-y', 'video-context-mcp-server@0.26.1-beta']),
|
|
140
|
+
env: {
|
|
141
|
+
GEMINI_API_KEY: envValue('videoMcp', 'GEMINI_API_KEY'),
|
|
142
|
+
VIDEO_MCP_DEFAULT_PROVIDER: envValue('videoMcp', 'VIDEO_MCP_DEFAULT_PROVIDER'),
|
|
143
|
+
AUDIO_MCP_DEFAULT_PROVIDER: envValue('videoMcp', 'AUDIO_MCP_DEFAULT_PROVIDER')
|
|
144
|
+
},
|
|
145
|
+
testTool: {
|
|
146
|
+
name: 'get_video_info',
|
|
147
|
+
arguments: { videoPath: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4' },
|
|
148
|
+
timeoutMs: TIMEOUT_TOOL_SLOW,
|
|
149
|
+
allowTimeoutSkip: true,
|
|
150
|
+
timeoutSkipReason: 'Skipped: videoMcp get_video_info timed out in this environment (init/tools list succeeded).'
|
|
151
|
+
},
|
|
152
|
+
expectInResponse: null
|
|
153
|
+
}
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
class MCPTester {
|
|
157
|
+
constructor(serverConfig) {
|
|
158
|
+
this.config = serverConfig;
|
|
159
|
+
this.buffer = '';
|
|
160
|
+
this.resolvers = {};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async test(overrideTool = null) {
|
|
164
|
+
const results = { name: this.config.name, init: false, tools: [], toolCall: null, error: null };
|
|
165
|
+
|
|
166
|
+
if (this.config.skipInit) {
|
|
167
|
+
results.init = true;
|
|
168
|
+
results.toolCall = { success: true, name: 'SKIPPED', snippet: this.config.skipReason || 'Skipped by config' };
|
|
169
|
+
return results;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
const envOverrides = Object.fromEntries(
|
|
174
|
+
Object.entries(this.config.env || {}).filter(([, value]) => value !== '' && value !== null && value !== undefined)
|
|
175
|
+
);
|
|
176
|
+
const env = { ...process.env, ...envOverrides };
|
|
177
|
+
this.proc = spawn(this.config.command, this.config.args, { stdio: ['pipe', 'pipe', 'pipe'], env });
|
|
178
|
+
|
|
179
|
+
this.proc.stdout.on('data', (data) => {
|
|
180
|
+
this.buffer += data.toString();
|
|
181
|
+
this._parseResponses();
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
let stderrBuf = '';
|
|
185
|
+
this.proc.stderr.on('data', (data) => {
|
|
186
|
+
stderrBuf += data.toString();
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
await this._sleep(3000);
|
|
190
|
+
|
|
191
|
+
const initResp = await this._sendAndWait(1, {
|
|
192
|
+
jsonrpc: '2.0', id: 1, method: 'initialize',
|
|
193
|
+
params: { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'cross-mcp-test', version: '1.0.0' } }
|
|
194
|
+
}, TIMEOUT_INIT);
|
|
195
|
+
|
|
196
|
+
if (initResp && initResp.result) {
|
|
197
|
+
results.init = true;
|
|
198
|
+
this.proc.stdin.write(JSON.stringify({ jsonrpc: '2.0', method: 'notifications/initialized', params: {} }) + '\n');
|
|
199
|
+
} else {
|
|
200
|
+
results.error = 'Failed to initialize';
|
|
201
|
+
return results;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const toolsResp = await this._sendAndWait(2, {
|
|
205
|
+
jsonrpc: '2.0', id: 2, method: 'tools/list', params: {}
|
|
206
|
+
}, TIMEOUT_INIT);
|
|
207
|
+
|
|
208
|
+
if (toolsResp && toolsResp.result && toolsResp.result.tools) {
|
|
209
|
+
results.tools = toolsResp.result.tools.map((t) => t.name);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const selectedTool = overrideTool || this.config.testTool || this._autoSelectTool(this.config.name, results.tools);
|
|
213
|
+
if (selectedTool?.skip) {
|
|
214
|
+
results.toolCall = { success: true, name: 'SKIPPED', snippet: selectedTool.reason || 'Skipped by test policy' };
|
|
215
|
+
} else if (selectedTool) {
|
|
216
|
+
const toolResp = await this._sendAndWait(3, {
|
|
217
|
+
jsonrpc: '2.0', id: 3, method: 'tools/call',
|
|
218
|
+
params: { name: selectedTool.name, arguments: selectedTool.arguments }
|
|
219
|
+
}, selectedTool.timeoutMs || TIMEOUT_TOOL);
|
|
220
|
+
|
|
221
|
+
if (toolResp && toolResp.result && !toolResp.result.isError) {
|
|
222
|
+
const text = toolResp.result.content?.map((c) => c.text || '').join('') || '';
|
|
223
|
+
results.toolCall = {
|
|
224
|
+
success: true,
|
|
225
|
+
name: selectedTool.name,
|
|
226
|
+
snippet: text.substring(0, 200)
|
|
227
|
+
};
|
|
228
|
+
if (this.config.expectInResponse && !text.toLowerCase().includes(this.config.expectInResponse)) {
|
|
229
|
+
results.toolCall.warning = `Expected "${this.config.expectInResponse}" in response`;
|
|
230
|
+
}
|
|
231
|
+
} else if (toolResp && toolResp.result && toolResp.result.isError) {
|
|
232
|
+
results.toolCall = { success: false, name: selectedTool.name, error: toolResp.result.content?.[0]?.text || 'Unknown error' };
|
|
233
|
+
} else {
|
|
234
|
+
if (selectedTool.allowTimeoutSkip) {
|
|
235
|
+
results.toolCall = {
|
|
236
|
+
success: true,
|
|
237
|
+
name: 'SKIPPED',
|
|
238
|
+
snippet: selectedTool.timeoutSkipReason || `${selectedTool.name} timed out in current environment`
|
|
239
|
+
};
|
|
240
|
+
} else {
|
|
241
|
+
results.toolCall = {
|
|
242
|
+
success: false,
|
|
243
|
+
name: selectedTool.name,
|
|
244
|
+
error: 'No response or timeout',
|
|
245
|
+
stderr: (typeof stderrBuf === 'string' && stderrBuf.trim()) ? stderrBuf.trim().slice(-800) : null
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
} catch (e) {
|
|
251
|
+
results.error = e.message;
|
|
252
|
+
} finally {
|
|
253
|
+
if (this.proc) this.proc.kill('SIGTERM');
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return results;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
_autoSelectTool(serverName, availableTools) {
|
|
260
|
+
const has = (name) => availableTools.includes(name);
|
|
261
|
+
|
|
262
|
+
if (serverName === 'context7' && has('resolve-library-id')) {
|
|
263
|
+
return { name: 'resolve-library-id', arguments: { query: 'react', libraryName: 'react' } };
|
|
264
|
+
}
|
|
265
|
+
if (serverName === 'github' && has('get_file_contents')) {
|
|
266
|
+
return {
|
|
267
|
+
name: 'get_file_contents',
|
|
268
|
+
arguments: { owner: 'anthropics', repo: 'claude-code', path: 'README.md' },
|
|
269
|
+
timeoutMs: TIMEOUT_TOOL_SLOW,
|
|
270
|
+
allowTimeoutSkip: true,
|
|
271
|
+
timeoutSkipReason: 'Skipped: github get_file_contents timed out in this environment (init/tools list succeeded).'
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
if (serverName === 'arxiv' && has('search_papers')) {
|
|
275
|
+
return { name: 'search_papers', arguments: { query: 'transformer', max_results: 1 } };
|
|
276
|
+
}
|
|
277
|
+
if (serverName === 'firecrawl' && has('firecrawl_search')) {
|
|
278
|
+
return { name: 'firecrawl_search', arguments: { query: 'anthropic claude code', limit: 1 } };
|
|
279
|
+
}
|
|
280
|
+
if (serverName === 'figma' && has('get_figma_data')) {
|
|
281
|
+
return { skip: true, reason: 'Skipped: get_figma_data requires a real Figma fileKey matching server validation regex.' };
|
|
282
|
+
}
|
|
283
|
+
if (serverName === 'chrome-devtools' && has('list_pages')) {
|
|
284
|
+
return { name: 'list_pages', arguments: {} };
|
|
285
|
+
}
|
|
286
|
+
if (serverName === 'sdl-mcp' && has('sdl.repo.status')) {
|
|
287
|
+
return { name: 'sdl.repo.status', arguments: { repoId: 'mcp-tutorial' } };
|
|
288
|
+
}
|
|
289
|
+
if (serverName === 'symdex' && has('list_repos')) {
|
|
290
|
+
return { name: 'list_repos', arguments: {} };
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
_parseResponses() {
|
|
297
|
+
const lines = this.buffer.split('\n');
|
|
298
|
+
this.buffer = lines.pop() || '';
|
|
299
|
+
for (const line of lines) {
|
|
300
|
+
if (!line.trim()) continue;
|
|
301
|
+
try {
|
|
302
|
+
const msg = JSON.parse(line.trim());
|
|
303
|
+
if (msg.id && this.resolvers[msg.id]) {
|
|
304
|
+
this.resolvers[msg.id](msg);
|
|
305
|
+
delete this.resolvers[msg.id];
|
|
306
|
+
}
|
|
307
|
+
} catch (_) {
|
|
308
|
+
// non-JSON line
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
_sendAndWait(id, msg, timeout) {
|
|
314
|
+
return new Promise((resolve) => {
|
|
315
|
+
const timer = setTimeout(() => {
|
|
316
|
+
delete this.resolvers[id];
|
|
317
|
+
resolve(null);
|
|
318
|
+
}, timeout);
|
|
319
|
+
|
|
320
|
+
this.resolvers[id] = (resp) => {
|
|
321
|
+
clearTimeout(timer);
|
|
322
|
+
resolve(resp);
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
this.proc.stdin.write(JSON.stringify(msg) + '\n');
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
_sleep(ms) {
|
|
330
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async function main() {
|
|
335
|
+
console.log('╔══════════════════════════════════════════════════════╗');
|
|
336
|
+
console.log('║ Cross-MCP Server Functionality Test ║');
|
|
337
|
+
console.log('╚══════════════════════════════════════════════════════╝\n');
|
|
338
|
+
|
|
339
|
+
const allResults = [];
|
|
340
|
+
|
|
341
|
+
for (const server of SERVERS) {
|
|
342
|
+
process.stdout.write(`⏳ Testing [${server.name}]...`);
|
|
343
|
+
const tester = new MCPTester(server);
|
|
344
|
+
const result = await tester.test();
|
|
345
|
+
allResults.push(result);
|
|
346
|
+
|
|
347
|
+
if (result.init) {
|
|
348
|
+
console.log(` ✅ Init OK | Tools: ${result.tools.length}`);
|
|
349
|
+
if (result.tools.length > 0) {
|
|
350
|
+
console.log(` 📦 Tools: ${result.tools.join(', ')}`);
|
|
351
|
+
}
|
|
352
|
+
if (result.toolCall) {
|
|
353
|
+
const toolName = result.toolCall.name || server.testTool?.name || 'unknown_tool';
|
|
354
|
+
if (result.toolCall.success) {
|
|
355
|
+
console.log(` 🔧 Tool call [${toolName}]: ✅ SUCCESS`);
|
|
356
|
+
if (result.toolCall.snippet) {
|
|
357
|
+
console.log(` 📄 Response: ${result.toolCall.snippet.substring(0, 120)}...`);
|
|
358
|
+
}
|
|
359
|
+
if (result.toolCall.warning) {
|
|
360
|
+
console.log(` ⚠️ ${result.toolCall.warning}`);
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
console.log(` 🔧 Tool call [${toolName}]: ❌ FAILED`);
|
|
364
|
+
console.log(` 💥 Error: ${result.toolCall.error?.substring(0, 120)}`);
|
|
365
|
+
if (result.toolCall.stderr) {
|
|
366
|
+
console.log(` 🧾 Stderr: ${result.toolCall.stderr.substring(0, 200).replace(/\n/g, ' | ')}`);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
} else {
|
|
371
|
+
console.log(' ❌ Init FAILED');
|
|
372
|
+
if (result.error) console.log(` 💥 ${result.error}`);
|
|
373
|
+
}
|
|
374
|
+
console.log('');
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
console.log('╔══════════════════════════════════════════════════════╗');
|
|
378
|
+
console.log('║ TEST SUMMARY ║');
|
|
379
|
+
console.log('╠══════════════════════════════════════════════════════╣');
|
|
380
|
+
const passed = allResults.filter((r) => r.init).length;
|
|
381
|
+
const toolsPassed = allResults.filter((r) => r.toolCall?.success).length;
|
|
382
|
+
const toolsTested = allResults.filter((r) => r.toolCall).length;
|
|
383
|
+
console.log(`║ Servers initialized: ${passed}/${allResults.length} ║`);
|
|
384
|
+
console.log(`║ Tool calls passed: ${toolsPassed}/${toolsTested} ║`);
|
|
385
|
+
console.log('╚══════════════════════════════════════════════════════╝');
|
|
386
|
+
|
|
387
|
+
process.exit(passed === allResults.length ? 0 : 1);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
main().catch((e) => {
|
|
391
|
+
console.error(e);
|
|
392
|
+
process.exit(1);
|
|
393
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
|
|
2
|
+
import { spawn } from 'child_process';
|
|
3
|
+
|
|
4
|
+
async function checkSchema(packageName, version) {
|
|
5
|
+
return new Promise((resolve) => {
|
|
6
|
+
// Construct npx command
|
|
7
|
+
const cmd = 'npx';
|
|
8
|
+
const args = ['-y', `${packageName}@${version}`];
|
|
9
|
+
|
|
10
|
+
// Start process
|
|
11
|
+
const proc = spawn(cmd, args, { stdio: ['pipe', 'pipe', 'inherit'] });
|
|
12
|
+
|
|
13
|
+
let buffer = '';
|
|
14
|
+
|
|
15
|
+
// Send initialization request
|
|
16
|
+
const initReq = {
|
|
17
|
+
jsonrpc: "2.0",
|
|
18
|
+
id: 1,
|
|
19
|
+
method: "initialize",
|
|
20
|
+
params: {
|
|
21
|
+
protocolVersion: "2024-11-05",
|
|
22
|
+
capabilities: {},
|
|
23
|
+
clientInfo: { name: "test-client", version: "1.0" }
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
proc.stdin.write(JSON.stringify(initReq) + '\n');
|
|
28
|
+
|
|
29
|
+
proc.stdout.on('data', (data) => {
|
|
30
|
+
buffer += data.toString();
|
|
31
|
+
const lines = buffer.split('\n');
|
|
32
|
+
buffer = lines.pop(); // Keep incomplete line
|
|
33
|
+
|
|
34
|
+
for (const line of lines) {
|
|
35
|
+
if (!line.trim()) continue;
|
|
36
|
+
try {
|
|
37
|
+
const msg = JSON.parse(line);
|
|
38
|
+
if (msg.id === 1) {
|
|
39
|
+
// Initialized, now fetch tools
|
|
40
|
+
const toolsReq = {
|
|
41
|
+
jsonrpc: "2.0",
|
|
42
|
+
id: 2,
|
|
43
|
+
method: "tools/list"
|
|
44
|
+
};
|
|
45
|
+
proc.stdin.write(JSON.stringify(toolsReq) + '\n');
|
|
46
|
+
// Notification initialized
|
|
47
|
+
proc.stdin.write(JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" }) + '\n');
|
|
48
|
+
} else if (msg.id === 2) {
|
|
49
|
+
// Got tools
|
|
50
|
+
resolve(msg.result);
|
|
51
|
+
proc.kill();
|
|
52
|
+
}
|
|
53
|
+
} catch (e) {
|
|
54
|
+
// ignore parsing errors for partial lines
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
setTimeout(() => {
|
|
60
|
+
proc.kill();
|
|
61
|
+
resolve(null);
|
|
62
|
+
}, 10000); // 10s timeout
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function run() {
|
|
67
|
+
console.log("Checking MCP schemas...");
|
|
68
|
+
|
|
69
|
+
// Check camoufox 1.4.0
|
|
70
|
+
console.log("Checking camoufox 1.4.0...");
|
|
71
|
+
const c14 = await checkSchema("camoufox-mcp-server", "1.4.0");
|
|
72
|
+
if (c14) {
|
|
73
|
+
// Look for browse tool schema
|
|
74
|
+
const browse = c14.tools.find(t => t.name === "mcp__camoufox__browse" || t.name === "browse" || t.name === "camoufox_browse");
|
|
75
|
+
if (browse) {
|
|
76
|
+
console.log("Camoufox 1.4.0 'browse' schema:", JSON.stringify(browse.inputSchema, null, 2));
|
|
77
|
+
} else {
|
|
78
|
+
console.log("Camoufox 1.4.0 'browse' tool not found. Tools: " + c14.tools.map(t => t.name));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
run();
|