alepha 0.13.8 → 0.14.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.
- package/dist/api/audits/index.d.ts +2 -1
- package/dist/api/audits/index.d.ts.map +1 -0
- package/dist/api/files/index.d.ts +2 -1
- package/dist/api/files/index.d.ts.map +1 -0
- package/dist/api/jobs/index.d.ts +158 -157
- package/dist/api/jobs/index.d.ts.map +1 -0
- package/dist/api/notifications/index.d.ts.map +1 -0
- package/dist/api/parameters/index.d.ts +4 -4
- package/dist/api/parameters/index.d.ts.map +1 -0
- package/dist/api/users/index.d.ts +132 -131
- package/dist/api/users/index.d.ts.map +1 -0
- package/dist/api/verifications/index.d.ts.map +1 -0
- package/dist/batch/index.d.ts.map +1 -0
- package/dist/bucket/index.d.ts.map +1 -0
- package/dist/cache/core/index.d.ts.map +1 -0
- package/dist/cache/redis/index.d.ts.map +1 -0
- package/dist/cli/index.d.ts +44 -32
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +380 -109
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +11 -1
- package/dist/command/index.d.ts.map +1 -0
- package/dist/command/index.js +45 -6
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +1334 -1318
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +75 -71
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +1337 -1321
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +1337 -1321
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.d.ts.map +1 -0
- package/dist/email/index.d.ts.map +1 -0
- package/dist/fake/index.d.ts.map +1 -0
- package/dist/file/index.d.ts.map +1 -0
- package/dist/lock/core/index.d.ts.map +1 -0
- package/dist/lock/redis/index.d.ts.map +1 -0
- package/dist/logger/index.d.ts +1 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/mcp/index.d.ts +820 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +978 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/orm/index.d.ts +180 -107
- package/dist/orm/index.d.ts.map +1 -0
- package/dist/orm/index.js +260 -174
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +4 -4
- package/dist/queue/core/index.d.ts.map +1 -0
- package/dist/queue/redis/index.d.ts.map +1 -0
- package/dist/redis/index.d.ts.map +1 -0
- package/dist/retry/index.d.ts.map +1 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/scheduler/index.d.ts.map +1 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/server/auth/index.d.ts +155 -155
- package/dist/server/auth/index.d.ts.map +1 -0
- package/dist/server/cache/index.d.ts.map +1 -0
- package/dist/server/compress/index.d.ts.map +1 -0
- package/dist/server/cookies/index.d.ts.map +1 -0
- package/dist/server/core/index.d.ts.map +1 -0
- package/dist/server/cors/index.d.ts.map +1 -0
- package/dist/server/health/index.d.ts.map +1 -0
- package/dist/server/helmet/index.d.ts.map +1 -0
- package/dist/server/links/index.d.ts +33 -33
- package/dist/server/links/index.d.ts.map +1 -0
- package/dist/server/metrics/index.d.ts.map +1 -0
- package/dist/server/multipart/index.d.ts.map +1 -0
- package/dist/server/proxy/index.d.ts.map +1 -0
- package/dist/server/rate-limit/index.d.ts.map +1 -0
- package/dist/server/security/index.d.ts +9 -9
- package/dist/server/security/index.d.ts.map +1 -0
- package/dist/server/static/index.d.ts.map +1 -0
- package/dist/server/swagger/index.d.ts.map +1 -0
- package/dist/sms/index.d.ts.map +1 -0
- package/dist/thread/index.d.ts.map +1 -0
- package/dist/topic/core/index.d.ts.map +1 -0
- package/dist/topic/redis/index.d.ts.map +1 -0
- package/dist/vite/index.d.ts +10 -2
- package/dist/vite/index.d.ts.map +1 -0
- package/dist/vite/index.js +36 -14
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.d.ts.map +1 -0
- package/package.json +9 -4
- package/src/cli/apps/AlephaCli.ts +2 -0
- package/src/cli/apps/AlephaPackageBuilderCli.ts +12 -8
- package/src/cli/assets/mainTs.ts +9 -10
- package/src/cli/commands/ChangelogCommands.ts +389 -0
- package/src/cli/commands/DrizzleCommands.ts +204 -4
- package/src/cli/commands/ViteCommands.ts +26 -16
- package/src/cli/services/AlephaCliUtils.ts +23 -150
- package/src/command/providers/CliProvider.ts +76 -5
- package/src/core/providers/SchemaValidator.ts +23 -1
- package/src/mcp/errors/McpError.ts +72 -0
- package/src/mcp/helpers/jsonrpc.ts +163 -0
- package/src/mcp/index.ts +132 -0
- package/src/mcp/interfaces/McpTypes.ts +248 -0
- package/src/mcp/primitives/$prompt.ts +188 -0
- package/src/mcp/primitives/$resource.ts +171 -0
- package/src/mcp/primitives/$tool.ts +285 -0
- package/src/mcp/providers/McpServerProvider.ts +382 -0
- package/src/mcp/transports/SseMcpTransport.ts +172 -0
- package/src/mcp/transports/StdioMcpTransport.ts +126 -0
- package/src/orm/index.ts +12 -0
- package/src/orm/providers/drivers/CloudflareD1Provider.ts +164 -0
- package/src/orm/providers/drivers/NodeSqliteProvider.ts +3 -1
- package/src/vite/plugins/viteAlephaBuild.ts +8 -2
- package/src/vite/plugins/viteAlephaDev.ts +6 -2
- package/src/vite/tasks/buildServer.ts +1 -1
- package/src/vite/tasks/generateCloudflare.ts +43 -15
- package/src/vite/tasks/runAlepha.ts +1 -0
|
@@ -0,0 +1,978 @@
|
|
|
1
|
+
import { $env, $hook, $inject, $module, Alepha, AlephaError, KIND, Primitive, createPrimitive, t } from "alepha";
|
|
2
|
+
import { $logger } from "alepha/logger";
|
|
3
|
+
import { $route } from "alepha/server";
|
|
4
|
+
import * as readline from "node:readline";
|
|
5
|
+
|
|
6
|
+
//#region ../../src/mcp/helpers/jsonrpc.ts
|
|
7
|
+
const JSONRPC_VERSION = "2.0";
|
|
8
|
+
const MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
9
|
+
const JsonRpcErrorCodes = {
|
|
10
|
+
PARSE_ERROR: -32700,
|
|
11
|
+
INVALID_REQUEST: -32600,
|
|
12
|
+
METHOD_NOT_FOUND: -32601,
|
|
13
|
+
INVALID_PARAMS: -32602,
|
|
14
|
+
INTERNAL_ERROR: -32603
|
|
15
|
+
};
|
|
16
|
+
function createResponse(id, result) {
|
|
17
|
+
return {
|
|
18
|
+
jsonrpc: JSONRPC_VERSION,
|
|
19
|
+
id,
|
|
20
|
+
result
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function createErrorResponse(id, error) {
|
|
24
|
+
return {
|
|
25
|
+
jsonrpc: JSONRPC_VERSION,
|
|
26
|
+
id,
|
|
27
|
+
error
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function createNotification(method, params) {
|
|
31
|
+
return {
|
|
32
|
+
jsonrpc: JSONRPC_VERSION,
|
|
33
|
+
method,
|
|
34
|
+
params
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function createParseError(message = "Parse error") {
|
|
38
|
+
return {
|
|
39
|
+
code: JsonRpcErrorCodes.PARSE_ERROR,
|
|
40
|
+
message
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function createInvalidRequestError(message = "Invalid request") {
|
|
44
|
+
return {
|
|
45
|
+
code: JsonRpcErrorCodes.INVALID_REQUEST,
|
|
46
|
+
message
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function createMethodNotFoundError(method) {
|
|
50
|
+
return {
|
|
51
|
+
code: JsonRpcErrorCodes.METHOD_NOT_FOUND,
|
|
52
|
+
message: `Method not found: ${method}`
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function createInvalidParamsError(message) {
|
|
56
|
+
return {
|
|
57
|
+
code: JsonRpcErrorCodes.INVALID_PARAMS,
|
|
58
|
+
message
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function createInternalError(message) {
|
|
62
|
+
return {
|
|
63
|
+
code: JsonRpcErrorCodes.INTERNAL_ERROR,
|
|
64
|
+
message
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function parseMessage(data) {
|
|
68
|
+
let parsed;
|
|
69
|
+
try {
|
|
70
|
+
parsed = JSON.parse(data);
|
|
71
|
+
} catch {
|
|
72
|
+
throw new JsonRpcParseError("Invalid JSON");
|
|
73
|
+
}
|
|
74
|
+
if (!isValidJsonRpcRequest(parsed)) throw new JsonRpcParseError("Invalid JSON-RPC request");
|
|
75
|
+
return parsed;
|
|
76
|
+
}
|
|
77
|
+
function isValidJsonRpcRequest(value) {
|
|
78
|
+
if (typeof value !== "object" || value === null) return false;
|
|
79
|
+
const obj = value;
|
|
80
|
+
if (obj.jsonrpc !== JSONRPC_VERSION) return false;
|
|
81
|
+
if (typeof obj.method !== "string") return false;
|
|
82
|
+
if (obj.id !== void 0 && typeof obj.id !== "string" && typeof obj.id !== "number") return false;
|
|
83
|
+
if (obj.params !== void 0 && typeof obj.params !== "object") return false;
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
function isNotification(request) {
|
|
87
|
+
return request.id === void 0;
|
|
88
|
+
}
|
|
89
|
+
var JsonRpcParseError = class extends AlephaError {
|
|
90
|
+
name = "JsonRpcParseError";
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region ../../src/mcp/errors/McpError.ts
|
|
95
|
+
var McpError = class extends Error {
|
|
96
|
+
name = "McpError";
|
|
97
|
+
code;
|
|
98
|
+
constructor(message, code = JsonRpcErrorCodes.INTERNAL_ERROR) {
|
|
99
|
+
super(message);
|
|
100
|
+
this.code = code;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
var McpMethodNotFoundError = class extends McpError {
|
|
104
|
+
name = "McpMethodNotFoundError";
|
|
105
|
+
constructor(method) {
|
|
106
|
+
super(`Method not found: ${method}`, JsonRpcErrorCodes.METHOD_NOT_FOUND);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
var McpToolNotFoundError = class extends McpError {
|
|
110
|
+
name = "McpToolNotFoundError";
|
|
111
|
+
tool;
|
|
112
|
+
constructor(tool) {
|
|
113
|
+
super(`Tool not found: ${tool}`, JsonRpcErrorCodes.METHOD_NOT_FOUND);
|
|
114
|
+
this.tool = tool;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
var McpResourceNotFoundError = class extends McpError {
|
|
118
|
+
name = "McpResourceNotFoundError";
|
|
119
|
+
uri;
|
|
120
|
+
constructor(uri) {
|
|
121
|
+
super(`Resource not found: ${uri}`, JsonRpcErrorCodes.METHOD_NOT_FOUND);
|
|
122
|
+
this.uri = uri;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
var McpPromptNotFoundError = class extends McpError {
|
|
126
|
+
name = "McpPromptNotFoundError";
|
|
127
|
+
prompt;
|
|
128
|
+
constructor(prompt) {
|
|
129
|
+
super(`Prompt not found: ${prompt}`, JsonRpcErrorCodes.METHOD_NOT_FOUND);
|
|
130
|
+
this.prompt = prompt;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
var McpInvalidParamsError = class extends McpError {
|
|
134
|
+
name = "McpInvalidParamsError";
|
|
135
|
+
constructor(message) {
|
|
136
|
+
super(message, JsonRpcErrorCodes.INVALID_PARAMS);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
//#endregion
|
|
141
|
+
//#region ../../src/mcp/providers/McpServerProvider.ts
|
|
142
|
+
/**
|
|
143
|
+
* Core MCP server provider that handles protocol messages.
|
|
144
|
+
*
|
|
145
|
+
* This provider maintains registries of tools, resources, and prompts,
|
|
146
|
+
* and routes incoming JSON-RPC requests to the appropriate handlers.
|
|
147
|
+
*
|
|
148
|
+
* It is transport-agnostic - actual communication is handled by
|
|
149
|
+
* transport providers like StdioMcpTransport or SseMcpTransport.
|
|
150
|
+
*/
|
|
151
|
+
var McpServerProvider = class {
|
|
152
|
+
log = $logger();
|
|
153
|
+
alepha = $inject(Alepha);
|
|
154
|
+
tools = /* @__PURE__ */ new Map();
|
|
155
|
+
resources = /* @__PURE__ */ new Map();
|
|
156
|
+
prompts = /* @__PURE__ */ new Map();
|
|
157
|
+
initialized = false;
|
|
158
|
+
serverInfo = {
|
|
159
|
+
name: "alepha-mcp",
|
|
160
|
+
version: "1.0.0"
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* Register a tool with the MCP server.
|
|
164
|
+
*/
|
|
165
|
+
registerTool(tool) {
|
|
166
|
+
this.log.trace(`Registering MCP tool: ${tool.name}`);
|
|
167
|
+
this.tools.set(tool.name, tool);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Register a resource with the MCP server.
|
|
171
|
+
*/
|
|
172
|
+
registerResource(resource) {
|
|
173
|
+
this.log.trace(`Registering MCP resource: ${resource.uri}`);
|
|
174
|
+
this.resources.set(resource.uri, resource);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Register a prompt with the MCP server.
|
|
178
|
+
*/
|
|
179
|
+
registerPrompt(prompt) {
|
|
180
|
+
this.log.trace(`Registering MCP prompt: ${prompt.name}`);
|
|
181
|
+
this.prompts.set(prompt.name, prompt);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Get the server capabilities based on registered primitives.
|
|
185
|
+
*/
|
|
186
|
+
getCapabilities() {
|
|
187
|
+
return {
|
|
188
|
+
tools: this.tools.size > 0 ? {} : void 0,
|
|
189
|
+
resources: this.resources.size > 0 ? {} : void 0,
|
|
190
|
+
prompts: this.prompts.size > 0 ? {} : void 0
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Get all registered tools.
|
|
195
|
+
*/
|
|
196
|
+
getTools() {
|
|
197
|
+
return Array.from(this.tools.values());
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Get all registered resources.
|
|
201
|
+
*/
|
|
202
|
+
getResources() {
|
|
203
|
+
return Array.from(this.resources.values());
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Get all registered prompts.
|
|
207
|
+
*/
|
|
208
|
+
getPrompts() {
|
|
209
|
+
return Array.from(this.prompts.values());
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Get a tool by name.
|
|
213
|
+
*/
|
|
214
|
+
getTool(name) {
|
|
215
|
+
return this.tools.get(name);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Get a resource by URI.
|
|
219
|
+
*/
|
|
220
|
+
getResource(uri) {
|
|
221
|
+
return this.resources.get(uri);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Get a prompt by name.
|
|
225
|
+
*/
|
|
226
|
+
getPrompt(name) {
|
|
227
|
+
return this.prompts.get(name);
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Handle an incoming JSON-RPC request.
|
|
231
|
+
*
|
|
232
|
+
* @param request - The parsed JSON-RPC request
|
|
233
|
+
* @param context - Optional context from the transport layer (headers, auth, etc.)
|
|
234
|
+
* @returns The JSON-RPC response, or null for notifications
|
|
235
|
+
*/
|
|
236
|
+
async handleMessage(request, context) {
|
|
237
|
+
const id = request.id;
|
|
238
|
+
if (id === void 0) {
|
|
239
|
+
await this.handleNotification(request);
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
try {
|
|
243
|
+
return createResponse(id, await this.handleRequest(request, context));
|
|
244
|
+
} catch (error) {
|
|
245
|
+
this.log.error("MCP request failed", error);
|
|
246
|
+
if (error instanceof McpError) return createErrorResponse(id, {
|
|
247
|
+
code: error.code,
|
|
248
|
+
message: error.message
|
|
249
|
+
});
|
|
250
|
+
return createErrorResponse(id, createInternalError(error.message));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Handle a JSON-RPC request that expects a response.
|
|
255
|
+
*/
|
|
256
|
+
async handleRequest(request, context) {
|
|
257
|
+
const { method, params = {} } = request;
|
|
258
|
+
switch (method) {
|
|
259
|
+
case "initialize": return this.handleInitialize(params);
|
|
260
|
+
case "ping": return this.handlePing();
|
|
261
|
+
case "tools/list": return this.handleToolsList();
|
|
262
|
+
case "tools/call": return this.handleToolsCall(params, context);
|
|
263
|
+
case "resources/list": return this.handleResourcesList();
|
|
264
|
+
case "resources/read": return this.handleResourcesRead(params, context);
|
|
265
|
+
case "prompts/list": return this.handlePromptsList();
|
|
266
|
+
case "prompts/get": return this.handlePromptsGet(params, context);
|
|
267
|
+
default: throw new McpMethodNotFoundError(method);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Handle a notification (no response expected).
|
|
272
|
+
*/
|
|
273
|
+
async handleNotification(request) {
|
|
274
|
+
const { method } = request;
|
|
275
|
+
switch (method) {
|
|
276
|
+
case "notifications/initialized":
|
|
277
|
+
this.log.debug("MCP client initialized");
|
|
278
|
+
break;
|
|
279
|
+
case "notifications/cancelled":
|
|
280
|
+
this.log.debug("MCP request cancelled", request.params);
|
|
281
|
+
break;
|
|
282
|
+
default: this.log.debug(`Unknown MCP notification: ${method}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
handleInitialize(params) {
|
|
286
|
+
this.log.info("MCP client initializing", {
|
|
287
|
+
clientInfo: params.clientInfo,
|
|
288
|
+
protocolVersion: params.protocolVersion
|
|
289
|
+
});
|
|
290
|
+
this.initialized = true;
|
|
291
|
+
return {
|
|
292
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
293
|
+
capabilities: this.getCapabilities(),
|
|
294
|
+
serverInfo: this.serverInfo
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
handlePing() {
|
|
298
|
+
return {};
|
|
299
|
+
}
|
|
300
|
+
handleToolsList() {
|
|
301
|
+
return { tools: Array.from(this.tools.values()).map((t$1) => t$1.toDescriptor()) };
|
|
302
|
+
}
|
|
303
|
+
async handleToolsCall(params, context) {
|
|
304
|
+
const name = params.name;
|
|
305
|
+
const args = params.arguments ?? {};
|
|
306
|
+
const tool = this.tools.get(name);
|
|
307
|
+
if (!tool) throw new McpToolNotFoundError(name);
|
|
308
|
+
try {
|
|
309
|
+
const result = await tool.execute(args, context);
|
|
310
|
+
return { content: [{
|
|
311
|
+
type: "text",
|
|
312
|
+
text: typeof result === "string" ? result : JSON.stringify(result ?? null)
|
|
313
|
+
}] };
|
|
314
|
+
} catch (error) {
|
|
315
|
+
return {
|
|
316
|
+
content: [{
|
|
317
|
+
type: "text",
|
|
318
|
+
text: `Error: ${error.message}`
|
|
319
|
+
}],
|
|
320
|
+
isError: true
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
handleResourcesList() {
|
|
325
|
+
return { resources: Array.from(this.resources.values()).map((r) => r.toDescriptor()) };
|
|
326
|
+
}
|
|
327
|
+
async handleResourcesRead(params, context) {
|
|
328
|
+
const uri = params.uri;
|
|
329
|
+
const resource = this.resources.get(uri);
|
|
330
|
+
if (!resource) throw new McpResourceNotFoundError(uri);
|
|
331
|
+
const content = await resource.read(context);
|
|
332
|
+
const resourceContent = {
|
|
333
|
+
uri,
|
|
334
|
+
mimeType: resource.mimeType
|
|
335
|
+
};
|
|
336
|
+
if (content.text !== void 0) resourceContent.text = content.text;
|
|
337
|
+
if (content.blob !== void 0) resourceContent.blob = Buffer.from(content.blob).toString("base64");
|
|
338
|
+
return { contents: [resourceContent] };
|
|
339
|
+
}
|
|
340
|
+
handlePromptsList() {
|
|
341
|
+
return { prompts: Array.from(this.prompts.values()).map((p) => p.toDescriptor()) };
|
|
342
|
+
}
|
|
343
|
+
async handlePromptsGet(params, context) {
|
|
344
|
+
const name = params.name;
|
|
345
|
+
const args = params.arguments ?? {};
|
|
346
|
+
const prompt = this.prompts.get(name);
|
|
347
|
+
if (!prompt) throw new McpPromptNotFoundError(name);
|
|
348
|
+
const mcpMessages = (await prompt.get(args, context)).map((msg) => ({
|
|
349
|
+
role: msg.role,
|
|
350
|
+
content: {
|
|
351
|
+
type: "text",
|
|
352
|
+
text: msg.content
|
|
353
|
+
}
|
|
354
|
+
}));
|
|
355
|
+
return {
|
|
356
|
+
description: prompt.description,
|
|
357
|
+
messages: mcpMessages
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
//#endregion
|
|
363
|
+
//#region ../../src/mcp/primitives/$prompt.ts
|
|
364
|
+
/**
|
|
365
|
+
* Creates an MCP prompt primitive for defining reusable prompt templates.
|
|
366
|
+
*
|
|
367
|
+
* Prompts allow you to define templated messages that can be filled in
|
|
368
|
+
* with arguments at runtime. They're useful for creating consistent
|
|
369
|
+
* interaction patterns.
|
|
370
|
+
*
|
|
371
|
+
* @example
|
|
372
|
+
* ```ts
|
|
373
|
+
* class Prompts {
|
|
374
|
+
* greeting = $prompt({
|
|
375
|
+
* description: "Generate a personalized greeting",
|
|
376
|
+
* args: t.object({
|
|
377
|
+
* name: t.text({ description: "Name of the person to greet" }),
|
|
378
|
+
* style: t.optional(t.enum(["formal", "casual"])),
|
|
379
|
+
* }),
|
|
380
|
+
* handler: async ({ args }) => [
|
|
381
|
+
* {
|
|
382
|
+
* role: "user",
|
|
383
|
+
* content: args.style === "formal"
|
|
384
|
+
* ? `Please greet ${args.name} in a formal manner.`
|
|
385
|
+
* : `Say hi to ${args.name}!`,
|
|
386
|
+
* },
|
|
387
|
+
* ],
|
|
388
|
+
* });
|
|
389
|
+
*
|
|
390
|
+
* codeReview = $prompt({
|
|
391
|
+
* description: "Request a code review",
|
|
392
|
+
* args: t.object({
|
|
393
|
+
* code: t.text({ description: "The code to review" }),
|
|
394
|
+
* language: t.text({ description: "Programming language" }),
|
|
395
|
+
* }),
|
|
396
|
+
* handler: async ({ args }) => [
|
|
397
|
+
* {
|
|
398
|
+
* role: "user",
|
|
399
|
+
* content: `Please review this ${args.language} code:\n\n${args.code}`,
|
|
400
|
+
* },
|
|
401
|
+
* ],
|
|
402
|
+
* });
|
|
403
|
+
* }
|
|
404
|
+
* ```
|
|
405
|
+
*/
|
|
406
|
+
const $prompt = (options) => {
|
|
407
|
+
return createPrimitive(PromptPrimitive, options);
|
|
408
|
+
};
|
|
409
|
+
var PromptPrimitive = class extends Primitive {
|
|
410
|
+
mcpServer = $inject(McpServerProvider);
|
|
411
|
+
/**
|
|
412
|
+
* Returns the name of the prompt.
|
|
413
|
+
*/
|
|
414
|
+
get name() {
|
|
415
|
+
return this.options.name ?? this.config.propertyKey;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Returns the description of the prompt.
|
|
419
|
+
*/
|
|
420
|
+
get description() {
|
|
421
|
+
return this.options.description;
|
|
422
|
+
}
|
|
423
|
+
onInit() {
|
|
424
|
+
this.mcpServer.registerPrompt(this);
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Get the prompt messages with the given arguments.
|
|
428
|
+
*
|
|
429
|
+
* @param rawArgs - Raw arguments to validate and pass to the handler
|
|
430
|
+
* @param context - Optional context from the transport layer
|
|
431
|
+
* @returns Array of prompt messages
|
|
432
|
+
*/
|
|
433
|
+
async get(rawArgs, context) {
|
|
434
|
+
let args = rawArgs ?? {};
|
|
435
|
+
if (this.options.args) args = this.alepha.codec.decode(this.options.args, rawArgs ?? {});
|
|
436
|
+
return this.options.handler({
|
|
437
|
+
args,
|
|
438
|
+
context
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Convert the prompt to an MCP prompt descriptor for protocol messages.
|
|
443
|
+
*/
|
|
444
|
+
toDescriptor() {
|
|
445
|
+
return {
|
|
446
|
+
name: this.name,
|
|
447
|
+
description: this.description,
|
|
448
|
+
arguments: this.options.args ? this.schemaToArguments(this.options.args) : []
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Convert a TypeBox schema to an array of prompt arguments.
|
|
453
|
+
*/
|
|
454
|
+
schemaToArguments(schema) {
|
|
455
|
+
const args = [];
|
|
456
|
+
for (const [name, propSchema] of Object.entries(schema.properties)) {
|
|
457
|
+
const prop = propSchema;
|
|
458
|
+
args.push({
|
|
459
|
+
name,
|
|
460
|
+
description: prop.description,
|
|
461
|
+
required: !t.schema.isOptional(propSchema)
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
return args;
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
$prompt[KIND] = PromptPrimitive;
|
|
468
|
+
|
|
469
|
+
//#endregion
|
|
470
|
+
//#region ../../src/mcp/primitives/$resource.ts
|
|
471
|
+
/**
|
|
472
|
+
* Creates an MCP resource primitive for exposing read-only data.
|
|
473
|
+
*
|
|
474
|
+
* Resources represent any kind of data that an LLM might want to read,
|
|
475
|
+
* such as files, database records, API responses, or computed data.
|
|
476
|
+
*
|
|
477
|
+
* **Key Features**
|
|
478
|
+
* - URI-based identification for resources
|
|
479
|
+
* - Support for text and binary content
|
|
480
|
+
* - MIME type specification
|
|
481
|
+
* - Lazy loading via handler function
|
|
482
|
+
*
|
|
483
|
+
* @example
|
|
484
|
+
* ```ts
|
|
485
|
+
* class ProjectResources {
|
|
486
|
+
* readme = $resource({
|
|
487
|
+
* uri: "file:///readme",
|
|
488
|
+
* description: "Project README file",
|
|
489
|
+
* mimeType: "text/markdown",
|
|
490
|
+
* handler: async () => ({
|
|
491
|
+
* text: await fs.readFile("README.md", "utf-8"),
|
|
492
|
+
* }),
|
|
493
|
+
* });
|
|
494
|
+
*
|
|
495
|
+
* config = $resource({
|
|
496
|
+
* uri: "config://app",
|
|
497
|
+
* name: "Application Configuration",
|
|
498
|
+
* mimeType: "application/json",
|
|
499
|
+
* handler: async () => ({
|
|
500
|
+
* text: JSON.stringify(this.configService.getConfig()),
|
|
501
|
+
* }),
|
|
502
|
+
* });
|
|
503
|
+
* }
|
|
504
|
+
* ```
|
|
505
|
+
*/
|
|
506
|
+
const $resource = (options) => {
|
|
507
|
+
return createPrimitive(ResourcePrimitive, options);
|
|
508
|
+
};
|
|
509
|
+
var ResourcePrimitive = class extends Primitive {
|
|
510
|
+
mcpServer = $inject(McpServerProvider);
|
|
511
|
+
/**
|
|
512
|
+
* Returns the name of the resource.
|
|
513
|
+
*/
|
|
514
|
+
get name() {
|
|
515
|
+
return this.options.name ?? this.config.propertyKey;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Returns the URI of the resource.
|
|
519
|
+
*/
|
|
520
|
+
get uri() {
|
|
521
|
+
return this.options.uri;
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Returns the description of the resource.
|
|
525
|
+
*/
|
|
526
|
+
get description() {
|
|
527
|
+
return this.options.description;
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Returns the MIME type of the resource.
|
|
531
|
+
*/
|
|
532
|
+
get mimeType() {
|
|
533
|
+
return this.options.mimeType ?? "text/plain";
|
|
534
|
+
}
|
|
535
|
+
onInit() {
|
|
536
|
+
this.mcpServer.registerResource(this);
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Read the resource content.
|
|
540
|
+
*
|
|
541
|
+
* @param context - Optional context from the transport layer
|
|
542
|
+
* @returns The resource content
|
|
543
|
+
*/
|
|
544
|
+
async read(context) {
|
|
545
|
+
return this.options.handler({ context });
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Convert the resource to an MCP resource descriptor for protocol messages.
|
|
549
|
+
*/
|
|
550
|
+
toDescriptor() {
|
|
551
|
+
return {
|
|
552
|
+
uri: this.uri,
|
|
553
|
+
name: this.name,
|
|
554
|
+
description: this.description,
|
|
555
|
+
mimeType: this.mimeType
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
$resource[KIND] = ResourcePrimitive;
|
|
560
|
+
|
|
561
|
+
//#endregion
|
|
562
|
+
//#region ../../src/mcp/primitives/$tool.ts
|
|
563
|
+
/**
|
|
564
|
+
* Creates an MCP tool primitive for defining callable functions.
|
|
565
|
+
*
|
|
566
|
+
* Tools are the primary way for LLMs to interact with external systems through MCP.
|
|
567
|
+
* Each tool has a name, description, typed parameters, and a handler function.
|
|
568
|
+
*
|
|
569
|
+
* **Key Features**
|
|
570
|
+
* - Full TypeScript inference for parameters and results
|
|
571
|
+
* - Automatic schema validation using TypeBox
|
|
572
|
+
* - JSON Schema generation for MCP protocol
|
|
573
|
+
* - Integration with MCP server provider
|
|
574
|
+
*
|
|
575
|
+
* @example
|
|
576
|
+
* ```ts
|
|
577
|
+
* class CalculatorTools {
|
|
578
|
+
* add = $tool({
|
|
579
|
+
* description: "Add two numbers together",
|
|
580
|
+
* schema: {
|
|
581
|
+
* params: t.object({
|
|
582
|
+
* a: t.number(),
|
|
583
|
+
* b: t.number(),
|
|
584
|
+
* }),
|
|
585
|
+
* result: t.number(),
|
|
586
|
+
* },
|
|
587
|
+
* handler: async ({ params }) => {
|
|
588
|
+
* return params.a + params.b;
|
|
589
|
+
* },
|
|
590
|
+
* });
|
|
591
|
+
*
|
|
592
|
+
* greet = $tool({
|
|
593
|
+
* description: "Generate a greeting message",
|
|
594
|
+
* schema: {
|
|
595
|
+
* params: t.object({
|
|
596
|
+
* name: t.text(),
|
|
597
|
+
* }),
|
|
598
|
+
* result: t.text(),
|
|
599
|
+
* },
|
|
600
|
+
* handler: async ({ params }) => {
|
|
601
|
+
* return `Hello, ${params.name}!`;
|
|
602
|
+
* },
|
|
603
|
+
* });
|
|
604
|
+
* }
|
|
605
|
+
* ```
|
|
606
|
+
*/
|
|
607
|
+
const $tool = (options) => {
|
|
608
|
+
return createPrimitive(ToolPrimitive, options);
|
|
609
|
+
};
|
|
610
|
+
var ToolPrimitive = class extends Primitive {
|
|
611
|
+
mcpServer = $inject(McpServerProvider);
|
|
612
|
+
/**
|
|
613
|
+
* Returns the name of the tool.
|
|
614
|
+
*/
|
|
615
|
+
get name() {
|
|
616
|
+
return this.options.name ?? this.config.propertyKey;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Returns the description of the tool.
|
|
620
|
+
*/
|
|
621
|
+
get description() {
|
|
622
|
+
return this.options.description;
|
|
623
|
+
}
|
|
624
|
+
onInit() {
|
|
625
|
+
this.mcpServer.registerTool(this);
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Execute the tool with the given parameters.
|
|
629
|
+
*
|
|
630
|
+
* @param params - Raw parameters to validate and pass to the handler
|
|
631
|
+
* @param context - Optional context from the transport layer
|
|
632
|
+
* @returns The tool result
|
|
633
|
+
*/
|
|
634
|
+
async execute(params, context) {
|
|
635
|
+
let validatedParams = params ?? {};
|
|
636
|
+
if (this.options.schema?.params) validatedParams = this.alepha.codec.decode(this.options.schema.params, validatedParams);
|
|
637
|
+
const result = await this.options.handler({
|
|
638
|
+
params: validatedParams,
|
|
639
|
+
context
|
|
640
|
+
});
|
|
641
|
+
if (this.options.schema?.result && result !== void 0) return this.alepha.codec.encode(this.options.schema.result, result);
|
|
642
|
+
return result;
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Convert the tool to an MCP tool descriptor for protocol messages.
|
|
646
|
+
*/
|
|
647
|
+
toDescriptor() {
|
|
648
|
+
return {
|
|
649
|
+
name: this.name,
|
|
650
|
+
description: this.description,
|
|
651
|
+
inputSchema: this.options.schema?.params ? this.schemaToJsonSchema(this.options.schema.params) : {
|
|
652
|
+
type: "object",
|
|
653
|
+
properties: {},
|
|
654
|
+
required: []
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Convert a TypeBox schema to JSON Schema format.
|
|
660
|
+
*/
|
|
661
|
+
schemaToJsonSchema(schema) {
|
|
662
|
+
const properties = {};
|
|
663
|
+
const required = [];
|
|
664
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
665
|
+
properties[key] = this.propertyToJsonSchema(propSchema);
|
|
666
|
+
if (!t.schema.isOptional(propSchema)) required.push(key);
|
|
667
|
+
}
|
|
668
|
+
return {
|
|
669
|
+
type: "object",
|
|
670
|
+
properties,
|
|
671
|
+
required
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Convert a single property schema to JSON Schema format.
|
|
676
|
+
*/
|
|
677
|
+
propertyToJsonSchema(schema) {
|
|
678
|
+
const result = {};
|
|
679
|
+
if ("description" in schema) result.description = schema.description;
|
|
680
|
+
if (t.schema.isString(schema)) {
|
|
681
|
+
result.type = "string";
|
|
682
|
+
if ("minLength" in schema) result.minLength = schema.minLength;
|
|
683
|
+
if ("maxLength" in schema) result.maxLength = schema.maxLength;
|
|
684
|
+
if ("pattern" in schema) result.pattern = schema.pattern;
|
|
685
|
+
if ("enum" in schema) result.enum = schema.enum;
|
|
686
|
+
} else if (t.schema.isNumber(schema)) {
|
|
687
|
+
result.type = "number";
|
|
688
|
+
if ("minimum" in schema) result.minimum = schema.minimum;
|
|
689
|
+
if ("maximum" in schema) result.maximum = schema.maximum;
|
|
690
|
+
} else if (t.schema.isInteger(schema)) {
|
|
691
|
+
result.type = "integer";
|
|
692
|
+
if ("minimum" in schema) result.minimum = schema.minimum;
|
|
693
|
+
if ("maximum" in schema) result.maximum = schema.maximum;
|
|
694
|
+
} else if (t.schema.isBoolean(schema)) result.type = "boolean";
|
|
695
|
+
else if (t.schema.isArray(schema)) {
|
|
696
|
+
result.type = "array";
|
|
697
|
+
if ("items" in schema) result.items = this.propertyToJsonSchema(schema.items);
|
|
698
|
+
} else if (t.schema.isObject(schema)) Object.assign(result, this.schemaToJsonSchema(schema));
|
|
699
|
+
else if (t.schema.isUnsafe(schema) || t.schema.isOptional(schema)) {
|
|
700
|
+
const schemaAny = schema;
|
|
701
|
+
if (schemaAny.type === "string") {
|
|
702
|
+
result.type = "string";
|
|
703
|
+
if ("enum" in schema) result.enum = schema.enum;
|
|
704
|
+
if ("pattern" in schema) result.pattern = schema.pattern;
|
|
705
|
+
} else if (schemaAny.type === "number") result.type = "number";
|
|
706
|
+
else if (schemaAny.type === "integer") result.type = "integer";
|
|
707
|
+
else if (schemaAny.type === "boolean") result.type = "boolean";
|
|
708
|
+
else if (schemaAny.type === "array") result.type = "array";
|
|
709
|
+
else if (schemaAny.type === "object") result.type = "object";
|
|
710
|
+
else result.type = "string";
|
|
711
|
+
} else result.type = "string";
|
|
712
|
+
return result;
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
$tool[KIND] = ToolPrimitive;
|
|
716
|
+
|
|
717
|
+
//#endregion
|
|
718
|
+
//#region ../../src/mcp/transports/SseMcpTransport.ts
|
|
719
|
+
const envSchema = t.object({ MCP_SSE_PATH: t.text({
|
|
720
|
+
description: "Path for MCP SSE endpoint",
|
|
721
|
+
default: "/mcp"
|
|
722
|
+
}) });
|
|
723
|
+
/**
|
|
724
|
+
* SSE (Server-Sent Events) transport for MCP communication.
|
|
725
|
+
*
|
|
726
|
+
* This transport uses HTTP with SSE for server-to-client messages
|
|
727
|
+
* and POST requests for client-to-server messages.
|
|
728
|
+
*
|
|
729
|
+
* Endpoints:
|
|
730
|
+
* - GET /mcp - SSE stream for server events
|
|
731
|
+
* - POST /mcp - JSON-RPC request endpoint
|
|
732
|
+
*
|
|
733
|
+
* @example
|
|
734
|
+
* ```ts
|
|
735
|
+
* import { Alepha, run } from "alepha";
|
|
736
|
+
* import { AlephaServer } from "alepha/server";
|
|
737
|
+
* import { AlephaMcp, AlephaMcpSse } from "alepha/mcp";
|
|
738
|
+
*
|
|
739
|
+
* class MyTools {
|
|
740
|
+
* // ... tool definitions
|
|
741
|
+
* }
|
|
742
|
+
*
|
|
743
|
+
* run(
|
|
744
|
+
* Alepha.create()
|
|
745
|
+
* .with(AlephaServer)
|
|
746
|
+
* .with(AlephaMcp)
|
|
747
|
+
* .with(AlephaMcpSse)
|
|
748
|
+
* .with(MyTools)
|
|
749
|
+
* );
|
|
750
|
+
* ```
|
|
751
|
+
*/
|
|
752
|
+
var SseMcpTransport = class {
|
|
753
|
+
log = $logger();
|
|
754
|
+
env = $env(envSchema);
|
|
755
|
+
mcpServer = $inject(McpServerProvider);
|
|
756
|
+
/**
|
|
757
|
+
* SSE endpoint for server-to-client messages.
|
|
758
|
+
*
|
|
759
|
+
* Returns a text/event-stream response with server capabilities
|
|
760
|
+
* and keeps the connection open for notifications.
|
|
761
|
+
*/
|
|
762
|
+
sse = $route({
|
|
763
|
+
method: "GET",
|
|
764
|
+
path: this.env.MCP_SSE_PATH,
|
|
765
|
+
handler: async (request) => {
|
|
766
|
+
this.log.debug("MCP SSE connection established");
|
|
767
|
+
const encoder = new TextEncoder();
|
|
768
|
+
const stream = new ReadableStream({
|
|
769
|
+
start: (controller) => {
|
|
770
|
+
const endpointEvent = this.formatSseEvent("endpoint", `${this.env.MCP_SSE_PATH}`);
|
|
771
|
+
controller.enqueue(encoder.encode(endpointEvent));
|
|
772
|
+
const capabilitiesNotification = createNotification("notifications/capabilities", { capabilities: this.mcpServer.getCapabilities() });
|
|
773
|
+
const capabilitiesEvent = this.formatSseEvent("message", JSON.stringify(capabilitiesNotification));
|
|
774
|
+
controller.enqueue(encoder.encode(capabilitiesEvent));
|
|
775
|
+
},
|
|
776
|
+
cancel: () => {
|
|
777
|
+
this.log.debug("MCP SSE connection closed");
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
request.reply.status = 200;
|
|
781
|
+
request.reply.headers = {
|
|
782
|
+
"content-type": "text/event-stream",
|
|
783
|
+
"cache-control": "no-cache",
|
|
784
|
+
connection: "keep-alive"
|
|
785
|
+
};
|
|
786
|
+
request.reply.body = stream;
|
|
787
|
+
}
|
|
788
|
+
});
|
|
789
|
+
/**
|
|
790
|
+
* POST endpoint for client-to-server JSON-RPC messages.
|
|
791
|
+
*/
|
|
792
|
+
message = $route({
|
|
793
|
+
method: "POST",
|
|
794
|
+
path: this.env.MCP_SSE_PATH,
|
|
795
|
+
secure: false,
|
|
796
|
+
schema: { body: t.json() },
|
|
797
|
+
handler: async (request) => {
|
|
798
|
+
try {
|
|
799
|
+
const body = typeof request.body === "string" ? request.body : JSON.stringify(request.body);
|
|
800
|
+
this.log.debug("MCP request body", {
|
|
801
|
+
body,
|
|
802
|
+
bodyType: typeof request.body
|
|
803
|
+
});
|
|
804
|
+
const rpcRequest = parseMessage(body);
|
|
805
|
+
const context = { headers: request.headers };
|
|
806
|
+
const response = await this.mcpServer.handleMessage(rpcRequest, context);
|
|
807
|
+
request.reply.headers["content-type"] = "application/json";
|
|
808
|
+
request.reply.body = response ? JSON.stringify(response) : "";
|
|
809
|
+
} catch (error) {
|
|
810
|
+
if (error instanceof JsonRpcParseError) {
|
|
811
|
+
request.reply.status = 400;
|
|
812
|
+
request.reply.headers["content-type"] = "application/json";
|
|
813
|
+
request.reply.body = JSON.stringify(createErrorResponse(0, createParseError(error.message)));
|
|
814
|
+
} else {
|
|
815
|
+
this.log.error("Failed to process MCP message", error);
|
|
816
|
+
request.reply.status = 500;
|
|
817
|
+
request.reply.body = JSON.stringify({ error: error.message });
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
/**
|
|
823
|
+
* Format a message as an SSE event.
|
|
824
|
+
*/
|
|
825
|
+
formatSseEvent(event, data) {
|
|
826
|
+
return `event: ${event}\ndata: ${data}\n\n`;
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
|
|
830
|
+
//#endregion
|
|
831
|
+
//#region ../../src/mcp/transports/StdioMcpTransport.ts
|
|
832
|
+
/**
|
|
833
|
+
* Stdio transport for MCP communication.
|
|
834
|
+
*
|
|
835
|
+
* This transport uses stdin/stdout for JSON-RPC message exchange,
|
|
836
|
+
* which is the standard transport for local MCP servers.
|
|
837
|
+
*
|
|
838
|
+
* Messages are newline-delimited JSON objects.
|
|
839
|
+
*
|
|
840
|
+
* @example
|
|
841
|
+
* ```ts
|
|
842
|
+
* import { Alepha, run } from "alepha";
|
|
843
|
+
* import { AlephaMcp, AlephaMcpStdio } from "alepha/mcp";
|
|
844
|
+
*
|
|
845
|
+
* class MyTools {
|
|
846
|
+
* // ... tool definitions
|
|
847
|
+
* }
|
|
848
|
+
*
|
|
849
|
+
* run(
|
|
850
|
+
* Alepha.create()
|
|
851
|
+
* .with(AlephaMcp)
|
|
852
|
+
* .with(AlephaMcpStdio)
|
|
853
|
+
* .with(MyTools)
|
|
854
|
+
* );
|
|
855
|
+
* ```
|
|
856
|
+
*/
|
|
857
|
+
var StdioMcpTransport = class {
|
|
858
|
+
log = $logger();
|
|
859
|
+
mcpServer = $inject(McpServerProvider);
|
|
860
|
+
rl;
|
|
861
|
+
started = false;
|
|
862
|
+
onStart = $hook({
|
|
863
|
+
on: "start",
|
|
864
|
+
handler: () => this.start()
|
|
865
|
+
});
|
|
866
|
+
onStop = $hook({
|
|
867
|
+
on: "stop",
|
|
868
|
+
handler: () => this.stop()
|
|
869
|
+
});
|
|
870
|
+
/**
|
|
871
|
+
* Start the stdio transport.
|
|
872
|
+
*/
|
|
873
|
+
start() {
|
|
874
|
+
if (this.started) return;
|
|
875
|
+
this.started = true;
|
|
876
|
+
this.rl = readline.createInterface({
|
|
877
|
+
input: process.stdin,
|
|
878
|
+
output: process.stdout,
|
|
879
|
+
terminal: false
|
|
880
|
+
});
|
|
881
|
+
this.rl.on("line", (line) => this.handleLine(line));
|
|
882
|
+
this.rl.on("close", () => this.handleClose());
|
|
883
|
+
this.log.info("MCP stdio transport started");
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* Stop the stdio transport.
|
|
887
|
+
*/
|
|
888
|
+
stop() {
|
|
889
|
+
if (!this.started) return;
|
|
890
|
+
this.started = false;
|
|
891
|
+
this.rl?.close();
|
|
892
|
+
this.rl = void 0;
|
|
893
|
+
this.log.info("MCP stdio transport stopped");
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Handle an incoming line from stdin.
|
|
897
|
+
*/
|
|
898
|
+
async handleLine(line) {
|
|
899
|
+
if (!line.trim()) return;
|
|
900
|
+
try {
|
|
901
|
+
const request = parseMessage(line);
|
|
902
|
+
const response = await this.mcpServer.handleMessage(request);
|
|
903
|
+
if (response) this.send(response);
|
|
904
|
+
} catch (error) {
|
|
905
|
+
if (error instanceof JsonRpcParseError) this.send(createErrorResponse(0, createParseError(error.message)));
|
|
906
|
+
else this.log.error("Failed to process MCP message", error);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* Handle stdin close event.
|
|
911
|
+
*/
|
|
912
|
+
handleClose() {
|
|
913
|
+
this.log.debug("MCP stdio input closed");
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* Send a message to stdout.
|
|
917
|
+
*/
|
|
918
|
+
send(message) {
|
|
919
|
+
const json = JSON.stringify(message);
|
|
920
|
+
process.stdout.write(json + "\n");
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
|
|
924
|
+
//#endregion
|
|
925
|
+
//#region ../../src/mcp/index.ts
|
|
926
|
+
/**
|
|
927
|
+
* Core MCP module with primitives and server provider.
|
|
928
|
+
*
|
|
929
|
+
* This module registers the $tool, $resource, and $prompt primitives
|
|
930
|
+
* and the McpServerProvider. You need to add a transport module
|
|
931
|
+
* (AlephaMcpStdio or AlephaMcpSse) for actual communication.
|
|
932
|
+
*
|
|
933
|
+
* @example
|
|
934
|
+
* ```ts
|
|
935
|
+
* import { Alepha, run } from "alepha";
|
|
936
|
+
* import { AlephaMcp, AlephaMcpStdio, $tool, t } from "alepha/mcp";
|
|
937
|
+
*
|
|
938
|
+
* class MyMcpServer {
|
|
939
|
+
* add = $tool({
|
|
940
|
+
* description: "Add two numbers",
|
|
941
|
+
* schema: {
|
|
942
|
+
* params: t.object({ a: t.number(), b: t.number() }),
|
|
943
|
+
* result: t.number(),
|
|
944
|
+
* },
|
|
945
|
+
* handler: async ({ params }) => params.a + params.b,
|
|
946
|
+
* });
|
|
947
|
+
* }
|
|
948
|
+
*
|
|
949
|
+
* run(
|
|
950
|
+
* Alepha.create()
|
|
951
|
+
* .with(AlephaMcp)
|
|
952
|
+
* .with(AlephaMcpStdio)
|
|
953
|
+
* .with(MyMcpServer)
|
|
954
|
+
* );
|
|
955
|
+
* ```
|
|
956
|
+
*
|
|
957
|
+
* @module alepha.mcp
|
|
958
|
+
*/
|
|
959
|
+
const AlephaMcp = $module({
|
|
960
|
+
name: "alepha.mcp",
|
|
961
|
+
primitives: [
|
|
962
|
+
$tool,
|
|
963
|
+
$resource,
|
|
964
|
+
$prompt
|
|
965
|
+
],
|
|
966
|
+
services: [
|
|
967
|
+
McpServerProvider,
|
|
968
|
+
SseMcpTransport,
|
|
969
|
+
StdioMcpTransport
|
|
970
|
+
],
|
|
971
|
+
register: (alepha) => {
|
|
972
|
+
alepha.with(McpServerProvider);
|
|
973
|
+
}
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
//#endregion
|
|
977
|
+
export { $prompt, $resource, $tool, AlephaMcp, JSONRPC_VERSION, JsonRpcErrorCodes, JsonRpcParseError, MCP_PROTOCOL_VERSION, McpError, McpInvalidParamsError, McpMethodNotFoundError, McpPromptNotFoundError, McpResourceNotFoundError, McpServerProvider, McpToolNotFoundError, PromptPrimitive, ResourcePrimitive, SseMcpTransport, StdioMcpTransport, ToolPrimitive, createErrorResponse, createInternalError, createInvalidParamsError, createInvalidRequestError, createMethodNotFoundError, createNotification, createParseError, createResponse, isNotification, isValidJsonRpcRequest, parseMessage };
|
|
978
|
+
//# sourceMappingURL=index.js.map
|