@copilotkit/aimock 1.7.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/.claude-plugin/marketplace.json +17 -0
- package/.claude-plugin/plugin.json +12 -0
- package/LICENSE +21 -0
- package/README.md +82 -0
- package/dist/_virtual/_rolldown/runtime.cjs +29 -0
- package/dist/a2a-handler.cjs +203 -0
- package/dist/a2a-handler.cjs.map +1 -0
- package/dist/a2a-handler.js +199 -0
- package/dist/a2a-handler.js.map +1 -0
- package/dist/a2a-mock.cjs +292 -0
- package/dist/a2a-mock.cjs.map +1 -0
- package/dist/a2a-mock.d.cts +41 -0
- package/dist/a2a-mock.d.cts.map +1 -0
- package/dist/a2a-mock.d.ts +41 -0
- package/dist/a2a-mock.d.ts.map +1 -0
- package/dist/a2a-mock.js +290 -0
- package/dist/a2a-mock.js.map +1 -0
- package/dist/a2a-stub.cjs +4 -0
- package/dist/a2a-stub.d.cts +3 -0
- package/dist/a2a-stub.d.ts +3 -0
- package/dist/a2a-stub.js +3 -0
- package/dist/a2a-types.d.cts +68 -0
- package/dist/a2a-types.d.cts.map +1 -0
- package/dist/a2a-types.d.ts +68 -0
- package/dist/a2a-types.d.ts.map +1 -0
- package/dist/aimock-cli.cjs +112 -0
- package/dist/aimock-cli.cjs.map +1 -0
- package/dist/aimock-cli.d.cts +19 -0
- package/dist/aimock-cli.d.cts.map +1 -0
- package/dist/aimock-cli.d.ts +19 -0
- package/dist/aimock-cli.d.ts.map +1 -0
- package/dist/aimock-cli.js +110 -0
- package/dist/aimock-cli.js.map +1 -0
- package/dist/aws-event-stream.cjs +117 -0
- package/dist/aws-event-stream.cjs.map +1 -0
- package/dist/aws-event-stream.d.cts +38 -0
- package/dist/aws-event-stream.d.cts.map +1 -0
- package/dist/aws-event-stream.d.ts +38 -0
- package/dist/aws-event-stream.d.ts.map +1 -0
- package/dist/aws-event-stream.js +114 -0
- package/dist/aws-event-stream.js.map +1 -0
- package/dist/bedrock-converse.cjs +445 -0
- package/dist/bedrock-converse.cjs.map +1 -0
- package/dist/bedrock-converse.d.cts +50 -0
- package/dist/bedrock-converse.d.cts.map +1 -0
- package/dist/bedrock-converse.d.ts +50 -0
- package/dist/bedrock-converse.d.ts.map +1 -0
- package/dist/bedrock-converse.js +443 -0
- package/dist/bedrock-converse.js.map +1 -0
- package/dist/bedrock.cjs +557 -0
- package/dist/bedrock.cjs.map +1 -0
- package/dist/bedrock.d.cts +41 -0
- package/dist/bedrock.d.cts.map +1 -0
- package/dist/bedrock.d.ts +41 -0
- package/dist/bedrock.d.ts.map +1 -0
- package/dist/bedrock.js +553 -0
- package/dist/bedrock.js.map +1 -0
- package/dist/chaos.cjs +114 -0
- package/dist/chaos.cjs.map +1 -0
- package/dist/chaos.d.cts +27 -0
- package/dist/chaos.d.cts.map +1 -0
- package/dist/chaos.d.ts +27 -0
- package/dist/chaos.d.ts.map +1 -0
- package/dist/chaos.js +113 -0
- package/dist/chaos.js.map +1 -0
- package/dist/cli.cjs +268 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +268 -0
- package/dist/cli.js.map +1 -0
- package/dist/cohere.cjs +434 -0
- package/dist/cohere.cjs.map +1 -0
- package/dist/cohere.d.cts +34 -0
- package/dist/cohere.d.cts.map +1 -0
- package/dist/cohere.d.ts +34 -0
- package/dist/cohere.d.ts.map +1 -0
- package/dist/cohere.js +433 -0
- package/dist/cohere.js.map +1 -0
- package/dist/config-loader.cjs +111 -0
- package/dist/config-loader.cjs.map +1 -0
- package/dist/config-loader.d.cts +100 -0
- package/dist/config-loader.d.cts.map +1 -0
- package/dist/config-loader.d.ts +100 -0
- package/dist/config-loader.d.ts.map +1 -0
- package/dist/config-loader.js +107 -0
- package/dist/config-loader.js.map +1 -0
- package/dist/embeddings.cjs +150 -0
- package/dist/embeddings.cjs.map +1 -0
- package/dist/embeddings.d.cts +12 -0
- package/dist/embeddings.d.cts.map +1 -0
- package/dist/embeddings.d.ts +12 -0
- package/dist/embeddings.d.ts.map +1 -0
- package/dist/embeddings.js +150 -0
- package/dist/embeddings.js.map +1 -0
- package/dist/fixture-loader.cjs +269 -0
- package/dist/fixture-loader.cjs.map +1 -0
- package/dist/fixture-loader.d.cts +17 -0
- package/dist/fixture-loader.d.cts.map +1 -0
- package/dist/fixture-loader.d.ts +17 -0
- package/dist/fixture-loader.d.ts.map +1 -0
- package/dist/fixture-loader.js +265 -0
- package/dist/fixture-loader.js.map +1 -0
- package/dist/gemini.cjs +403 -0
- package/dist/gemini.cjs.map +1 -0
- package/dist/gemini.d.cts +10 -0
- package/dist/gemini.d.cts.map +1 -0
- package/dist/gemini.d.ts +10 -0
- package/dist/gemini.d.ts.map +1 -0
- package/dist/gemini.js +403 -0
- package/dist/gemini.js.map +1 -0
- package/dist/helpers.cjs +276 -0
- package/dist/helpers.cjs.map +1 -0
- package/dist/helpers.d.cts +39 -0
- package/dist/helpers.d.cts.map +1 -0
- package/dist/helpers.d.ts +39 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +259 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.cjs +113 -0
- package/dist/index.d.cts +42 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +39 -0
- package/dist/interruption.cjs +40 -0
- package/dist/interruption.cjs.map +1 -0
- package/dist/interruption.d.cts +15 -0
- package/dist/interruption.d.cts.map +1 -0
- package/dist/interruption.d.ts +15 -0
- package/dist/interruption.d.ts.map +1 -0
- package/dist/interruption.js +39 -0
- package/dist/interruption.js.map +1 -0
- package/dist/journal.cjs +65 -0
- package/dist/journal.cjs.map +1 -0
- package/dist/journal.d.cts +23 -0
- package/dist/journal.d.cts.map +1 -0
- package/dist/journal.d.ts +23 -0
- package/dist/journal.d.ts.map +1 -0
- package/dist/journal.js +65 -0
- package/dist/journal.js.map +1 -0
- package/dist/jsonrpc.cjs +91 -0
- package/dist/jsonrpc.cjs.map +1 -0
- package/dist/jsonrpc.d.cts +24 -0
- package/dist/jsonrpc.d.cts.map +1 -0
- package/dist/jsonrpc.d.ts +24 -0
- package/dist/jsonrpc.d.ts.map +1 -0
- package/dist/jsonrpc.js +90 -0
- package/dist/jsonrpc.js.map +1 -0
- package/dist/llmock.cjs +223 -0
- package/dist/llmock.cjs.map +1 -0
- package/dist/llmock.d.cts +70 -0
- package/dist/llmock.d.cts.map +1 -0
- package/dist/llmock.d.ts +70 -0
- package/dist/llmock.d.ts.map +1 -0
- package/dist/llmock.js +223 -0
- package/dist/llmock.js.map +1 -0
- package/dist/logger.cjs +29 -0
- package/dist/logger.cjs.map +1 -0
- package/dist/logger.d.cts +14 -0
- package/dist/logger.d.cts.map +1 -0
- package/dist/logger.d.ts +14 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +28 -0
- package/dist/logger.js.map +1 -0
- package/dist/mcp-handler.cjs +189 -0
- package/dist/mcp-handler.cjs.map +1 -0
- package/dist/mcp-handler.js +188 -0
- package/dist/mcp-handler.js.map +1 -0
- package/dist/mcp-mock.cjs +169 -0
- package/dist/mcp-mock.cjs.map +1 -0
- package/dist/mcp-mock.d.cts +40 -0
- package/dist/mcp-mock.d.cts.map +1 -0
- package/dist/mcp-mock.d.ts +40 -0
- package/dist/mcp-mock.d.ts.map +1 -0
- package/dist/mcp-mock.js +167 -0
- package/dist/mcp-mock.js.map +1 -0
- package/dist/mcp-stub.cjs +4 -0
- package/dist/mcp-stub.d.cts +3 -0
- package/dist/mcp-stub.d.ts +3 -0
- package/dist/mcp-stub.js +3 -0
- package/dist/mcp-types.d.cts +65 -0
- package/dist/mcp-types.d.cts.map +1 -0
- package/dist/mcp-types.d.ts +65 -0
- package/dist/mcp-types.d.ts.map +1 -0
- package/dist/messages.cjs +489 -0
- package/dist/messages.cjs.map +1 -0
- package/dist/messages.d.cts +10 -0
- package/dist/messages.d.cts.map +1 -0
- package/dist/messages.d.ts +10 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +489 -0
- package/dist/messages.js.map +1 -0
- package/dist/metrics.cjs +160 -0
- package/dist/metrics.cjs.map +1 -0
- package/dist/metrics.d.cts +24 -0
- package/dist/metrics.d.cts.map +1 -0
- package/dist/metrics.d.ts +24 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +158 -0
- package/dist/metrics.js.map +1 -0
- package/dist/moderation.cjs +91 -0
- package/dist/moderation.cjs.map +1 -0
- package/dist/moderation.d.cts +23 -0
- package/dist/moderation.d.cts.map +1 -0
- package/dist/moderation.d.ts +23 -0
- package/dist/moderation.d.ts.map +1 -0
- package/dist/moderation.js +91 -0
- package/dist/moderation.js.map +1 -0
- package/dist/ndjson-writer.cjs +31 -0
- package/dist/ndjson-writer.cjs.map +1 -0
- package/dist/ndjson-writer.d.cts +17 -0
- package/dist/ndjson-writer.d.cts.map +1 -0
- package/dist/ndjson-writer.d.ts +17 -0
- package/dist/ndjson-writer.d.ts.map +1 -0
- package/dist/ndjson-writer.js +31 -0
- package/dist/ndjson-writer.js.map +1 -0
- package/dist/ollama.cjs +519 -0
- package/dist/ollama.cjs.map +1 -0
- package/dist/ollama.d.cts +34 -0
- package/dist/ollama.d.cts.map +1 -0
- package/dist/ollama.d.ts +34 -0
- package/dist/ollama.d.ts.map +1 -0
- package/dist/ollama.js +517 -0
- package/dist/ollama.js.map +1 -0
- package/dist/recorder.cjs +311 -0
- package/dist/recorder.cjs.map +1 -0
- package/dist/recorder.d.cts +23 -0
- package/dist/recorder.d.cts.map +1 -0
- package/dist/recorder.d.ts +23 -0
- package/dist/recorder.d.ts.map +1 -0
- package/dist/recorder.js +305 -0
- package/dist/recorder.js.map +1 -0
- package/dist/rerank.cjs +71 -0
- package/dist/rerank.cjs.map +1 -0
- package/dist/rerank.d.cts +22 -0
- package/dist/rerank.d.cts.map +1 -0
- package/dist/rerank.d.ts +22 -0
- package/dist/rerank.d.ts.map +1 -0
- package/dist/rerank.js +71 -0
- package/dist/rerank.js.map +1 -0
- package/dist/responses.cjs +637 -0
- package/dist/responses.cjs.map +1 -0
- package/dist/responses.d.cts +16 -0
- package/dist/responses.d.cts.map +1 -0
- package/dist/responses.d.ts +16 -0
- package/dist/responses.d.ts.map +1 -0
- package/dist/responses.js +634 -0
- package/dist/responses.js.map +1 -0
- package/dist/router.cjs +68 -0
- package/dist/router.cjs.map +1 -0
- package/dist/router.d.cts +16 -0
- package/dist/router.d.cts.map +1 -0
- package/dist/router.d.ts +16 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +65 -0
- package/dist/router.js.map +1 -0
- package/dist/search.cjs +59 -0
- package/dist/search.cjs.map +1 -0
- package/dist/search.d.cts +23 -0
- package/dist/search.d.cts.map +1 -0
- package/dist/search.d.ts +23 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +59 -0
- package/dist/search.js.map +1 -0
- package/dist/server.cjs +935 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +28 -0
- package/dist/server.d.cts.map +1 -0
- package/dist/server.d.ts +28 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +933 -0
- package/dist/server.js.map +1 -0
- package/dist/sse-writer.cjs +59 -0
- package/dist/sse-writer.cjs.map +1 -0
- package/dist/sse-writer.d.cts +19 -0
- package/dist/sse-writer.d.cts.map +1 -0
- package/dist/sse-writer.d.ts +19 -0
- package/dist/sse-writer.d.ts.map +1 -0
- package/dist/sse-writer.js +55 -0
- package/dist/sse-writer.js.map +1 -0
- package/dist/stream-collapse.cjs +496 -0
- package/dist/stream-collapse.cjs.map +1 -0
- package/dist/stream-collapse.d.cts +70 -0
- package/dist/stream-collapse.d.cts.map +1 -0
- package/dist/stream-collapse.d.ts +70 -0
- package/dist/stream-collapse.d.ts.map +1 -0
- package/dist/stream-collapse.js +489 -0
- package/dist/stream-collapse.js.map +1 -0
- package/dist/suite.cjs +46 -0
- package/dist/suite.cjs.map +1 -0
- package/dist/suite.d.cts +31 -0
- package/dist/suite.d.cts.map +1 -0
- package/dist/suite.d.ts +31 -0
- package/dist/suite.d.ts.map +1 -0
- package/dist/suite.js +46 -0
- package/dist/suite.js.map +1 -0
- package/dist/types.d.cts +243 -0
- package/dist/types.d.cts.map +1 -0
- package/dist/types.d.ts +243 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/url.cjs +21 -0
- package/dist/url.cjs.map +1 -0
- package/dist/url.d.cts +16 -0
- package/dist/url.d.cts.map +1 -0
- package/dist/url.d.ts +16 -0
- package/dist/url.d.ts.map +1 -0
- package/dist/url.js +20 -0
- package/dist/url.js.map +1 -0
- package/dist/vector-handler.cjs +239 -0
- package/dist/vector-handler.cjs.map +1 -0
- package/dist/vector-handler.js +238 -0
- package/dist/vector-handler.js.map +1 -0
- package/dist/vector-mock.cjs +229 -0
- package/dist/vector-mock.cjs.map +1 -0
- package/dist/vector-mock.d.cts +39 -0
- package/dist/vector-mock.d.cts.map +1 -0
- package/dist/vector-mock.d.ts +39 -0
- package/dist/vector-mock.d.ts.map +1 -0
- package/dist/vector-mock.js +227 -0
- package/dist/vector-mock.js.map +1 -0
- package/dist/vector-stub.cjs +4 -0
- package/dist/vector-stub.d.cts +3 -0
- package/dist/vector-stub.d.ts +3 -0
- package/dist/vector-stub.js +3 -0
- package/dist/vector-types.d.cts +32 -0
- package/dist/vector-types.d.cts.map +1 -0
- package/dist/vector-types.d.ts +32 -0
- package/dist/vector-types.d.ts.map +1 -0
- package/dist/watcher.cjs +59 -0
- package/dist/watcher.cjs.map +1 -0
- package/dist/watcher.js +58 -0
- package/dist/watcher.js.map +1 -0
- package/dist/ws-framing.cjs +187 -0
- package/dist/ws-framing.cjs.map +1 -0
- package/dist/ws-framing.d.cts +26 -0
- package/dist/ws-framing.d.cts.map +1 -0
- package/dist/ws-framing.d.ts +26 -0
- package/dist/ws-framing.d.ts.map +1 -0
- package/dist/ws-framing.js +184 -0
- package/dist/ws-framing.js.map +1 -0
- package/dist/ws-gemini-live.cjs +364 -0
- package/dist/ws-gemini-live.cjs.map +1 -0
- package/dist/ws-gemini-live.d.cts +18 -0
- package/dist/ws-gemini-live.d.cts.map +1 -0
- package/dist/ws-gemini-live.d.ts +18 -0
- package/dist/ws-gemini-live.d.ts.map +1 -0
- package/dist/ws-gemini-live.js +364 -0
- package/dist/ws-gemini-live.js.map +1 -0
- package/dist/ws-realtime.cjs +435 -0
- package/dist/ws-realtime.cjs.map +1 -0
- package/dist/ws-realtime.d.cts +17 -0
- package/dist/ws-realtime.d.cts.map +1 -0
- package/dist/ws-realtime.d.ts +17 -0
- package/dist/ws-realtime.d.ts.map +1 -0
- package/dist/ws-realtime.js +435 -0
- package/dist/ws-realtime.js.map +1 -0
- package/dist/ws-responses.cjs +164 -0
- package/dist/ws-responses.cjs.map +1 -0
- package/dist/ws-responses.d.cts +18 -0
- package/dist/ws-responses.d.cts.map +1 -0
- package/dist/ws-responses.d.ts +18 -0
- package/dist/ws-responses.d.ts.map +1 -0
- package/dist/ws-responses.js +164 -0
- package/dist/ws-responses.js.map +1 -0
- package/fixtures/example-greeting.json +12 -0
- package/fixtures/example-multi-turn.json +14 -0
- package/fixtures/example-tool-call.json +15 -0
- package/package.json +118 -0
- package/skills/write-fixtures/SKILL.md +625 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { createJsonRpcDispatcher } from "./jsonrpc.js";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
|
|
4
|
+
//#region src/mcp-handler.ts
|
|
5
|
+
function jsonRpcResult(id, result) {
|
|
6
|
+
return {
|
|
7
|
+
jsonrpc: "2.0",
|
|
8
|
+
id,
|
|
9
|
+
result
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function jsonRpcError(id, code, message) {
|
|
13
|
+
return {
|
|
14
|
+
jsonrpc: "2.0",
|
|
15
|
+
id,
|
|
16
|
+
error: {
|
|
17
|
+
code,
|
|
18
|
+
message
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function createMCPRequestHandler(state) {
|
|
23
|
+
const dispatcher = createJsonRpcDispatcher({ methods: {
|
|
24
|
+
initialize: async (_params, id) => {
|
|
25
|
+
return jsonRpcResult(id, {
|
|
26
|
+
protocolVersion: "2025-03-26",
|
|
27
|
+
capabilities: {
|
|
28
|
+
tools: {},
|
|
29
|
+
resources: {},
|
|
30
|
+
prompts: {}
|
|
31
|
+
},
|
|
32
|
+
serverInfo: state.serverInfo
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
"notifications/initialized": async (_params, _id, req) => {
|
|
36
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
37
|
+
const session = state.sessions.get(sessionId);
|
|
38
|
+
if (session) session.initialized = true;
|
|
39
|
+
return null;
|
|
40
|
+
},
|
|
41
|
+
ping: async (_params, id) => {
|
|
42
|
+
return jsonRpcResult(id, {});
|
|
43
|
+
},
|
|
44
|
+
"tools/list": async (_params, id) => {
|
|
45
|
+
const tools = [];
|
|
46
|
+
for (const { def } of state.tools.values()) tools.push(def);
|
|
47
|
+
return jsonRpcResult(id, { tools });
|
|
48
|
+
},
|
|
49
|
+
"tools/call": async (params, id) => {
|
|
50
|
+
const { name, arguments: args } = params ?? {};
|
|
51
|
+
if (!name) return jsonRpcError(id, -32602, "Missing tool name");
|
|
52
|
+
const entry = state.tools.get(name);
|
|
53
|
+
if (!entry) return jsonRpcError(id, -32602, `Unknown tool: ${name}`);
|
|
54
|
+
if (entry.handler) try {
|
|
55
|
+
const result = await entry.handler(args);
|
|
56
|
+
return jsonRpcResult(id, {
|
|
57
|
+
content: Array.isArray(result) ? result : [{
|
|
58
|
+
type: "text",
|
|
59
|
+
text: String(result)
|
|
60
|
+
}],
|
|
61
|
+
isError: false
|
|
62
|
+
});
|
|
63
|
+
} catch (err) {
|
|
64
|
+
return jsonRpcResult(id, {
|
|
65
|
+
content: [{
|
|
66
|
+
type: "text",
|
|
67
|
+
text: err instanceof Error ? err.message : String(err)
|
|
68
|
+
}],
|
|
69
|
+
isError: true
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return jsonRpcResult(id, {
|
|
73
|
+
content: [],
|
|
74
|
+
isError: false
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
"resources/list": async (_params, id) => {
|
|
78
|
+
const resources = [];
|
|
79
|
+
for (const { def } of state.resources.values()) resources.push(def);
|
|
80
|
+
return jsonRpcResult(id, { resources });
|
|
81
|
+
},
|
|
82
|
+
"resources/read": async (params, id) => {
|
|
83
|
+
const { uri } = params ?? {};
|
|
84
|
+
if (!uri) return jsonRpcError(id, -32602, "Missing resource URI");
|
|
85
|
+
const entry = state.resources.get(uri);
|
|
86
|
+
if (!entry) return jsonRpcError(id, -32602, `Unknown resource: ${uri}`);
|
|
87
|
+
return jsonRpcResult(id, { contents: [{
|
|
88
|
+
uri,
|
|
89
|
+
...entry.content?.text !== void 0 && { text: entry.content.text },
|
|
90
|
+
...entry.content?.blob !== void 0 && { blob: entry.content.blob },
|
|
91
|
+
...entry.content?.mimeType !== void 0 && { mimeType: entry.content.mimeType }
|
|
92
|
+
}] });
|
|
93
|
+
},
|
|
94
|
+
"prompts/list": async (_params, id) => {
|
|
95
|
+
const prompts = [];
|
|
96
|
+
for (const { def } of state.prompts.values()) prompts.push(def);
|
|
97
|
+
return jsonRpcResult(id, { prompts });
|
|
98
|
+
},
|
|
99
|
+
"prompts/get": async (params, id) => {
|
|
100
|
+
const { name, arguments: args } = params ?? {};
|
|
101
|
+
if (!name) return jsonRpcError(id, -32602, "Missing prompt name");
|
|
102
|
+
const entry = state.prompts.get(name);
|
|
103
|
+
if (!entry) return jsonRpcError(id, -32602, `Unknown prompt: ${name}`);
|
|
104
|
+
if (entry.handler) try {
|
|
105
|
+
return jsonRpcResult(id, await entry.handler(args));
|
|
106
|
+
} catch (err) {
|
|
107
|
+
return jsonRpcError(id, -32603, `Prompt handler error: ${err instanceof Error ? err.message : String(err)}`);
|
|
108
|
+
}
|
|
109
|
+
return jsonRpcResult(id, { messages: [] });
|
|
110
|
+
}
|
|
111
|
+
} });
|
|
112
|
+
return async (req, res, body) => {
|
|
113
|
+
if (req.method === "DELETE") {
|
|
114
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
115
|
+
if (!sessionId) {
|
|
116
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
117
|
+
res.end(JSON.stringify({ error: "Missing mcp-session-id header" }));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (!state.sessions.has(sessionId)) {
|
|
121
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
122
|
+
res.end(JSON.stringify({ error: "Session not found" }));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
state.sessions.delete(sessionId);
|
|
126
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
127
|
+
res.end(JSON.stringify({ ok: true }));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
let parsed;
|
|
131
|
+
try {
|
|
132
|
+
parsed = JSON.parse(body);
|
|
133
|
+
} catch {
|
|
134
|
+
await dispatcher(req, res, body);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const method = typeof parsed === "object" && parsed !== null && "method" in parsed ? parsed.method : void 0;
|
|
138
|
+
if (method === "initialize") {
|
|
139
|
+
const id = typeof parsed === "object" && parsed !== null && "id" in parsed ? parsed.id : null;
|
|
140
|
+
const sessionId = randomUUID();
|
|
141
|
+
state.sessions.set(sessionId, {
|
|
142
|
+
id: sessionId,
|
|
143
|
+
initialized: false,
|
|
144
|
+
createdAt: Date.now()
|
|
145
|
+
});
|
|
146
|
+
const response = {
|
|
147
|
+
jsonrpc: "2.0",
|
|
148
|
+
id,
|
|
149
|
+
result: {
|
|
150
|
+
protocolVersion: "2025-03-26",
|
|
151
|
+
capabilities: {
|
|
152
|
+
tools: {},
|
|
153
|
+
resources: {},
|
|
154
|
+
prompts: {}
|
|
155
|
+
},
|
|
156
|
+
serverInfo: state.serverInfo
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
res.writeHead(200, {
|
|
160
|
+
"Content-Type": "application/json",
|
|
161
|
+
"Mcp-Session-Id": sessionId
|
|
162
|
+
});
|
|
163
|
+
res.end(JSON.stringify(response));
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
167
|
+
if (!sessionId) {
|
|
168
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
169
|
+
res.end(JSON.stringify({ error: "Missing mcp-session-id header" }));
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (!state.sessions.has(sessionId)) {
|
|
173
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
174
|
+
res.end(JSON.stringify({ error: "Session not found" }));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (!state.sessions.get(sessionId).initialized && method !== "notifications/initialized") {
|
|
178
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
179
|
+
res.end(JSON.stringify(jsonRpcError(typeof parsed === "object" && parsed !== null && "id" in parsed ? parsed.id : null, -32002, "Session not initialized")));
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
await dispatcher(req, res, body);
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
//#endregion
|
|
187
|
+
export { createMCPRequestHandler };
|
|
188
|
+
//# sourceMappingURL=mcp-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-handler.js","names":[],"sources":["../src/mcp-handler.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport { randomUUID } from \"node:crypto\";\nimport { createJsonRpcDispatcher } from \"./jsonrpc.js\";\nimport type {\n MCPToolDefinition,\n MCPResourceDefinition,\n MCPResourceContent,\n MCPPromptDefinition,\n MCPPromptResult,\n MCPContent,\n MCPSession,\n} from \"./mcp-types.js\";\n\nexport interface MCPState {\n serverInfo: { name: string; version: string };\n tools: Map<string, { def: MCPToolDefinition; handler?: (...args: unknown[]) => unknown }>;\n resources: Map<string, { def: MCPResourceDefinition; content?: MCPResourceContent }>;\n prompts: Map<\n string,\n {\n def: MCPPromptDefinition;\n handler?: (...args: unknown[]) => MCPPromptResult | Promise<MCPPromptResult>;\n }\n >;\n sessions: Map<string, MCPSession>;\n}\n\nfunction jsonRpcResult(id: string | number, result: unknown) {\n return { jsonrpc: \"2.0\" as const, id, result };\n}\n\nfunction jsonRpcError(id: string | number | null, code: number, message: string) {\n return { jsonrpc: \"2.0\" as const, id, error: { code, message } };\n}\n\nexport function createMCPRequestHandler(state: MCPState) {\n const dispatcher = createJsonRpcDispatcher({\n methods: {\n // initialize is handled directly in the outer function — this entry is\n // only here so the dispatcher doesn't return \"Method not found\" if the\n // request somehow reaches it.\n initialize: async (_params, id) => {\n return jsonRpcResult(id, {\n protocolVersion: \"2025-03-26\",\n capabilities: { tools: {}, resources: {}, prompts: {} },\n serverInfo: state.serverInfo,\n });\n },\n\n \"notifications/initialized\": async (_params, _id, req) => {\n const sessionId = req.headers[\"mcp-session-id\"] as string;\n const session = state.sessions.get(sessionId);\n if (session) {\n session.initialized = true;\n }\n return null;\n },\n\n ping: async (_params, id) => {\n return jsonRpcResult(id, {});\n },\n\n \"tools/list\": async (_params, id) => {\n const tools: MCPToolDefinition[] = [];\n for (const { def } of state.tools.values()) {\n tools.push(def);\n }\n return jsonRpcResult(id, { tools });\n },\n\n \"tools/call\": async (params, id) => {\n const { name, arguments: args } = (params ?? {}) as { name?: string; arguments?: unknown };\n if (!name) {\n return jsonRpcError(id, -32602, \"Missing tool name\");\n }\n const entry = state.tools.get(name);\n if (!entry) {\n return jsonRpcError(id, -32602, `Unknown tool: ${name}`);\n }\n if (entry.handler) {\n try {\n const result = await entry.handler(args);\n const content: MCPContent[] = Array.isArray(result)\n ? result\n : [{ type: \"text\", text: String(result) }];\n return jsonRpcResult(id, { content, isError: false });\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n return jsonRpcResult(id, {\n content: [{ type: \"text\", text: message }],\n isError: true,\n });\n }\n }\n // No handler — return empty content\n return jsonRpcResult(id, { content: [], isError: false });\n },\n\n \"resources/list\": async (_params, id) => {\n const resources: MCPResourceDefinition[] = [];\n for (const { def } of state.resources.values()) {\n resources.push(def);\n }\n return jsonRpcResult(id, { resources });\n },\n\n \"resources/read\": async (params, id) => {\n const { uri } = (params ?? {}) as { uri?: string };\n if (!uri) {\n return jsonRpcError(id, -32602, \"Missing resource URI\");\n }\n const entry = state.resources.get(uri);\n if (!entry) {\n return jsonRpcError(id, -32602, `Unknown resource: ${uri}`);\n }\n return jsonRpcResult(id, {\n contents: [\n {\n uri,\n ...(entry.content?.text !== undefined && { text: entry.content.text }),\n ...(entry.content?.blob !== undefined && { blob: entry.content.blob }),\n ...(entry.content?.mimeType !== undefined && { mimeType: entry.content.mimeType }),\n },\n ],\n });\n },\n\n \"prompts/list\": async (_params, id) => {\n const prompts: MCPPromptDefinition[] = [];\n for (const { def } of state.prompts.values()) {\n prompts.push(def);\n }\n return jsonRpcResult(id, { prompts });\n },\n\n \"prompts/get\": async (params, id) => {\n const { name, arguments: args } = (params ?? {}) as { name?: string; arguments?: unknown };\n if (!name) {\n return jsonRpcError(id, -32602, \"Missing prompt name\");\n }\n const entry = state.prompts.get(name);\n if (!entry) {\n return jsonRpcError(id, -32602, `Unknown prompt: ${name}`);\n }\n if (entry.handler) {\n try {\n const result = await entry.handler(args);\n return jsonRpcResult(id, result);\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n return jsonRpcError(id, -32603, `Prompt handler error: ${message}`);\n }\n }\n // No handler — return empty messages\n return jsonRpcResult(id, { messages: [] });\n },\n },\n });\n\n return async (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n body: string,\n ): Promise<void> => {\n // DELETE handler: session teardown\n if (req.method === \"DELETE\") {\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n if (!sessionId) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Missing mcp-session-id header\" }));\n return;\n }\n if (!state.sessions.has(sessionId)) {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Session not found\" }));\n return;\n }\n state.sessions.delete(sessionId);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: true }));\n return;\n }\n\n // Parse the body to determine method for session validation\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n // Let the dispatcher handle parse errors\n await dispatcher(req, res, body);\n return;\n }\n\n const method =\n typeof parsed === \"object\" && parsed !== null && \"method\" in parsed\n ? (parsed as { method: unknown }).method\n : undefined;\n\n // Handle initialize directly to control response headers\n if (method === \"initialize\") {\n const id =\n typeof parsed === \"object\" && parsed !== null && \"id\" in parsed\n ? (parsed as { id: unknown }).id\n : null;\n\n const sessionId = randomUUID();\n state.sessions.set(sessionId, {\n id: sessionId,\n initialized: false,\n createdAt: Date.now(),\n });\n\n const response = {\n jsonrpc: \"2.0\",\n id,\n result: {\n protocolVersion: \"2025-03-26\",\n capabilities: { tools: {}, resources: {}, prompts: {} },\n serverInfo: state.serverInfo,\n },\n };\n\n res.writeHead(200, {\n \"Content-Type\": \"application/json\",\n \"Mcp-Session-Id\": sessionId,\n });\n res.end(JSON.stringify(response));\n return;\n }\n\n // Session validation for all other methods\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n if (!sessionId) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Missing mcp-session-id header\" }));\n return;\n }\n if (!state.sessions.has(sessionId)) {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Session not found\" }));\n return;\n }\n\n // Enforce initialization: only allow notifications/initialized through\n // before the session is fully initialized\n const session = state.sessions.get(sessionId)!;\n if (!session.initialized && method !== \"notifications/initialized\") {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify(\n jsonRpcError(\n typeof parsed === \"object\" && parsed !== null && \"id\" in parsed\n ? ((parsed as { id: unknown }).id as string | number)\n : null,\n -32002,\n \"Session not initialized\",\n ),\n ),\n );\n return;\n }\n\n // Delegate to the JSON-RPC dispatcher for all other methods\n await dispatcher(req, res, body);\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
2
|
+
const require_helpers = require('./helpers.cjs');
|
|
3
|
+
const require_mcp_handler = require('./mcp-handler.cjs');
|
|
4
|
+
let node_http = require("node:http");
|
|
5
|
+
node_http = require_runtime.__toESM(node_http);
|
|
6
|
+
|
|
7
|
+
//#region src/mcp-mock.ts
|
|
8
|
+
var MCPMock = class {
|
|
9
|
+
tools = /* @__PURE__ */ new Map();
|
|
10
|
+
resources = /* @__PURE__ */ new Map();
|
|
11
|
+
prompts = /* @__PURE__ */ new Map();
|
|
12
|
+
sessions = /* @__PURE__ */ new Map();
|
|
13
|
+
server = null;
|
|
14
|
+
journal = null;
|
|
15
|
+
registry = null;
|
|
16
|
+
options;
|
|
17
|
+
requestHandler;
|
|
18
|
+
constructor(options) {
|
|
19
|
+
this.options = options ?? {};
|
|
20
|
+
this.requestHandler = this.buildHandler();
|
|
21
|
+
}
|
|
22
|
+
addTool(def) {
|
|
23
|
+
this.tools.set(def.name, { def });
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
onToolCall(name, handler) {
|
|
27
|
+
const entry = this.tools.get(name);
|
|
28
|
+
if (entry) entry.handler = handler;
|
|
29
|
+
else this.tools.set(name, {
|
|
30
|
+
def: { name },
|
|
31
|
+
handler
|
|
32
|
+
});
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
addResource(def, content) {
|
|
36
|
+
this.resources.set(def.uri, {
|
|
37
|
+
def,
|
|
38
|
+
content
|
|
39
|
+
});
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
addPrompt(def, handler) {
|
|
43
|
+
this.prompts.set(def.name, {
|
|
44
|
+
def,
|
|
45
|
+
handler
|
|
46
|
+
});
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
async handleRequest(req, res, pathname) {
|
|
50
|
+
if (pathname !== "/" && pathname !== "") return false;
|
|
51
|
+
if (req.method !== "POST" && req.method !== "DELETE") return false;
|
|
52
|
+
const body = await require_helpers.readBody(req);
|
|
53
|
+
if (this.registry) if (req.method === "DELETE") this.registry.incrementCounter("aimock_mcp_requests_total", { method: "session/delete" });
|
|
54
|
+
else try {
|
|
55
|
+
const parsed = JSON.parse(body);
|
|
56
|
+
const method = typeof parsed === "object" && parsed !== null && "method" in parsed ? String(parsed.method) : "unknown";
|
|
57
|
+
this.registry.incrementCounter("aimock_mcp_requests_total", { method });
|
|
58
|
+
} catch {
|
|
59
|
+
this.registry.incrementCounter("aimock_mcp_requests_total", { method: "unknown" });
|
|
60
|
+
}
|
|
61
|
+
await this.requestHandler(req, res, body);
|
|
62
|
+
if (this.journal) this.journal.add({
|
|
63
|
+
method: req.method ?? "POST",
|
|
64
|
+
path: req.url ?? "/",
|
|
65
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
66
|
+
body: null,
|
|
67
|
+
service: "mcp",
|
|
68
|
+
response: {
|
|
69
|
+
status: res.statusCode,
|
|
70
|
+
fixture: null
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
health() {
|
|
76
|
+
return {
|
|
77
|
+
status: "ok",
|
|
78
|
+
tools: this.tools.size,
|
|
79
|
+
resources: this.resources.size,
|
|
80
|
+
prompts: this.prompts.size,
|
|
81
|
+
sessions: this.sessions.size
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
setJournal(journal) {
|
|
85
|
+
this.journal = journal;
|
|
86
|
+
}
|
|
87
|
+
setRegistry(registry) {
|
|
88
|
+
this.registry = registry;
|
|
89
|
+
}
|
|
90
|
+
async start() {
|
|
91
|
+
if (this.server) throw new Error("Server already started");
|
|
92
|
+
const host = this.options.host ?? "127.0.0.1";
|
|
93
|
+
const port = this.options.port ?? 0;
|
|
94
|
+
return new Promise((resolve, reject) => {
|
|
95
|
+
const srv = node_http.createServer((req, res) => {
|
|
96
|
+
const chunks = [];
|
|
97
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
98
|
+
req.on("end", () => {
|
|
99
|
+
const body = Buffer.concat(chunks).toString();
|
|
100
|
+
this.requestHandler(req, res, body).then(() => {
|
|
101
|
+
if (this.journal) this.journal.add({
|
|
102
|
+
method: req.method ?? "POST",
|
|
103
|
+
path: req.url ?? "/",
|
|
104
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
105
|
+
body: null,
|
|
106
|
+
service: "mcp",
|
|
107
|
+
response: {
|
|
108
|
+
status: res.statusCode,
|
|
109
|
+
fixture: null
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}).catch((err) => {
|
|
113
|
+
console.error("MCPMock request error:", err);
|
|
114
|
+
if (!res.headersSent) {
|
|
115
|
+
res.writeHead(500);
|
|
116
|
+
res.end("Internal server error");
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
srv.listen(port, host, () => {
|
|
122
|
+
this.server = srv;
|
|
123
|
+
const addr = srv.address();
|
|
124
|
+
if (typeof addr === "object" && addr !== null) resolve(`http://${host}:${addr.port}`);
|
|
125
|
+
else resolve(`http://${host}:${port}`);
|
|
126
|
+
});
|
|
127
|
+
srv.on("error", reject);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
async stop() {
|
|
131
|
+
if (!this.server) throw new Error("Server not started");
|
|
132
|
+
const srv = this.server;
|
|
133
|
+
this.server = null;
|
|
134
|
+
await new Promise((resolve, reject) => {
|
|
135
|
+
srv.close((err) => err ? reject(err) : resolve());
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
getRequests() {
|
|
139
|
+
if (!this.journal) return [];
|
|
140
|
+
return this.journal.getAll().filter((e) => e.service === "mcp");
|
|
141
|
+
}
|
|
142
|
+
getSessions() {
|
|
143
|
+
return new Map(this.sessions);
|
|
144
|
+
}
|
|
145
|
+
reset() {
|
|
146
|
+
this.tools.clear();
|
|
147
|
+
this.resources.clear();
|
|
148
|
+
this.prompts.clear();
|
|
149
|
+
this.sessions.clear();
|
|
150
|
+
this.requestHandler = this.buildHandler();
|
|
151
|
+
return this;
|
|
152
|
+
}
|
|
153
|
+
buildHandler() {
|
|
154
|
+
return require_mcp_handler.createMCPRequestHandler({
|
|
155
|
+
serverInfo: this.options.serverInfo ?? {
|
|
156
|
+
name: "mcp-mock",
|
|
157
|
+
version: "1.0.0"
|
|
158
|
+
},
|
|
159
|
+
tools: this.tools,
|
|
160
|
+
resources: this.resources,
|
|
161
|
+
prompts: this.prompts,
|
|
162
|
+
sessions: this.sessions
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
//#endregion
|
|
168
|
+
exports.MCPMock = MCPMock;
|
|
169
|
+
//# sourceMappingURL=mcp-mock.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-mock.cjs","names":["readBody","flattenHeaders","http","createMCPRequestHandler"],"sources":["../src/mcp-mock.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport type { Mountable } from \"./types.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { MetricsRegistry } from \"./metrics.js\";\nimport type {\n MCPMockOptions,\n MCPToolDefinition,\n MCPResourceDefinition,\n MCPResourceContent,\n MCPPromptDefinition,\n MCPPromptResult,\n MCPContent,\n MCPSession,\n} from \"./mcp-types.js\";\nimport { createMCPRequestHandler, type MCPState } from \"./mcp-handler.js\";\nimport { flattenHeaders, readBody } from \"./helpers.js\";\n\nexport class MCPMock implements Mountable {\n private tools: Map<\n string,\n { def: MCPToolDefinition; handler?: (...args: unknown[]) => unknown }\n > = new Map();\n private resources: Map<string, { def: MCPResourceDefinition; content?: MCPResourceContent }> =\n new Map();\n private prompts: Map<\n string,\n {\n def: MCPPromptDefinition;\n handler?: (...args: unknown[]) => MCPPromptResult | Promise<MCPPromptResult>;\n }\n > = new Map();\n private sessions: Map<string, MCPSession> = new Map();\n private server: http.Server | null = null;\n private journal: Journal | null = null;\n private registry: MetricsRegistry | null = null;\n private options: MCPMockOptions;\n private requestHandler: ReturnType<typeof createMCPRequestHandler>;\n\n constructor(options?: MCPMockOptions) {\n this.options = options ?? {};\n this.requestHandler = this.buildHandler();\n }\n\n // ---- Configuration: Tools ----\n\n addTool(def: MCPToolDefinition): this {\n this.tools.set(def.name, { def });\n return this;\n }\n\n onToolCall(\n name: string,\n handler: (args: unknown) => MCPContent[] | string | Promise<MCPContent[] | string>,\n ): this {\n const entry = this.tools.get(name);\n if (entry) {\n entry.handler = handler;\n } else {\n this.tools.set(name, { def: { name }, handler });\n }\n return this;\n }\n\n // ---- Configuration: Resources ----\n\n addResource(def: MCPResourceDefinition, content?: MCPResourceContent): this {\n this.resources.set(def.uri, { def, content });\n return this;\n }\n\n // ---- Configuration: Prompts ----\n\n addPrompt(\n def: MCPPromptDefinition,\n handler?: (args: unknown) => MCPPromptResult | Promise<MCPPromptResult>,\n ): this {\n this.prompts.set(def.name, { def, handler });\n return this;\n }\n\n // ---- Mountable interface ----\n\n async handleRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n pathname: string,\n ): Promise<boolean> {\n // Only handle POST and DELETE to the root of the mount\n if (pathname !== \"/\" && pathname !== \"\") {\n return false;\n }\n if (req.method !== \"POST\" && req.method !== \"DELETE\") {\n return false;\n }\n\n const body = await readBody(req);\n\n // Extract JSON-RPC method for metrics (skip for DELETE — no JSON-RPC body)\n if (this.registry) {\n if (req.method === \"DELETE\") {\n this.registry.incrementCounter(\"aimock_mcp_requests_total\", { method: \"session/delete\" });\n } else {\n try {\n const parsed = JSON.parse(body);\n const method =\n typeof parsed === \"object\" && parsed !== null && \"method\" in parsed\n ? String(parsed.method)\n : \"unknown\";\n this.registry.incrementCounter(\"aimock_mcp_requests_total\", { method });\n } catch {\n this.registry.incrementCounter(\"aimock_mcp_requests_total\", { method: \"unknown\" });\n }\n }\n }\n\n await this.requestHandler(req, res, body);\n\n // Journal the request after the handler completes\n if (this.journal) {\n this.journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"mcp\",\n response: { status: res.statusCode, fixture: null },\n });\n }\n\n return true;\n }\n\n health(): { status: string; [key: string]: unknown } {\n return {\n status: \"ok\",\n tools: this.tools.size,\n resources: this.resources.size,\n prompts: this.prompts.size,\n sessions: this.sessions.size,\n };\n }\n\n setJournal(journal: Journal): void {\n this.journal = journal;\n }\n\n setRegistry(registry: MetricsRegistry): void {\n this.registry = registry;\n }\n\n // ---- Standalone mode ----\n\n async start(): Promise<string> {\n if (this.server) {\n throw new Error(\"Server already started\");\n }\n\n const host = this.options.host ?? \"127.0.0.1\";\n const port = this.options.port ?? 0;\n\n return new Promise((resolve, reject) => {\n const srv = http.createServer((req, res) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => {\n const body = Buffer.concat(chunks).toString();\n\n this.requestHandler(req, res, body)\n .then(() => {\n if (this.journal) {\n this.journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"mcp\",\n response: { status: res.statusCode, fixture: null },\n });\n }\n })\n .catch((err) => {\n console.error(\"MCPMock request error:\", err);\n if (!res.headersSent) {\n res.writeHead(500);\n res.end(\"Internal server error\");\n }\n });\n });\n });\n\n srv.listen(port, host, () => {\n this.server = srv;\n const addr = srv.address();\n if (typeof addr === \"object\" && addr !== null) {\n resolve(`http://${host}:${addr.port}`);\n } else {\n resolve(`http://${host}:${port}`);\n }\n });\n\n srv.on(\"error\", reject);\n });\n }\n\n async stop(): Promise<void> {\n if (!this.server) {\n throw new Error(\"Server not started\");\n }\n const srv = this.server;\n this.server = null;\n await new Promise<void>((resolve, reject) => {\n srv.close((err) => (err ? reject(err) : resolve()));\n });\n }\n\n // ---- Inspection ----\n\n getRequests(): unknown[] {\n if (!this.journal) return [];\n return this.journal.getAll().filter((e) => e.service === \"mcp\");\n }\n\n getSessions(): Map<string, MCPSession> {\n return new Map(this.sessions);\n }\n\n reset(): this {\n this.tools.clear();\n this.resources.clear();\n this.prompts.clear();\n this.sessions.clear();\n this.requestHandler = this.buildHandler();\n return this;\n }\n\n // ---- Internal ----\n\n private buildHandler() {\n const state: MCPState = {\n serverInfo: this.options.serverInfo ?? { name: \"mcp-mock\", version: \"1.0.0\" },\n tools: this.tools,\n resources: this.resources,\n prompts: this.prompts,\n sessions: this.sessions,\n };\n return createMCPRequestHandler(state);\n }\n}\n"],"mappings":";;;;;;;AAiBA,IAAa,UAAb,MAA0C;CACxC,AAAQ,wBAGJ,IAAI,KAAK;CACb,AAAQ,4BACN,IAAI,KAAK;CACX,AAAQ,0BAMJ,IAAI,KAAK;CACb,AAAQ,2BAAoC,IAAI,KAAK;CACrD,AAAQ,SAA6B;CACrC,AAAQ,UAA0B;CAClC,AAAQ,WAAmC;CAC3C,AAAQ;CACR,AAAQ;CAER,YAAY,SAA0B;AACpC,OAAK,UAAU,WAAW,EAAE;AAC5B,OAAK,iBAAiB,KAAK,cAAc;;CAK3C,QAAQ,KAA8B;AACpC,OAAK,MAAM,IAAI,IAAI,MAAM,EAAE,KAAK,CAAC;AACjC,SAAO;;CAGT,WACE,MACA,SACM;EACN,MAAM,QAAQ,KAAK,MAAM,IAAI,KAAK;AAClC,MAAI,MACF,OAAM,UAAU;MAEhB,MAAK,MAAM,IAAI,MAAM;GAAE,KAAK,EAAE,MAAM;GAAE;GAAS,CAAC;AAElD,SAAO;;CAKT,YAAY,KAA4B,SAAoC;AAC1E,OAAK,UAAU,IAAI,IAAI,KAAK;GAAE;GAAK;GAAS,CAAC;AAC7C,SAAO;;CAKT,UACE,KACA,SACM;AACN,OAAK,QAAQ,IAAI,IAAI,MAAM;GAAE;GAAK;GAAS,CAAC;AAC5C,SAAO;;CAKT,MAAM,cACJ,KACA,KACA,UACkB;AAElB,MAAI,aAAa,OAAO,aAAa,GACnC,QAAO;AAET,MAAI,IAAI,WAAW,UAAU,IAAI,WAAW,SAC1C,QAAO;EAGT,MAAM,OAAO,MAAMA,yBAAS,IAAI;AAGhC,MAAI,KAAK,SACP,KAAI,IAAI,WAAW,SACjB,MAAK,SAAS,iBAAiB,6BAA6B,EAAE,QAAQ,kBAAkB,CAAC;MAEzF,KAAI;GACF,MAAM,SAAS,KAAK,MAAM,KAAK;GAC/B,MAAM,SACJ,OAAO,WAAW,YAAY,WAAW,QAAQ,YAAY,SACzD,OAAO,OAAO,OAAO,GACrB;AACN,QAAK,SAAS,iBAAiB,6BAA6B,EAAE,QAAQ,CAAC;UACjE;AACN,QAAK,SAAS,iBAAiB,6BAA6B,EAAE,QAAQ,WAAW,CAAC;;AAKxF,QAAM,KAAK,eAAe,KAAK,KAAK,KAAK;AAGzC,MAAI,KAAK,QACP,MAAK,QAAQ,IAAI;GACf,QAAQ,IAAI,UAAU;GACtB,MAAM,IAAI,OAAO;GACjB,SAASC,+BAAe,IAAI,QAAQ;GACpC,MAAM;GACN,SAAS;GACT,UAAU;IAAE,QAAQ,IAAI;IAAY,SAAS;IAAM;GACpD,CAAC;AAGJ,SAAO;;CAGT,SAAqD;AACnD,SAAO;GACL,QAAQ;GACR,OAAO,KAAK,MAAM;GAClB,WAAW,KAAK,UAAU;GAC1B,SAAS,KAAK,QAAQ;GACtB,UAAU,KAAK,SAAS;GACzB;;CAGH,WAAW,SAAwB;AACjC,OAAK,UAAU;;CAGjB,YAAY,UAAiC;AAC3C,OAAK,WAAW;;CAKlB,MAAM,QAAyB;AAC7B,MAAI,KAAK,OACP,OAAM,IAAI,MAAM,yBAAyB;EAG3C,MAAM,OAAO,KAAK,QAAQ,QAAQ;EAClC,MAAM,OAAO,KAAK,QAAQ,QAAQ;AAElC,SAAO,IAAI,SAAS,SAAS,WAAW;GACtC,MAAM,MAAMC,UAAK,cAAc,KAAK,QAAQ;IAC1C,MAAM,SAAmB,EAAE;AAC3B,QAAI,GAAG,SAAS,UAAkB,OAAO,KAAK,MAAM,CAAC;AACrD,QAAI,GAAG,aAAa;KAClB,MAAM,OAAO,OAAO,OAAO,OAAO,CAAC,UAAU;AAE7C,UAAK,eAAe,KAAK,KAAK,KAAK,CAChC,WAAW;AACV,UAAI,KAAK,QACP,MAAK,QAAQ,IAAI;OACf,QAAQ,IAAI,UAAU;OACtB,MAAM,IAAI,OAAO;OACjB,SAASD,+BAAe,IAAI,QAAQ;OACpC,MAAM;OACN,SAAS;OACT,UAAU;QAAE,QAAQ,IAAI;QAAY,SAAS;QAAM;OACpD,CAAC;OAEJ,CACD,OAAO,QAAQ;AACd,cAAQ,MAAM,0BAA0B,IAAI;AAC5C,UAAI,CAAC,IAAI,aAAa;AACpB,WAAI,UAAU,IAAI;AAClB,WAAI,IAAI,wBAAwB;;OAElC;MACJ;KACF;AAEF,OAAI,OAAO,MAAM,YAAY;AAC3B,SAAK,SAAS;IACd,MAAM,OAAO,IAAI,SAAS;AAC1B,QAAI,OAAO,SAAS,YAAY,SAAS,KACvC,SAAQ,UAAU,KAAK,GAAG,KAAK,OAAO;QAEtC,SAAQ,UAAU,KAAK,GAAG,OAAO;KAEnC;AAEF,OAAI,GAAG,SAAS,OAAO;IACvB;;CAGJ,MAAM,OAAsB;AAC1B,MAAI,CAAC,KAAK,OACR,OAAM,IAAI,MAAM,qBAAqB;EAEvC,MAAM,MAAM,KAAK;AACjB,OAAK,SAAS;AACd,QAAM,IAAI,SAAe,SAAS,WAAW;AAC3C,OAAI,OAAO,QAAS,MAAM,OAAO,IAAI,GAAG,SAAS,CAAE;IACnD;;CAKJ,cAAyB;AACvB,MAAI,CAAC,KAAK,QAAS,QAAO,EAAE;AAC5B,SAAO,KAAK,QAAQ,QAAQ,CAAC,QAAQ,MAAM,EAAE,YAAY,MAAM;;CAGjE,cAAuC;AACrC,SAAO,IAAI,IAAI,KAAK,SAAS;;CAG/B,QAAc;AACZ,OAAK,MAAM,OAAO;AAClB,OAAK,UAAU,OAAO;AACtB,OAAK,QAAQ,OAAO;AACpB,OAAK,SAAS,OAAO;AACrB,OAAK,iBAAiB,KAAK,cAAc;AACzC,SAAO;;CAKT,AAAQ,eAAe;AAQrB,SAAOE,4CAPiB;GACtB,YAAY,KAAK,QAAQ,cAAc;IAAE,MAAM;IAAY,SAAS;IAAS;GAC7E,OAAO,KAAK;GACZ,WAAW,KAAK;GAChB,SAAS,KAAK;GACd,UAAU,KAAK;GAChB,CACoC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Journal } from "./journal.cjs";
|
|
2
|
+
import { MetricsRegistry } from "./metrics.cjs";
|
|
3
|
+
import { Mountable } from "./types.cjs";
|
|
4
|
+
import { MCPContent, MCPMockOptions, MCPPromptDefinition, MCPPromptResult, MCPResourceContent, MCPResourceDefinition, MCPSession, MCPToolDefinition } from "./mcp-types.cjs";
|
|
5
|
+
import * as http from "node:http";
|
|
6
|
+
|
|
7
|
+
//#region src/mcp-mock.d.ts
|
|
8
|
+
declare class MCPMock implements Mountable {
|
|
9
|
+
private tools;
|
|
10
|
+
private resources;
|
|
11
|
+
private prompts;
|
|
12
|
+
private sessions;
|
|
13
|
+
private server;
|
|
14
|
+
private journal;
|
|
15
|
+
private registry;
|
|
16
|
+
private options;
|
|
17
|
+
private requestHandler;
|
|
18
|
+
constructor(options?: MCPMockOptions);
|
|
19
|
+
addTool(def: MCPToolDefinition): this;
|
|
20
|
+
onToolCall(name: string, handler: (args: unknown) => MCPContent[] | string | Promise<MCPContent[] | string>): this;
|
|
21
|
+
addResource(def: MCPResourceDefinition, content?: MCPResourceContent): this;
|
|
22
|
+
addPrompt(def: MCPPromptDefinition, handler?: (args: unknown) => MCPPromptResult | Promise<MCPPromptResult>): this;
|
|
23
|
+
handleRequest(req: http.IncomingMessage, res: http.ServerResponse, pathname: string): Promise<boolean>;
|
|
24
|
+
health(): {
|
|
25
|
+
status: string;
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
};
|
|
28
|
+
setJournal(journal: Journal): void;
|
|
29
|
+
setRegistry(registry: MetricsRegistry): void;
|
|
30
|
+
start(): Promise<string>;
|
|
31
|
+
stop(): Promise<void>;
|
|
32
|
+
getRequests(): unknown[];
|
|
33
|
+
getSessions(): Map<string, MCPSession>;
|
|
34
|
+
reset(): this;
|
|
35
|
+
private buildHandler;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=mcp-mock.d.ts.map
|
|
38
|
+
//#endregion
|
|
39
|
+
export { MCPMock };
|
|
40
|
+
//# sourceMappingURL=mcp-mock.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-mock.d.cts","names":[],"sources":["../src/mcp-mock.ts"],"sourcesContent":[],"mappings":";;;;;;;cAiBa,OAAA,YAAmB;;EAAnB,QAAA,SAAQ;EAAA,QAAA,OAAA;UAqBG,QAAA;UAOT,MAAA;UAOiB,OAAA;UAAgC,QAAA;UAAR,OAAA;UAarC,cAAA;aAAiC,CAAA,OAAA,CAAA,EA3B5B,cA2B4B;SAQ3C,CAAA,GAAA,EA5BM,iBA4BN,CAAA,EAAA,IAAA;YACwB,CAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,IAAA,EAAA,OAAA,EAAA,GAtBD,UAsBC,EAAA,GAAA,MAAA,GAtBuB,OAsBvB,CAtB+B,UAsB/B,EAAA,GAAA,MAAA,CAAA,CAAA,EAAA,IAAA;aAA0B,CAAA,GAAA,EATxC,qBASwC,EAAA,OAAA,CAAA,EATP,kBASO,CAAA,EAAA,IAAA;WAAR,CAAA,GAAA,EAD1C,mBAC0C,EAAA,OAAA,CAAA,EAAA,CAAA,IAAA,EAAA,OAAA,EAAA,GAAlB,eAAkB,GAAA,OAAA,CAAQ,eAAR,CAAA,CAAA,EAAA,IAAA;eASrC,CAAA,GAAA,EAAL,IAAA,CAAK,eAAA,EAAA,GAAA,EACL,IAAA,CAAK,cADA,EAAA,QAAA,EAAA,MAAA,CAAA,EAGT,OAHS,CAAA,OAAA,CAAA;QACL,CAAA,CAAK,EAAA;IAET,MAAA,EAAA,MAAA;IAwDiB,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,OAAA;;YAUL,CAAA,OAAA,EAVK,OAUL,CAAA,EAAA,IAAA;aAoDD,CAAA,QAAA,EA1DQ,eA0DR,CAAA,EAAA,IAAA;OAkBa,CAAA,CAAA,EAtEZ,OAsEY,CAAA,MAAA,CAAA;MAAZ,CAAA,CAAA,EAlBD,OAkBC,CAAA,IAAA,CAAA;aA7Me,CAAA,CAAA,EAAA,OAAA,EAAA;EAAS,WAAA,CAAA,CAAA,EA6MxB,GA7MwB,CAAA,MAAA,EA6MZ,UA7MY,CAAA"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Journal } from "./journal.js";
|
|
2
|
+
import { MetricsRegistry } from "./metrics.js";
|
|
3
|
+
import { Mountable } from "./types.js";
|
|
4
|
+
import { MCPContent, MCPMockOptions, MCPPromptDefinition, MCPPromptResult, MCPResourceContent, MCPResourceDefinition, MCPSession, MCPToolDefinition } from "./mcp-types.js";
|
|
5
|
+
import * as http from "node:http";
|
|
6
|
+
|
|
7
|
+
//#region src/mcp-mock.d.ts
|
|
8
|
+
declare class MCPMock implements Mountable {
|
|
9
|
+
private tools;
|
|
10
|
+
private resources;
|
|
11
|
+
private prompts;
|
|
12
|
+
private sessions;
|
|
13
|
+
private server;
|
|
14
|
+
private journal;
|
|
15
|
+
private registry;
|
|
16
|
+
private options;
|
|
17
|
+
private requestHandler;
|
|
18
|
+
constructor(options?: MCPMockOptions);
|
|
19
|
+
addTool(def: MCPToolDefinition): this;
|
|
20
|
+
onToolCall(name: string, handler: (args: unknown) => MCPContent[] | string | Promise<MCPContent[] | string>): this;
|
|
21
|
+
addResource(def: MCPResourceDefinition, content?: MCPResourceContent): this;
|
|
22
|
+
addPrompt(def: MCPPromptDefinition, handler?: (args: unknown) => MCPPromptResult | Promise<MCPPromptResult>): this;
|
|
23
|
+
handleRequest(req: http.IncomingMessage, res: http.ServerResponse, pathname: string): Promise<boolean>;
|
|
24
|
+
health(): {
|
|
25
|
+
status: string;
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
};
|
|
28
|
+
setJournal(journal: Journal): void;
|
|
29
|
+
setRegistry(registry: MetricsRegistry): void;
|
|
30
|
+
start(): Promise<string>;
|
|
31
|
+
stop(): Promise<void>;
|
|
32
|
+
getRequests(): unknown[];
|
|
33
|
+
getSessions(): Map<string, MCPSession>;
|
|
34
|
+
reset(): this;
|
|
35
|
+
private buildHandler;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=mcp-mock.d.ts.map
|
|
38
|
+
//#endregion
|
|
39
|
+
export { MCPMock };
|
|
40
|
+
//# sourceMappingURL=mcp-mock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-mock.d.ts","names":[],"sources":["../src/mcp-mock.ts"],"sourcesContent":[],"mappings":";;;;;;;cAiBa,OAAA,YAAmB;;EAAnB,QAAA,SAAQ;EAAA,QAAA,OAAA;UAqBG,QAAA;UAOT,MAAA;UAOiB,OAAA;UAAgC,QAAA;UAAR,OAAA;UAarC,cAAA;aAAiC,CAAA,OAAA,CAAA,EA3B5B,cA2B4B;SAQ3C,CAAA,GAAA,EA5BM,iBA4BN,CAAA,EAAA,IAAA;YACwB,CAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,IAAA,EAAA,OAAA,EAAA,GAtBD,UAsBC,EAAA,GAAA,MAAA,GAtBuB,OAsBvB,CAtB+B,UAsB/B,EAAA,GAAA,MAAA,CAAA,CAAA,EAAA,IAAA;aAA0B,CAAA,GAAA,EATxC,qBASwC,EAAA,OAAA,CAAA,EATP,kBASO,CAAA,EAAA,IAAA;WAAR,CAAA,GAAA,EAD1C,mBAC0C,EAAA,OAAA,CAAA,EAAA,CAAA,IAAA,EAAA,OAAA,EAAA,GAAlB,eAAkB,GAAA,OAAA,CAAQ,eAAR,CAAA,CAAA,EAAA,IAAA;eASrC,CAAA,GAAA,EAAL,IAAA,CAAK,eAAA,EAAA,GAAA,EACL,IAAA,CAAK,cADA,EAAA,QAAA,EAAA,MAAA,CAAA,EAGT,OAHS,CAAA,OAAA,CAAA;QACL,CAAA,CAAK,EAAA;IAET,MAAA,EAAA,MAAA;IAwDiB,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,OAAA;;YAUL,CAAA,OAAA,EAVK,OAUL,CAAA,EAAA,IAAA;aAoDD,CAAA,QAAA,EA1DQ,eA0DR,CAAA,EAAA,IAAA;OAkBa,CAAA,CAAA,EAtEZ,OAsEY,CAAA,MAAA,CAAA;MAAZ,CAAA,CAAA,EAlBD,OAkBC,CAAA,IAAA,CAAA;aA7Me,CAAA,CAAA,EAAA,OAAA,EAAA;EAAS,WAAA,CAAA,CAAA,EA6MxB,GA7MwB,CAAA,MAAA,EA6MZ,UA7MY,CAAA"}
|
package/dist/mcp-mock.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { flattenHeaders, readBody } from "./helpers.js";
|
|
2
|
+
import { createMCPRequestHandler } from "./mcp-handler.js";
|
|
3
|
+
import * as http from "node:http";
|
|
4
|
+
|
|
5
|
+
//#region src/mcp-mock.ts
|
|
6
|
+
var MCPMock = class {
|
|
7
|
+
tools = /* @__PURE__ */ new Map();
|
|
8
|
+
resources = /* @__PURE__ */ new Map();
|
|
9
|
+
prompts = /* @__PURE__ */ new Map();
|
|
10
|
+
sessions = /* @__PURE__ */ new Map();
|
|
11
|
+
server = null;
|
|
12
|
+
journal = null;
|
|
13
|
+
registry = null;
|
|
14
|
+
options;
|
|
15
|
+
requestHandler;
|
|
16
|
+
constructor(options) {
|
|
17
|
+
this.options = options ?? {};
|
|
18
|
+
this.requestHandler = this.buildHandler();
|
|
19
|
+
}
|
|
20
|
+
addTool(def) {
|
|
21
|
+
this.tools.set(def.name, { def });
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
onToolCall(name, handler) {
|
|
25
|
+
const entry = this.tools.get(name);
|
|
26
|
+
if (entry) entry.handler = handler;
|
|
27
|
+
else this.tools.set(name, {
|
|
28
|
+
def: { name },
|
|
29
|
+
handler
|
|
30
|
+
});
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
addResource(def, content) {
|
|
34
|
+
this.resources.set(def.uri, {
|
|
35
|
+
def,
|
|
36
|
+
content
|
|
37
|
+
});
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
addPrompt(def, handler) {
|
|
41
|
+
this.prompts.set(def.name, {
|
|
42
|
+
def,
|
|
43
|
+
handler
|
|
44
|
+
});
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
async handleRequest(req, res, pathname) {
|
|
48
|
+
if (pathname !== "/" && pathname !== "") return false;
|
|
49
|
+
if (req.method !== "POST" && req.method !== "DELETE") return false;
|
|
50
|
+
const body = await readBody(req);
|
|
51
|
+
if (this.registry) if (req.method === "DELETE") this.registry.incrementCounter("aimock_mcp_requests_total", { method: "session/delete" });
|
|
52
|
+
else try {
|
|
53
|
+
const parsed = JSON.parse(body);
|
|
54
|
+
const method = typeof parsed === "object" && parsed !== null && "method" in parsed ? String(parsed.method) : "unknown";
|
|
55
|
+
this.registry.incrementCounter("aimock_mcp_requests_total", { method });
|
|
56
|
+
} catch {
|
|
57
|
+
this.registry.incrementCounter("aimock_mcp_requests_total", { method: "unknown" });
|
|
58
|
+
}
|
|
59
|
+
await this.requestHandler(req, res, body);
|
|
60
|
+
if (this.journal) this.journal.add({
|
|
61
|
+
method: req.method ?? "POST",
|
|
62
|
+
path: req.url ?? "/",
|
|
63
|
+
headers: flattenHeaders(req.headers),
|
|
64
|
+
body: null,
|
|
65
|
+
service: "mcp",
|
|
66
|
+
response: {
|
|
67
|
+
status: res.statusCode,
|
|
68
|
+
fixture: null
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
health() {
|
|
74
|
+
return {
|
|
75
|
+
status: "ok",
|
|
76
|
+
tools: this.tools.size,
|
|
77
|
+
resources: this.resources.size,
|
|
78
|
+
prompts: this.prompts.size,
|
|
79
|
+
sessions: this.sessions.size
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
setJournal(journal) {
|
|
83
|
+
this.journal = journal;
|
|
84
|
+
}
|
|
85
|
+
setRegistry(registry) {
|
|
86
|
+
this.registry = registry;
|
|
87
|
+
}
|
|
88
|
+
async start() {
|
|
89
|
+
if (this.server) throw new Error("Server already started");
|
|
90
|
+
const host = this.options.host ?? "127.0.0.1";
|
|
91
|
+
const port = this.options.port ?? 0;
|
|
92
|
+
return new Promise((resolve, reject) => {
|
|
93
|
+
const srv = http.createServer((req, res) => {
|
|
94
|
+
const chunks = [];
|
|
95
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
96
|
+
req.on("end", () => {
|
|
97
|
+
const body = Buffer.concat(chunks).toString();
|
|
98
|
+
this.requestHandler(req, res, body).then(() => {
|
|
99
|
+
if (this.journal) this.journal.add({
|
|
100
|
+
method: req.method ?? "POST",
|
|
101
|
+
path: req.url ?? "/",
|
|
102
|
+
headers: flattenHeaders(req.headers),
|
|
103
|
+
body: null,
|
|
104
|
+
service: "mcp",
|
|
105
|
+
response: {
|
|
106
|
+
status: res.statusCode,
|
|
107
|
+
fixture: null
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}).catch((err) => {
|
|
111
|
+
console.error("MCPMock request error:", err);
|
|
112
|
+
if (!res.headersSent) {
|
|
113
|
+
res.writeHead(500);
|
|
114
|
+
res.end("Internal server error");
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
srv.listen(port, host, () => {
|
|
120
|
+
this.server = srv;
|
|
121
|
+
const addr = srv.address();
|
|
122
|
+
if (typeof addr === "object" && addr !== null) resolve(`http://${host}:${addr.port}`);
|
|
123
|
+
else resolve(`http://${host}:${port}`);
|
|
124
|
+
});
|
|
125
|
+
srv.on("error", reject);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
async stop() {
|
|
129
|
+
if (!this.server) throw new Error("Server not started");
|
|
130
|
+
const srv = this.server;
|
|
131
|
+
this.server = null;
|
|
132
|
+
await new Promise((resolve, reject) => {
|
|
133
|
+
srv.close((err) => err ? reject(err) : resolve());
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
getRequests() {
|
|
137
|
+
if (!this.journal) return [];
|
|
138
|
+
return this.journal.getAll().filter((e) => e.service === "mcp");
|
|
139
|
+
}
|
|
140
|
+
getSessions() {
|
|
141
|
+
return new Map(this.sessions);
|
|
142
|
+
}
|
|
143
|
+
reset() {
|
|
144
|
+
this.tools.clear();
|
|
145
|
+
this.resources.clear();
|
|
146
|
+
this.prompts.clear();
|
|
147
|
+
this.sessions.clear();
|
|
148
|
+
this.requestHandler = this.buildHandler();
|
|
149
|
+
return this;
|
|
150
|
+
}
|
|
151
|
+
buildHandler() {
|
|
152
|
+
return createMCPRequestHandler({
|
|
153
|
+
serverInfo: this.options.serverInfo ?? {
|
|
154
|
+
name: "mcp-mock",
|
|
155
|
+
version: "1.0.0"
|
|
156
|
+
},
|
|
157
|
+
tools: this.tools,
|
|
158
|
+
resources: this.resources,
|
|
159
|
+
prompts: this.prompts,
|
|
160
|
+
sessions: this.sessions
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
//#endregion
|
|
166
|
+
export { MCPMock };
|
|
167
|
+
//# sourceMappingURL=mcp-mock.js.map
|