feat(brain): add brain_answer and brain_classify MCP tools
All checks were successful
CI / Lint / Test / Vet (push) Successful in 11s
CI / Mirror to GitHub (push) Successful in 3s

Adds two new LLM-backed MCP tools to the ingestion service:

- brain_answer(query): BM25 retrieval + LLM synthesis → answer + sources
- brain_classify(text): classifies doc into type/title/tags via LLM

Adds llm.Router for primary→fallback routing (berget.ai → iguana).
Wired via BRAIN_LLM_PRIMARY_URL/BRAIN_LLM_FALLBACK_URL env vars;
no-op when unset so existing deployments are unaffected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Mathias Bergqvist
2026-05-12 11:06:17 +02:00
parent c7e0192486
commit 189ff89c34
10 changed files with 379 additions and 23 deletions

View File

@@ -32,19 +32,21 @@ type rpcError struct {
// Server handles MCP JSON-RPC over HTTP for the ingestion service.
type Server struct {
brainDir string
pipeline pipeline.Config
llm pipeline.CompleteFunc
brainDir string
pipeline pipeline.Config
llm pipeline.CompleteFunc
answerLLM pipeline.CompleteFunc // nil = brain_answer and brain_classify unavailable
}
// NewServer constructs a Server bound to brainDir. pipelineCfg supplies the
// LLM-backed pipeline; llm may be nil for non-LLM tools only.
func NewServer(brainDir string, pipelineCfg *pipeline.Config, llm pipeline.CompleteFunc) *Server {
// answerLLM drives brain_answer and brain_classify; nil disables those tools.
func NewServer(brainDir string, pipelineCfg *pipeline.Config, llm pipeline.CompleteFunc, answerLLM pipeline.CompleteFunc) *Server {
cfg := pipeline.Config{}
if pipelineCfg != nil {
cfg = *pipelineCfg
}
return &Server{brainDir: brainDir, pipeline: cfg, llm: llm}
return &Server{brainDir: brainDir, pipeline: cfg, llm: llm, answerLLM: answerLLM}
}
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@@ -140,6 +142,10 @@ func (s *Server) handleCall(ctx context.Context, name string, args json.RawMessa
return s.brainIngest(ctx, args)
case "session_log":
return s.sessionLog(ctx, args)
case "brain_answer":
return s.brainAnswer(ctx, args)
case "brain_classify":
return s.brainClassify(ctx, args)
default:
return nil, fmt.Errorf("unknown tool: %s", name)
}