@letoribo/mcp-graphql-enhanced 3.8.0 → 3.8.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.
|
@@ -8,8 +8,9 @@ export declare function introspectEndpoint(endpoint: string, headers?: Record<st
|
|
|
8
8
|
*/
|
|
9
9
|
export declare function introspectLocalSchema(path: string): Promise<string>;
|
|
10
10
|
/**
|
|
11
|
-
* Extract and filter specific types from a schema
|
|
12
|
-
*
|
|
11
|
+
* Extract and filter specific types or root fields from a GraphQL schema.
|
|
12
|
+
* This prevents "No result received" errors by only sending the requested
|
|
13
|
+
* parts of the graph to the agent, maintaining a stable context window.
|
|
13
14
|
*/
|
|
14
15
|
export declare function introspectSpecificTypes(schema: GraphQLSchema, typeNames: string[]): Record<string, any>;
|
|
15
16
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"introspection.d.ts","sourceRoot":"","sources":["../../src/helpers/introspection.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,aAAa,EAQd,MAAM,SAAS,CAAC;AAGjB;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,mBAiBjC;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,mBAEvD;AAED
|
|
1
|
+
{"version":3,"file":"introspection.d.ts","sourceRoot":"","sources":["../../src/helpers/introspection.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,aAAa,EAQd,MAAM,SAAS,CAAC;AAGjB;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,mBAiBjC;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,mBAEvD;AAED;;;;GAIG;AAEH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,EAAE,uBAgGjF;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,YAAK,EACpC,SAAS,EAAE,MAAM,EAAE,mBAYpB"}
|
|
@@ -31,12 +31,39 @@ async function introspectLocalSchema(path) {
|
|
|
31
31
|
return await (0, promises_1.readFile)(path, "utf8");
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
|
-
* Extract and filter specific types from a schema
|
|
35
|
-
*
|
|
34
|
+
* Extract and filter specific types or root fields from a GraphQL schema.
|
|
35
|
+
* This prevents "No result received" errors by only sending the requested
|
|
36
|
+
* parts of the graph to the agent, maintaining a stable context window.
|
|
36
37
|
*/
|
|
37
38
|
function introspectSpecificTypes(schema, typeNames) {
|
|
38
39
|
const result = {};
|
|
40
|
+
// Cache root field maps to avoid repeated lookups during the loop
|
|
41
|
+
const queryType = schema.getQueryType();
|
|
42
|
+
const queryFields = queryType ? queryType.getFields() : {};
|
|
43
|
+
const mutationType = schema.getMutationType();
|
|
44
|
+
const mutationFields = mutationType ? mutationType.getFields() : {};
|
|
39
45
|
for (const name of typeNames) {
|
|
46
|
+
// --- ROOT FIELD RESOLUTION ---
|
|
47
|
+
// Check if the name refers to a root field (Query or Mutation)
|
|
48
|
+
// rather than a named Type.
|
|
49
|
+
const rootField = queryFields[name] || mutationFields[name];
|
|
50
|
+
if (rootField) {
|
|
51
|
+
result[name] = {
|
|
52
|
+
kind: queryFields[name] ? "QUERY_FIELD" : "MUTATION_FIELD",
|
|
53
|
+
description: rootField.description,
|
|
54
|
+
type: rootField.type.toString(),
|
|
55
|
+
args: rootField.args
|
|
56
|
+
.filter(arg => !arg.deprecationReason)
|
|
57
|
+
.map(arg => ({
|
|
58
|
+
name: arg.name,
|
|
59
|
+
type: arg.type.toString(),
|
|
60
|
+
description: arg.description,
|
|
61
|
+
}))
|
|
62
|
+
};
|
|
63
|
+
continue; // Field found, move to next requested name
|
|
64
|
+
}
|
|
65
|
+
// --- NAMED TYPE RESOLUTION ---
|
|
66
|
+
// Fallback to standard type introspection if no root field matches
|
|
40
67
|
const type = schema.getType(name);
|
|
41
68
|
if (!type)
|
|
42
69
|
continue;
|
package/dist/index.js
CHANGED
|
@@ -268,76 +268,89 @@ toolHandlers.set("query-graphql", queryGraphqlHandler);
|
|
|
268
268
|
variables: zod_1.default.string().optional(),
|
|
269
269
|
headers: zod_1.default.string().optional(),
|
|
270
270
|
}, queryGraphqlHandler);
|
|
271
|
-
|
|
272
|
-
|
|
271
|
+
/**
|
|
272
|
+
* Tool: introspect-schema
|
|
273
|
+
* Main handler for the introspection tool.
|
|
274
|
+
* Implements "Agent Recovery" logic to guide the LLM when entities are missing.
|
|
275
|
+
*/
|
|
273
276
|
const introspectHandler = async ({ typeNames }) => {
|
|
274
|
-
// 1. Fetch the schema directly
|
|
277
|
+
// 1. Fetch the schema directly from the source
|
|
275
278
|
const result = await getSchema(true);
|
|
276
|
-
// Explicitly check if the result is
|
|
279
|
+
// Explicitly check if the result is a valid GraphQLSchema object
|
|
277
280
|
if (!result || typeof result === 'string') {
|
|
278
281
|
return {
|
|
279
282
|
content: [{
|
|
280
283
|
type: "text",
|
|
281
284
|
text: `❌ SCHEMA_ERROR: ${typeof result === 'string' ? result : 'GraphQL schema is not initialized yet.'}\n` +
|
|
282
|
-
`ACTION: Please wait 5-10 seconds for the
|
|
285
|
+
`ACTION: Please wait 5-10 seconds for the backend endpoint to respond.`
|
|
283
286
|
}]
|
|
284
287
|
};
|
|
285
288
|
}
|
|
286
|
-
//
|
|
289
|
+
// --- 1. INITIALIZE MAPPINGS ---
|
|
287
290
|
const schema = result;
|
|
288
291
|
const typeMap = schema.getTypeMap();
|
|
292
|
+
// Cache Root Type fields for rapid gap analysis
|
|
293
|
+
const queryType = schema.getQueryType();
|
|
294
|
+
const queryFields = queryType ? queryType.getFields() : {};
|
|
289
295
|
const mutationType = schema.getMutationType();
|
|
290
296
|
const mutationFields = mutationType ? mutationType.getFields() : {};
|
|
291
|
-
// --- GAP ANALYSIS ---
|
|
297
|
+
// --- 2. GAP ANALYSIS & SELF-HEALING LOOP ---
|
|
298
|
+
// If specific types were requested, verify their existence in the current schema
|
|
292
299
|
if (typeNames && typeNames.length > 0) {
|
|
293
300
|
const missing = typeNames.filter(name => {
|
|
294
301
|
const existsAsType = !!typeMap[name];
|
|
295
302
|
const existsAsMutation = !!mutationFields[name];
|
|
296
|
-
|
|
303
|
+
const existsAsQueryField = !!queryFields[name];
|
|
304
|
+
return !existsAsType && !existsAsMutation && !existsAsQueryField;
|
|
297
305
|
});
|
|
306
|
+
// If some requested entities are missing, provide the agent with a recovery map
|
|
298
307
|
if (missing.length > 0) {
|
|
299
|
-
|
|
300
|
-
const
|
|
308
|
+
// Filter out internal GraphQL types to reduce noise for the agent
|
|
309
|
+
const internalTypes = ['Query', 'Mutation', 'Subscription'];
|
|
310
|
+
const availableEntities = Object.keys(typeMap).filter(t => !t.startsWith('__') && !internalTypes.includes(t));
|
|
311
|
+
// Generate a pseudo-version ID based on schema state and time
|
|
312
|
+
const schemaVersion = `v${availableEntities.length}.${Math.floor(Date.now() / 10000) % 1000}`;
|
|
301
313
|
return {
|
|
302
314
|
content: [{
|
|
303
315
|
type: "text",
|
|
304
|
-
text: `❌ PARTIAL RESULTS [ID: ${schemaVersion}]\n\n` +
|
|
316
|
+
text: `❌ PARTIAL RESULTS [Schema ID: ${schemaVersion}]\n\n` +
|
|
305
317
|
`MISSING ENTITIES: ${missing.join(", ")}\n` +
|
|
306
|
-
`REASON: These specific types or
|
|
307
|
-
`ACTION:
|
|
308
|
-
`AVAILABLE_ENTITIES: ${
|
|
318
|
+
`REASON: These specific types or fields were not found in the current schema.\n` +
|
|
319
|
+
`ACTION: Re-examine the available entities below and correct your query intent.\n\n` +
|
|
320
|
+
`AVAILABLE_ENTITIES: ${availableEntities.join(", ")}`
|
|
309
321
|
}]
|
|
310
322
|
};
|
|
311
323
|
}
|
|
312
324
|
}
|
|
313
|
-
//
|
|
325
|
+
// --- 3. GENERAL MANIFEST GENERATION ---
|
|
326
|
+
// If no typeNames provided, return a high-level overview of the entry points
|
|
314
327
|
if (!typeNames || typeNames.length === 0) {
|
|
315
|
-
const queryType = schema.getQueryType();
|
|
316
328
|
const discoveredEntities = new Set();
|
|
317
329
|
if (queryType) {
|
|
318
|
-
//
|
|
319
|
-
const
|
|
320
|
-
Object.values(
|
|
330
|
+
// Map Query fields to their underlying Object Types
|
|
331
|
+
const fields = queryType.getFields();
|
|
332
|
+
Object.values(fields).forEach((field) => {
|
|
321
333
|
const namedType = (0, graphql_1.getNamedType)(field.type);
|
|
322
334
|
if ((0, graphql_1.isObjectType)(namedType) && !namedType.name.startsWith('__')) {
|
|
323
335
|
discoveredEntities.add(namedType.name);
|
|
324
336
|
}
|
|
325
337
|
});
|
|
326
338
|
}
|
|
327
|
-
const
|
|
328
|
-
const
|
|
329
|
-
const schemaVersion = `v${
|
|
339
|
+
const entryPoints = Array.from(discoveredEntities).sort();
|
|
340
|
+
const allTypes = Object.keys(typeMap).filter(t => !t.startsWith('__'));
|
|
341
|
+
const schemaVersion = `v${allTypes.length}.${Math.floor(Date.now() / 10000) % 1000}`;
|
|
330
342
|
return {
|
|
331
343
|
content: [{
|
|
332
344
|
type: "text",
|
|
333
345
|
text: `GraphQL Schema Manifest [ID: ${schemaVersion}]\n\n` +
|
|
334
|
-
`ENTRY_POINT_ENTITIES: ${
|
|
335
|
-
`TOTAL_SCHEMA_TYPES: ${
|
|
336
|
-
`ALL_AVAILABLE_TYPES: ${
|
|
346
|
+
`ENTRY_POINT_ENTITIES: ${entryPoints.join(", ") || "None"}\n` +
|
|
347
|
+
`TOTAL_SCHEMA_TYPES: ${allTypes.length}\n\n` +
|
|
348
|
+
`ALL_AVAILABLE_TYPES: ${allTypes.join(", ")}`
|
|
337
349
|
}]
|
|
338
350
|
};
|
|
339
351
|
}
|
|
340
|
-
//
|
|
352
|
+
// --- 4. DETAILED INTROSPECTION ---
|
|
353
|
+
// Return filtered schema metadata for the requested types or root fields
|
|
341
354
|
const filtered = (0, introspection_js_1.introspectSpecificTypes)(schema, typeNames);
|
|
342
355
|
return {
|
|
343
356
|
content: [{ type: "text", text: JSON.stringify(filtered, null, 2) }]
|
package/package.json
CHANGED