@letoribo/mcp-graphql-enhanced 3.6.0 → 3.7.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/dist/helpers/prompt-registry.d.ts +5 -0
- package/dist/helpers/prompt-registry.d.ts.map +1 -0
- package/dist/helpers/prompt-registry.js +22 -0
- package/dist/helpers/tool-registry.d.ts +6 -0
- package/dist/helpers/tool-registry.d.ts.map +1 -0
- package/dist/helpers/tool-registry.js +42 -0
- package/dist/index.js +41 -60
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-registry.d.ts","sourceRoot":"","sources":["../../src/helpers/prompt-registry.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,cAAc,CAC1B,MAAM,EAAE,GAAG,EACX,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,QAenB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/helpers/prompt-registry.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.registerPrompt = registerPrompt;
|
|
5
|
+
/**
|
|
6
|
+
* Universal prompt wrapper for MCP server
|
|
7
|
+
*/
|
|
8
|
+
function registerPrompt(server, name, description, template) {
|
|
9
|
+
server.prompt(name, description, (args) => {
|
|
10
|
+
return {
|
|
11
|
+
messages: [
|
|
12
|
+
{
|
|
13
|
+
role: "user",
|
|
14
|
+
content: {
|
|
15
|
+
type: "text",
|
|
16
|
+
text: template + (args?.focus ? ` Focus on: ${args.focus}` : "")
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Universal tool wrapper that registers tools for both MCP and HTTP Discovery.
|
|
3
|
+
* Handles Zod schema parsing to generate metadata for external clients.
|
|
4
|
+
*/
|
|
5
|
+
export declare function registerTool(server: any, toolHandlers: Map<string, (args: any) => Promise<any>>, registeredToolsMetadata: any[], name: string, description: string, schema: any, handler: (args: any) => Promise<any>): void;
|
|
6
|
+
//# sourceMappingURL=tool-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-registry.d.ts","sourceRoot":"","sources":["../../src/helpers/tool-registry.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,YAAY,CACxB,MAAM,EAAE,GAAG,EACX,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC,EACtD,uBAAuB,EAAE,GAAG,EAAE,EAC9B,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,GAAG,EACX,OAAO,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,QAwCvC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/helpers/tool-registry.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.registerTool = registerTool;
|
|
5
|
+
/**
|
|
6
|
+
* Universal tool wrapper that registers tools for both MCP and HTTP Discovery.
|
|
7
|
+
* Handles Zod schema parsing to generate metadata for external clients.
|
|
8
|
+
*/
|
|
9
|
+
function registerTool(server, toolHandlers, registeredToolsMetadata, name, description, schema, handler) {
|
|
10
|
+
// 1. Official MCP Server registration
|
|
11
|
+
server.tool(name, description, schema, handler);
|
|
12
|
+
// 2. Map handler for internal HTTP routing
|
|
13
|
+
toolHandlers.set(name, handler);
|
|
14
|
+
// 3. Smart metadata generation for Discovery (HTTP list-tools)
|
|
15
|
+
registeredToolsMetadata.push({
|
|
16
|
+
name,
|
|
17
|
+
description,
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: Object.fromEntries(Object.entries(schema).map(([key, value]) => {
|
|
21
|
+
// Recursive helper to extract type info from Zod objects
|
|
22
|
+
const getZodType = (v) => {
|
|
23
|
+
const type = v?._def?.typeName?.replace('Zod', '').toLowerCase() || "string";
|
|
24
|
+
if (type === 'array') {
|
|
25
|
+
return {
|
|
26
|
+
type: "array",
|
|
27
|
+
items: { type: getZodType(v._def.type) }
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
return { type };
|
|
31
|
+
};
|
|
32
|
+
const typeInfo = getZodType(value);
|
|
33
|
+
return [key, typeInfo];
|
|
34
|
+
})),
|
|
35
|
+
// Identify required fields by checking for ZodOptional or '?' in key
|
|
36
|
+
required: Object.keys(schema).filter(key => {
|
|
37
|
+
const val = schema[key];
|
|
38
|
+
return val?._def?.typeName !== 'ZodOptional' && !key.includes('?');
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -14,13 +14,15 @@ const graphql_1 = require("graphql");
|
|
|
14
14
|
// Helper imports
|
|
15
15
|
const deprecation_js_1 = require("./helpers/deprecation.js");
|
|
16
16
|
const introspection_js_1 = require("./helpers/introspection.js");
|
|
17
|
+
const tool_registry_js_1 = require("./helpers/tool-registry.js");
|
|
18
|
+
const prompt_registry_js_1 = require("./helpers/prompt-registry.js");
|
|
17
19
|
const getVersion = () => {
|
|
18
20
|
try {
|
|
19
21
|
const pkg = require("../package.json");
|
|
20
22
|
return pkg.version;
|
|
21
23
|
}
|
|
22
24
|
catch {
|
|
23
|
-
return "3.
|
|
25
|
+
return "3.6.0";
|
|
24
26
|
}
|
|
25
27
|
};
|
|
26
28
|
(0, deprecation_js_1.checkDeprecatedArguments)();
|
|
@@ -59,6 +61,11 @@ const server = new mcp_js_1.McpServer({
|
|
|
59
61
|
name: env.NAME,
|
|
60
62
|
version: getVersion(),
|
|
61
63
|
description: "Start of the #mcp-graphql-enhanced channel on GraphQL server. Join here: https://discord.com/channels/622115132221685760/1348633379555184640"
|
|
64
|
+
}, {
|
|
65
|
+
capabilities: {
|
|
66
|
+
prompts: {},
|
|
67
|
+
tools: {}
|
|
68
|
+
}
|
|
62
69
|
});
|
|
63
70
|
// --- CACHE STATE ---
|
|
64
71
|
let cachedSDL = null;
|
|
@@ -202,37 +209,6 @@ async function performUpdate(force) {
|
|
|
202
209
|
const toolHandlers = new Map();
|
|
203
210
|
// This will store schemas for our dynamic HTTP 'list-tools' response
|
|
204
211
|
const registeredToolsMetadata = [];
|
|
205
|
-
/**
|
|
206
|
-
* Helper to register tools in both MCP Server and our local registry
|
|
207
|
-
* Handles both plain objects and Zod schemas
|
|
208
|
-
*/
|
|
209
|
-
function registerTool(name, description, schema, handler) {
|
|
210
|
-
// 1. Register in official MCP Server
|
|
211
|
-
server.tool(name, description, schema, handler);
|
|
212
|
-
// 2. Save handler for our HTTP routing
|
|
213
|
-
toolHandlers.set(name, handler);
|
|
214
|
-
// 3. Store metadata for dynamic Discovery
|
|
215
|
-
// We treat 'schema' as a plain object for the JSON output
|
|
216
|
-
registeredToolsMetadata.push({
|
|
217
|
-
name,
|
|
218
|
-
description,
|
|
219
|
-
inputSchema: {
|
|
220
|
-
type: "object",
|
|
221
|
-
properties: Object.fromEntries(Object.entries(schema).map(([key, value]) => {
|
|
222
|
-
// Detect type from Zod or default to string
|
|
223
|
-
const typeName = value?._def?.typeName
|
|
224
|
-
? value._def.typeName.replace('Zod', '').toLowerCase()
|
|
225
|
-
: "string";
|
|
226
|
-
return [key, { type: typeName }];
|
|
227
|
-
})),
|
|
228
|
-
// Assume all fields in the object are required unless specified
|
|
229
|
-
required: Object.keys(schema).filter(key => {
|
|
230
|
-
const val = schema[key];
|
|
231
|
-
return val?._def?.typeName !== 'ZodOptional' && !key.includes('?');
|
|
232
|
-
})
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
212
|
/** * executionLogs stores the last 5 GraphQL operations.
|
|
237
213
|
* This allows the AI to "inspect" its own generated queries and the raw data
|
|
238
214
|
* for debugging or bridging to 3D visualization tools.
|
|
@@ -288,7 +264,7 @@ const queryGraphqlHandler = async ({ query, variables, headers }) => {
|
|
|
288
264
|
}
|
|
289
265
|
};
|
|
290
266
|
toolHandlers.set("query-graphql", queryGraphqlHandler);
|
|
291
|
-
registerTool("query-graphql", "Execute a GraphQL query against the endpoint", {
|
|
267
|
+
(0, tool_registry_js_1.registerTool)(server, toolHandlers, registeredToolsMetadata, "query-graphql", "Execute a GraphQL query against the endpoint", {
|
|
292
268
|
query: zod_1.default.string(),
|
|
293
269
|
variables: zod_1.default.string().optional(),
|
|
294
270
|
headers: zod_1.default.string().optional(),
|
|
@@ -355,9 +331,16 @@ const introspectHandler = async ({ typeNames }) => {
|
|
|
355
331
|
};
|
|
356
332
|
};
|
|
357
333
|
toolHandlers.set("introspect-schema", introspectHandler);
|
|
358
|
-
registerTool("introspect-schema", "Introspect the GraphQL schema with optional type filtering", {
|
|
334
|
+
(0, tool_registry_js_1.registerTool)(server, toolHandlers, registeredToolsMetadata, "introspect-schema", "Introspect the GraphQL schema with optional type filtering", {
|
|
359
335
|
typeNames: zod_1.default.array(zod_1.default.string()).optional(),
|
|
360
336
|
}, introspectHandler);
|
|
337
|
+
// --- PROMPTS (The "Add from ..." buttons in Claude UI) ---
|
|
338
|
+
// 1. Connection check
|
|
339
|
+
(0, prompt_registry_js_1.registerPrompt)(server, "health-check", "Check if the GraphQL endpoint is alive", "Run 'query-graphql' with query '{ __typename }' to verify connection.");
|
|
340
|
+
// 2. High-level overview
|
|
341
|
+
(0, prompt_registry_js_1.registerPrompt)(server, "schema-overview", "List all available types", "Run 'introspect-schema' to see all types and entry points.");
|
|
342
|
+
// 3. Data types analysis
|
|
343
|
+
(0, prompt_registry_js_1.registerPrompt)(server, "list-scalars", "List all scalar types", "Run 'introspect-schema' and identify all scalars in the schema.");
|
|
361
344
|
// --- HTTP SERVER LOGIC ---
|
|
362
345
|
async function handleHttpRequest(req, res) {
|
|
363
346
|
// Standard CORS headers for cross-origin compatibility
|
|
@@ -371,10 +354,18 @@ async function handleHttpRequest(req, res) {
|
|
|
371
354
|
return;
|
|
372
355
|
}
|
|
373
356
|
const url = new URL(req.url || '', `http://${req.headers.host}`);
|
|
374
|
-
// Serve Web GUI (GraphiQL)
|
|
357
|
+
// Serve Web GUI (GraphiQL) - ONLY if explicitly enabled
|
|
375
358
|
if (req.method === 'GET' && (url.pathname === '/' || url.pathname === '/graphiql')) {
|
|
376
|
-
|
|
377
|
-
|
|
359
|
+
// Check our explicit flag
|
|
360
|
+
if (process.env.ENABLE_HTTP === 'true') {
|
|
361
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
362
|
+
return res.end((0, graphiql_js_1.renderGraphiQL)(env.ENDPOINT, env.HEADERS));
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
// Forbidden if not explicitly enabled
|
|
366
|
+
res.writeHead(403, { 'Content-Type': 'text/plain' });
|
|
367
|
+
return res.end("Forbidden: GraphiQL UI is disabled. Start with ENABLE_HTTP=true to use it.");
|
|
368
|
+
}
|
|
378
369
|
}
|
|
379
370
|
// Process MCP JSON-RPC Endpoint
|
|
380
371
|
if (url.pathname === '/mcp' && req.method === 'POST') {
|
|
@@ -389,7 +380,6 @@ async function handleHttpRequest(req, res) {
|
|
|
389
380
|
console.error(`[HTTP-RPC] Method: ${method} | ID: ${id}`);
|
|
390
381
|
// --- DYNAMIC DISCOVERY ---
|
|
391
382
|
if (method === "list-tools" || method === "tools/list") {
|
|
392
|
-
// Просто отдаем то, что накопили в нашем реестре при регистрации
|
|
393
383
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
394
384
|
return res.end(JSON.stringify({
|
|
395
385
|
jsonrpc: '2.0',
|
|
@@ -446,38 +436,29 @@ async function handleHttpRequest(req, res) {
|
|
|
446
436
|
}
|
|
447
437
|
// --- STARTUP ---
|
|
448
438
|
async function main() {
|
|
449
|
-
const rawEnableHttp = process.env.ENABLE_HTTP;
|
|
450
|
-
// Determine if we should open the HTTP port.
|
|
451
|
-
// 1. Check if we're not running inside the MCP Inspector.
|
|
452
|
-
// 2. Ensure the user hasn't explicitly disabled HTTP (ENABLE_HTTP="false").
|
|
453
439
|
const isInspector = !!(process.env.MCP_INSPECTOR || process.env.INSPECTOR_PORT);
|
|
454
|
-
const
|
|
455
|
-
|
|
440
|
+
const isHttpExplicitlyEnabled = process.env.ENABLE_HTTP === "true";
|
|
441
|
+
// Open HTTP port by default for MCP SSE, unless explicitly disabled
|
|
442
|
+
if (process.env.ENABLE_HTTP !== "false" && !isInspector) {
|
|
456
443
|
const serverHttp = node_http_1.default.createServer(handleHttpRequest);
|
|
457
|
-
// Error handling to prevent crashes if the port is already in use
|
|
458
444
|
serverHttp.on('error', (e) => {
|
|
459
|
-
if (e.code
|
|
460
|
-
console.error(`[HTTP-ERROR] ${
|
|
445
|
+
if (e.code === 'EADDRINUSE') {
|
|
446
|
+
console.error(`[HTTP-ERROR] Port ${env.MCP_PORT} is busy.`);
|
|
447
|
+
}
|
|
461
448
|
});
|
|
462
449
|
serverHttp.listen(env.MCP_PORT, () => {
|
|
463
|
-
//
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
450
|
+
// All-in-one status report
|
|
451
|
+
console.error(`[SYSTEM] Server "${env.NAME}" v${getVersion()} active`);
|
|
452
|
+
console.error(`🤖 MCP SSE: http://localhost:${env.MCP_PORT}/mcp`);
|
|
453
|
+
// Show UI only if explicitly requested
|
|
454
|
+
if (isHttpExplicitlyEnabled) {
|
|
455
|
+
console.error(`🎨 GraphiQL UI: http://localhost:${env.MCP_PORT}/graphiql`);
|
|
467
456
|
}
|
|
468
|
-
console.error(`🤖 MCP Endpoint: http://localhost:${env.MCP_PORT}/mcp`);
|
|
469
457
|
});
|
|
470
458
|
}
|
|
471
459
|
const transport = new stdio_js_1.StdioServerTransport();
|
|
472
460
|
await server.connect(transport);
|
|
473
|
-
|
|
474
|
-
if (!isInspector) {
|
|
475
|
-
console.error(`[STDIO] MCP Server "${env.NAME}" v${getVersion()} started`);
|
|
476
|
-
}
|
|
477
|
-
getSchema().catch(e => {
|
|
478
|
-
if (!isInspector)
|
|
479
|
-
console.error(`[SCHEMA] Warning: ${e.message}`);
|
|
480
|
-
});
|
|
461
|
+
getSchema().catch(() => { });
|
|
481
462
|
}
|
|
482
463
|
process.on('SIGINT', () => process.exit(0));
|
|
483
464
|
process.on('SIGTERM', () => process.exit(0));
|
package/package.json
CHANGED