@letoribo/mcp-graphql-enhanced 2.0.5 → 2.0.7

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/README.md CHANGED
@@ -1,8 +1,7 @@
1
1
  # mcp-graphql-enhanced
2
2
 
3
- [![Smithery](https://smithery.ai/badge/mcp-graphql-enhanced)](https://smithery.ai/server/mcp-graphql-enhanced)
4
- [![Glama](https://glama.ai/mcp/servers/@letoribo/mcp-graphql-enhanced)](https://glama.ai/mcp/servers/@letoribo/mcp-graphql-enhanced)
5
-
3
+ [![Smithery](https://smithery.ai/badge/@letoribo/mcp-graphql-enhanced)](https://smithery.ai/server/@letoribo/mcp-graphql-enhanced)
4
+ [![Glama](https://glama.ai/mcp/servers/@letoribo/mcp-graphql-enhanced/badge)](https://glama.ai/mcp/servers/@letoribo/mcp-graphql-enhanced)
6
5
  An **enhanced MCP (Model Context Protocol) server for GraphQL** that fixes real-world interoperability issues between LLMs and GraphQL APIs.
7
6
 
8
7
  > Drop-in replacement for `mcp-graphql` — with dynamic headers, robust variables parsing, and zero breaking changes.
@@ -11,9 +10,16 @@ An **enhanced MCP (Model Context Protocol) server for GraphQL** that fixes real-
11
10
 
12
11
  - ✅ **Dynamic headers** — pass `Authorization`, `X-API-Key`, etc., via tool arguments (no config restarts)
13
12
  - ✅ **Robust variables parsing** — fixes `“Query variables must be a null or an object”` error
13
+ - ✅ **Filtered introspection** — request only specific types (e.g., `typeNames: ["Query", "User"]`) to reduce LLM context noise
14
14
  - ✅ **Full MCP compatibility** — works with **Claude Desktop**, **Cursor**, **Glama**, and **Smithery**
15
15
  - ✅ **Secure by default** — mutations disabled unless explicitly enabled
16
16
 
17
+ ## 🔍 Filtered Introspection (New!)
18
+
19
+ Avoid 50k-line schema dumps. Ask for only what you need:
20
+
21
+ @introspect-schema typeNames ["Query", "User"]
22
+
17
23
  ## 🔍 Debug & Inspect
18
24
 
19
25
  Use the official MCP Inspector to test your server live:
@@ -21,7 +27,7 @@ Use the official MCP Inspector to test your server live:
21
27
  ```bash
22
28
  npx @modelcontextprotocol/inspector \
23
29
  -e ENDPOINT=https://api.example.com/graphql \
24
- npx mcp-graphql-enhanced --debug
30
+ npx @letoribo/mcp-graphql-enhanced --debug
25
31
  ```
26
32
 
27
33
  ### Environment Variables (Breaking change in 1.0.0)
@@ -40,22 +46,22 @@ npx @modelcontextprotocol/inspector \
40
46
 
41
47
  ```bash
42
48
  # Basic usage
43
- ENDPOINT=http://localhost:3000/graphql npx mcp-graphql-enhanced
49
+ ENDPOINT=http://localhost:3000/graphql npx @letoribo/mcp-graphql-enhanced
44
50
 
45
51
  # With auth header
46
52
  ENDPOINT=https://api.example.com/graphql \
47
53
  HEADERS='{"Authorization":"Bearer xyz"}' \
48
- npx mcp-graphql-enhanced
54
+ npx @letoribo/mcp-graphql-enhanced
49
55
 
50
56
  # Enable mutations
51
57
  ENDPOINT=http://localhost:3000/graphql \
52
58
  ALLOW_MUTATIONS=true \
53
- npx mcp-graphql-enhanced
59
+ npx @letoribo/mcp-graphql-enhanced
54
60
 
55
61
  # Use local schema file
56
62
  ENDPOINT=http://localhost:3000/graphql \
57
63
  SCHEMA=./schema.graphql \
58
- npx mcp-graphql-enhanced
64
+ npx @letoribo/mcp-graphql-enhanced
59
65
  ```
60
66
 
61
67
  ## Resources
@@ -66,8 +72,9 @@ npx mcp-graphql-enhanced
66
72
 
67
73
  The server provides two main tools:
68
74
 
69
- 1. **introspect-schema**: This tool retrieves the GraphQL schema. Use this first if you don't have access to the schema as a resource.
75
+ 1. **introspect-schema**: This tool retrieves the GraphQL schema or a filtered subset (via typeNames). Use this first if you don't have access to the schema as a resource.
70
76
  This uses either the local schema file, a schema file hosted at a URL, or an introspection query.
77
+ Filtered introspection (typeNames) is only available when using a live GraphQL endpoint (not with SCHEMA file or URL).
71
78
 
72
79
  2. **query-graphql**: Execute GraphQL queries against the endpoint. By default, mutations are disabled unless `ALLOW_MUTATIONS` is set to `true`.
73
80
 
@@ -75,10 +82,10 @@ This uses either the local schema file, a schema file hosted at a URL, or an int
75
82
 
76
83
  ### Installing via Smithery
77
84
 
78
- To install GraphQL MCP Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/mcp-graphql-enhanced):
85
+ To install GraphQL MCP Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@letoribo/mcp-graphql-enhanced):
79
86
 
80
87
  ```bash
81
- npx -y @smithery/cli install mcp-graphql-enhanced --client claude
88
+ npx -y @smithery/cli install @letoribo/mcp-graphql-enhanced --client claude
82
89
  ```
83
90
 
84
91
  ### Installing Manually
@@ -89,7 +96,7 @@ It can be manually installed to Claude:
89
96
  "mcpServers": {
90
97
  "mcp-graphql": {
91
98
  "command": "npx",
92
- "args": ["mcp-graphql-enhanced"],
99
+ "args": ["@letoribo/mcp-graphql-enhanced"],
93
100
  "env": {
94
101
  "ENDPOINT": "https://your-api.com/graphql"
95
102
  }
@@ -17,4 +17,5 @@ export declare function introspectSchemaFromUrl(url: string): Promise<string>;
17
17
  * @returns The schema
18
18
  */
19
19
  export declare function introspectLocalSchema(path: string): Promise<string>;
20
+ export declare function introspectTypes(endpoint: string, headers: Record<string, string> | undefined, typeNames: string[]): Promise<string>;
20
21
  //# sourceMappingURL=introspection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"introspection.d.ts","sourceRoot":"","sources":["../../src/helpers/introspection.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACvC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,mBAuBhC;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,GAAG,EAAE,MAAM,mBASxD;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,mBAGvD"}
1
+ {"version":3,"file":"introspection.d.ts","sourceRoot":"","sources":["../../src/helpers/introspection.ts"],"names":[],"mappings":"AAcA;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACvC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,mBAuBhC;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,GAAG,EAAE,MAAM,mBASxD;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,mBAGvD;AAkBD,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,YAAK,EACpC,SAAS,EAAE,MAAM,EAAE,mBA6EpB"}
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.introspectEndpoint = introspectEndpoint;
4
4
  exports.introspectSchemaFromUrl = introspectSchemaFromUrl;
5
5
  exports.introspectLocalSchema = introspectLocalSchema;
6
+ exports.introspectTypes = introspectTypes;
6
7
  const graphql_1 = require("graphql");
7
8
  const promises_1 = require("node:fs/promises");
8
9
  /**
@@ -53,3 +54,86 @@ async function introspectLocalSchema(path) {
53
54
  const schema = await (0, promises_1.readFile)(path, "utf8");
54
55
  return schema;
55
56
  }
57
+ function isObjectLikeType(type) {
58
+ return 'getFields' in type;
59
+ }
60
+ function isUnionType(type) {
61
+ return 'getTypes' in type;
62
+ }
63
+ function isEnumType(type) {
64
+ return 'getValues' in type;
65
+ }
66
+ function isInputObjectType(type) {
67
+ return 'getFields' in type;
68
+ }
69
+ async function introspectTypes(endpoint, headers = {}, typeNames) {
70
+ const response = await fetch(endpoint, {
71
+ method: "POST",
72
+ headers: { "Content-Type": "application/json", ...headers },
73
+ body: JSON.stringify({ query: (0, graphql_1.getIntrospectionQuery)() }),
74
+ });
75
+ const data = await response.json();
76
+ const schema = (0, graphql_1.buildClientSchema)(data.data);
77
+ const result = {};
78
+ for (const name of typeNames) {
79
+ const type = schema.getType(name);
80
+ if (!type)
81
+ continue;
82
+ // Handle object/interface types
83
+ if (isObjectLikeType(type)) {
84
+ result[name] = {
85
+ kind: type instanceof graphql_1.GraphQLObjectType ? "OBJECT" : "INTERFACE",
86
+ description: type.description,
87
+ fields: Object.fromEntries(Object.entries(type.getFields()).map(([fieldName, field]) => [
88
+ fieldName,
89
+ {
90
+ type: field.type.toString(),
91
+ description: field.description,
92
+ args: field.args.map(arg => ({
93
+ name: arg.name,
94
+ type: arg.type.toString(),
95
+ description: arg.description,
96
+ }))
97
+ }
98
+ ]))
99
+ };
100
+ }
101
+ // Handle union types
102
+ else if (isUnionType(type)) {
103
+ result[name] = {
104
+ kind: "UNION",
105
+ description: type.description,
106
+ possibleTypes: type.getTypes().map(t => t.name)
107
+ };
108
+ }
109
+ // Handle enums
110
+ else if (isEnumType(type)) {
111
+ result[name] = {
112
+ kind: "ENUM",
113
+ description: type.description,
114
+ values: type.getValues().map(v => ({
115
+ name: v.name,
116
+ description: v.description
117
+ }))
118
+ };
119
+ }
120
+ // Handle scalars and input objects
121
+ else if (isInputObjectType(type)) {
122
+ result[name] = {
123
+ kind: "INPUT_OBJECT",
124
+ description: type.description,
125
+ fields: Object.fromEntries(Object.entries(type.getFields()).map(([fieldName, field]) => [
126
+ fieldName,
127
+ { type: field.type.toString(), description: field.description }
128
+ ]))
129
+ };
130
+ }
131
+ else if (type instanceof graphql_1.GraphQLScalarType) {
132
+ result[name] = {
133
+ kind: "SCALAR",
134
+ description: type.description
135
+ };
136
+ }
137
+ }
138
+ return JSON.stringify(result, null, 2);
139
+ }
package/dist/index.d.ts CHANGED
@@ -4,10 +4,15 @@ declare const StdioServerTransport: any;
4
4
  declare const parse: any;
5
5
  declare const z: any;
6
6
  declare const checkDeprecatedArguments: any;
7
- declare const introspectEndpoint: any, introspectLocalSchema: any, introspectSchemaFromUrl: any;
7
+ declare const introspectEndpoint: any, introspectLocalSchema: any, introspectSchemaFromUrl: any, introspectTypes: any;
8
8
  declare const getVersion: () => any;
9
9
  declare const EnvSchema: any;
10
10
  declare const env: any;
11
11
  declare const server: any;
12
+ interface IntrospectSchemaArgs {
13
+ typeNames?: string[];
14
+ descriptions?: boolean;
15
+ directives?: boolean;
16
+ }
12
17
  declare function main(): Promise<void>;
13
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,QAAA,MAAQ,SAAS,KAAuD,CAAC;AACzE,QAAA,MAAQ,oBAAoB,KAAyD,CAAC;AACtF,QAAA,MAAQ,KAAK,KAAgC,CAAC;AAC9C,QAAA,MAAM,CAAC,KAAyB,CAAC;AACjC,QAAA,MAAQ,wBAAwB,KAAwC,CAAC;AACzE,QAAA,MACC,kBAAkB,OAClB,qBAAqB,OACrB,uBAAuB,KACiB,CAAC;AAG1C,QAAA,MAAM,UAAU,WAIf,CAAC;AAKF,QAAA,MAAM,SAAS,KAkBb,CAAC;AAEH,QAAA,MAAM,GAAG,KAA+B,CAAC;AAEzC,QAAA,MAAM,MAAM,KAIV,CAAC;AAoMH,iBAAe,IAAI,kBAOlB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,QAAA,MAAQ,SAAS,KAAuD,CAAC;AACzE,QAAA,MAAQ,oBAAoB,KAAyD,CAAC;AACtF,QAAA,MAAQ,KAAK,KAAgC,CAAC;AAC9C,QAAA,MAAM,CAAC,KAAyB,CAAC;AACjC,QAAA,MAAQ,wBAAwB,KAAwC,CAAC;AACzE,QAAA,MACC,kBAAkB,OAClB,qBAAqB,OACrB,uBAAuB,OACvB,eAAe,KACyB,CAAC;AAG1C,QAAA,MAAM,UAAU,WAIf,CAAC;AAKF,QAAA,MAAM,SAAS,KAqBb,CAAC;AAEH,QAAA,MAAM,GAAG,KAA+B,CAAC;AAEzC,QAAA,MAAM,MAAM,KAIV,CAAC;AA+BH,UAAU,oBAAoB;IAC5B,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAoKD,iBAAe,IAAI,kBAOlB"}
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio
5
5
  const { parse } = require("graphql/language");
6
6
  const z = require("zod").default;
7
7
  const { checkDeprecatedArguments } = require("./helpers/deprecation.js");
8
- const { introspectEndpoint, introspectLocalSchema, introspectSchemaFromUrl, } = require("./helpers/introspection.js");
8
+ const { introspectEndpoint, introspectLocalSchema, introspectSchemaFromUrl, introspectTypes, } = require("./helpers/introspection.js");
9
9
  // Simulate macro import — since "with { type: 'macro' }" is not CommonJS compatible
10
10
  const getVersion = () => {
11
11
  // Replace with your actual version or read from package.json
@@ -16,7 +16,7 @@ const getVersion = () => {
16
16
  checkDeprecatedArguments();
17
17
  const EnvSchema = z.object({
18
18
  NAME: z.string().default("mcp-graphql"),
19
- ENDPOINT: z.string().url().default("http://localhost:4000/graphql"),
19
+ ENDPOINT: z.preprocess((val) => (typeof val === 'string' ? val.trim() : val), z.string().url("ENDPOINT must be a valid URL (e.g., https://example.com/graphql)")).default("http://localhost:4000/graphql"),
20
20
  ALLOW_MUTATIONS: z
21
21
  .enum(["true", "false"])
22
22
  .transform((value) => value === "true")
@@ -68,38 +68,38 @@ server.resource("graphql-schema", new URL(env.ENDPOINT).href, async (uri) => {
68
68
  throw new Error(`Failed to get GraphQL schema: ${error}`);
69
69
  }
70
70
  });
71
- server.tool("introspect-schema", "Introspect the GraphQL schema, use this tool before doing a query to get the schema information if you do not have it available as a resource already.", {
72
- __ignore__: z
73
- .boolean()
74
- .default(false)
75
- .describe("This does not do anything"),
76
- }, async () => {
71
+ server.tool("introspect-schema", "Introspect the GraphQL schema. Optionally filter to specific types.", {
72
+ typeNames: z.array(z.string()).optional().describe("e.g., [\"Query\", \"User\"]"),
73
+ descriptions: z.boolean().optional().default(true),
74
+ directives: z.boolean().optional().default(true),
75
+ }, async ({ typeNames, descriptions = true, directives = true }) => {
77
76
  try {
78
- let schema;
79
- if (env.SCHEMA) {
80
- schema = await introspectLocalSchema(env.SCHEMA);
77
+ if (typeNames && typeNames.length > 0) {
78
+ // Use your existing introspectTypes helper
79
+ const filtered = await introspectTypes(env.ENDPOINT, env.HEADERS, typeNames);
80
+ return { content: [{ type: "text", text: filtered }] };
81
81
  }
82
82
  else {
83
- schema = await introspectEndpoint(env.ENDPOINT, env.HEADERS);
83
+ // Fallback to full schema
84
+ let schema;
85
+ if (env.SCHEMA) {
86
+ if (env.SCHEMA.startsWith("http://") || env.SCHEMA.startsWith("https://")) {
87
+ schema = await introspectSchemaFromUrl(env.SCHEMA);
88
+ }
89
+ else {
90
+ schema = await introspectLocalSchema(env.SCHEMA);
91
+ }
92
+ }
93
+ else {
94
+ schema = await introspectEndpoint(env.ENDPOINT, env.HEADERS);
95
+ }
96
+ return { content: [{ type: "text", text: schema }] };
84
97
  }
85
- return {
86
- content: [
87
- {
88
- type: "text",
89
- text: schema,
90
- },
91
- ],
92
- };
93
98
  }
94
99
  catch (error) {
95
100
  return {
96
101
  isError: true,
97
- content: [
98
- {
99
- type: "text",
100
- text: `Failed to introspect schema: ${error}`,
101
- },
102
- ],
102
+ content: [{ type: "text", text: `Introspection failed: ${error}` }],
103
103
  };
104
104
  }
105
105
  });
package/package.json CHANGED
@@ -31,7 +31,7 @@
31
31
  "format": "prettier --write ."
32
32
  },
33
33
  "dependencies": {
34
- "@modelcontextprotocol/sdk": "1.12.0",
34
+ "@modelcontextprotocol/sdk": "^1.20.1",
35
35
  "graphql": "^16.11.0",
36
36
  "yargs": "17.7.2",
37
37
  "zod": "3.25.30",
@@ -46,5 +46,5 @@
46
46
  "ts-node": "^10.9.2",
47
47
  "typescript": "5.8.3"
48
48
  },
49
- "version": "2.0.5"
49
+ "version": "2.0.7"
50
50
  }