Skip to content

Commit 1dc9c55

Browse files
committed
add logic to register specific tools
1 parent 7bbd6c4 commit 1dc9c55

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed

pkg/github/tools.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,3 +516,30 @@ func ContainsToolset(tools []string, toCheck string) bool {
516516
}
517517
return false
518518
}
519+
520+
// CleanTools validates and cleans tool names:
521+
// - Duplicates are removed from the result
522+
// - Removes whitespaces
523+
// - Validation of tool existence is done during registration
524+
// Returns: (cleaned tools, invalid tools)
525+
// Note: Invalid tools are identified during registration, so this function only cleans and deduplicates.
526+
func CleanTools(toolNames []string) ([]string, []string) {
527+
seen := make(map[string]bool)
528+
result := make([]string, 0, len(toolNames))
529+
invalid := make([]string, 0)
530+
531+
// Remove duplicates and trim whitespace
532+
for _, tool := range toolNames {
533+
trimmed := strings.TrimSpace(tool)
534+
if trimmed == "" {
535+
continue
536+
}
537+
if !seen[trimmed] {
538+
seen[trimmed] = true
539+
result = append(result, trimmed)
540+
}
541+
}
542+
543+
// Validation will happen during registration, so we return empty invalid list here
544+
return result, invalid
545+
}

pkg/toolsets/toolsets.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,3 +263,62 @@ func (tg *ToolsetGroup) GetToolset(name string) (*Toolset, error) {
263263
}
264264
return toolset, nil
265265
}
266+
267+
type ToolDoesNotExistError struct {
268+
Name string
269+
}
270+
271+
func (e *ToolDoesNotExistError) Error() string {
272+
return fmt.Sprintf("tool %s does not exist", e.Name)
273+
}
274+
275+
func NewToolDoesNotExistError(name string) *ToolDoesNotExistError {
276+
return &ToolDoesNotExistError{Name: name}
277+
}
278+
279+
// FindToolByName searches all toolsets (enabled or disabled) for a tool by name.
280+
// Returns the tool, its parent toolset name, and an error if not found.
281+
func (tg *ToolsetGroup) FindToolByName(toolName string) (*server.ServerTool, string, error) {
282+
for toolsetName, toolset := range tg.Toolsets {
283+
// Check read tools
284+
for _, tool := range toolset.readTools {
285+
if tool.Tool.Name == toolName {
286+
return &tool, toolsetName, nil
287+
}
288+
}
289+
// Check write tools
290+
for _, tool := range toolset.writeTools {
291+
if tool.Tool.Name == toolName {
292+
return &tool, toolsetName, nil
293+
}
294+
}
295+
}
296+
return nil, "", NewToolDoesNotExistError(toolName)
297+
}
298+
299+
// RegisterSpecificTools registers only the specified tools, bypassing toolset enablement.
300+
// Respects read-only mode (skips write tools if readOnly=true).
301+
// Returns error if any tool is not found.
302+
func (tg *ToolsetGroup) RegisterSpecificTools(s *server.MCPServer, toolNames []string, readOnly bool) error {
303+
for _, toolName := range toolNames {
304+
tool, toolsetName, err := tg.FindToolByName(toolName)
305+
if err != nil {
306+
return fmt.Errorf("tool %s not found: %w", toolName, err)
307+
}
308+
309+
// Check if it's a write tool and we're in read-only mode
310+
// ReadOnlyHint should always be set, but add defensive check
311+
if tool.Tool.Annotations.ReadOnlyHint != nil {
312+
isWriteTool := !*tool.Tool.Annotations.ReadOnlyHint
313+
if isWriteTool && readOnly {
314+
// Skip write tools in read-only mode
315+
continue
316+
}
317+
}
318+
319+
// Register the tool
320+
s.AddTool(tool.Tool, tool.Handler)
321+
_ = toolsetName // toolsetName is available for potential future use (logging, etc.)
322+
}
323+
return nil
324+
}

0 commit comments

Comments
 (0)