discovery

package
v0.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 6, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package discovery provides a unified facade for tool discovery operations.

It combines the functionality of the index, search, semantic, and tooldoc packages into a single, easy-to-use API. This package is the recommended entry point for most tool discovery use cases.

Basic Usage

Create a Discovery instance with default options:

disc, err := discovery.New(discovery.Options{})
if err != nil {
    log.Fatal(err)
}

// Register a tool with documentation
err = disc.RegisterTool(tool, backend, &tooldoc.DocEntry{
    Summary:  "Creates GitHub issues",
    Notes:    "Requires GITHUB_TOKEN",
    Examples: []tooldoc.ToolExample{{Title: "Create bug", Args: map[string]any{"title": "Bug"}}},
})

// Search for tools
results, err := disc.Search(ctx, "create issue", 10)

// Get progressive documentation
doc, err := disc.DescribeTool("github:create-issue", tooldoc.DetailFull)

Enable hybrid search by providing an embedder:

disc, err := discovery.New(discovery.Options{
    Embedder:    myEmbedder,  // implements semantic.Embedder
    HybridAlpha: 0.7,         // 70% BM25, 30% semantic
})

Components

The Discovery facade integrates:

  • index.Index: Tool registration and lookup
  • index.Searcher: BM25-based text search
  • semantic.Strategy: Embedding-based semantic search (optional)
  • tooldoc.Store: Progressive documentation

Thread Safety

All Discovery methods are safe for concurrent use.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrNotFound = errors.New("tool not found")
)

Error values for discovery operations.

Functions

This section is empty.

Types

type BM25OnlySearcher

type BM25OnlySearcher struct {
	// contains filtered or unexported fields
}

BM25OnlySearcher wraps the index's lexical search with score tracking.

func NewBM25OnlySearcher

func NewBM25OnlySearcher(scorer semantic.BM25Scorer) *BM25OnlySearcher

NewBM25OnlySearcher creates a searcher using only BM25 scoring.

func (*BM25OnlySearcher) Deterministic

func (s *BM25OnlySearcher) Deterministic() bool

Deterministic reports whether this searcher provides deterministic ordering.

func (*BM25OnlySearcher) GetScoreType

func (s *BM25OnlySearcher) GetScoreType() ScoreType

GetScoreType returns ScoreBM25.

func (*BM25OnlySearcher) Search

func (s *BM25OnlySearcher) Search(query string, limit int, docs []index.SearchDoc) ([]index.Summary, error)

Search implements index.Searcher.

func (*BM25OnlySearcher) SearchWithScores

func (s *BM25OnlySearcher) SearchWithScores(ctx context.Context, query string, limit int, docs []index.SearchDoc) (Results, error)

SearchWithScores returns results with BM25 scores.

type CompositeSearcher

type CompositeSearcher interface {
	index.Searcher

	// SearchWithScores returns results with detailed score information.
	SearchWithScores(ctx context.Context, query string, limit int, docs []index.SearchDoc) (Results, error)

	// ScoreType returns the type of scoring used by this searcher.
	GetScoreType() ScoreType
}

CompositeSearcher combines multiple search strategies into a unified searcher. It implements index.Searcher for compatibility with InMemoryIndex.

type Discovery

type Discovery struct {
	// contains filtered or unexported fields
}

Discovery is the unified facade for tool discovery operations. It combines index, search, and documentation functionality.

func New

func New(opts Options) (*Discovery, error)

New creates a new Discovery instance with the given options.

Example
package main

import (
	"fmt"

	"github.com/modelcontextprotocol/go-sdk/mcp"

	"github.com/jonwraymond/tooldiscovery/discovery"
	"github.com/jonwraymond/toolfoundation/model"
)

func main() {
	// Create a Discovery instance with default options (BM25 search)
	disc, err := discovery.New(discovery.Options{})
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	// Register a tool
	tool := model.Tool{
		Tool: mcp.Tool{
			Name:        "create_issue",
			Description: "Create a new GitHub issue",
			InputSchema: map[string]any{"type": "object"},
		},
		Namespace: "github",
	}
	_ = disc.RegisterTool(tool, model.NewMCPBackend("github-server"), nil)

	fmt.Println("Discovery initialized")
}
Output:

Discovery initialized

func (*Discovery) DescribeProvider added in v0.3.0

func (d *Discovery) DescribeProvider(id string) (adapter.CanonicalProvider, error)

DescribeProvider returns provider metadata by ID.

func (*Discovery) DescribeTool

func (d *Discovery) DescribeTool(id string, level tooldoc.DetailLevel) (tooldoc.ToolDoc, error)

DescribeTool returns documentation at the specified detail level.

Example
package main

import (
	"fmt"

	"github.com/modelcontextprotocol/go-sdk/mcp"

	"github.com/jonwraymond/tooldiscovery/discovery"
	"github.com/jonwraymond/tooldiscovery/tooldoc"
	"github.com/jonwraymond/toolfoundation/model"
)

func main() {
	disc, _ := discovery.New(discovery.Options{})

	tool := model.Tool{
		Tool: mcp.Tool{
			Name:        "run_query",
			Description: "Execute a database query",
			InputSchema: map[string]any{
				"type": "object",
				"properties": map[string]any{
					"sql": map[string]any{"type": "string"},
				},
				"required": []string{"sql"},
			},
		},
		Namespace: "db",
	}
	_ = disc.RegisterTool(tool, model.NewMCPBackend("db-server"), &tooldoc.DocEntry{
		Summary: "Run SQL queries against the database",
		Notes:   "Use parameterized queries to prevent SQL injection.",
	})

	// Get summary (minimal)
	summary, _ := disc.DescribeTool("db:run_query", tooldoc.DetailSummary)
	fmt.Println("Summary:", summary.Summary)

	// Get full documentation
	full, _ := disc.DescribeTool("db:run_query", tooldoc.DetailFull)
	fmt.Println("Notes:", full.Notes)
}
Output:

Summary: Run SQL queries against the database
Notes: Use parameterized queries to prevent SQL injection.

func (*Discovery) DocStore

func (d *Discovery) DocStore() *tooldoc.InMemoryStore

DocStore returns the underlying documentation store.

func (*Discovery) GetAllBackends

func (d *Discovery) GetAllBackends(id string) ([]model.ToolBackend, error)

GetAllBackends returns all backends for a tool.

func (*Discovery) GetTool

func (d *Discovery) GetTool(id string) (model.Tool, model.ToolBackend, error)

GetTool retrieves a tool by its canonical ID.

func (*Discovery) Index

func (d *Discovery) Index() index.Index

Index returns the underlying index for advanced operations.

func (*Discovery) ListExamples

func (d *Discovery) ListExamples(id string, maxExamples int) ([]tooldoc.ToolExample, error)

ListExamples returns examples for a tool.

func (*Discovery) ListNamespaces

func (d *Discovery) ListNamespaces() ([]string, error)

ListNamespaces returns all registered namespaces.

func (*Discovery) ListProviders added in v0.3.0

func (d *Discovery) ListProviders() ([]adapter.CanonicalProvider, error)

ListProviders returns all registered providers.

func (*Discovery) OnChange

func (d *Discovery) OnChange(listener index.ChangeListener) func()

OnChange registers a listener for index changes. Returns an unsubscribe function.

func (*Discovery) ProviderStore added in v0.3.0

func (d *Discovery) ProviderStore() provider.Store

ProviderStore returns the underlying provider store.

func (*Discovery) RegisterDoc

func (d *Discovery) RegisterDoc(toolID string, doc tooldoc.DocEntry) error

RegisterDoc registers or updates documentation for a tool.

func (*Discovery) RegisterExamples

func (d *Discovery) RegisterExamples(toolID string, examples []tooldoc.ToolExample) error

RegisterExamples adds examples to a tool's documentation.

func (*Discovery) RegisterProvider added in v0.3.0

func (d *Discovery) RegisterProvider(id string, p adapter.CanonicalProvider) (string, error)

RegisterProvider registers a provider and returns the resolved ID.

func (*Discovery) RegisterTool

func (d *Discovery) RegisterTool(tool model.Tool, backend model.ToolBackend, doc *tooldoc.DocEntry) error

RegisterTool registers a tool with its backend and optional documentation. If doc is nil, the tool is registered without additional documentation.

Example
package main

import (
	"fmt"

	"github.com/modelcontextprotocol/go-sdk/mcp"

	"github.com/jonwraymond/tooldiscovery/discovery"
	"github.com/jonwraymond/tooldiscovery/tooldoc"
	"github.com/jonwraymond/toolfoundation/model"
)

func main() {
	disc, _ := discovery.New(discovery.Options{})

	tool := model.Tool{
		Tool: mcp.Tool{
			Name:        "search_code",
			Description: "Search for code across repositories",
			InputSchema: map[string]any{"type": "object"},
		},
		Namespace: "github",
		Tags:      []string{"search", "code"},
	}
	backend := model.NewMCPBackend("github-mcp")

	// Register with documentation
	doc := &tooldoc.DocEntry{
		Summary: "Search code across GitHub repositories",
		Notes:   "Requires GITHUB_TOKEN. Rate limited to 30 req/min.",
		Examples: []tooldoc.ToolExample{
			{Title: "Search for function", Args: map[string]any{"query": "func main"}},
		},
	}

	err := disc.RegisterTool(tool, backend, doc)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Println("Tool registered with documentation")
}
Output:

Tool registered with documentation

func (*Discovery) RegisterTools

func (d *Discovery) RegisterTools(regs []index.ToolRegistration) error

RegisterTools registers multiple tools with their backends.

func (*Discovery) Search

func (d *Discovery) Search(ctx context.Context, query string, limit int) (Results, error)

Search performs a search using the configured strategy. Returns results ordered by relevance score.

Example
package main

import (
	"context"
	"fmt"

	"github.com/modelcontextprotocol/go-sdk/mcp"

	"github.com/jonwraymond/tooldiscovery/discovery"
	"github.com/jonwraymond/toolfoundation/model"
)

func main() {
	disc, _ := discovery.New(discovery.Options{})

	// Register some tools
	tools := []model.Tool{
		{
			Tool:      mcp.Tool{Name: "git_status", Description: "Show working tree status", InputSchema: map[string]any{"type": "object"}},
			Namespace: "git",
			Tags:      []string{"vcs"},
		},
		{
			Tool:      mcp.Tool{Name: "git_commit", Description: "Record changes to repository", InputSchema: map[string]any{"type": "object"}},
			Namespace: "git",
			Tags:      []string{"vcs"},
		},
		{
			Tool:      mcp.Tool{Name: "docker_ps", Description: "List containers", InputSchema: map[string]any{"type": "object"}},
			Namespace: "docker",
			Tags:      []string{"containers"},
		},
	}
	for _, t := range tools {
		_ = disc.RegisterTool(t, model.NewMCPBackend("server"), nil)
	}

	// Search for git tools
	ctx := context.Background()
	results, _ := disc.Search(ctx, "git", 10)

	fmt.Println("Found:", len(results), "git-related tools")
	for _, r := range results {
		fmt.Printf("  - %s (%s)\n", r.Summary.Name, r.Summary.Namespace)
	}
}
Output:

Found: 2 git-related tools
  - git_commit (git)
  - git_status (git)

func (*Discovery) SearchPage

func (d *Discovery) SearchPage(ctx context.Context, query string, limit int, cursor string) (Results, string, error)

SearchPage performs paginated search.

type HybridOptions

type HybridOptions struct {
	// BM25Scorer is an optional custom BM25 scorer. If nil, uses default.
	BM25Scorer semantic.BM25Scorer

	// Embedder generates embeddings for semantic search. Required.
	Embedder semantic.Embedder

	// Alpha is the BM25 weight (0.0 to 1.0). Semantic weight is 1-Alpha.
	// Default: 0.5 (equal weighting)
	Alpha float64
}

HybridOptions configures a HybridSearcher.

type HybridSearcher

type HybridSearcher struct {
	// contains filtered or unexported fields
}

HybridSearcher combines BM25 and semantic search with configurable weighting.

func NewHybridSearcher

func NewHybridSearcher(opts HybridOptions) (*HybridSearcher, error)

NewHybridSearcher creates a new hybrid searcher combining BM25 and semantic search.

func (*HybridSearcher) Deterministic

func (h *HybridSearcher) Deterministic() bool

Deterministic reports whether this searcher provides deterministic ordering.

func (*HybridSearcher) GetScoreType

func (h *HybridSearcher) GetScoreType() ScoreType

GetScoreType returns ScoreHybrid.

func (*HybridSearcher) Search

func (h *HybridSearcher) Search(query string, limit int, docs []index.SearchDoc) ([]index.Summary, error)

Search implements index.Searcher using hybrid scoring.

func (*HybridSearcher) SearchWithScores

func (h *HybridSearcher) SearchWithScores(ctx context.Context, query string, limit int, docs []index.SearchDoc) (Results, error)

SearchWithScores returns results with detailed hybrid scores.

type Options

type Options struct {
	// Index is the tool registry. If nil, creates a new InMemoryIndex.
	Index index.Index

	// Searcher is the search implementation. If nil, uses BM25Searcher.
	// This is ignored if Embedder is provided (uses HybridSearcher instead).
	Searcher index.Searcher

	// DocStore is the documentation store. If nil, creates a new InMemoryStore.
	DocStore tooldoc.Store

	// ProviderStore is the provider registry. If nil, creates a new InMemoryStore.
	ProviderStore provider.Store

	// Embedder enables hybrid search when provided.
	// If nil, uses BM25-only search.
	Embedder semantic.Embedder

	// HybridAlpha is the BM25 weight for hybrid search (0.0 to 1.0).
	// Semantic weight is 1-HybridAlpha.
	// Default: 0.5 (equal weighting). Only used when Embedder is provided.
	HybridAlpha float64

	// BM25Config configures the BM25 searcher.
	// Only used when Searcher is nil and Embedder is nil.
	BM25Config search.BM25Config

	// MaxExamples is the default maximum number of examples to return.
	// Default: 10
	MaxExamples int
}

Options configures a Discovery instance.

type Result

type Result struct {
	// Summary contains the tool's metadata (ID, name, namespace, description, tags).
	Summary index.Summary

	// Score is the relevance score for this result.
	// The score's interpretation depends on ScoreType.
	Score float64

	// ScoreType indicates how the Score was computed.
	ScoreType ScoreType
}

Result represents a unified search result with score details.

type Results

type Results []Result

Results is a slice of Result with helper methods.

func (Results) FilterByMinScore

func (r Results) FilterByMinScore(minScore float64) Results

FilterByMinScore returns results with score >= minScore.

func (Results) FilterByNamespace

func (r Results) FilterByNamespace(namespace string) Results

FilterByNamespace returns results matching the given namespace.

Example
package main

import (
	"context"
	"fmt"

	"github.com/modelcontextprotocol/go-sdk/mcp"

	"github.com/jonwraymond/tooldiscovery/discovery"
	"github.com/jonwraymond/toolfoundation/model"
)

func main() {
	disc, _ := discovery.New(discovery.Options{})

	tools := []model.Tool{
		{Tool: mcp.Tool{Name: "status", Description: "Git status", InputSchema: map[string]any{"type": "object"}}, Namespace: "git"},
		{Tool: mcp.Tool{Name: "commit", Description: "Git commit", InputSchema: map[string]any{"type": "object"}}, Namespace: "git"},
		{Tool: mcp.Tool{Name: "ps", Description: "Docker ps", InputSchema: map[string]any{"type": "object"}}, Namespace: "docker"},
	}
	for _, t := range tools {
		_ = disc.RegisterTool(t, model.NewMCPBackend("server"), nil)
	}

	ctx := context.Background()
	results, _ := disc.Search(ctx, "", 10) // Get all

	// Filter to just git tools
	gitTools := results.FilterByNamespace("git")
	fmt.Println("Git tools:", len(gitTools))
}
Output:

Git tools: 2

func (Results) IDs

func (r Results) IDs() []string

IDs returns just the tool IDs from the results.

Example
package main

import (
	"context"
	"fmt"

	"github.com/modelcontextprotocol/go-sdk/mcp"

	"github.com/jonwraymond/tooldiscovery/discovery"
	"github.com/jonwraymond/toolfoundation/model"
)

func main() {
	disc, _ := discovery.New(discovery.Options{})

	tools := []model.Tool{
		{Tool: mcp.Tool{Name: "create_issue", Description: "Create issue", InputSchema: map[string]any{"type": "object"}}, Namespace: "github"},
		{Tool: mcp.Tool{Name: "list_repos", Description: "List repos", InputSchema: map[string]any{"type": "object"}}, Namespace: "github"},
	}
	for _, t := range tools {
		_ = disc.RegisterTool(t, model.NewMCPBackend("server"), nil)
	}

	ctx := context.Background()
	results, _ := disc.Search(ctx, "github", 10)

	ids := results.IDs()
	fmt.Println("Found tool IDs:", len(ids))
}
Output:

Found tool IDs: 2

func (Results) Summaries

func (r Results) Summaries() []index.Summary

Summaries returns just the summaries from the results.

type ScoreType

type ScoreType string

ScoreType indicates the source of a search result's score.

const (
	// ScoreBM25 indicates the score came from BM25 lexical search.
	ScoreBM25 ScoreType = "bm25"

	// ScoreEmbedding indicates the score came from embedding-based semantic search.
	ScoreEmbedding ScoreType = "embedding"

	// ScoreHybrid indicates the score is a weighted combination of BM25 and embedding.
	ScoreHybrid ScoreType = "hybrid"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL