11import { describe , it , expect } from "vitest" ;
2- import { registerMetaTools } from "../src/meta/registerMetaTools.js" ;
2+ import { registerMetaTools , META_TOOLSET_KEY } from "../src/meta/registerMetaTools.js" ;
33import { DynamicToolManager } from "../src/core/DynamicToolManager.js" ;
44import { ModuleResolver } from "../src/mode/ModuleResolver.js" ;
55import { ToolRegistry } from "../src/core/ToolRegistry.js" ;
@@ -49,8 +49,8 @@ describe("Meta-tools return formats", () => {
4949 resolver,
5050 toolRegistry,
5151 } ) ;
52- registerMetaTools ( server , manager , { mode : "DYNAMIC" } ) ;
53- return { server, tools, manager } ;
52+ registerMetaTools ( server , manager , toolRegistry , { mode : "DYNAMIC" } ) ;
53+ return { server, tools, manager, toolRegistry } ;
5454 }
5555
5656 function findTool ( tools : RegisteredTool [ ] , name : string ) : RegisteredTool | undefined {
@@ -66,17 +66,23 @@ describe("Meta-tools return formats", () => {
6666 }
6767
6868 describe ( "list_tools" , ( ) => {
69- it ( "returns { tools: [], toolsetToTools: {} } when no toolsets enabled" , async ( ) => {
69+ it ( "returns meta- tools when no user toolsets enabled" , async ( ) => {
7070 const { tools } = createTestSetup ( ) ;
7171 const result = await callTool ( tools , "list_tools" ) ;
7272
7373 expect ( result ) . toHaveProperty ( "tools" ) ;
7474 expect ( result ) . toHaveProperty ( "toolsetToTools" ) ;
7575 expect ( Array . isArray ( result . tools ) ) . toBe ( true ) ;
7676 expect ( typeof result . toolsetToTools ) . toBe ( "object" ) ;
77- // No toolsets enabled, so no user tools (only meta-tools not tracked)
78- expect ( result . tools ) . toEqual ( [ ] ) ;
79- expect ( result . toolsetToTools ) . toEqual ( { } ) ;
77+ // Meta-tools are now tracked in the registry
78+ expect ( result . tools ) . toContain ( "enable_toolset" ) ;
79+ expect ( result . tools ) . toContain ( "disable_toolset" ) ;
80+ expect ( result . tools ) . toContain ( "list_toolsets" ) ;
81+ expect ( result . tools ) . toContain ( "describe_toolset" ) ;
82+ expect ( result . tools ) . toContain ( "list_tools" ) ;
83+ // Meta-tools appear under _meta key
84+ expect ( result . toolsetToTools [ META_TOOLSET_KEY ] ) . toBeDefined ( ) ;
85+ expect ( result . toolsetToTools [ META_TOOLSET_KEY ] ) . toContain ( "list_tools" ) ;
8086 } ) ;
8187
8288 it ( "returns correct structure after enabling toolsets" , async ( ) => {
@@ -236,9 +242,10 @@ describe("Meta-tools return formats", () => {
236242 it ( "only registers list_tools in STATIC mode" , ( ) => {
237243 const { server, tools } = createFakeMcpServer ( ) ;
238244 const resolver = new ModuleResolver ( { catalog } ) ;
239- const manager = new DynamicToolManager ( { server, resolver } ) ;
245+ const toolRegistry = new ToolRegistry ( { namespaceWithToolset : true } ) ;
246+ const manager = new DynamicToolManager ( { server, resolver, toolRegistry } ) ;
240247
241- registerMetaTools ( server , manager , { mode : "STATIC" } ) ;
248+ registerMetaTools ( server , manager , toolRegistry , { mode : "STATIC" } ) ;
242249
243250 const toolNames = tools . map ( ( t ) => t . name ) ;
244251 expect ( toolNames ) . toContain ( "list_tools" ) ;
@@ -247,5 +254,79 @@ describe("Meta-tools return formats", () => {
247254 expect ( toolNames ) . not . toContain ( "list_toolsets" ) ;
248255 expect ( toolNames ) . not . toContain ( "describe_toolset" ) ;
249256 } ) ;
257+
258+ it ( "registers list_tools in ToolRegistry in STATIC mode" , ( ) => {
259+ const { server } = createFakeMcpServer ( ) ;
260+ const resolver = new ModuleResolver ( { catalog } ) ;
261+ const toolRegistry = new ToolRegistry ( { namespaceWithToolset : true } ) ;
262+ const manager = new DynamicToolManager ( { server, resolver, toolRegistry } ) ;
263+
264+ registerMetaTools ( server , manager , toolRegistry , { mode : "STATIC" } ) ;
265+
266+ expect ( toolRegistry . has ( "list_tools" ) ) . toBe ( true ) ;
267+ expect ( toolRegistry . listByToolset ( ) [ META_TOOLSET_KEY ] ) . toContain ( "list_tools" ) ;
268+ } ) ;
269+ } ) ;
270+
271+ describe ( "ToolRegistry integration" , ( ) => {
272+ it ( "meta-tools appear in toolRegistry.list()" , ( ) => {
273+ const { toolRegistry } = createTestSetup ( ) ;
274+
275+ const registeredTools = toolRegistry . list ( ) ;
276+ expect ( registeredTools ) . toContain ( "enable_toolset" ) ;
277+ expect ( registeredTools ) . toContain ( "disable_toolset" ) ;
278+ expect ( registeredTools ) . toContain ( "list_toolsets" ) ;
279+ expect ( registeredTools ) . toContain ( "describe_toolset" ) ;
280+ expect ( registeredTools ) . toContain ( "list_tools" ) ;
281+ } ) ;
282+
283+ it ( "meta-tools appear in toolRegistry.listByToolset() under _meta key" , ( ) => {
284+ const { toolRegistry } = createTestSetup ( ) ;
285+
286+ const byToolset = toolRegistry . listByToolset ( ) ;
287+ expect ( byToolset [ META_TOOLSET_KEY ] ) . toBeDefined ( ) ;
288+ expect ( byToolset [ META_TOOLSET_KEY ] ) . toContain ( "enable_toolset" ) ;
289+ expect ( byToolset [ META_TOOLSET_KEY ] ) . toContain ( "disable_toolset" ) ;
290+ expect ( byToolset [ META_TOOLSET_KEY ] ) . toContain ( "list_toolsets" ) ;
291+ expect ( byToolset [ META_TOOLSET_KEY ] ) . toContain ( "describe_toolset" ) ;
292+ expect ( byToolset [ META_TOOLSET_KEY ] ) . toContain ( "list_tools" ) ;
293+ } ) ;
294+
295+ it ( "returns collision error when user tool collides with meta-tool" , async ( ) => {
296+ const { server } = createFakeMcpServer ( ) ;
297+ const toolRegistry = new ToolRegistry ( { namespaceWithToolset : false } ) ; // No namespacing to force collision
298+ const resolver = new ModuleResolver ( {
299+ catalog : {
300+ conflict : {
301+ name : "Conflict" ,
302+ description : "Toolset with conflicting tool name" ,
303+ tools : [
304+ {
305+ name : "enable_toolset" , // Collides with meta-tool
306+ description : "A user tool" ,
307+ inputSchema : { type : "object" , properties : { } } ,
308+ handler : async ( ) => ( { content : [ { type : "text" , text : "user" } ] } ) ,
309+ } ,
310+ ] ,
311+ } ,
312+ } ,
313+ } ) ;
314+ const manager = new DynamicToolManager ( { server, resolver, toolRegistry } ) ;
315+
316+ // Register meta-tools first
317+ registerMetaTools ( server , manager , toolRegistry , { mode : "DYNAMIC" } ) ;
318+
319+ // Enabling a toolset with a conflicting tool name returns failure with collision message
320+ const result = await manager . enableToolset ( "conflict" ) ;
321+ expect ( result . success ) . toBe ( false ) ;
322+ expect ( result . message ) . toMatch ( / c o l l i s i o n / i) ;
323+ } ) ;
324+
325+ it ( "cannot register tool named enable_toolset after meta-tools registered" , ( ) => {
326+ const { toolRegistry } = createTestSetup ( ) ;
327+
328+ // Attempting to add a tool with the same name should throw
329+ expect ( ( ) => toolRegistry . add ( "enable_toolset" ) ) . toThrow ( / c o l l i s i o n / i) ;
330+ } ) ;
250331 } ) ;
251332} ) ;
0 commit comments