@elsium-ai/mcp 0.1.6
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/client.d.ts +24 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +530 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.d.ts.map +1 -0
- package/package.json +36 -0
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Tool } from '@elsium-ai/tools';
|
|
2
|
+
export interface MCPClientConfig {
|
|
3
|
+
name: string;
|
|
4
|
+
transport: 'stdio';
|
|
5
|
+
command: string;
|
|
6
|
+
args?: string[];
|
|
7
|
+
env?: Record<string, string>;
|
|
8
|
+
timeoutMs?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface MCPToolInfo {
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
inputSchema: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
export interface MCPClient {
|
|
16
|
+
connect(): Promise<void>;
|
|
17
|
+
disconnect(): Promise<void>;
|
|
18
|
+
listTools(): Promise<MCPToolInfo[]>;
|
|
19
|
+
callTool(name: string, args: Record<string, unknown>): Promise<unknown>;
|
|
20
|
+
toElsiumTools(): Promise<Tool[]>;
|
|
21
|
+
readonly connected: boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare function createMCPClient(config: MCPClientConfig): MCPClient;
|
|
24
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAoC,MAAM,kBAAkB,CAAA;AAE9E,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,OAAO,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACpC;AAgBD,MAAM,WAAW,SAAS;IACzB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3B,SAAS,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAAA;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACvE,aAAa,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,CAAA;IAChC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAC3B;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CAuQlE"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAGvE,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/client.ts
|
|
3
|
+
import { spawn } from "child_process";
|
|
4
|
+
|
|
5
|
+
// ../core/src/errors.ts
|
|
6
|
+
class ElsiumError extends Error {
|
|
7
|
+
code;
|
|
8
|
+
provider;
|
|
9
|
+
model;
|
|
10
|
+
statusCode;
|
|
11
|
+
retryable;
|
|
12
|
+
retryAfterMs;
|
|
13
|
+
cause;
|
|
14
|
+
metadata;
|
|
15
|
+
constructor(details) {
|
|
16
|
+
super(details.message);
|
|
17
|
+
this.name = "ElsiumError";
|
|
18
|
+
this.code = details.code;
|
|
19
|
+
this.provider = details.provider;
|
|
20
|
+
this.model = details.model;
|
|
21
|
+
this.statusCode = details.statusCode;
|
|
22
|
+
this.retryable = details.retryable;
|
|
23
|
+
this.retryAfterMs = details.retryAfterMs;
|
|
24
|
+
this.cause = details.cause;
|
|
25
|
+
this.metadata = details.metadata;
|
|
26
|
+
}
|
|
27
|
+
toJSON() {
|
|
28
|
+
return {
|
|
29
|
+
name: this.name,
|
|
30
|
+
code: this.code,
|
|
31
|
+
message: this.message,
|
|
32
|
+
provider: this.provider,
|
|
33
|
+
model: this.model,
|
|
34
|
+
statusCode: this.statusCode,
|
|
35
|
+
retryable: this.retryable,
|
|
36
|
+
retryAfterMs: this.retryAfterMs,
|
|
37
|
+
metadata: this.metadata
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
static providerError(message, opts) {
|
|
41
|
+
return new ElsiumError({
|
|
42
|
+
code: "PROVIDER_ERROR",
|
|
43
|
+
message,
|
|
44
|
+
provider: opts.provider,
|
|
45
|
+
statusCode: opts.statusCode,
|
|
46
|
+
retryable: opts.retryable ?? false,
|
|
47
|
+
cause: opts.cause
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
static rateLimit(provider, retryAfterMs) {
|
|
51
|
+
return new ElsiumError({
|
|
52
|
+
code: "RATE_LIMIT",
|
|
53
|
+
message: `Rate limited by ${provider}`,
|
|
54
|
+
provider,
|
|
55
|
+
statusCode: 429,
|
|
56
|
+
retryable: true,
|
|
57
|
+
retryAfterMs
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
static authError(provider) {
|
|
61
|
+
return new ElsiumError({
|
|
62
|
+
code: "AUTH_ERROR",
|
|
63
|
+
message: `Authentication failed for ${provider}. Check your API key.`,
|
|
64
|
+
provider,
|
|
65
|
+
statusCode: 401,
|
|
66
|
+
retryable: false
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
static timeout(provider, timeoutMs) {
|
|
70
|
+
return new ElsiumError({
|
|
71
|
+
code: "TIMEOUT",
|
|
72
|
+
message: `Request to ${provider} timed out after ${timeoutMs}ms`,
|
|
73
|
+
provider,
|
|
74
|
+
retryable: true
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
static validation(message, metadata) {
|
|
78
|
+
return new ElsiumError({
|
|
79
|
+
code: "VALIDATION_ERROR",
|
|
80
|
+
message,
|
|
81
|
+
retryable: false,
|
|
82
|
+
metadata
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
static budgetExceeded(spent, budget) {
|
|
86
|
+
return new ElsiumError({
|
|
87
|
+
code: "BUDGET_EXCEEDED",
|
|
88
|
+
message: `Token budget exceeded: spent ${spent}, budget ${budget}`,
|
|
89
|
+
retryable: false,
|
|
90
|
+
metadata: { spent, budget }
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// ../core/src/utils.ts
|
|
95
|
+
import { randomBytes } from "crypto";
|
|
96
|
+
function cryptoHex(bytes) {
|
|
97
|
+
return randomBytes(bytes).toString("hex");
|
|
98
|
+
}
|
|
99
|
+
function generateId(prefix = "els") {
|
|
100
|
+
const timestamp = Date.now().toString(36);
|
|
101
|
+
const random = cryptoHex(4);
|
|
102
|
+
return `${prefix}_${timestamp}_${random}`;
|
|
103
|
+
}
|
|
104
|
+
// ../core/src/logger.ts
|
|
105
|
+
var LOG_LEVELS = {
|
|
106
|
+
debug: 0,
|
|
107
|
+
info: 1,
|
|
108
|
+
warn: 2,
|
|
109
|
+
error: 3
|
|
110
|
+
};
|
|
111
|
+
function createLogger(options = {}) {
|
|
112
|
+
const { level = "info", pretty = false, context = {} } = options;
|
|
113
|
+
const minLevel = LOG_LEVELS[level];
|
|
114
|
+
function log(logLevel, message, data) {
|
|
115
|
+
if (LOG_LEVELS[logLevel] < minLevel)
|
|
116
|
+
return;
|
|
117
|
+
const entry = {
|
|
118
|
+
...context,
|
|
119
|
+
level: logLevel,
|
|
120
|
+
message,
|
|
121
|
+
timestamp: new Date().toISOString(),
|
|
122
|
+
...data ? { data } : {}
|
|
123
|
+
};
|
|
124
|
+
const output = pretty ? JSON.stringify(entry, null, 2) : JSON.stringify(entry);
|
|
125
|
+
if (logLevel === "error") {
|
|
126
|
+
console.error(output);
|
|
127
|
+
} else if (logLevel === "warn") {
|
|
128
|
+
console.warn(output);
|
|
129
|
+
} else {
|
|
130
|
+
console.log(output);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
debug: (msg, data) => log("debug", msg, data),
|
|
135
|
+
info: (msg, data) => log("info", msg, data),
|
|
136
|
+
warn: (msg, data) => log("warn", msg, data),
|
|
137
|
+
error: (msg, data) => log("error", msg, data),
|
|
138
|
+
child(childContext) {
|
|
139
|
+
return createLogger({
|
|
140
|
+
level,
|
|
141
|
+
pretty,
|
|
142
|
+
context: { ...context, ...childContext }
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
// src/client.ts
|
|
148
|
+
function createMCPClient(config) {
|
|
149
|
+
let process2 = null;
|
|
150
|
+
let connected = false;
|
|
151
|
+
let requestId = 0;
|
|
152
|
+
const pendingRequests = new Map;
|
|
153
|
+
let buffer = "";
|
|
154
|
+
const timeoutMs = config.timeoutMs ?? 30000;
|
|
155
|
+
function sendRequest(method, params) {
|
|
156
|
+
if (!process2?.stdin) {
|
|
157
|
+
return Promise.reject(new ElsiumError({
|
|
158
|
+
code: "NETWORK_ERROR",
|
|
159
|
+
message: "MCP client not connected",
|
|
160
|
+
retryable: false
|
|
161
|
+
}));
|
|
162
|
+
}
|
|
163
|
+
const id = ++requestId;
|
|
164
|
+
const request = {
|
|
165
|
+
jsonrpc: "2.0",
|
|
166
|
+
id,
|
|
167
|
+
method,
|
|
168
|
+
...params ? { params } : {}
|
|
169
|
+
};
|
|
170
|
+
return new Promise((resolve, reject) => {
|
|
171
|
+
const timer = setTimeout(() => {
|
|
172
|
+
pendingRequests.delete(id);
|
|
173
|
+
reject(new ElsiumError({
|
|
174
|
+
code: "TIMEOUT",
|
|
175
|
+
message: `MCP request timed out after ${timeoutMs}ms`,
|
|
176
|
+
retryable: true
|
|
177
|
+
}));
|
|
178
|
+
}, timeoutMs);
|
|
179
|
+
pendingRequests.set(id, {
|
|
180
|
+
resolve: (value) => {
|
|
181
|
+
clearTimeout(timer);
|
|
182
|
+
resolve(value);
|
|
183
|
+
},
|
|
184
|
+
reject: (error) => {
|
|
185
|
+
clearTimeout(timer);
|
|
186
|
+
reject(error);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
const proc = process2;
|
|
190
|
+
if (proc?.stdin) {
|
|
191
|
+
proc.stdin.write(`${JSON.stringify(request)}
|
|
192
|
+
`);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
function sendNotification(method, params) {
|
|
197
|
+
const proc = process2;
|
|
198
|
+
if (!proc?.stdin)
|
|
199
|
+
return;
|
|
200
|
+
const notification = {
|
|
201
|
+
jsonrpc: "2.0",
|
|
202
|
+
method,
|
|
203
|
+
...params ? { params } : {}
|
|
204
|
+
};
|
|
205
|
+
proc.stdin.write(`${JSON.stringify(notification)}
|
|
206
|
+
`);
|
|
207
|
+
}
|
|
208
|
+
const MAX_LINE_LENGTH = 1024 * 1024;
|
|
209
|
+
function processResponseLine(line) {
|
|
210
|
+
if (!line.trim())
|
|
211
|
+
return;
|
|
212
|
+
let response;
|
|
213
|
+
try {
|
|
214
|
+
response = JSON.parse(line);
|
|
215
|
+
} catch {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const pending = pendingRequests.get(response.id);
|
|
219
|
+
if (!pending)
|
|
220
|
+
return;
|
|
221
|
+
pendingRequests.delete(response.id);
|
|
222
|
+
if (response.error) {
|
|
223
|
+
pending.reject(new ElsiumError({
|
|
224
|
+
code: "PROVIDER_ERROR",
|
|
225
|
+
message: `MCP error: ${response.error.message}`,
|
|
226
|
+
retryable: false,
|
|
227
|
+
metadata: { code: response.error.code }
|
|
228
|
+
}));
|
|
229
|
+
} else {
|
|
230
|
+
pending.resolve(response.result);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function handleData(data) {
|
|
234
|
+
buffer += data;
|
|
235
|
+
if (buffer.length > MAX_LINE_LENGTH) {
|
|
236
|
+
buffer = "";
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const lines = buffer.split(`
|
|
240
|
+
`);
|
|
241
|
+
buffer = lines.pop() ?? "";
|
|
242
|
+
for (const line of lines) {
|
|
243
|
+
processResponseLine(line);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
get connected() {
|
|
248
|
+
return connected;
|
|
249
|
+
},
|
|
250
|
+
async connect() {
|
|
251
|
+
if (connected)
|
|
252
|
+
return;
|
|
253
|
+
const childEnv = {
|
|
254
|
+
PATH: globalThis.process?.env?.PATH ?? "",
|
|
255
|
+
HOME: globalThis.process?.env?.HOME ?? "",
|
|
256
|
+
...config.env ?? {}
|
|
257
|
+
};
|
|
258
|
+
process2 = spawn(config.command, config.args ?? [], {
|
|
259
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
260
|
+
env: childEnv
|
|
261
|
+
});
|
|
262
|
+
process2.stdout?.setEncoding("utf-8");
|
|
263
|
+
process2.stdout?.on("data", handleData);
|
|
264
|
+
process2.on("error", (err2) => {
|
|
265
|
+
connected = false;
|
|
266
|
+
for (const [, pending] of pendingRequests) {
|
|
267
|
+
pending.reject(err2);
|
|
268
|
+
}
|
|
269
|
+
pendingRequests.clear();
|
|
270
|
+
});
|
|
271
|
+
process2.on("exit", (code) => {
|
|
272
|
+
connected = false;
|
|
273
|
+
if (pendingRequests.size > 0) {
|
|
274
|
+
const err2 = new Error(`MCP subprocess exited with code ${code}`);
|
|
275
|
+
for (const [, pending] of pendingRequests) {
|
|
276
|
+
pending.reject(err2);
|
|
277
|
+
}
|
|
278
|
+
pendingRequests.clear();
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
await sendRequest("initialize", {
|
|
282
|
+
protocolVersion: "2024-11-05",
|
|
283
|
+
capabilities: {},
|
|
284
|
+
clientInfo: { name: `elsium-mcp-${config.name}`, version: "0.1.0" }
|
|
285
|
+
});
|
|
286
|
+
sendNotification("notifications/initialized");
|
|
287
|
+
connected = true;
|
|
288
|
+
},
|
|
289
|
+
async disconnect() {
|
|
290
|
+
if (!connected || !process2)
|
|
291
|
+
return;
|
|
292
|
+
try {
|
|
293
|
+
process2.stdin?.end();
|
|
294
|
+
process2.kill();
|
|
295
|
+
} catch {}
|
|
296
|
+
connected = false;
|
|
297
|
+
process2 = null;
|
|
298
|
+
for (const [, { reject }] of pendingRequests) {
|
|
299
|
+
reject(new Error("MCP client disconnected"));
|
|
300
|
+
}
|
|
301
|
+
pendingRequests.clear();
|
|
302
|
+
},
|
|
303
|
+
async listTools() {
|
|
304
|
+
const result = await sendRequest("tools/list");
|
|
305
|
+
return result.tools ?? [];
|
|
306
|
+
},
|
|
307
|
+
async callTool(name, args) {
|
|
308
|
+
const result = await sendRequest("tools/call", { name, arguments: args });
|
|
309
|
+
const textContent = result.content?.filter((c) => c.type === "text").map((c) => c.text).join(`
|
|
310
|
+
`);
|
|
311
|
+
return textContent ?? result;
|
|
312
|
+
},
|
|
313
|
+
async toElsiumTools() {
|
|
314
|
+
const mcpTools = await this.listTools();
|
|
315
|
+
const client = this;
|
|
316
|
+
return mcpTools.map((mcpTool) => {
|
|
317
|
+
const tool = {
|
|
318
|
+
name: mcpTool.name,
|
|
319
|
+
description: mcpTool.description,
|
|
320
|
+
inputSchema: { _def: { typeName: "ZodObject" } },
|
|
321
|
+
rawSchema: mcpTool.inputSchema,
|
|
322
|
+
timeoutMs,
|
|
323
|
+
async execute(input, partialCtx) {
|
|
324
|
+
const toolCallId = partialCtx?.toolCallId ?? generateId("tc");
|
|
325
|
+
const startTime = performance.now();
|
|
326
|
+
try {
|
|
327
|
+
const result = await client.callTool(mcpTool.name, input ?? {});
|
|
328
|
+
return {
|
|
329
|
+
success: true,
|
|
330
|
+
data: result,
|
|
331
|
+
toolCallId,
|
|
332
|
+
durationMs: Math.round(performance.now() - startTime)
|
|
333
|
+
};
|
|
334
|
+
} catch (error) {
|
|
335
|
+
return {
|
|
336
|
+
success: false,
|
|
337
|
+
error: error instanceof Error ? error.message : String(error),
|
|
338
|
+
toolCallId,
|
|
339
|
+
durationMs: Math.round(performance.now() - startTime)
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
toDefinition() {
|
|
344
|
+
return {
|
|
345
|
+
name: mcpTool.name,
|
|
346
|
+
description: mcpTool.description,
|
|
347
|
+
inputSchema: mcpTool.inputSchema
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
return tool;
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
// src/server.ts
|
|
357
|
+
var log = createLogger();
|
|
358
|
+
function createMCPServer(config) {
|
|
359
|
+
let running = false;
|
|
360
|
+
const toolMap = new Map(config.tools.map((t) => [t.name, t]));
|
|
361
|
+
function handleRequest(request) {
|
|
362
|
+
const id = request.id ?? 0;
|
|
363
|
+
switch (request.method) {
|
|
364
|
+
case "initialize": {
|
|
365
|
+
return {
|
|
366
|
+
jsonrpc: "2.0",
|
|
367
|
+
id,
|
|
368
|
+
result: {
|
|
369
|
+
protocolVersion: "2024-11-05",
|
|
370
|
+
capabilities: { tools: {} },
|
|
371
|
+
serverInfo: {
|
|
372
|
+
name: config.name,
|
|
373
|
+
version: config.version ?? "0.1.0"
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
case "notifications/initialized": {
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
case "tools/list": {
|
|
382
|
+
const tools = config.tools.map((t) => {
|
|
383
|
+
const def = t.toDefinition();
|
|
384
|
+
return {
|
|
385
|
+
name: def.name,
|
|
386
|
+
description: def.description,
|
|
387
|
+
inputSchema: def.inputSchema
|
|
388
|
+
};
|
|
389
|
+
});
|
|
390
|
+
return {
|
|
391
|
+
jsonrpc: "2.0",
|
|
392
|
+
id,
|
|
393
|
+
result: { tools }
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
case "tools/call": {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
default: {
|
|
400
|
+
return {
|
|
401
|
+
jsonrpc: "2.0",
|
|
402
|
+
id,
|
|
403
|
+
error: {
|
|
404
|
+
code: -32601,
|
|
405
|
+
message: `Method not found: ${request.method}`
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
async function handleToolCall(request) {
|
|
412
|
+
const id = request.id ?? 0;
|
|
413
|
+
const name = request.params?.name;
|
|
414
|
+
const args = request.params?.arguments ?? {};
|
|
415
|
+
const tool = toolMap.get(name);
|
|
416
|
+
if (!tool) {
|
|
417
|
+
return {
|
|
418
|
+
jsonrpc: "2.0",
|
|
419
|
+
id,
|
|
420
|
+
error: {
|
|
421
|
+
code: -32602,
|
|
422
|
+
message: `Unknown tool: ${name}`
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
const result = await tool.execute(args, { toolCallId: generateId("tc") });
|
|
427
|
+
if (result.success) {
|
|
428
|
+
return {
|
|
429
|
+
jsonrpc: "2.0",
|
|
430
|
+
id,
|
|
431
|
+
result: {
|
|
432
|
+
content: [
|
|
433
|
+
{
|
|
434
|
+
type: "text",
|
|
435
|
+
text: typeof result.data === "string" ? result.data : JSON.stringify(result.data, null, 2)
|
|
436
|
+
}
|
|
437
|
+
]
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
return {
|
|
442
|
+
jsonrpc: "2.0",
|
|
443
|
+
id,
|
|
444
|
+
result: {
|
|
445
|
+
content: [{ type: "text", text: result.error ?? "Tool execution failed" }],
|
|
446
|
+
isError: true
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
function writeLine(data) {
|
|
451
|
+
process.stdout.write(`${JSON.stringify(data)}
|
|
452
|
+
`);
|
|
453
|
+
}
|
|
454
|
+
async function processRequestLine(line) {
|
|
455
|
+
if (!line.trim())
|
|
456
|
+
return;
|
|
457
|
+
let request;
|
|
458
|
+
try {
|
|
459
|
+
request = JSON.parse(line);
|
|
460
|
+
} catch {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
if (request.method === "tools/call") {
|
|
464
|
+
const response2 = await handleToolCall(request);
|
|
465
|
+
writeLine(response2);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
const response = handleRequest(request);
|
|
469
|
+
if (response) {
|
|
470
|
+
writeLine(response);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return {
|
|
474
|
+
get running() {
|
|
475
|
+
return running;
|
|
476
|
+
},
|
|
477
|
+
async start() {
|
|
478
|
+
if (running)
|
|
479
|
+
return;
|
|
480
|
+
running = true;
|
|
481
|
+
const MAX_BUFFER_SIZE = 1024 * 1024;
|
|
482
|
+
process.stdin.setEncoding("utf-8");
|
|
483
|
+
let buffer = "";
|
|
484
|
+
let processing = false;
|
|
485
|
+
const pendingChunks = [];
|
|
486
|
+
async function drainPendingChunks() {
|
|
487
|
+
while (pendingChunks.length > 0) {
|
|
488
|
+
const chunk = pendingChunks[0];
|
|
489
|
+
pendingChunks.shift();
|
|
490
|
+
buffer += chunk;
|
|
491
|
+
if (buffer.length > MAX_BUFFER_SIZE) {
|
|
492
|
+
log.error("MCP server: buffer size limit exceeded, resetting");
|
|
493
|
+
buffer = "";
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
const lines = buffer.split(`
|
|
497
|
+
`);
|
|
498
|
+
buffer = lines.pop() ?? "";
|
|
499
|
+
for (const line of lines) {
|
|
500
|
+
await processRequestLine(line);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
async function processQueue() {
|
|
505
|
+
if (processing)
|
|
506
|
+
return;
|
|
507
|
+
processing = true;
|
|
508
|
+
try {
|
|
509
|
+
await drainPendingChunks();
|
|
510
|
+
} finally {
|
|
511
|
+
processing = false;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
process.stdin.on("data", (chunk) => {
|
|
515
|
+
pendingChunks.push(chunk);
|
|
516
|
+
processQueue();
|
|
517
|
+
});
|
|
518
|
+
process.stdin.on("end", () => {
|
|
519
|
+
running = false;
|
|
520
|
+
});
|
|
521
|
+
},
|
|
522
|
+
stop() {
|
|
523
|
+
running = false;
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
export {
|
|
528
|
+
createMCPServer,
|
|
529
|
+
createMCPClient
|
|
530
|
+
};
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Tool } from '@elsium-ai/tools';
|
|
2
|
+
export interface MCPServerConfig {
|
|
3
|
+
name: string;
|
|
4
|
+
version?: string;
|
|
5
|
+
tools: Tool[];
|
|
6
|
+
}
|
|
7
|
+
export interface MCPServer {
|
|
8
|
+
start(): Promise<void>;
|
|
9
|
+
stop(): void;
|
|
10
|
+
readonly running: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function createMCPServer(config: MCPServerConfig): MCPServer;
|
|
13
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAI5C,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,IAAI,EAAE,CAAA;CACb;AAgBD,MAAM,WAAW,SAAS;IACzB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,IAAI,IAAI,IAAI,CAAA;IACZ,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CACzB;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CAmMlE"}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@elsium-ai/mcp",
|
|
3
|
+
"version": "0.1.6",
|
|
4
|
+
"description": "Model Context Protocol (MCP) support for ElsiumAI — bidirectional bridge",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Eric Utrera <ebutrera9103@gmail.com>",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/elsium-ai/elsium-ai",
|
|
10
|
+
"directory": "packages/mcp"
|
|
11
|
+
},
|
|
12
|
+
"type": "module",
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"import": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "bun build ./src/index.ts --outdir ./dist --target bun && bun x tsc -p tsconfig.build.json --emitDeclarationOnly",
|
|
26
|
+
"dev": "bun --watch src/index.ts"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@elsium-ai/core": "workspace:*",
|
|
30
|
+
"@elsium-ai/tools": "workspace:*"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"bun-types": "^1.3.0",
|
|
34
|
+
"typescript": "^5.7.0"
|
|
35
|
+
}
|
|
36
|
+
}
|