Skip to content

Migration Guide

This guide helps you migrate to tooldiscovery from other tool discovery systems.

Migrating from toolindex

The tooldiscovery/index package is the successor to github.com/jonwraymond/toolindex. The migration is straightforward as the API is largely compatible.

Import Changes

```go // Before import "github.com/jonwraymond/toolindex"

// After import "github.com/jonwraymond/tooldiscovery/index" ```

Type Mapping

toolindex tooldiscovery/index Notes
Index Index Same interface
InMemoryIndex InMemoryIndex Same implementation
Summary Summary Same fields
SearchDoc SearchDoc Same fields
Searcher Searcher Same interface
ToolRegistration ToolRegistration Same struct

New Features

tooldiscovery adds: - discovery package for unified facade - semantic package for embedding-based search - tooldoc package for progressive documentation - search package with production BM25

Code Changes

Most code works unchanged:

```go // Before idx := toolindex.NewInMemoryIndex() idx.RegisterTool(tool, backend) results, _ := idx.Search("query", 10)

// After (identical) idx := index.NewInMemoryIndex() idx.RegisterTool(tool, backend) results, _ := idx.Search("query", 10) ```

For new code, use the discovery package:

go // New recommended approach disc, _ := discovery.New(discovery.Options{}) disc.RegisterTool(tool, backend, &tooldoc.DocEntry{ Summary: "Tool description", }) results, _ := disc.Search(ctx, "query", 10)

Migrating from Custom Tool Registries

If you have a custom tool registry, here's how to migrate:

Step 1: Map Your Tool Type

Create a function to convert your tools to model.Tool:

go func convertTool(myTool MyTool) model.Tool { return model.Tool{ Tool: mcp.Tool{ Name: myTool.Name, Description: myTool.Description, InputSchema: myTool.Schema, }, Namespace: myTool.Category, // Map to namespace Tags: myTool.Keywords, // Map to tags } }

Step 2: Map Your Backend Type

go func convertBackend(myBackend MyBackend) model.ToolBackend { switch myBackend.Type { case "mcp": return model.NewMCPBackend(myBackend.ServerName) case "local": return model.NewLocalBackend(myBackend.HandlerName) case "external": return model.NewProviderBackend(myBackend.Provider, myBackend.ID) default: return model.NewMCPBackend("default") } }

Step 3: Migrate Registration

```go // Create index idx := index.NewInMemoryIndex()

// Migrate existing tools for _, myTool := range myRegistry.GetAllTools() { tool := convertTool(myTool) backend := convertBackend(myTool.Backend)

if err := idx.RegisterTool(tool, backend); err != nil {
    log.Printf("Failed to migrate %s: %v", myTool.Name, err)
    continue
}

} ```

```go // Before (custom registry) results := myRegistry.Search(query)

// After summaries, _ := idx.Search(query, 100) ```

Step 5: Add Documentation (Optional)

```go store := tooldoc.NewInMemoryStore(tooldoc.StoreOptions{Index: idx})

for _, myTool := range myRegistry.GetAllTools() { toolID := fmt.Sprintf("%s:%s", myTool.Category, myTool.Name)

store.RegisterDoc(toolID, tooldoc.DocEntry{
    Summary:  myTool.ShortHelp,
    Notes:    myTool.LongHelp,
    Examples: convertExamples(myTool.Examples),
})

} ```

Integrating with Existing MCP Servers

Receiving Tools from MCP Server

```go // When you receive tools from an MCP server func onToolsReceived(serverName string, mcpTools []mcp.Tool) { // Convert to model.Tool (which embeds mcp.Tool) tools := make([]model.Tool, len(mcpTools)) for i, t := range mcpTools { tools[i] = model.Tool{Tool: t} }

// Register all tools from this server
if err := idx.RegisterToolsFromMCP(serverName, tools); err != nil {
    log.Printf("Failed to register tools from %s: %v", serverName, err)
}

} ```

Handling Server Disconnect

go // When an MCP server disconnects, remove its tools func onServerDisconnect(serverName string) { // Get all tools and find ones from this server // (You'd need to track this mapping separately) for _, toolID := range toolsFromServer[serverName] { idx.UnregisterBackend(toolID, model.BackendKindMCP, serverName) } }

Using with toolfoundation v0.2.0

tooldiscovery requires toolfoundation v0.2.0+:

bash go get github.com/jonwraymond/toolfoundation@v0.2.0

Key features from toolfoundation used by tooldiscovery: - model.Tool with embedded mcp.Tool - model.ToolBackend with MCP/Provider/Local variants - model.NormalizeTags for consistent tag handling - Backend factory functions (NewMCPBackend, etc.)

Gradual Migration Strategy

For large codebases, migrate gradually:

Phase 1: Add tooldiscovery Alongside Existing

```go // Keep existing registry oldRegistry := myRegistry.New()

// Add tooldiscovery idx := index.NewInMemoryIndex()

// Sync tools to both func registerTool(tool MyTool) { oldRegistry.Register(tool) idx.RegisterTool(convertTool(tool), convertBackend(tool.Backend)) } ```

Phase 2: Shadow Read

```go // Search both, compare results func search(query string) []MyTool { oldResults := oldRegistry.Search(query)

// Shadow: also search new index
newResults, _ := idx.Search(query, 100)
logComparison(oldResults, newResults)

return oldResults // Still use old results

} ```

Phase 3: Switch Read Path

go func search(query string) []MyTool { summaries, _ := idx.Search(query, 100) return convertToMyTools(summaries) }

Phase 4: Remove Old Registry

go // Only use tooldiscovery idx := index.NewInMemoryIndex() // ... registration and search ...

Common Migration Issues

Tool ID Format

tooldiscovery uses namespace:name:version format for tool IDs when version is set (otherwise namespace:name):

```go // Tool with namespace "git" and name "status" toolID := "git:status"

// Tool without namespace toolID := "simple_tool" ```

Ensure your code handles both formats.

Search Result Differences

tooldiscovery's BM25 search may return different results than simple substring matching. Tune the BM25Config to match your expected behavior:

go // For behavior closer to substring matching searcher := search.NewBM25Searcher(search.BM25Config{ NameBoost: 5, // Strongly prefer name matches NamespaceBoost: 1, TagsBoost: 1, })

Backend Selection

If you have multiple backends per tool, customize the selector:

go idx := index.NewInMemoryIndex(index.IndexOptions{ BackendSelector: func(backends []model.ToolBackend) model.ToolBackend { // Your custom logic for _, b := range backends { if b.Kind == model.BackendKindLocal { return b // Prefer local backends } } return backends[0] }, })