@letoribo/mcp-graphql-enhanced 3.5.1 → 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.
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Universal prompt wrapper for MCP server
3
+ */
4
+ export declare function registerPrompt(server: any, name: string, description: string, template: string): void;
5
+ //# sourceMappingURL=prompt-registry.d.ts.map
@@ -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.2.1";
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;
@@ -198,8 +205,10 @@ async function performUpdate(force) {
198
205
  updatePromise = null;
199
206
  }
200
207
  }
201
- // --- TOOL HANDLERS ---
208
+ // --- TOOL REGISTRY ---
202
209
  const toolHandlers = new Map();
210
+ // This will store schemas for our dynamic HTTP 'list-tools' response
211
+ const registeredToolsMetadata = [];
203
212
  /** * executionLogs stores the last 5 GraphQL operations.
204
213
  * This allows the AI to "inspect" its own generated queries and the raw data
205
214
  * for debugging or bridging to 3D visualization tools.
@@ -255,7 +264,7 @@ const queryGraphqlHandler = async ({ query, variables, headers }) => {
255
264
  }
256
265
  };
257
266
  toolHandlers.set("query-graphql", queryGraphqlHandler);
258
- server.tool("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", {
259
268
  query: zod_1.default.string(),
260
269
  variables: zod_1.default.string().optional(),
261
270
  headers: zod_1.default.string().optional(),
@@ -322,91 +331,134 @@ const introspectHandler = async ({ typeNames }) => {
322
331
  };
323
332
  };
324
333
  toolHandlers.set("introspect-schema", introspectHandler);
325
- server.tool("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", {
326
335
  typeNames: zod_1.default.array(zod_1.default.string()).optional(),
327
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.");
328
344
  // --- HTTP SERVER LOGIC ---
329
345
  async function handleHttpRequest(req, res) {
346
+ // Standard CORS headers for cross-origin compatibility
330
347
  res.setHeader('Access-Control-Allow-Origin', '*');
331
348
  res.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
332
349
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
350
+ // Handle preflight requests
333
351
  if (req.method === 'OPTIONS') {
334
352
  res.writeHead(204);
335
353
  res.end();
336
354
  return;
337
355
  }
338
356
  const url = new URL(req.url || '', `http://${req.headers.host}`);
339
- // NEW FEATURE: Web GUI for Humans
357
+ // Serve Web GUI (GraphiQL) - ONLY if explicitly enabled
340
358
  if (req.method === 'GET' && (url.pathname === '/' || url.pathname === '/graphiql')) {
341
- res.writeHead(200, { 'Content-Type': 'text/html' });
342
- // Pass env.HEADERS to the explorer
343
- return res.end((0, graphiql_js_1.renderGraphiQL)(env.ENDPOINT, env.HEADERS));
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
+ }
344
369
  }
370
+ // Process MCP JSON-RPC Endpoint
345
371
  if (url.pathname === '/mcp' && req.method === 'POST') {
346
372
  let body = '';
347
373
  req.on('data', chunk => { body += chunk; });
348
374
  req.on('end', async () => {
375
+ let requestId = null; // Defined early to be accessible in catch block
349
376
  try {
350
- const { method, params, id } = JSON.parse(body);
377
+ const request = JSON.parse(body);
378
+ const { method, id, params } = request;
379
+ requestId = id;
351
380
  console.error(`[HTTP-RPC] Method: ${method} | ID: ${id}`);
352
- const handler = toolHandlers.get(method);
381
+ // --- DYNAMIC DISCOVERY ---
382
+ if (method === "list-tools" || method === "tools/list") {
383
+ res.writeHead(200, { 'Content-Type': 'application/json' });
384
+ return res.end(JSON.stringify({
385
+ jsonrpc: '2.0',
386
+ id: requestId,
387
+ result: { tools: registeredToolsMetadata }
388
+ }));
389
+ }
390
+ // --- TOOL EXECUTION (CALL) ---
391
+ let targetMethod = method;
392
+ let toolArgs = params;
393
+ // Support both direct calls and standard MCP "call-tool" structure
394
+ if (method === "call-tool" || method === "tools/call") {
395
+ targetMethod = params.name;
396
+ toolArgs = params.arguments;
397
+ }
398
+ const handler = toolHandlers.get(targetMethod);
353
399
  if (!handler) {
354
400
  res.writeHead(404);
355
- return res.end(JSON.stringify({ jsonrpc: '2.0', id, error: { code: -32601, message: "Method not found" } }));
401
+ return res.end(JSON.stringify({
402
+ jsonrpc: '2.0',
403
+ id: requestId,
404
+ error: { code: -32601, message: `Tool ${targetMethod} not found` }
405
+ }));
356
406
  }
357
- const result = await handler(params);
407
+ // Execute the actual business logic for the tool
408
+ const result = await handler(toolArgs);
358
409
  res.writeHead(200, { 'Content-Type': 'application/json' });
359
- res.end(JSON.stringify({ jsonrpc: '2.0', id, result }));
410
+ res.end(JSON.stringify({
411
+ jsonrpc: '2.0',
412
+ id: requestId,
413
+ result
414
+ }));
360
415
  }
361
416
  catch (e) {
362
417
  console.error(`[HTTP-ERROR] ${e.message}`);
363
418
  res.writeHead(500);
364
- res.end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32603, message: "Internal Error" } }));
419
+ res.end(JSON.stringify({
420
+ jsonrpc: '2.0',
421
+ id: requestId,
422
+ error: { code: -32603, message: e.message || "Internal Server Error" }
423
+ }));
365
424
  }
366
425
  });
367
426
  return;
368
427
  }
428
+ // Health check endpoint for monitoring
369
429
  if (url.pathname === '/health') {
370
430
  res.writeHead(200, { 'Content-Type': 'application/json' });
371
431
  return res.end(JSON.stringify({ status: "ok", server: env.NAME }));
372
432
  }
433
+ // Default 404 for unknown paths
373
434
  res.writeHead(404);
374
435
  res.end("Not Found");
375
436
  }
376
437
  // --- STARTUP ---
377
438
  async function main() {
378
- const rawEnableHttp = process.env.ENABLE_HTTP;
379
- // Determine if we should open the HTTP port.
380
- // 1. Check if we're not running inside the MCP Inspector.
381
- // 2. Ensure the user hasn't explicitly disabled HTTP (ENABLE_HTTP="false").
382
439
  const isInspector = !!(process.env.MCP_INSPECTOR || process.env.INSPECTOR_PORT);
383
- const shouldOpenPort = rawEnableHttp !== "false" && !isInspector;
384
- if (shouldOpenPort) {
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) {
385
443
  const serverHttp = node_http_1.default.createServer(handleHttpRequest);
386
- // Error handling to prevent crashes if the port is already in use
387
444
  serverHttp.on('error', (e) => {
388
- if (e.code !== 'EADDRINUSE')
389
- console.error(`[HTTP-ERROR] ${e.message}`);
445
+ if (e.code === 'EADDRINUSE') {
446
+ console.error(`[HTTP-ERROR] Port ${env.MCP_PORT} is busy.`);
447
+ }
390
448
  });
391
449
  serverHttp.listen(env.MCP_PORT, () => {
392
- // Log URLs only if HTTP is explicitly enabled
393
- if (env.ENABLE_HTTP === true) {
394
- console.error(`[HTTP] Server started on http://localhost:${env.MCP_PORT}`);
395
- console.error(`🎨 GraphiQL IDE: http://localhost:${env.MCP_PORT}/graphiql`);
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`);
396
456
  }
397
- console.error(`🤖 MCP Endpoint: http://localhost:${env.MCP_PORT}/mcp`);
398
457
  });
399
458
  }
400
459
  const transport = new stdio_js_1.StdioServerTransport();
401
460
  await server.connect(transport);
402
- // Maintain quiet mode when running inside the Inspector to avoid protocol interference
403
- if (!isInspector) {
404
- console.error(`[STDIO] MCP Server "${env.NAME}" v${getVersion()} started`);
405
- }
406
- getSchema().catch(e => {
407
- if (!isInspector)
408
- console.error(`[SCHEMA] Warning: ${e.message}`);
409
- });
461
+ getSchema().catch(() => { });
410
462
  }
411
463
  process.on('SIGINT', () => process.exit(0));
412
464
  process.on('SIGTERM', () => process.exit(0));
package/package.json CHANGED
@@ -35,7 +35,7 @@
35
35
  "format": "prettier --write ."
36
36
  },
37
37
  "dependencies": {
38
- "@modelcontextprotocol/sdk": "^1.20.1",
38
+ "@modelcontextprotocol/sdk": "^1.27.1",
39
39
  "graphql": "^16.11.0",
40
40
  "yargs": "17.7.2",
41
41
  "zod": "3.25.30",
@@ -50,5 +50,5 @@
50
50
  "tsx": "^4.21.0",
51
51
  "typescript": "5.8.3"
52
52
  },
53
- "version": "3.5.1"
53
+ "version": "3.7.0"
54
54
  }