@@ -393,3 +393,209 @@ func TestRegisterSpecificTools(t *testing.T) {
393393 t .Error ("expected error for non-existent tool" )
394394 }
395395}
396+
397+ // mockToolWithMeta creates a ServerTool with metadata for testing NewToolsetGroupFromTools
398+ func mockToolWithMeta (name string , toolsetName string , readOnly bool ) ServerTool {
399+ return ServerTool {
400+ Tool : mcp.Tool {
401+ Name : name ,
402+ Meta : mcp.Meta {"toolset" : toolsetName },
403+ Annotations : & mcp.ToolAnnotations {
404+ ReadOnlyHint : readOnly ,
405+ },
406+ },
407+ RegisterFunc : func (_ * mcp.Server ) {},
408+ }
409+ }
410+
411+ func TestNewToolsetGroupFromTools (t * testing.T ) {
412+ toolsetInfos := map [string ]ToolsetInfo {
413+ "repos" : {Name : "repos" , Description : "Repository tools" },
414+ "issues" : {Name : "issues" , Description : "Issue tools" },
415+ }
416+
417+ // Create tools with meta containing toolset info
418+ tools := []ServerTool {
419+ mockToolWithMeta ("get_repo" , "repos" , true ),
420+ mockToolWithMeta ("create_repo" , "repos" , false ),
421+ mockToolWithMeta ("get_issue" , "issues" , true ),
422+ mockToolWithMeta ("create_issue" , "issues" , false ),
423+ mockToolWithMeta ("list_issues" , "issues" , true ),
424+ }
425+
426+ tsg := NewToolsetGroupFromTools (false , toolsetInfos , tools ... )
427+
428+ // Verify toolsets were created
429+ if len (tsg .Toolsets ) != 2 {
430+ t .Fatalf ("expected 2 toolsets, got %d" , len (tsg .Toolsets ))
431+ }
432+
433+ // Verify repos toolset
434+ reposToolset , exists := tsg .Toolsets ["repos" ]
435+ if ! exists {
436+ t .Fatal ("expected 'repos' toolset to exist" )
437+ }
438+ if reposToolset .Description != "Repository tools" {
439+ t .Errorf ("expected repos description 'Repository tools', got '%s'" , reposToolset .Description )
440+ }
441+ if len (reposToolset .readTools ) != 1 {
442+ t .Errorf ("expected 1 read tool in repos, got %d" , len (reposToolset .readTools ))
443+ }
444+ if len (reposToolset .writeTools ) != 1 {
445+ t .Errorf ("expected 1 write tool in repos, got %d" , len (reposToolset .writeTools ))
446+ }
447+
448+ // Verify issues toolset
449+ issuesToolset , exists := tsg .Toolsets ["issues" ]
450+ if ! exists {
451+ t .Fatal ("expected 'issues' toolset to exist" )
452+ }
453+ if len (issuesToolset .readTools ) != 2 {
454+ t .Errorf ("expected 2 read tools in issues, got %d" , len (issuesToolset .readTools ))
455+ }
456+ if len (issuesToolset .writeTools ) != 1 {
457+ t .Errorf ("expected 1 write tool in issues, got %d" , len (issuesToolset .writeTools ))
458+ }
459+ }
460+
461+ func TestNewToolsetGroupFromToolsReadOnly (t * testing.T ) {
462+ toolsetInfos := map [string ]ToolsetInfo {
463+ "repos" : {Name : "repos" , Description : "Repository tools" },
464+ }
465+
466+ tools := []ServerTool {
467+ mockToolWithMeta ("get_repo" , "repos" , true ),
468+ mockToolWithMeta ("create_repo" , "repos" , false ),
469+ }
470+
471+ // Create with readOnly=true
472+ tsg := NewToolsetGroupFromTools (true , toolsetInfos , tools ... )
473+
474+ reposToolset := tsg .Toolsets ["repos" ]
475+ if ! reposToolset .readOnly {
476+ t .Error ("expected toolset to be in read-only mode" )
477+ }
478+
479+ // GetActiveTools should only return read tools
480+ activeTools := reposToolset .GetActiveTools ()
481+ if len (activeTools ) != 0 {
482+ // Toolset is not enabled yet
483+ t .Errorf ("expected 0 active tools (not enabled), got %d" , len (activeTools ))
484+ }
485+
486+ reposToolset .Enabled = true
487+ activeTools = reposToolset .GetActiveTools ()
488+ if len (activeTools ) != 1 {
489+ t .Errorf ("expected 1 active tool in read-only mode, got %d" , len (activeTools ))
490+ }
491+ if activeTools [0 ].Tool .Name != "get_repo" {
492+ t .Errorf ("expected only read tool 'get_repo', got '%s'" , activeTools [0 ].Tool .Name )
493+ }
494+ }
495+
496+ func TestNewToolsetGroupFromToolsPanicsOnMissingToolset (t * testing.T ) {
497+ defer func () {
498+ if r := recover (); r == nil {
499+ t .Error ("expected panic when tool has no toolset in Meta" )
500+ }
501+ }()
502+
503+ // Tool without toolset in meta
504+ tools := []ServerTool {
505+ {
506+ Tool : mcp.Tool {
507+ Name : "bad_tool" ,
508+ Meta : nil , // No meta
509+ Annotations : & mcp.ToolAnnotations {
510+ ReadOnlyHint : true ,
511+ },
512+ },
513+ RegisterFunc : func (_ * mcp.Server ) {},
514+ },
515+ }
516+
517+ NewToolsetGroupFromTools (false , nil , tools ... )
518+ }
519+
520+ func TestIsReadOnlyTool (t * testing.T ) {
521+ tests := []struct {
522+ name string
523+ tool ServerTool
524+ expected bool
525+ }{
526+ {
527+ name : "read-only tool" ,
528+ tool : ServerTool {
529+ Tool : mcp.Tool {
530+ Annotations : & mcp.ToolAnnotations {ReadOnlyHint : true },
531+ },
532+ },
533+ expected : true ,
534+ },
535+ {
536+ name : "write tool" ,
537+ tool : ServerTool {
538+ Tool : mcp.Tool {
539+ Annotations : & mcp.ToolAnnotations {ReadOnlyHint : false },
540+ },
541+ },
542+ expected : false ,
543+ },
544+ {
545+ name : "no annotations (assume write)" ,
546+ tool : ServerTool {
547+ Tool : mcp.Tool {
548+ Annotations : nil ,
549+ },
550+ },
551+ expected : false ,
552+ },
553+ }
554+
555+ for _ , tt := range tests {
556+ t .Run (tt .name , func (t * testing.T ) {
557+ result := isReadOnlyTool (tt .tool )
558+ if result != tt .expected {
559+ t .Errorf ("expected %v, got %v" , tt .expected , result )
560+ }
561+ })
562+ }
563+ }
564+
565+ func TestGetToolsetFromMeta (t * testing.T ) {
566+ tests := []struct {
567+ name string
568+ meta mcp.Meta
569+ expected string
570+ }{
571+ {
572+ name : "valid toolset" ,
573+ meta : mcp.Meta {"toolset" : "repos" },
574+ expected : "repos" ,
575+ },
576+ {
577+ name : "nil meta" ,
578+ meta : nil ,
579+ expected : "" ,
580+ },
581+ {
582+ name : "missing toolset key" ,
583+ meta : mcp.Meta {"other" : "value" },
584+ expected : "" ,
585+ },
586+ {
587+ name : "wrong type for toolset" ,
588+ meta : mcp.Meta {"toolset" : 123 },
589+ expected : "" ,
590+ },
591+ }
592+
593+ for _ , tt := range tests {
594+ t .Run (tt .name , func (t * testing.T ) {
595+ result := getToolsetFromMeta (tt .meta )
596+ if result != tt .expected {
597+ t .Errorf ("expected %q, got %q" , tt .expected , result )
598+ }
599+ })
600+ }
601+ }
0 commit comments