Repository navigation
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/server
Steps to reproduce
- Use a Pi provider instance on a custom OpenAI-compatible gateway (any provider whose Pi model
apiisopenai-completions— e.g. a self-hosted LiteLLM/Bifrost/OpenRouter-style endpoint added to Pi'smodels.json). - Start a Pi thread (or delegate a task to Pi) and send any short prompt.
- Capture the outgoing
/chat/completionsrequest body and counttools.
The tool catalog that #18643 activates is a pure function of the T3 MCP server, so this reproduces without a specific gateway: I measured it by pointing Pi at a logging proxy in front of the gateway, and independently by running Pi with T3's own bridge extension against T3's live /mcp endpoint.
Expected behavior
A Pi session on a completion gateway should declare the three orchestration tools that #17220 kept direct (orchestrator_capabilities, delegate_task, task_status) and leave the other ~80 T3 MCP tools discoverable through tool_search, as #17220 intended ("the initial request fell from about 88.7 KB to 16.4 KB").
Actual behavior
Every mcp__t3-code__* tool is activated on every turn, so all ~83 tool schemas are sent on every request, on every provider, regardless of whether the session ever uses them.
Measured on two builds of the same container (same gateway, same model, same prompt, one turn each):
| build | tools declared | of which mcp__t3-code__* |
tool-schema tokens |
|---|---|---|---|
0.0.46-nightly.20261013.3066 (last good) |
8 | 3 | ~1.5k |
0.0.46-nightly.20261013.3080 (first bad) |
93 | 83 | ~26k |
Whole-request prompt tokens for the same one-line prompt went from 4,591 → 32,125. Breakdown of the 26k tokens of tool schema on the bad build:
| family | tools | ~tokens |
|---|---|---|
t3_thread_* / t3_* |
40 | 8,777 |
preview_* |
19 | 7,246 |
schedule_task |
1 | 1,296 |
html_* |
2 | 1,185 |
device_*, t3_watch_pull_request, delegate_task, … |
21 | 6,000 |
mcp__exa__* + MCP resource tools + builtins |
10 | ~1,900 |
This is the same prefix-bloat class #16651/#17220 fixed, now reintroduced on the openai-completions path.
Bisect: the activation is the block added in #18643 (4d768355, merged 2026-10-13T17:40Z):
...(ctx.model?.api === "openai-completions" ? catalog.map((tool) => "mcp__t3-code__" + tool.name) : [])in reconcileDiscovery in packages/provider-pi/src/server/mcpExtensionSource.ts. Checking the compiled extension string in each published platform binary, the block is absent in ...3066 and present in ...3080; 3080 is the first affected nightly.
Impact
Major degradation or frequent failure
Version or commit
0.0.46-nightly.20261013.3080 (first bad; introduced by #18643 / 4d768355). Last good: 0.0.46-nightly.20261013.3066.
Environment
Ubuntu 26.04 container, T3 Code nightly server (t3 serve), Pi 1.2.0, Pi provider on a self-hosted OpenAI-compatible gateway (api: openai-completions), models opencode-go/*. Not desktop-specific — reproduced with the standalone server binary.
Logs or stack traces
Raw gateway request bodies, one short prompt each, same session config:
# 0.0.46-nightly.20261013.3066 (last good)
model=... api=openai-completions
token_usage: {"prompt_tokens":4591,"completion_tokens":60}
params.tools.length = 8
read, bash, edit, write,
mcp__t3-code__orchestrator_capabilities, mcp__t3-code__delegate_task,
mcp__t3-code__task_status, tool_search
# 0.0.46-nightly.20261013.3080 (first bad)
model=... api=openai-completions
token_usage: {"prompt_tokens":32125,"completion_tokens":52}
params.tools.length = 93 (JSON.stringify(tools).length = 104769 chars)
83 x mcp__t3-code__* (preview_*, device_*, t3_thread_*, schedule_task, ...)
3 x *_mcp_resource* (Pi builtins, because the exa MCP server exposes resources)
2 x mcp__exa__*
5 x builtins
I also confirmed it is not caused by an `mcp.json` server: running the same config with and without an extra MCP server gives an identical `active=88` (83 T3 + 5 builtins) on the bad build.Screenshots, recordings, or supporting files
No response
Workaround
--exclude-tools works but is per-tool and has to be kept in sync with T3's catalog:
--exclude-tools 'mcp__t3-code__preview_*' # 93 -> 74 tools (26.2k -> 19.0k schema tokens)
Excluding the whole namespace (--exclude-tools 'mcp__t3-code__*') does drop it to ~5 tools, but that also removes delegate_task/task_status, so it is not a usable workaround for orchestrating threads.
One request about the tradeoff in #18643
#18643 deliberately traded request size for correctness, to stop completion gateways from silently swallowing calls to undeclared tools (#17500). That fix is clearly needed for gateways that drop undeclared tool calls — but it is applied to every openai-completions session, including gateways that route those calls correctly.
Concretely: on the gateway we use, a deferred T3 tool loaded through tool_search and then called works on the bad build — the call executes and returns a result, so the #17500 failure mode does not occur here, and we pay the full ~26k tokens for nothing.
Would you consider making this conditional? For example: keep the eager activation only when the gateway is known to need it (opt-in setting, or a per-provider-instance flag), or re-activate the schemas only for the specific tools named in the conversation rather than the whole catalog. Happy to help test a narrower version if that is useful — we can measure request size and the deferred-call path directly.
Activity
Anneo22 commentedon Oct 30, 2026
I measured the same regression independently, on Pi 1.4.0 with T3 0.1.5's bridge and its 64-tool catalog, in case more data helps:
tool_searchdeclared, OpenRouter-hosted DeepSeek, Kimi and GLM calledtool_searchbefore an undeclared T3 tool in 35 of 36 realistic runs. The swallowing in [Bug]: agent turns stop silently mid-task: the planned tool call is swallowed, provider returns a clean finish_reason=stop and the billed usage exceeds the delivered content #17500 looks specific to some gateways.Two suggestions:
openai-completionsand make eager loading opt-in (per provider instance, or an env flag). Or limit it to T3 tools named in the conversation, called, or loaded on the branch: that covers the "named in history" case from fix(pi): load T3 tool schemas for completion gateways #18643 and keeps the declared set stable for the prompt cache.tool_searchfirst" avoids it.Locally we work around it with a small Pi extension that takes the unused T3 tools back after the bridge's reconcile.
(Drafted with an AI agent; the numbers come from our request logs.)
StiensWout commentedon Nov 9, 2026
Note
🤖 GPT 6.1 Sol responding on behalf of StiensWout
We confirmed the unconditional catalog activation and investigated an independent lazy-activation fix. Full-catalog real Pi gateway captures show the prototype removes most schema bytes, but a context-only injected reference can be declared after Pi snapshots executable tools: the same response then gets
Tool ... not foundand makes no MCP call. We are not filing that regressing prototype. Correct support needs a native executable-snapshot refresh after context hooks, or an equivalent supported seam. This issue remains unresolved; the measured proof and prerequisite are recorded at https://p0gpfcbqx0qi.postplan.dev .