Skip to main content
Glama

hypertool-mcp

manager.annotations.test.tsโ€ข11.6 kB
/** * Tests for ToolsetManager annotation functionality */ import { describe, it, expect, beforeEach } from "vitest"; import { EventEmitter } from "events"; import { ToolsetManager } from "./manager.js"; import { ToolsetConfig, ToolsetToolNote } from "./types.js"; import { DiscoveredTool, IToolDiscoveryEngine } from "../discovery/types.js"; import { Tool } from "@modelcontextprotocol/sdk/types.js"; // Mock discovery engine class MockDiscoveryEngine extends EventEmitter implements IToolDiscoveryEngine { private tools: DiscoveredTool[] = []; setTools(tools: DiscoveredTool[]) { this.tools = tools; } async initialize() {} async start() {} async stop() {} async discoverTools(): Promise<DiscoveredTool[]> { return this.tools; } async getToolByName(name: string): Promise<DiscoveredTool | null> { return ( this.tools.find((t) => t.name === name || t.namespacedName === name) || null ); } async searchTools(): Promise<DiscoveredTool[]> { return this.tools; } // eslint-disable-next-line @typescript-eslint/no-unused-vars getAvailableTools(_includeDisabled?: boolean): DiscoveredTool[] { return this.tools; } resolveToolReference( ref: { namespacedName?: string; refId?: string }, options?: { allowStaleRefs?: boolean } ) { const tool = this.tools.find( (t) => t.namespacedName === ref.namespacedName || t.toolHash === ref.refId ); const exists = !!tool; const namespacedNameMatch = !!tool && tool.namespacedName === ref.namespacedName; const refIdMatch = !!tool && tool.toolHash === ref.refId; const warnings: string[] = []; const errors: string[] = []; // Check for mismatches when both identifiers are provided if (exists && ref.namespacedName && ref.refId) { if (!namespacedNameMatch && !refIdMatch) { errors.push( `Tool reference mismatch: neither namespacedName nor refId match` ); } else if (!namespacedNameMatch || !refIdMatch) { const msg = `Tool reference partial mismatch: ${!namespacedNameMatch ? "namespacedName" : "refId"} doesn't match`; if (options?.allowStaleRefs) { warnings.push(msg); } else { errors.push(msg); } } } // In secure mode, reject tools with errors const shouldReject = !options?.allowStaleRefs && errors.length > 0; return { exists: exists && !shouldReject, tool: shouldReject ? undefined : tool, serverName: tool?.serverName, serverStatus: undefined, namespacedNameMatch, refIdMatch, warnings, errors, }; } async refreshCache() {} async clearCache() {} getStats() { return { totalServers: 1, connectedServers: 1, totalTools: this.tools.length, cacheHitRate: 0.8, averageDiscoveryTime: 100, }; } } describe("ToolsetManager - Annotations", () => { let manager: ToolsetManager; let mockEngine: MockDiscoveryEngine; const createMockTools = (): DiscoveredTool[] => [ { name: "status", namespacedName: "git.status", description: "Show the working tree status", serverName: "git", serverStatus: "connected", toolHash: "abc123", lastCached: new Date(), tool: { name: "status", description: "Show the working tree status", inputSchema: { type: "object", properties: {} }, }, }, { name: "create_issue", namespacedName: "linear.create_issue", description: "Creates a new issue in Linear", serverName: "linear", serverStatus: "connected", toolHash: "def456", lastCached: new Date(), tool: { name: "create_issue", description: "Creates a new issue in Linear", inputSchema: { type: "object", properties: {} }, }, }, ]; beforeEach(() => { manager = new ToolsetManager(); mockEngine = new MockDiscoveryEngine(); mockEngine.setTools(createMockTools()); manager.setDiscoveryEngine(mockEngine); }); describe("_hydrateToolNotes", () => { it("should return tool unchanged when no toolset is loaded", () => { const tool: Tool = { name: "git_status", description: "Show the working tree status", inputSchema: { type: "object", properties: {} }, }; const result = manager._hydrateToolNotes(tool); expect(result).toEqual(tool); expect(result.description).toBe("Show the working tree status"); }); it("should return tool unchanged when toolset has no notes", () => { const toolset: ToolsetConfig = { name: "test-toolset", tools: [{ namespacedName: "git.status" }], }; manager.setCurrentToolset(toolset); const tool: Tool = { name: "git_status", description: "Show the working tree status", inputSchema: { type: "object", properties: {} }, }; const result = manager._hydrateToolNotes(tool); expect(result).toEqual(tool); expect(result.description).toBe("Show the working tree status"); }); it("should add notes to tool description when notes exist", () => { const toolset: ToolsetConfig = { name: "test-toolset", tools: [{ namespacedName: "git.status" }], toolNotes: [ { toolRef: { namespacedName: "git.status" }, notes: [ { name: "usage-tip", note: "Always run this before committing", }, { name: "warning", note: "Be careful with untracked files", }, ], }, ], }; manager.setCurrentToolset(toolset); const tool: Tool = { name: "git_status", description: "Show the working tree status", inputSchema: { type: "object", properties: {} }, }; const result = manager._hydrateToolNotes(tool); expect(result.description).toContain("Show the working tree status"); expect(result.description).toContain("### Additional Tool Notes"); expect(result.description).toContain( "โ€ข **usage-tip**: Always run this before committing" ); expect(result.description).toContain( "โ€ข **warning**: Be careful with untracked files" ); }); it("should match notes by refId when both namespacedName and refId provided in notes", () => { const toolset: ToolsetConfig = { name: "test-toolset", tools: [{ namespacedName: "git.status" }], toolNotes: [ { // Notes reference has both namespacedName and refId toolRef: { namespacedName: "git.status", refId: "abc123" }, notes: [ { name: "tip", note: "Use with caution", }, ], }, ], }; manager.setCurrentToolset(toolset); const tools = manager.getMcpTools(); expect(tools.length).toBe(1); const gitTool = tools[0]; expect(gitTool.name).toBe("git_status"); expect(gitTool.description).toContain("### Additional Tool Notes"); expect(gitTool.description).toContain("โ€ข **tip**: Use with caution"); }); it("should handle tools without description", () => { const toolset: ToolsetConfig = { name: "test-toolset", tools: [{ namespacedName: "git.status" }], toolNotes: [ { toolRef: { namespacedName: "git.status" }, notes: [ { name: "usage", note: "Essential git command", }, ], }, ], }; manager.setCurrentToolset(toolset); const tool: Tool = { name: "git_status", inputSchema: { type: "object", properties: {} }, }; const result = manager._hydrateToolNotes(tool); expect(result.description).toBe( "### Additional Tool Notes\n\nโ€ข **usage**: Essential git command" ); }); }); describe("formatNotesForLLM", () => { it("should format notes with markdown", () => { const notes: ToolsetToolNote[] = [ { name: "tip-1", note: "First tip" }, { name: "tip-2", note: "Second tip" }, { name: "warning", note: "Important warning" }, ]; // Access private method through type assertion const formatNotesForLLM = (manager as any).formatNotesForLLM.bind( manager ); const result = formatNotesForLLM(notes); expect(result).toBe( "### Additional Tool Notes\n\n" + "โ€ข **tip-1**: First tip\n" + "โ€ข **tip-2**: Second tip\n" + "โ€ข **warning**: Important warning" ); }); }); describe("getMcpTools with annotations", () => { it("should include annotations in exposed tools", () => { // Ensure we have a fresh manager and engine manager = new ToolsetManager(); mockEngine = new MockDiscoveryEngine(); mockEngine.setTools(createMockTools()); manager.setDiscoveryEngine(mockEngine); const toolset: ToolsetConfig = { name: "test-toolset", tools: [ { namespacedName: "git.status" }, { namespacedName: "linear.create_issue" }, ], toolNotes: [ { toolRef: { namespacedName: "linear.create_issue" }, notes: [ { name: "team-selection", note: "Always confirm team with user first", }, ], }, ], }; manager.setCurrentToolset(toolset); const tools = manager.getMcpTools(); // Find the linear tool const linearTool = tools.find((t) => t.name === "linear_create_issue"); expect(linearTool).toBeDefined(); expect(linearTool!.description).toContain( "Creates a new issue in Linear" ); expect(linearTool!.description).toContain("### Additional Tool Notes"); expect(linearTool!.description).toContain( "โ€ข **team-selection**: Always confirm team with user first" ); // Git tool should not have annotations const gitTool = tools.find((t) => t.name === "git_status"); expect(gitTool).toBeDefined(); expect(gitTool!.description).toBe("Show the working tree status"); expect(gitTool!.description).not.toContain("### Additional Tool Notes"); }); }); describe("findDiscoveredToolByFlattenedName", () => { it("should find tool by flattened name", () => { const toolset: ToolsetConfig = { name: "test-toolset", tools: [ { namespacedName: "git.status" }, { namespacedName: "linear.create_issue" }, ], }; manager.setCurrentToolset(toolset); // Access private method through type assertion const findDiscoveredToolByFlattenedName = ( manager as any ).findDiscoveredToolByFlattenedName.bind(manager); const gitTool = findDiscoveredToolByFlattenedName("git_status"); expect(gitTool).toBeDefined(); expect(gitTool.namespacedName).toBe("git.status"); const linearTool = findDiscoveredToolByFlattenedName( "linear_create_issue" ); expect(linearTool).toBeDefined(); expect(linearTool.namespacedName).toBe("linear.create_issue"); const notFound = findDiscoveredToolByFlattenedName("nonexistent_tool"); expect(notFound).toBeNull(); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/toolprint/hypertool-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server