Skip to content

Commit 42c11a7

Browse files
committed
feat(tests): add unit tests for MCP tool registration and validation
- Introduced a new test file `server.test.ts` to validate MCP tool registration. - Added tests to ensure at least one tool is registered, uniqueness of tool names, and compliance with naming and description constraints. - Updated the description of the `currents-list-affected-tests` tool for clarity.
1 parent cd8062e commit 42c11a7

2 files changed

Lines changed: 87 additions & 1 deletion

File tree

mcp-server/src/server.test.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { describe, expect, it, vi } from "vitest";
2+
3+
const { registeredTools } = vi.hoisted(() => {
4+
const registeredTools: Array<{ name: string; description: string }> = [];
5+
return { registeredTools };
6+
});
7+
8+
vi.mock("@modelcontextprotocol/sdk/server/mcp.js", () => ({
9+
McpServer: class {
10+
registerTool(
11+
name: string,
12+
opts: { description: string; inputSchema: unknown },
13+
_handler: unknown
14+
) {
15+
registeredTools.push({ name, description: opts.description });
16+
}
17+
},
18+
}));
19+
20+
vi.mock("@modelcontextprotocol/sdk/server/stdio.js", () => ({
21+
StdioServerTransport: class {},
22+
}));
23+
24+
// Triggers all registerTool calls at module scope
25+
import "./server.js";
26+
27+
// SEP-986 (Final): tool names SHOULD be 1–64 characters
28+
const TOOL_NAME_MAX_LENGTH = 64;
29+
// Allowed: a-zA-Z0-9 _ - . /
30+
const TOOL_NAME_PATTERN = /^[a-zA-Z0-9_\-./]+$/;
31+
// No hard spec limit; 1024 is the practical ceiling across major LLM providers
32+
const TOOL_DESCRIPTION_MAX_LENGTH = 1024;
33+
34+
describe("MCP tool best practices", () => {
35+
it("has at least one registered tool", () => {
36+
expect(registeredTools.length).toBeGreaterThan(0);
37+
});
38+
39+
it("tool names are unique", () => {
40+
const names = registeredTools.map((t) => t.name);
41+
const dupes = names.filter((n, i) => names.indexOf(n) !== i);
42+
expect(dupes, `duplicate tool names: ${dupes.join(", ")}`).toHaveLength(0);
43+
});
44+
45+
describe.each(registeredTools)("$name", ({ name, description }) => {
46+
// ── name constraints (SEP-986) ──────────────────────────────
47+
it(`name length ≤ ${TOOL_NAME_MAX_LENGTH}`, () => {
48+
expect(
49+
name.length,
50+
`"${name}" is ${name.length} chars`
51+
).toBeLessThanOrEqual(TOOL_NAME_MAX_LENGTH);
52+
});
53+
54+
it("name contains only allowed characters", () => {
55+
expect(name).toMatch(TOOL_NAME_PATTERN);
56+
});
57+
58+
it("name is not empty", () => {
59+
expect(name.length).toBeGreaterThan(0);
60+
});
61+
62+
// ── description constraints ─────────────────────────────────
63+
it("description is not empty", () => {
64+
expect(description.length).toBeGreaterThan(0);
65+
});
66+
67+
it(`description length ≤ ${TOOL_DESCRIPTION_MAX_LENGTH}`, () => {
68+
expect(
69+
description.length,
70+
`description is ${description.length} chars`
71+
).toBeLessThanOrEqual(TOOL_DESCRIPTION_MAX_LENGTH);
72+
});
73+
74+
it("description has no leading/trailing whitespace", () => {
75+
expect(description).toBe(description.trim());
76+
});
77+
78+
it("description starts with a capital letter", () => {
79+
expect(description).toMatch(/^[A-Z]/);
80+
});
81+
82+
it("description ends with a period", () => {
83+
expect(description.at(-1)).toBe(".");
84+
});
85+
});
86+
});

mcp-server/src/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ server.registerTool(
124124
"currents-list-affected-tests",
125125
{
126126
description:
127-
"List tests affected by actions (quarantine, skip, tag) for a project within a date range. Returns aggregated data grouped by test signature. Supports filtering by action types, action ID, status, and search. Requires projectId, date_start, and date_end. (Preview endpoint: fields and path may change.)",
127+
"List tests affected by actions (quarantine, skip, tag) for a project within a date range. Returns aggregated data grouped by test signature. Supports filtering by action types, action ID, status, and search. Requires projectId, date_start, and date_end. Preview endpoint: fields and path may change.",
128128
inputSchema: listAffectedTestsTool.schema,
129129
},
130130
listAffectedTestsTool.handler

0 commit comments

Comments
 (0)