Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/modules/mcp/mcp-registry.server.test.ts
T

54 lines
2.4 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
import {
__clearMcpRegistryForTests,
createMcpExternalToolsProvider,
listMcpToolsForChatbot,
registerMcpToolForChatbot,
unregisterMcpToolForChatbot,
} from "./mcp-registry.server";
describe("mcp-registry", () => {
beforeEach(() => {
__clearMcpRegistryForTests();
});
it("registers, lists, and unregisters tools", () => {
registerMcpToolForChatbot({ toolName: "ping", description: "ping" });
registerMcpToolForChatbot({ toolName: "stat", description: "stat" });
expect(listMcpToolsForChatbot().map((t) => t.toolName)).toEqual(["ping", "stat"]);
unregisterMcpToolForChatbot("ping");
expect(listMcpToolsForChatbot().map((t) => t.toolName)).toEqual(["stat"]);
});
it("ExternalToolsProvider.list returns the registered tools (tenant-aware boundary)", async () => {
registerMcpToolForChatbot({ toolName: "ping", description: "ping" });
const provider = createMcpExternalToolsProvider();
const tools = await provider.list("tenant-a");
expect(tools).toEqual([{ toolName: "ping", description: "ping" }]);
});
it("ExternalToolsProvider.invoke forwards tenant + user to the tool invoker", async () => {
const invoke = vi.fn(async (inputs, ctx) => ({ ok: true, ctx, inputs }));
registerMcpToolForChatbot({ toolName: "ping", description: "ping", invoke });
const provider = createMcpExternalToolsProvider();
const out = await provider.invoke({ tenantId: "tenant-a", userId: "u1", toolName: "ping", inputs: { hi: 1 } });
expect(invoke).toHaveBeenCalledOnce();
expect(invoke).toHaveBeenCalledWith({ hi: 1 }, { tenantId: "tenant-a", userId: "u1" });
expect(out).toMatchObject({ ok: true });
});
it("invoke throws when the tool is not registered (no LLM-driven impersonation)", async () => {
const provider = createMcpExternalToolsProvider();
await expect(
provider.invoke({ tenantId: "tenant-a", userId: "u1", toolName: "unknown", inputs: {} }),
).rejects.toThrow(/not registered/);
});
it("invoke returns a structured not-invocable payload for read-only tools", async () => {
registerMcpToolForChatbot({ toolName: "ping", description: "ping" });
const provider = createMcpExternalToolsProvider();
const out = await provider.invoke({ tenantId: "tenant-a", userId: "u1", toolName: "ping", inputs: {} });
expect(out).toMatchObject({ status: "not-invocable", toolName: "ping" });
});
});