Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,254 @@
/**
* MCPServer wire-side edge-case coverage — exercises branches in the
* dispatcher-mounted JSON-RPC handlers and helpers: `tools/call` with no
* arguments / no `_meta`, signal forwarding to `createSamplingMessage` /
* `listClientRoots`, `emitLog` without a logger, authorizer accept-branch,
* invokeTool progress + abort listeners, Zod root-level rejection.
*/
import { describe, expect, it } from "@suaveplan/testing/runner";
import { MCPClient } from "../client/mcp-client/mcp-client.js";
import { MCP_CODES } from "../error-codes/error-codes.js";
import { JsonRpcDispatcher } from "../jsonrpc/dispatcher/dispatcher.js";
import { MCPServer } from "../server/mcp-server/mcp-server.js";
import { InMemoryTransport } from "../transport/in-memory/in-memory.js";
describe("server wire edge cases", () => {
it("tools/call without arguments uses empty-object schema input", {
timeout: 5000,
}, async () => {
const z = await import("zod");
const server = new MCPServer({
info: { name: "s", version: "0.1.0" },
});
server.registerTool({
name: "no-arg",
inputSchema: {},
paramsSchema: z.default.object({}).optional(),
async handler() {
return { content: [{ type: "text", text: "ok" }] };
},
});
const [a, b] = InMemoryTransport.pair();
await server.start(a);
const peer = new JsonRpcDispatcher({
side: "client",
transport: b,
dispatcherId: "X",
});
const out = (await peer.request("tools/call", {
name: "no-arg",
})) as { content: Array<{ text: string }> };
expect(out.content[0]?.text).toBe("ok");
await peer.close();
await server.stop();
});
it("tools/call without _meta has undefined progressToken", {
timeout: 5000,
}, async () => {
const server = new MCPServer({
info: { name: "s", version: "0.1.0" },
});
server.registerTool({
name: "no-meta",
inputSchema: {},
async handler(_args, ctx) {
ctx.progress(1, 2);
return { content: [{ type: "text", text: "done" }] };
},
});
const [a, b] = InMemoryTransport.pair();
await server.start(a);
const peer = new JsonRpcDispatcher({
side: "client",
transport: b,
dispatcherId: "X",
});
const out = (await peer.request("tools/call", {
name: "no-meta",
arguments: {},
})) as { content: Array<{ text: string }> };
expect(out.content[0]?.text).toBe("done");
await peer.close();
await server.stop();
});
it("createSamplingMessage and listClientRoots forward signal", {
timeout: 5000,
}, async () => {
const server = new MCPServer({
info: { name: "s", version: "0.1.0" },
});
const client = new MCPClient({
info: { name: "c", version: "0.1.0" },
});
client.setSamplingProvider(async () => ({
role: "assistant" as const,
content: { type: "text" as const, text: "ok" },
model: "demo",
}));
client.setRootsProvider(async () => [{ uri: "file:///x" }]);
const [a, b] = InMemoryTransport.pair();
await server.start(a);
await client.connect(b);
const ac = new AbortController();
const r1 = await server.createSamplingMessage(
{
messages: [
{
role: "user",
content: { type: "text", text: "hi" },
},
],
maxTokens: 10,
},
{ signal: ac.signal }
);
expect(r1.role).toBe("assistant");
const r2 = await server.listClientRoots({ signal: ac.signal });
expect(r2.length).toBe(1);
await client.disconnect();
await server.stop();
});
it("emitLog without logger argument omits the logger field", {
timeout: 5000,
}, async () => {
const server = new MCPServer({
info: { name: "s", version: "0.1.0" },
});
const [a, b] = InMemoryTransport.pair();
await server.start(a);
const peer = new JsonRpcDispatcher({
side: "client",
transport: b,
dispatcherId: "X",
});
const observed: Array<{ logger?: string }> = [];
peer.onNotification("notifications/message", (params) =>
observed.push(params as { logger?: string })
);
server.emitLog("info", { x: 1 });
await new Promise((r) => setTimeout(r, 10));
expect(observed[0]?.logger).toBeUndefined();
await peer.close();
await server.stop();
});
it("authorize hook accepting the call passes the throw branch", {
timeout: 5000,
}, async () => {
const server = new MCPServer({
info: { name: "s", version: "0.1.0" },
});
server.setAuthorizer(async () => true);
server.registerTool({
name: "ok",
inputSchema: {},
async handler() {
return { content: [{ type: "text", text: "yes" }] };
},
});
const [a, b] = InMemoryTransport.pair();
await server.start(a);
const peer = new JsonRpcDispatcher({
side: "client",
transport: b,
dispatcherId: "X",
});
const result = (await peer.request("tools/call", {
name: "ok",
arguments: {},
})) as { content: Array<{ text: string }> };
expect(result.content[0]?.text).toBe("yes");
await peer.close();
await server.stop();
});
it("invokeTool handler progress and abort-listener fire", {
timeout: 5000,
}, async () => {
const server = new MCPServer({
info: { name: "s", version: "0.1.0" },
});
server.registerTool({
name: "p",
inputSchema: {},
async handler(_args, ctx) {
ctx.progress(1, 2);
ctx.progress(2, 2);
return { content: [{ type: "text", text: "done" }] };
},
});
const ac = new AbortController();
const result = await server.invokeTool("p", {}, { signal: ac.signal });
expect(result.content[0]).toMatchObject({ text: "done" });
ac.abort();
});
it("server readResource and getPromptMessages with aborting signal", {
timeout: 5000,
}, async () => {
const server = new MCPServer({
info: { name: "s", version: "0.1.0" },
});
server.registerResource({
uri: "mem://r",
name: "r",
async handler() {
return { uri: "mem://r", text: "" };
},
});
server.registerPrompt({
name: "p",
async handler() {
return [];
},
});
const ac = new AbortController();
await server.readResource("mem://r", { signal: ac.signal });
await server.getPromptMessages("p", {}, { signal: ac.signal });
ac.abort();
});
it("Zod root-level rejection formats as `(root)` in error message", {
timeout: 5000,
}, async () => {
const z = await import("zod");
const server = new MCPServer({
info: { name: "s", version: "0.1.0" },
});
server.registerTool({
name: "needs-string",
inputSchema: {},
paramsSchema: z.default.string(),
async handler() {
return { content: [] };
},
});
await expect(
server.invokeTool("needs-string", { not: "a-string" })
).rejects.toMatchObject({ code: MCP_CODES.INVALID_PARAMS });
const [a, b] = InMemoryTransport.pair();
const left = new JsonRpcDispatcher({
side: "server",
transport: a,
dispatcherId: "L",
paramSchemas: {
strictly: z.default.string(),
},
});
const right = new JsonRpcDispatcher({
side: "client",
transport: b,
dispatcherId: "R",
});
left.registerHandler("strictly", async () => ({}));
await expect(
right.request("strictly", { not: "a-string" })
).rejects.toMatchObject({ code: MCP_CODES.INVALID_PARAMS });
await left.close();
await right.close();
});
});