feat(mcp): add snippet CRUD tools to MCP server
- register list_snippets, get_snippet, create_snippet, update_snippet, delete_snippet - inject SnippetService into McpService and import SnippetModule into McpModule - cover all five tools with integration tests including error cases
This commit is contained in:
@@ -284,4 +284,173 @@ describe("McpController", () => {
|
||||
expect(cleared.timeoutSeconds).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
// ── Snippet CRUD tools ────────────────────────────────────────────────────
|
||||
|
||||
describe("list_snippets", () => {
|
||||
it("returns a paginated result with a data array", async () => {
|
||||
const { status, rpc } = await mcpCall("list_snippets");
|
||||
expect(status).toBe(200);
|
||||
const result = rpc.result as { content: { text: string }[] };
|
||||
const body = JSON.parse(result.content[0].text) as {
|
||||
data: unknown[];
|
||||
total: number;
|
||||
};
|
||||
expect(Array.isArray(body.data)).toBe(true);
|
||||
expect(typeof body.total).toBe("number");
|
||||
});
|
||||
});
|
||||
|
||||
describe("create_snippet", () => {
|
||||
it("creates a snippet and returns it with alias and title", async () => {
|
||||
const { status, rpc } = await mcpCall("create_snippet", {
|
||||
alias: "mcp-test-snippet",
|
||||
title: "MCP Test Snippet",
|
||||
description: "Created by integration test",
|
||||
code: "await page.click('#btn');",
|
||||
});
|
||||
expect(status).toBe(200);
|
||||
const result = rpc.result as { content: { text: string }[] };
|
||||
const created = JSON.parse(result.content[0].text) as {
|
||||
id: string;
|
||||
alias: string;
|
||||
title: string;
|
||||
description: string | null;
|
||||
code: string;
|
||||
};
|
||||
expect(created.alias).toBe("mcp-test-snippet");
|
||||
expect(created.title).toBe("MCP Test Snippet");
|
||||
expect(created.description).toBe("Created by integration test");
|
||||
expect(created.code).toBe("await page.click('#btn');");
|
||||
expect(typeof created.id).toBe("string");
|
||||
});
|
||||
|
||||
it("returns an MCP error when alias already exists", async () => {
|
||||
await mcpCall("create_snippet", {
|
||||
alias: "mcp-duplicate-snippet",
|
||||
title: "First",
|
||||
code: "return 1;",
|
||||
});
|
||||
const { status, rpc } = await mcpCall("create_snippet", {
|
||||
alias: "mcp-duplicate-snippet",
|
||||
title: "Second",
|
||||
code: "return 2;",
|
||||
});
|
||||
expect(status).toBe(200);
|
||||
const result = rpc.result as { isError: boolean };
|
||||
expect(result.isError).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("get_snippet", () => {
|
||||
it("returns the created snippet by id", async () => {
|
||||
const createRpc = (
|
||||
await mcpCall("create_snippet", {
|
||||
alias: "mcp-get-snippet",
|
||||
title: "Get Me",
|
||||
code: "return 42;",
|
||||
})
|
||||
).rpc;
|
||||
const created = JSON.parse(
|
||||
(createRpc.result as { content: { text: string }[] }).content[0].text,
|
||||
) as { id: string };
|
||||
|
||||
const { status, rpc } = await mcpCall("get_snippet", { id: created.id });
|
||||
expect(status).toBe(200);
|
||||
const result = rpc.result as { content: { text: string }[] };
|
||||
const fetched = JSON.parse(result.content[0].text) as {
|
||||
id: string;
|
||||
alias: string;
|
||||
};
|
||||
expect(fetched.id).toBe(created.id);
|
||||
expect(fetched.alias).toBe("mcp-get-snippet");
|
||||
});
|
||||
|
||||
it("returns an MCP error for a non-existent snippet id", async () => {
|
||||
const { status, rpc } = await mcpCall("get_snippet", {
|
||||
id: "00000000-0000-0000-0000-000000000000",
|
||||
});
|
||||
expect(status).toBe(200);
|
||||
const result = rpc.result as { isError: boolean };
|
||||
expect(result.isError).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("update_snippet", () => {
|
||||
it("updates alias, title, and code of an existing snippet", async () => {
|
||||
const createRpc = (
|
||||
await mcpCall("create_snippet", {
|
||||
alias: "mcp-upd-snippet-orig",
|
||||
title: "Original Title",
|
||||
code: "return 1;",
|
||||
})
|
||||
).rpc;
|
||||
const created = JSON.parse(
|
||||
(createRpc.result as { content: { text: string }[] }).content[0].text,
|
||||
) as { id: string };
|
||||
|
||||
const { status, rpc } = await mcpCall("update_snippet", {
|
||||
id: created.id,
|
||||
alias: "mcp-upd-snippet-new",
|
||||
title: "Updated Title",
|
||||
code: "return 2;",
|
||||
});
|
||||
expect(status).toBe(200);
|
||||
const result = rpc.result as { content: { text: string }[] };
|
||||
const updated = JSON.parse(result.content[0].text) as {
|
||||
alias: string;
|
||||
title: string;
|
||||
code: string;
|
||||
};
|
||||
expect(updated.alias).toBe("mcp-upd-snippet-new");
|
||||
expect(updated.title).toBe("Updated Title");
|
||||
expect(updated.code).toBe("return 2;");
|
||||
});
|
||||
|
||||
it("returns an MCP error for a non-existent snippet id", async () => {
|
||||
const { status, rpc } = await mcpCall("update_snippet", {
|
||||
id: "00000000-0000-0000-0000-000000000000",
|
||||
title: "Ghost",
|
||||
});
|
||||
expect(status).toBe(200);
|
||||
const result = rpc.result as { isError: boolean };
|
||||
expect(result.isError).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("delete_snippet", () => {
|
||||
it("deletes an existing snippet and confirms deletion", async () => {
|
||||
const createRpc = (
|
||||
await mcpCall("create_snippet", {
|
||||
alias: "mcp-del-snippet",
|
||||
title: "Delete Me",
|
||||
code: "return 0;",
|
||||
})
|
||||
).rpc;
|
||||
const created = JSON.parse(
|
||||
(createRpc.result as { content: { text: string }[] }).content[0].text,
|
||||
) as { id: string };
|
||||
|
||||
const { status, rpc } = await mcpCall("delete_snippet", {
|
||||
id: created.id,
|
||||
});
|
||||
expect(status).toBe(200);
|
||||
const result = rpc.result as { content: { text: string }[] };
|
||||
expect(result.content[0].text).toContain(created.id);
|
||||
|
||||
// confirm gone
|
||||
const getResult = (await mcpCall("get_snippet", { id: created.id }))
|
||||
.rpc.result as { isError: boolean };
|
||||
expect(getResult.isError).toBe(true);
|
||||
});
|
||||
|
||||
it("returns an MCP error for a non-existent snippet id", async () => {
|
||||
const { status, rpc } = await mcpCall("delete_snippet", {
|
||||
id: "00000000-0000-0000-0000-000000000000",
|
||||
});
|
||||
expect(status).toBe(200);
|
||||
const result = rpc.result as { isError: boolean };
|
||||
expect(result.isError).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user