@letoribo/mcp-graphql-enhanced 3.2.0 → 3.2.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.
- package/README.md +7 -3
- package/dist/index.js +95 -198
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -17,13 +17,16 @@ An **enhanced MCP (Model Context Protocol) server for GraphQL** that fixes real-
|
|
|
17
17
|
|
|
18
18
|
This server now runs in **dual transport mode**, supporting both the standard **STDIO** communication (used by most MCP clients) and a new **HTTP JSON-RPC** endpoint on port `6274`.
|
|
19
19
|
|
|
20
|
-
This allows external systems, web applications, and direct `curl` commands to access the server's tools.
|
|
20
|
+
This allows external systems, web applications, and direct `curl` commands to access the server's tools with **live request logging** in your terminal (`[HTTP-RPC]` logs).
|
|
21
21
|
|
|
22
22
|
| **Endpoint** | **Method** | **Description** |
|
|
23
23
|
| :--- | :--- | :--- |
|
|
24
|
-
| `/mcp` | `POST` | The main JSON-RPC endpoint for tool execution. |
|
|
24
|
+
| `/mcp` | `POST` | The main JSON-RPC 2.0 endpoint for tool execution. |
|
|
25
25
|
| `/health` | `GET` | Simple health check, returns `{ status: 'ok' }`. |
|
|
26
26
|
|
|
27
|
+
### Automatic Port Selection
|
|
28
|
+
The server defaults to port `6274`. If you encounter an `EADDRINUSE` error, the server will automatically find the next available port. **Check the server logs for the final bound port** (e.g., `[HTTP] Started server on http://localhost:6275`).
|
|
29
|
+
|
|
27
30
|
### Resolving Port Conflicts (EADDRINUSE) and Automatic Port Selection
|
|
28
31
|
|
|
29
32
|
The server defaults to port `6274`. If you encounter an `EADDRINUSE: address already in use :::6274` error (common in local development due to stale processes), the server will automatically find the next available port (up to 10 attempts, not spawning multiple servers).
|
|
@@ -62,9 +65,10 @@ npx @modelcontextprotocol/inspector \
|
|
|
62
65
|
| `HEADERS` | JSON string containing headers for requests | `{}` |
|
|
63
66
|
| `ALLOW_MUTATIONS` | Enable mutation operations (disabled by default) | `false` |
|
|
64
67
|
| `NAME` | Name of the MCP server | `mcp-graphql-enhanced` |
|
|
65
|
-
| `SCHEMA` | Path to a local GraphQL schema file or URL
|
|
68
|
+
| `SCHEMA` | Path to a local GraphQL schema file or URL | - |
|
|
66
69
|
| `MCP_PORT` | Port for the HTTP/JSON-RPC server. | `6274` |
|
|
67
70
|
| `ENABLE_HTTP` | Enable HTTP transport: `auto` (default), `true`, or `false` | `auto` |
|
|
71
|
+
| `DEBUG` | Set to `mcp:*` for detailed SDK logs | - |
|
|
68
72
|
**Note on `ENABLE_HTTP`:**
|
|
69
73
|
- `auto` (default): Automatically enables HTTP only when running in MCP Inspector...
|
|
70
74
|
- `true`: Always enable HTTP server
|
package/dist/index.js
CHANGED
|
@@ -5,25 +5,31 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
const node_http_1 = __importDefault(require("node:http"));
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const
|
|
8
|
+
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
9
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
10
|
+
const language_1 = require("graphql/language");
|
|
11
|
+
const zod_1 = __importDefault(require("zod"));
|
|
12
|
+
// Helper imports
|
|
13
|
+
const deprecation_js_1 = require("./helpers/deprecation.js");
|
|
14
|
+
const introspection_js_1 = require("./helpers/introspection.js");
|
|
14
15
|
const getVersion = () => {
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
try {
|
|
17
|
+
const pkg = require("../package.json");
|
|
18
|
+
return pkg.version;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return "3.2.1";
|
|
22
|
+
}
|
|
17
23
|
};
|
|
18
|
-
checkDeprecatedArguments();
|
|
19
|
-
const EnvSchema =
|
|
20
|
-
NAME:
|
|
21
|
-
ENDPOINT:
|
|
22
|
-
ALLOW_MUTATIONS:
|
|
24
|
+
(0, deprecation_js_1.checkDeprecatedArguments)();
|
|
25
|
+
const EnvSchema = zod_1.default.object({
|
|
26
|
+
NAME: zod_1.default.string().default("mcp-graphql-enhanced"),
|
|
27
|
+
ENDPOINT: zod_1.default.preprocess((val) => (typeof val === 'string' ? val.trim() : val), zod_1.default.string().url()).default("https://mcp-neo4j-discord.vercel.app/api/graphiql"),
|
|
28
|
+
ALLOW_MUTATIONS: zod_1.default
|
|
23
29
|
.enum(["true", "false"])
|
|
24
30
|
.transform((value) => value === "true")
|
|
25
31
|
.default("false"),
|
|
26
|
-
HEADERS:
|
|
32
|
+
HEADERS: zod_1.default
|
|
27
33
|
.string()
|
|
28
34
|
.default("{}")
|
|
29
35
|
.transform((val) => {
|
|
@@ -34,32 +40,27 @@ const EnvSchema = z.object({
|
|
|
34
40
|
throw new Error("HEADERS must be a valid JSON string");
|
|
35
41
|
}
|
|
36
42
|
}),
|
|
37
|
-
SCHEMA:
|
|
38
|
-
MCP_PORT:
|
|
39
|
-
ENABLE_HTTP:
|
|
43
|
+
SCHEMA: zod_1.default.string().optional(),
|
|
44
|
+
MCP_PORT: zod_1.default.preprocess((val) => (val ? parseInt(val) : 6274), zod_1.default.number().int().min(1024).max(65535)).default(6274),
|
|
45
|
+
ENABLE_HTTP: zod_1.default
|
|
40
46
|
.enum(["true", "false", "auto"])
|
|
41
47
|
.transform((value) => {
|
|
42
48
|
if (value === "auto") {
|
|
43
|
-
return !!(process.env.MCP_INSPECTOR || process.env.INSPECTOR_PORT);
|
|
49
|
+
return !!(process.env.MCP_INSPECTOR || process.env.INSPECTOR_PORT || process.env.MCP_PORT);
|
|
44
50
|
}
|
|
45
51
|
return value === "true";
|
|
46
52
|
})
|
|
47
53
|
.default("auto"),
|
|
48
54
|
});
|
|
49
55
|
const env = EnvSchema.parse(process.env);
|
|
50
|
-
const server = new McpServer({
|
|
56
|
+
const server = new mcp_js_1.McpServer({
|
|
51
57
|
name: env.NAME,
|
|
52
58
|
version: getVersion(),
|
|
53
|
-
description: `GraphQL MCP server for ${env.ENDPOINT}`,
|
|
54
59
|
});
|
|
55
60
|
// --- CACHE STATE ---
|
|
56
61
|
let cachedSDL = null;
|
|
57
62
|
let cachedSchemaObject = null;
|
|
58
63
|
let schemaLoadError = null;
|
|
59
|
-
/**
|
|
60
|
-
* Loads the schema into memory.
|
|
61
|
-
* Populates both cachedSDL (string) and cachedSchemaObject (GraphQLSchema object).
|
|
62
|
-
*/
|
|
63
64
|
async function getSchema() {
|
|
64
65
|
if (cachedSDL)
|
|
65
66
|
return cachedSDL;
|
|
@@ -69,17 +70,18 @@ async function getSchema() {
|
|
|
69
70
|
const { buildClientSchema, getIntrospectionQuery, printSchema, buildASTSchema, parse: gqlParse } = require("graphql");
|
|
70
71
|
let sdl;
|
|
71
72
|
if (env.SCHEMA) {
|
|
73
|
+
// Check if it's a URL or local path
|
|
72
74
|
if (env.SCHEMA.startsWith("http")) {
|
|
73
|
-
|
|
75
|
+
const response = await fetch(env.SCHEMA);
|
|
76
|
+
sdl = await response.text();
|
|
74
77
|
}
|
|
75
78
|
else {
|
|
76
|
-
sdl = await introspectLocalSchema(env.SCHEMA);
|
|
79
|
+
sdl = await (0, introspection_js_1.introspectLocalSchema)(env.SCHEMA);
|
|
77
80
|
}
|
|
78
81
|
cachedSchemaObject = buildASTSchema(gqlParse(sdl));
|
|
79
82
|
cachedSDL = sdl;
|
|
80
83
|
}
|
|
81
84
|
else {
|
|
82
|
-
// Live Introspection
|
|
83
85
|
const response = await fetch(env.ENDPOINT, {
|
|
84
86
|
method: "POST",
|
|
85
87
|
headers: { "Content-Type": "application/json", ...env.HEADERS },
|
|
@@ -99,93 +101,15 @@ async function getSchema() {
|
|
|
99
101
|
throw schemaLoadError;
|
|
100
102
|
}
|
|
101
103
|
}
|
|
102
|
-
// --- RESOURCES ---
|
|
103
|
-
server.resource("graphql-schema", new URL(env.ENDPOINT).href, async (uri) => {
|
|
104
|
-
try {
|
|
105
|
-
const sdl = await getSchema();
|
|
106
|
-
return { contents: [{ uri: uri.href, text: sdl }] };
|
|
107
|
-
}
|
|
108
|
-
catch (error) {
|
|
109
|
-
throw error;
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
104
|
// --- TOOL HANDLERS ---
|
|
113
105
|
const toolHandlers = new Map();
|
|
114
|
-
|
|
115
|
-
try {
|
|
116
|
-
// Ensure cache is populated
|
|
117
|
-
await getSchema();
|
|
118
|
-
// Safety: If no specific types requested, return the 'Map' of types to prevent bridge crash
|
|
119
|
-
if (!typeNames || typeNames.length === 0) {
|
|
120
|
-
const allTypeNames = Object.keys(cachedSchemaObject.getTypeMap())
|
|
121
|
-
.filter(t => !t.startsWith('__'));
|
|
122
|
-
return {
|
|
123
|
-
content: [{
|
|
124
|
-
type: "text",
|
|
125
|
-
text: `Schema is large. Please request specific types for full details.\n\nAvailable types: ${allTypeNames.join(", ")}`
|
|
126
|
-
}]
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
// Use the new filtering logic from helpers/introspection.js
|
|
130
|
-
const filteredResult = introspectSpecificTypes(cachedSchemaObject, typeNames);
|
|
131
|
-
return {
|
|
132
|
-
content: [{
|
|
133
|
-
type: "text",
|
|
134
|
-
text: JSON.stringify(filteredResult, null, 2)
|
|
135
|
-
}]
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
catch (error) {
|
|
139
|
-
throw new Error(`Introspection failed: ${error.message}`);
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
toolHandlers.set("introspect-schema", introspectSchemaHandler);
|
|
143
|
-
server.tool("introspect-schema", "Introspect the GraphQL schema. Optionally filter to specific types.", {
|
|
144
|
-
typeNames: z.array(z.string()).optional().describe("A list of specific type names to filter (e.g. ['User', 'Post'])."),
|
|
145
|
-
}, async ({ typeNames }) => {
|
|
146
|
-
try {
|
|
147
|
-
console.error(`[TOOL] Introspect called with types: ${JSON.stringify(typeNames || "NONE")}`);
|
|
148
|
-
// 1. Ensure the schema is loaded into the cache
|
|
149
|
-
await getSchema();
|
|
150
|
-
// 2. THE GATEKEEPER: If no types requested, send ONLY names.
|
|
151
|
-
if (!typeNames || typeNames.length === 0) {
|
|
152
|
-
const allTypeNames = Object.keys(cachedSchemaObject.getTypeMap())
|
|
153
|
-
.filter(t => !t.startsWith('__'));
|
|
154
|
-
console.error(`[TOOL] Sending summary list of ${allTypeNames.length} types.`);
|
|
155
|
-
return {
|
|
156
|
-
content: [{
|
|
157
|
-
type: "text",
|
|
158
|
-
text: `Schema is large. Please request specific types for details.\n\nAvailable types: ${allTypeNames.join(", ")}`
|
|
159
|
-
}]
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
// 3. DRILL DOWN: If Claude asks for specific types, use the helper.
|
|
163
|
-
console.error(`[TOOL] Filtering for: ${typeNames.join(", ")}`);
|
|
164
|
-
const filteredResult = introspectSpecificTypes(cachedSchemaObject, typeNames);
|
|
165
|
-
return {
|
|
166
|
-
content: [{
|
|
167
|
-
type: "text",
|
|
168
|
-
text: JSON.stringify(filteredResult, null, 2)
|
|
169
|
-
}]
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
catch (error) {
|
|
173
|
-
console.error(`[TOOL ERROR] ${error.message}`);
|
|
174
|
-
throw new Error(`Introspection failed: ${error.message}`);
|
|
175
|
-
}
|
|
176
|
-
});
|
|
106
|
+
// Tool: query-graphql
|
|
177
107
|
const queryGraphqlHandler = async ({ query, variables, headers }) => {
|
|
178
108
|
try {
|
|
179
|
-
const parsedQuery = parse(query);
|
|
109
|
+
const parsedQuery = (0, language_1.parse)(query);
|
|
180
110
|
const isMutation = parsedQuery.definitions.some((def) => def.kind === "OperationDefinition" && def.operation === "mutation");
|
|
181
|
-
if (isMutation && !env.ALLOW_MUTATIONS)
|
|
182
|
-
throw new Error("Mutations are not allowed.
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
catch (error) {
|
|
186
|
-
throw new Error(`Invalid GraphQL query: ${error}`);
|
|
187
|
-
}
|
|
188
|
-
try {
|
|
111
|
+
if (isMutation && !env.ALLOW_MUTATIONS)
|
|
112
|
+
throw new Error("Mutations are not allowed.");
|
|
189
113
|
const toolHeaders = headers ? JSON.parse(headers) : {};
|
|
190
114
|
const allHeaders = { "Content-Type": "application/json", ...env.HEADERS, ...toolHeaders };
|
|
191
115
|
let parsedVariables = variables;
|
|
@@ -196,124 +120,97 @@ const queryGraphqlHandler = async ({ query, variables, headers }) => {
|
|
|
196
120
|
headers: allHeaders,
|
|
197
121
|
body: JSON.stringify({ query, variables: parsedVariables }),
|
|
198
122
|
});
|
|
199
|
-
if (!response.ok) {
|
|
200
|
-
const responseText = await response.text();
|
|
201
|
-
throw new Error(`GraphQL request failed: ${response.statusText}\n${responseText}`);
|
|
202
|
-
}
|
|
203
123
|
const data = await response.json();
|
|
204
|
-
if (data.errors && data.errors.length > 0) {
|
|
205
|
-
throw new Error(`GraphQL errors: ${JSON.stringify(data.errors, null, 2)}`);
|
|
206
|
-
}
|
|
207
124
|
return {
|
|
208
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
125
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
209
126
|
};
|
|
210
127
|
}
|
|
211
128
|
catch (error) {
|
|
212
|
-
throw new Error(`
|
|
129
|
+
throw new Error(`Execution failed: ${error.message}`);
|
|
213
130
|
}
|
|
214
131
|
};
|
|
215
132
|
toolHandlers.set("query-graphql", queryGraphqlHandler);
|
|
216
|
-
server.tool("query-graphql", "
|
|
217
|
-
query:
|
|
218
|
-
variables:
|
|
219
|
-
headers:
|
|
133
|
+
server.tool("query-graphql", "Execute a GraphQL query against the endpoint", {
|
|
134
|
+
query: zod_1.default.string(),
|
|
135
|
+
variables: zod_1.default.string().optional(),
|
|
136
|
+
headers: zod_1.default.string().optional(),
|
|
220
137
|
}, queryGraphqlHandler);
|
|
138
|
+
// Tool: introspect-schema
|
|
139
|
+
const introspectHandler = async ({ typeNames }) => {
|
|
140
|
+
await getSchema();
|
|
141
|
+
if (!typeNames || typeNames.length === 0) {
|
|
142
|
+
const allTypeNames = Object.keys(cachedSchemaObject.getTypeMap()).filter(t => !t.startsWith('__'));
|
|
143
|
+
return {
|
|
144
|
+
content: [{ type: "text", text: `Schema is large. Available types: ${allTypeNames.join(", ")}` }]
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const filtered = (0, introspection_js_1.introspectSpecificTypes)(cachedSchemaObject, typeNames);
|
|
148
|
+
return {
|
|
149
|
+
content: [{ type: "text", text: JSON.stringify(filtered, null, 2) }]
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
toolHandlers.set("introspect-schema", introspectHandler);
|
|
153
|
+
server.tool("introspect-schema", "Introspect the GraphQL schema with optional type filtering", {
|
|
154
|
+
typeNames: zod_1.default.array(zod_1.default.string()).optional(),
|
|
155
|
+
}, introspectHandler);
|
|
221
156
|
// --- HTTP SERVER LOGIC ---
|
|
222
|
-
function readBody(req) {
|
|
223
|
-
return new Promise((resolve, reject) => {
|
|
224
|
-
let body = '';
|
|
225
|
-
req.on('data', chunk => body += chunk.toString());
|
|
226
|
-
req.on('end', () => resolve(body));
|
|
227
|
-
req.on('error', reject);
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
157
|
async function handleHttpRequest(req, res) {
|
|
231
158
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
232
|
-
res.setHeader('Access-Control-Allow-Methods', '
|
|
159
|
+
res.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
|
|
233
160
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
234
161
|
if (req.method === 'OPTIONS') {
|
|
235
162
|
res.writeHead(204);
|
|
236
163
|
res.end();
|
|
237
164
|
return;
|
|
238
165
|
}
|
|
239
|
-
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
240
|
-
if (url.pathname === '/health' && req.method === 'GET') {
|
|
241
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
242
|
-
res.end(JSON.stringify({ status: 'ok', server: env.NAME }));
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
166
|
+
const url = new URL(req.url || '', `http://${req.headers.host}`);
|
|
245
167
|
if (url.pathname === '/mcp' && req.method === 'POST') {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
168
|
+
let body = '';
|
|
169
|
+
req.on('data', chunk => { body += chunk; });
|
|
170
|
+
req.on('end', async () => {
|
|
171
|
+
try {
|
|
172
|
+
const { method, params, id } = JSON.parse(body);
|
|
173
|
+
console.error(`[HTTP-RPC] Method: ${method} | ID: ${id}`);
|
|
174
|
+
const handler = toolHandlers.get(method);
|
|
175
|
+
if (!handler) {
|
|
176
|
+
res.writeHead(404);
|
|
177
|
+
return res.end(JSON.stringify({ jsonrpc: '2.0', id, error: { code: -32601, message: "Method not found" } }));
|
|
178
|
+
}
|
|
179
|
+
const result = await handler(params);
|
|
180
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
181
|
+
res.end(JSON.stringify({ jsonrpc: '2.0', id, result }));
|
|
254
182
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
res.end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32603, message: 'Internal error' } }));
|
|
262
|
-
}
|
|
183
|
+
catch (e) {
|
|
184
|
+
console.error(`[HTTP-ERROR] ${e.message}`);
|
|
185
|
+
res.writeHead(500);
|
|
186
|
+
res.end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32603, message: "Internal Error" } }));
|
|
187
|
+
}
|
|
188
|
+
});
|
|
263
189
|
return;
|
|
264
190
|
}
|
|
191
|
+
if (url.pathname === '/health') {
|
|
192
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
193
|
+
return res.end(JSON.stringify({ status: "ok", server: env.NAME }));
|
|
194
|
+
}
|
|
265
195
|
res.writeHead(404);
|
|
266
|
-
res.end(
|
|
267
|
-
}
|
|
268
|
-
let httpServer = null;
|
|
269
|
-
async function startHttpServer(initialPort) {
|
|
270
|
-
return new Promise((resolve, reject) => {
|
|
271
|
-
let port = initialPort;
|
|
272
|
-
const server = node_http_1.default.createServer(handleHttpRequest);
|
|
273
|
-
server.once('error', (err) => {
|
|
274
|
-
if (err.code === 'EADDRINUSE') {
|
|
275
|
-
server.close();
|
|
276
|
-
resolve(startHttpServer(port + 1));
|
|
277
|
-
}
|
|
278
|
-
else
|
|
279
|
-
reject(err);
|
|
280
|
-
});
|
|
281
|
-
server.listen(port, () => {
|
|
282
|
-
httpServer = server;
|
|
283
|
-
resolve(port);
|
|
284
|
-
});
|
|
285
|
-
});
|
|
196
|
+
res.end("Not Found");
|
|
286
197
|
}
|
|
287
198
|
// --- STARTUP ---
|
|
288
199
|
async function main() {
|
|
289
|
-
console.error(`[SCHEMA] Pre-loading GraphQL schema...`);
|
|
290
|
-
const schemaPromise = getSchema().catch(error => {
|
|
291
|
-
console.error(`[SCHEMA] Warning: Failed to pre-load schema: ${error}`);
|
|
292
|
-
});
|
|
293
|
-
const stdioTransport = new StdioServerTransport();
|
|
294
|
-
await server.connect(stdioTransport);
|
|
295
|
-
console.error(`[STDIO] Started GraphQL MCP server ${env.NAME}`);
|
|
296
200
|
if (env.ENABLE_HTTP) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
console.error(`[HTTP] Server started on http://localhost:${
|
|
300
|
-
}
|
|
301
|
-
catch (error) {
|
|
302
|
-
console.error(`[HTTP] Failed to start: ${error}`);
|
|
303
|
-
}
|
|
201
|
+
const serverHttp = node_http_1.default.createServer(handleHttpRequest);
|
|
202
|
+
serverHttp.listen(env.MCP_PORT, () => {
|
|
203
|
+
console.error(`[HTTP] Server started on http://localhost:${env.MCP_PORT}`);
|
|
204
|
+
});
|
|
304
205
|
}
|
|
305
|
-
|
|
206
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
207
|
+
await server.connect(transport);
|
|
208
|
+
console.error(`[STDIO] MCP Server "${env.NAME}" v${getVersion()} started`);
|
|
209
|
+
getSchema().catch(e => console.error(`[SCHEMA] Warning: Preload failed: ${e.message}`));
|
|
306
210
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
if (httpServer)
|
|
310
|
-
httpServer.close(() => process.exit(0));
|
|
311
|
-
else
|
|
312
|
-
process.exit(0);
|
|
313
|
-
};
|
|
314
|
-
process.on('SIGINT', shutdown);
|
|
315
|
-
process.on('SIGTERM', shutdown);
|
|
211
|
+
process.on('SIGINT', () => process.exit(0));
|
|
212
|
+
process.on('SIGTERM', () => process.exit(0));
|
|
316
213
|
main().catch(error => {
|
|
317
|
-
console.error(`
|
|
214
|
+
console.error(`[FATAL] ${error}`);
|
|
318
215
|
process.exit(1);
|
|
319
216
|
});
|
package/package.json
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"node": ">=18"
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
|
-
"dev": "
|
|
32
|
+
"dev": "tsx --watch src/index.ts",
|
|
33
33
|
"build": "tsc && chmod +x dist/index.js",
|
|
34
34
|
"start": "node dist/index.js",
|
|
35
35
|
"format": "prettier --write ."
|
|
@@ -47,7 +47,8 @@
|
|
|
47
47
|
"@types/yargs": "17.0.33",
|
|
48
48
|
"prettier": "^3.6.2",
|
|
49
49
|
"ts-node": "^10.9.2",
|
|
50
|
+
"tsx": "^4.21.0",
|
|
50
51
|
"typescript": "5.8.3"
|
|
51
52
|
},
|
|
52
|
-
"version": "3.2.
|
|
53
|
+
"version": "3.2.1"
|
|
53
54
|
}
|