Skip to content

Commit 42b5533

Browse files
Copilotomgitsads
andauthored
Migrate dynamic toolset to modelcontextprotocol/go-sdk (#1450)
* Initial plan * Migrate dynamic toolset to modelcontextprotocol/go-sdk This commit migrates the dynamic toolset (enable_toolset, list_available_toolsets, get_toolset_tools) from mark3labs/mcp-go to modelcontextprotocol/go-sdk. Changes: - Removed //go:build ignore tag - Updated imports to use modelcontextprotocol/go-sdk - Migrated all tool functions to use new SDK patterns - Updated ToolsetEnum helper to return []any instead of mcp.PropertyOption - Converted DSL-based schema definitions to jsonschema.Schema structures - Updated handler signatures to use map[string]any args - Replaced old result helpers with utils package equivalents - Fixed EnableToolset to use RegisterFunc instead of AddTools - Created comprehensive test suite for all three tools - Generated toolsnaps for the new tools Related to #1428 Co-authored-by: omgitsads <[email protected]> * Enable dynamic tools * Remove new test and toolsnaps, we can follow up with this * Just return the tool and handler directly instead of assigning to variables first. This stops copilot complaining in review that the variables are unused. --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: omgitsads <[email protected]> Co-authored-by: Adam Holt <[email protected]>
1 parent 948fe76 commit 42b5533

File tree

2 files changed

+80
-60
lines changed

2 files changed

+80
-60
lines changed

pkg/github/dynamic_tools.go

Lines changed: 77 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
//go:build ignore
2-
31
package github
42

53
import (
@@ -9,44 +7,52 @@ import (
97

108
"github.com/github/github-mcp-server/pkg/toolsets"
119
"github.com/github/github-mcp-server/pkg/translations"
12-
"github.com/mark3labs/mcp-go/mcp"
13-
"github.com/mark3labs/mcp-go/server"
10+
"github.com/github/github-mcp-server/pkg/utils"
11+
"github.com/google/jsonschema-go/jsonschema"
12+
"github.com/modelcontextprotocol/go-sdk/mcp"
1413
)
1514

16-
func ToolsetEnum(toolsetGroup *toolsets.ToolsetGroup) mcp.PropertyOption {
17-
toolsetNames := make([]string, 0, len(toolsetGroup.Toolsets))
15+
func ToolsetEnum(toolsetGroup *toolsets.ToolsetGroup) []any {
16+
toolsetNames := make([]any, 0, len(toolsetGroup.Toolsets))
1817
for name := range toolsetGroup.Toolsets {
1918
toolsetNames = append(toolsetNames, name)
2019
}
21-
return mcp.Enum(toolsetNames...)
20+
return toolsetNames
2221
}
2322

24-
func EnableToolset(s *server.MCPServer, toolsetGroup *toolsets.ToolsetGroup, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
25-
return mcp.NewTool("enable_toolset",
26-
mcp.WithDescription(t("TOOL_ENABLE_TOOLSET_DESCRIPTION", "Enable one of the sets of tools the GitHub MCP server provides, use get_toolset_tools and list_available_toolsets first to see what this will enable")),
27-
mcp.WithToolAnnotation(mcp.ToolAnnotation{
23+
func EnableToolset(s *mcp.Server, toolsetGroup *toolsets.ToolsetGroup, t translations.TranslationHelperFunc) (mcp.Tool, mcp.ToolHandlerFor[map[string]any, any]) {
24+
return mcp.Tool{
25+
Name: "enable_toolset",
26+
Description: t("TOOL_ENABLE_TOOLSET_DESCRIPTION", "Enable one of the sets of tools the GitHub MCP server provides, use get_toolset_tools and list_available_toolsets first to see what this will enable"),
27+
Annotations: &mcp.ToolAnnotations{
2828
Title: t("TOOL_ENABLE_TOOLSET_USER_TITLE", "Enable a toolset"),
2929
// Not modifying GitHub data so no need to show a warning
30-
ReadOnlyHint: ToBoolPtr(true),
31-
}),
32-
mcp.WithString("toolset",
33-
mcp.Required(),
34-
mcp.Description("The name of the toolset to enable"),
35-
ToolsetEnum(toolsetGroup),
36-
),
37-
),
38-
func(_ context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
30+
ReadOnlyHint: true,
31+
},
32+
InputSchema: &jsonschema.Schema{
33+
Type: "object",
34+
Properties: map[string]*jsonschema.Schema{
35+
"toolset": {
36+
Type: "string",
37+
Description: "The name of the toolset to enable",
38+
Enum: ToolsetEnum(toolsetGroup),
39+
},
40+
},
41+
Required: []string{"toolset"},
42+
},
43+
},
44+
mcp.ToolHandlerFor[map[string]any, any](func(_ context.Context, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) {
3945
// We need to convert the toolsets back to a map for JSON serialization
40-
toolsetName, err := RequiredParam[string](request, "toolset")
46+
toolsetName, err := RequiredParam[string](args, "toolset")
4147
if err != nil {
42-
return mcp.NewToolResultError(err.Error()), nil
48+
return utils.NewToolResultError(err.Error()), nil, nil
4349
}
4450
toolset := toolsetGroup.Toolsets[toolsetName]
4551
if toolset == nil {
46-
return mcp.NewToolResultError(fmt.Sprintf("Toolset %s not found", toolsetName)), nil
52+
return utils.NewToolResultError(fmt.Sprintf("Toolset %s not found", toolsetName)), nil, nil
4753
}
4854
if toolset.Enabled {
49-
return mcp.NewToolResultText(fmt.Sprintf("Toolset %s is already enabled", toolsetName)), nil
55+
return utils.NewToolResultText(fmt.Sprintf("Toolset %s is already enabled", toolsetName)), nil, nil
5056
}
5157

5258
toolset.Enabled = true
@@ -55,21 +61,28 @@ func EnableToolset(s *server.MCPServer, toolsetGroup *toolsets.ToolsetGroup, t t
5561
//
5662
// Send notification to all initialized sessions
5763
// s.sendNotificationToAllClients("notifications/tools/list_changed", nil)
58-
s.AddTools(toolset.GetActiveTools()...)
64+
for _, serverTool := range toolset.GetActiveTools() {
65+
serverTool.RegisterFunc(s)
66+
}
5967

60-
return mcp.NewToolResultText(fmt.Sprintf("Toolset %s enabled", toolsetName)), nil
61-
}
68+
return utils.NewToolResultText(fmt.Sprintf("Toolset %s enabled", toolsetName)), nil, nil
69+
})
6270
}
6371

64-
func ListAvailableToolsets(toolsetGroup *toolsets.ToolsetGroup, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
65-
return mcp.NewTool("list_available_toolsets",
66-
mcp.WithDescription(t("TOOL_LIST_AVAILABLE_TOOLSETS_DESCRIPTION", "List all available toolsets this GitHub MCP server can offer, providing the enabled status of each. Use this when a task could be achieved with a GitHub tool and the currently available tools aren't enough. Call get_toolset_tools with these toolset names to discover specific tools you can call")),
67-
mcp.WithToolAnnotation(mcp.ToolAnnotation{
72+
func ListAvailableToolsets(toolsetGroup *toolsets.ToolsetGroup, t translations.TranslationHelperFunc) (mcp.Tool, mcp.ToolHandlerFor[map[string]any, any]) {
73+
return mcp.Tool{
74+
Name: "list_available_toolsets",
75+
Description: t("TOOL_LIST_AVAILABLE_TOOLSETS_DESCRIPTION", "List all available toolsets this GitHub MCP server can offer, providing the enabled status of each. Use this when a task could be achieved with a GitHub tool and the currently available tools aren't enough. Call get_toolset_tools with these toolset names to discover specific tools you can call"),
76+
Annotations: &mcp.ToolAnnotations{
6877
Title: t("TOOL_LIST_AVAILABLE_TOOLSETS_USER_TITLE", "List available toolsets"),
69-
ReadOnlyHint: ToBoolPtr(true),
70-
}),
71-
),
72-
func(_ context.Context, _ mcp.CallToolRequest) (*mcp.CallToolResult, error) {
78+
ReadOnlyHint: true,
79+
},
80+
InputSchema: &jsonschema.Schema{
81+
Type: "object",
82+
Properties: map[string]*jsonschema.Schema{},
83+
},
84+
},
85+
mcp.ToolHandlerFor[map[string]any, any](func(_ context.Context, _ *mcp.CallToolRequest, _ map[string]any) (*mcp.CallToolResult, any, error) {
7386
// We need to convert the toolsetGroup back to a map for JSON serialization
7487

7588
payload := []map[string]string{}
@@ -88,35 +101,42 @@ func ListAvailableToolsets(toolsetGroup *toolsets.ToolsetGroup, t translations.T
88101

89102
r, err := json.Marshal(payload)
90103
if err != nil {
91-
return nil, fmt.Errorf("failed to marshal features: %w", err)
104+
return nil, nil, fmt.Errorf("failed to marshal features: %w", err)
92105
}
93106

94-
return mcp.NewToolResultText(string(r)), nil
95-
}
107+
return utils.NewToolResultText(string(r)), nil, nil
108+
})
96109
}
97110

98-
func GetToolsetsTools(toolsetGroup *toolsets.ToolsetGroup, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
99-
return mcp.NewTool("get_toolset_tools",
100-
mcp.WithDescription(t("TOOL_GET_TOOLSET_TOOLS_DESCRIPTION", "Lists all the capabilities that are enabled with the specified toolset, use this to get clarity on whether enabling a toolset would help you to complete a task")),
101-
mcp.WithToolAnnotation(mcp.ToolAnnotation{
111+
func GetToolsetsTools(toolsetGroup *toolsets.ToolsetGroup, t translations.TranslationHelperFunc) (mcp.Tool, mcp.ToolHandlerFor[map[string]any, any]) {
112+
return mcp.Tool{
113+
Name: "get_toolset_tools",
114+
Description: t("TOOL_GET_TOOLSET_TOOLS_DESCRIPTION", "Lists all the capabilities that are enabled with the specified toolset, use this to get clarity on whether enabling a toolset would help you to complete a task"),
115+
Annotations: &mcp.ToolAnnotations{
102116
Title: t("TOOL_GET_TOOLSET_TOOLS_USER_TITLE", "List all tools in a toolset"),
103-
ReadOnlyHint: ToBoolPtr(true),
104-
}),
105-
mcp.WithString("toolset",
106-
mcp.Required(),
107-
mcp.Description("The name of the toolset you want to get the tools for"),
108-
ToolsetEnum(toolsetGroup),
109-
),
110-
),
111-
func(_ context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
117+
ReadOnlyHint: true,
118+
},
119+
InputSchema: &jsonschema.Schema{
120+
Type: "object",
121+
Properties: map[string]*jsonschema.Schema{
122+
"toolset": {
123+
Type: "string",
124+
Description: "The name of the toolset you want to get the tools for",
125+
Enum: ToolsetEnum(toolsetGroup),
126+
},
127+
},
128+
Required: []string{"toolset"},
129+
},
130+
},
131+
mcp.ToolHandlerFor[map[string]any, any](func(_ context.Context, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) {
112132
// We need to convert the toolsetGroup back to a map for JSON serialization
113-
toolsetName, err := RequiredParam[string](request, "toolset")
133+
toolsetName, err := RequiredParam[string](args, "toolset")
114134
if err != nil {
115-
return mcp.NewToolResultError(err.Error()), nil
135+
return utils.NewToolResultError(err.Error()), nil, nil
116136
}
117137
toolset := toolsetGroup.Toolsets[toolsetName]
118138
if toolset == nil {
119-
return mcp.NewToolResultError(fmt.Sprintf("Toolset %s not found", toolsetName)), nil
139+
return utils.NewToolResultError(fmt.Sprintf("Toolset %s not found", toolsetName)), nil, nil
120140
}
121141
payload := []map[string]string{}
122142

@@ -132,9 +152,9 @@ func GetToolsetsTools(toolsetGroup *toolsets.ToolsetGroup, t translations.Transl
132152

133153
r, err := json.Marshal(payload)
134154
if err != nil {
135-
return nil, fmt.Errorf("failed to marshal features: %w", err)
155+
return nil, nil, fmt.Errorf("failed to marshal features: %w", err)
136156
}
137157

138-
return mcp.NewToolResultText(string(r)), nil
139-
}
158+
return utils.NewToolResultText(string(r)), nil, nil
159+
})
140160
}

pkg/github/tools.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -390,9 +390,9 @@ func InitDynamicToolset(s *mcp.Server, tsg *toolsets.ToolsetGroup, t translation
390390
// Need to add the dynamic toolset last so it can be used to enable other toolsets
391391
dynamicToolSelection := toolsets.NewToolset(ToolsetMetadataDynamic.ID, ToolsetMetadataDynamic.Description).
392392
AddReadTools(
393-
// toolsets.NewServerTool(ListAvailableToolsets(tsg, t)),
394-
// toolsets.NewServerTool(GetToolsetsTools(tsg, t)),
395-
// toolsets.NewServerTool(EnableToolset(s, tsg, t)),
393+
toolsets.NewServerTool(ListAvailableToolsets(tsg, t)),
394+
toolsets.NewServerTool(GetToolsetsTools(tsg, t)),
395+
toolsets.NewServerTool(EnableToolset(s, tsg, t)),
396396
)
397397

398398
dynamicToolSelection.Enabled = true

0 commit comments

Comments
 (0)