feat: add MCP integration with context7 compatibility

This commit is contained in:
Danieldd28
2026-02-18 00:35:35 +07:00
parent 4fde0175cf
commit 403e048821
17 changed files with 1727 additions and 6 deletions
+85
View File
@@ -0,0 +1,85 @@
package tools
import (
"context"
"errors"
"fmt"
"github.com/sipeed/picoclaw/pkg/mcp"
)
type MCPTool struct {
manager *mcp.Manager
name string
description string
parameters map[string]any
}
func NewMCPTool(manager *mcp.Manager, tool mcp.RegisteredTool) *MCPTool {
description := tool.Description
if description == "" {
description = fmt.Sprintf("MCP tool %s from server %s", tool.ToolName, tool.ServerName)
}
return &MCPTool{
manager: manager,
name: tool.QualifiedName,
description: description,
parameters: tool.Parameters,
}
}
func (t *MCPTool) Name() string {
return t.name
}
func (t *MCPTool) Description() string {
return t.description
}
func (t *MCPTool) Parameters() map[string]interface{} {
return t.parameters
}
func (t *MCPTool) Execute(ctx context.Context, args map[string]interface{}) *ToolResult {
if t.manager == nil {
return ErrorResult("MCP manager is not configured")
}
result, err := t.manager.CallTool(ctx, t.name, args)
if err != nil {
return ErrorResult(fmt.Sprintf("MCP tool %s failed: %v", t.name, err)).WithError(err)
}
if result.IsError {
err := errors.New(result.Content)
return ErrorResult(result.Content).WithError(err)
}
return SilentResult(result.Content)
}
// RegisterMCPTools discovers tools from MCP servers and registers them into the registry.
func RegisterMCPTools(ctx context.Context, registry *ToolRegistry, manager *mcp.Manager) (int, error) {
if registry == nil || manager == nil {
return 0, nil
}
discoveredTools, err := manager.DiscoverTools(ctx)
if err != nil {
return 0, err
}
return RegisterKnownMCPTools(registry, manager, discoveredTools), nil
}
// RegisterKnownMCPTools registers already-discovered MCP tools.
// This avoids repeated discovery work when multiple registries share one manager.
func RegisterKnownMCPTools(registry *ToolRegistry, manager *mcp.Manager, discoveredTools []mcp.RegisteredTool) int {
if registry == nil || manager == nil || len(discoveredTools) == 0 {
return 0
}
for _, tool := range discoveredTools {
registry.Register(NewMCPTool(manager, tool))
}
return len(discoveredTools)
}
+44
View File
@@ -0,0 +1,44 @@
package tools
import (
"testing"
"github.com/sipeed/picoclaw/pkg/mcp"
)
func TestRegisterKnownMCPTools_RegistersAllTools(t *testing.T) {
registry := NewToolRegistry()
manager := &mcp.Manager{}
discovered := []mcp.RegisteredTool{
{
QualifiedName: "mcp_context7__resolve_library_id",
ServerName: "context7",
ToolName: "resolve-library-id",
Description: "Resolve library ID",
Parameters: map[string]any{
"type": "object",
},
},
{
QualifiedName: "mcp_context7__query_docs",
ServerName: "context7",
ToolName: "query-docs",
Description: "Query docs",
Parameters: map[string]any{
"type": "object",
},
},
}
count := RegisterKnownMCPTools(registry, manager, discovered)
if count != 2 {
t.Fatalf("RegisterKnownMCPTools count = %d, want 2", count)
}
if _, ok := registry.Get("mcp_context7__resolve_library_id"); !ok {
t.Fatalf("expected mcp_context7__resolve_library_id to be registered")
}
if _, ok := registry.Get("mcp_context7__query_docs"); !ok {
t.Fatalf("expected mcp_context7__query_docs to be registered")
}
}