Minor release. The indexer named composite types but could not describe their shape: every declaration was truncated at the first brace, so a struct symbol carried only its `type X struct` header and an interface symbol only its `type X interface` header. Field names, field types, and — most painfully — struct tags such as `env:"EINHERJAR_PG_HOST"` were dropped, as were the method sets of every port interface. An assistant could be told that `db-postgres` has a `Config` and a `Provider`, but not what env vars configure the one or what methods the other requires. This change captures both. internal/index (schema): - Symbol gains two optional fields. `fields` ([]Field) carries a struct's field set — name, type, raw struct tag (surrounding backticks stripped), doc comment, and an `embedded` marker. `methods` ([]Method) carries an interface's method set — name, signature without the leading `func`, and doc comment, including embedded interfaces. Both are omitempty and absent for every other kind. - SchemaVersion is deliberately unchanged. The two additions are additive and omitempty, so an older consumer parses the new index unchanged; per the existing rule the constant only bumps on a breaking format change. internal/index (builder): - collectSymbols now inspects each type's TypeSpec and, for a *ast.StructType or *ast.InterfaceType, fills the new Symbol members. A grouped field declaration (`x, y int`) yields one Field per name; an embedded field or interface yields an entry with an empty name. - New helpers: typeSpecType (underlying type expr of a lone type spec), extractFields, extractIfaceMethods, fieldDoc (doc comment or trailing line comment), and nodeString — a non-truncating printer used for field types, tags, and method signatures, distinct from formatNode which keeps truncating to produce the one-line header. internal/index (search): - matches() now also tests the query against struct field names, field types, and struct tags, and against interface method names and signatures. A query like an env-var key or a method name now resolves to the type that declares it. internal/tools: - get_symbol and search_symbols descriptions updated to advertise the new struct-field and interface-method coverage. No input/output schema change beyond the additive Symbol fields, which get_symbol already returns whole. internal/index (tests): - New builder_test.go — the package previously had no tests. Builds a temporary module fixture and asserts capture of struct fields (with tags and docs), embedded fields, interface methods (with signatures and docs), embedded interfaces, and discovery of a struct by one of its struct tags (the failure mode that motivated the change). Docs: - CHANGELOG.md gains an [Unreleased] entry; README.md tool table updated to state that get_symbol returns struct fields and interface methods and that search_symbols matches fields, tags, and methods. No new dependencies. The committed data/index.json placeholder is untouched — the index is regenerated at image build (Dockerfile runs cmd/indexer), so a deployment must be rebuilt to serve the richer index; a server still running the prior image keeps serving the older, member-less one. Reviewed-on: #1 Co-authored-by: Rene Nochebuena Guerrero <rene@nochebuena.dev> Co-committed-by: Rene Nochebuena Guerrero <rene@nochebuena.dev>
49 lines
1.8 KiB
Go
49 lines
1.8 KiB
Go
package tools
|
|
|
|
import (
|
|
"context"
|
|
|
|
"code.nochebuena.dev/einherjar/mcp/internal/index"
|
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
|
)
|
|
|
|
type searchSymbolsInput struct {
|
|
Query string `json:"query" jsonschema:"text to match against symbol name, doc comment, sub-package, or module"`
|
|
Limit int `json:"limit,omitempty" jsonschema:"max results (default 25)"`
|
|
Module string `json:"module,omitempty" jsonschema:"restrict to one module by name"`
|
|
Kind string `json:"kind,omitempty" jsonschema:"restrict to one kind: type, interface, func, method, const, var"`
|
|
}
|
|
|
|
type searchSymbolsOutput struct {
|
|
Results []index.Symbol `json:"results"`
|
|
Total int `json:"total"`
|
|
}
|
|
|
|
func registerSearchSymbols(s *mcp.Server, idx *index.Index) {
|
|
mcp.AddTool(s, &mcp.Tool{
|
|
Name: "search_symbols",
|
|
Description: "Search Einherjar's exported symbols (types, interfaces, funcs, methods, consts, vars) by name or doc text. Structs also match on a field name, field type, or struct tag (e.g. an env-var key); interfaces also match on a required method. Optionally filter by module or kind. Use when you need to find where a type, function, field, or tag lives.",
|
|
}, func(ctx context.Context, req *mcp.CallToolRequest, args searchSymbolsInput) (*mcp.CallToolResult, searchSymbolsOutput, error) {
|
|
all := idx.SearchSymbols(args.Query, args.Limit*4+25)
|
|
filtered := all[:0]
|
|
for _, sym := range all {
|
|
if args.Module != "" && sym.Module != args.Module {
|
|
continue
|
|
}
|
|
if args.Kind != "" && sym.Kind != args.Kind {
|
|
continue
|
|
}
|
|
filtered = append(filtered, sym)
|
|
}
|
|
limit := args.Limit
|
|
if limit <= 0 {
|
|
limit = 25
|
|
}
|
|
if len(filtered) > limit {
|
|
filtered = filtered[:limit]
|
|
}
|
|
out := searchSymbolsOutput{Results: filtered, Total: len(filtered)}
|
|
return jsonText(out), out, nil
|
|
})
|
|
}
|