@@ -133,4 +133,146 @@ describe("DynamicToolManager", () => {
133133 expect ( res . results . length ) . toBe ( 2 ) ;
134134 expect ( res . success ) . toBe ( true ) ;
135135 } ) ;
136+
137+ it ( "registers tools with annotations when provided" , async ( ) => {
138+ const { server, tools } = createFakeMcpServer ( ) ;
139+ const catalogWithAnnotations = {
140+ annotated : {
141+ name : "Annotated Tools" ,
142+ description : "Tools with annotations" ,
143+ tools : [
144+ {
145+ name : "read_data" ,
146+ description : "Read-only tool" ,
147+ inputSchema : { } ,
148+ handler : async ( ) => ( { data : "test" } ) ,
149+ annotations : {
150+ readOnlyHint : true ,
151+ idempotentHint : true ,
152+ } ,
153+ } ,
154+ {
155+ name : "delete_data" ,
156+ description : "Destructive tool" ,
157+ inputSchema : { } ,
158+ handler : async ( ) => ( { deleted : true } ) ,
159+ annotations : {
160+ destructiveHint : true ,
161+ idempotentHint : false ,
162+ } ,
163+ } ,
164+ {
165+ name : "fetch_weather" ,
166+ description : "External API tool" ,
167+ inputSchema : { } ,
168+ handler : async ( ) => ( { weather : "sunny" } ) ,
169+ annotations : {
170+ readOnlyHint : true ,
171+ openWorldHint : true ,
172+ idempotentHint : false ,
173+ } ,
174+ } ,
175+ ] ,
176+ } ,
177+ } as any ;
178+
179+ const resolver = new ModuleResolver ( { catalog : catalogWithAnnotations } ) ;
180+ const manager = new DynamicToolManager ( {
181+ server,
182+ resolver,
183+ toolRegistry : new ToolRegistry ( { namespaceWithToolset : true } ) ,
184+ } ) ;
185+
186+ const res = await manager . enableToolset ( "annotated" ) ;
187+ expect ( res . success ) . toBe ( true ) ;
188+
189+ // Verify all tools were registered with correct annotations
190+ expect ( tools . length ) . toBe ( 3 ) ;
191+
192+ const readTool = tools . find ( ( t ) => t . name === "annotated.read_data" ) ;
193+ expect ( readTool ) . toBeDefined ( ) ;
194+ expect ( readTool ?. annotations ) . toEqual ( {
195+ readOnlyHint : true ,
196+ idempotentHint : true ,
197+ } ) ;
198+
199+ const deleteTool = tools . find ( ( t ) => t . name === "annotated.delete_data" ) ;
200+ expect ( deleteTool ) . toBeDefined ( ) ;
201+ expect ( deleteTool ?. annotations ) . toEqual ( {
202+ destructiveHint : true ,
203+ idempotentHint : false ,
204+ } ) ;
205+
206+ const fetchTool = tools . find ( ( t ) => t . name === "annotated.fetch_weather" ) ;
207+ expect ( fetchTool ) . toBeDefined ( ) ;
208+ expect ( fetchTool ?. annotations ) . toEqual ( {
209+ readOnlyHint : true ,
210+ openWorldHint : true ,
211+ idempotentHint : false ,
212+ } ) ;
213+ } ) ;
214+
215+ it ( "registers tools without annotations when not provided" , async ( ) => {
216+ const { server, tools } = createFakeMcpServer ( ) ;
217+ const resolver = new ModuleResolver ( { catalog } ) ;
218+ const manager = new DynamicToolManager ( {
219+ server,
220+ resolver,
221+ toolRegistry : new ToolRegistry ( { namespaceWithToolset : true } ) ,
222+ } ) ;
223+
224+ const res = await manager . enableToolset ( "core" ) ;
225+ expect ( res . success ) . toBe ( true ) ;
226+
227+ const tool = tools . find ( ( t ) => t . name === "core.ping" ) ;
228+ expect ( tool ) . toBeDefined ( ) ;
229+ expect ( tool ?. annotations ) . toBeUndefined ( ) ;
230+ } ) ;
231+
232+ it ( "registers module-loaded tools with annotations" , async ( ) => {
233+ const { server, tools } = createFakeMcpServer ( ) ;
234+ const catalogWithModules = {
235+ external : {
236+ name : "External" ,
237+ description : "External tools" ,
238+ modules : [ "external" ] ,
239+ } ,
240+ } as any ;
241+
242+ const resolver = new ModuleResolver ( {
243+ catalog : catalogWithModules ,
244+ moduleLoaders : {
245+ external : async ( ) => [
246+ {
247+ name : "api_call" ,
248+ description : "Call external API" ,
249+ inputSchema : { } ,
250+ handler : async ( ) => ( { result : "ok" } ) ,
251+ annotations : {
252+ openWorldHint : true ,
253+ readOnlyHint : false ,
254+ idempotentHint : false ,
255+ } ,
256+ } ,
257+ ] ,
258+ } ,
259+ } ) ;
260+
261+ const manager = new DynamicToolManager ( {
262+ server,
263+ resolver,
264+ toolRegistry : new ToolRegistry ( { namespaceWithToolset : true } ) ,
265+ } ) ;
266+
267+ const res = await manager . enableToolset ( "external" ) ;
268+ expect ( res . success ) . toBe ( true ) ;
269+
270+ const tool = tools . find ( ( t ) => t . name === "external.api_call" ) ;
271+ expect ( tool ) . toBeDefined ( ) ;
272+ expect ( tool ?. annotations ) . toEqual ( {
273+ openWorldHint : true ,
274+ readOnlyHint : false ,
275+ idempotentHint : false ,
276+ } ) ;
277+ } ) ;
136278} ) ;
0 commit comments