Optimizer is unavailable to Modern (2026-07-28) clients
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 2.2k
- Forks
- 300
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 184
Description
The product gap
Any go-sdk v1.7+ client connecting to an optimizer-enabled vMCP silently receives the full raw aggregated tool set instead of find_tool/call_tool, and tools/call find_tool fails with -32603 not found.
This is default client behaviour, not an exotic configuration: go-sdk v1.7's Connect is Modern-first — it probes server/discover before initialize and upgrades to 2026-07-28 whenever the server advertises it.
First observed as e2e failures on #6033's earlier heads: vmcp_optimizer_test.go, vmcp_cli_features_test.go:485, virtualmcp_optimizer_composite_test.go:201, virtualmcp_optimizer_multibackend_test.go.
Mechanism
The optimizer is Serve-layer and session-scoped (pkg/vmcp/server/serve_optimizer.go): at session registration a per-session optimizer is built over the core's advertised set, upserting tools into a shared FTS5 store, and the session advertises exactly find_tool/call_tool in place of the raw tools. That store is transport/session state and is deliberately not in the stateless core.
The Modern dispatch path has no session: dispatchModernToolsList/dispatchModernToolCall serve straight from core.ListTools/core.CallTool, so the optimizer transformation never runs.
Interim state
#6033 adds a capability gate (pkg/vmcp/server/modern_gate.go, modernDispatchBlockers) that keeps optimizer-enabled instances Legacy-only: server/discover falls through to the SDK and advertises a Legacy-only version list — steering Modern-first clients onto the Legacy handshake with no error surfaced — and any other well-formed Modern request is refused with a conformant 400 + -32022 listing the Legacy version.
So the feature works for every client, but an optimizer-enabled instance cannot serve the Modern revision at all. This issue is that trade being made explicit rather than left silent.
What parity needs
- An index not keyed by session. The advertised set is identity-filtered (
core.ListTools(ctx, identity)), so per-identity is the natural scope — the session index today piggybacks on the session↔identity binding. An instance-scoped index over the unfiltered set with identity-filtered search results is the alternative. Either way the FTS5 store's upsert-by-tool-name semantics need a scoping key. - Advertisement: when the optimizer is enabled,
dispatchModernToolsListreturns the two meta-tools (pagination is trivial at n=2). - Dispatch:
dispatchModernToolCallrecognisesfind_tool/call_tooland routes through an optimizer built over the caller identity's core tools.call_tool's inner invocation must dispatch throughcore.CallToolso the core admission seam authorizes the inner target by its real name — the same design constraintoptimizerSessionToolssatisfies on the Legacy path. - A cost model. The session path indexes once per registration; a naive Modern implementation would rebuild per request. Needs identity-scoped caching with a TTL (cf.
capabilityCacheTTL= 30s) or a shared store with per-identity filtering. When embeddings are configured each upsert does an embedding round-trip per tool, so caching is mandatory, not an optimization.
Definition of done
Delete the "optimizer" entry in modernDispatchBlockers. The gate is pinned by TestModernDispatchBlockers, TestClassifyingHandler_ModernCapabilityGate, and the full-handler pair in modern_gate_integration_test.go — removing the entry flips those tests, which then assert the new behaviour (optimizer instance advertises Modern; Modern tools/list returns the meta-tools). The e2e optimizer suites then cover Modern clients end-to-end.
Refs #6033, #5959, #5743.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with pkg/vmcp/server/serve_optimizer.go and the Modern dispatch paths, then review modern_gate.go and its blocker tests. Use TestModernDispatchBlockers, TestClassifyingHandler_ModernCapabilityGate, and modern_gate_integration_test.go to understand the expected transition; done means Modern optimizer instances advertise and dispatch the meta-tools, with the e2e optimizer suites passing.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- api, backend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100