Repository navigation
[Bug]: With agent browser/device access off, the agent still sees every preview_* and device_* tool #17202
Description
Activity
Note
Grok responding on behalf of Julius.
Thanks for the clear report. I traced this through the code on
main(a4c9494). I didn't reproduce it at runtime, but the code matches what you saw: the settings only control which tool calls are allowed. They don't change which tools the agent is offered. The doc comment insettings.tslooks out of date.What happens
- A credential is always issued.
ProviderSessionManager.prepareMcpSessiongives every thread a credential withorchestration,worktreeandpull-requests. It addspreviewordeviceonly when the matching setting is on (apps/server/src/orchestration-v2/ProviderSessionManager.ts:485-491, issued at:523-528). So thet3-codeserver is attached whatever "Agent browser access" is set to. - The tool list ignores the credential.
/mcpis a singleMcpServerwith every toolkit registered at startup, includinglayerPreviewToolkitandlayerDeviceToolkit(apps/server/src/mcp/McpHttpServer.ts:850-861). The auth middleware resolves the credential and passes it to the request (McpHttpServer.ts:147-180). I couldn't find anything that filterstools/listby it, so every credential appears to get the full list. - The block happens at call time. The preview handlers call
requireThreadMcpCapability("preview")(apps/server/src/mcp/toolkits/preview/handlers.ts:65,:252). That produces thePreviewAutomationUnavailableErrortext you quoted (packages/contracts/src/previewAutomation.ts:830). The device tools userequireThreadMcpCapability("device"), which maps to "Agent device access is turned off for this environment." (apps/server/src/mcp/toolkits/device/handlers.ts:70-77). This is a server-wide setting (one per environment), and the capability is fixed when the credential is issued, so the device tools are gated the same way. - This isn't specific to Claude. Claude attaches
t3-codewhenever a session credential exists and pre-approvesmcp__t3-code__*(apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts:960-988). Codex adds it tomcp_serverswithout a tool filter (CodexAdapterV2.ts:1336-1347). Pi registers every tool it gets back fromtools/list(piT3McpExtensionSource.ts:272-280). So Codex, Pi and the others most likely show the full list too. Only the prompt text is gated: Codex drops the browser and device blocks (apps/server/src/provider/CodexDeveloperInstructions.ts:31-41), and Claude never gets the browser block.
When it diverged
#7083 originally withheld the whole credential, so the
t3-codeserver wasn't attached at all, and thesettings.tscomment was written for that version. #10839 (merged Sep 9) started issuing a credential every time, forpull-requests, and turnedpreviewinto a capability on it. Its own comment says this "withholds the preview tools without taking the server away". The comment atpackages/contracts/src/settings.ts:1299-1310was never updated. A day later, #10677 addedenableAgentDeviceAccesswith "the same way" wording (settings.ts:1344-1352). By then the gate was already capability-based, so it looks like the device tools were never hidden from the list. The orchestration and thread tools added since then mean the server now has to stay attached anyway.Impact
Calls fail cleanly and tell the agent what to do instead, so the calls themselves appear to stay blocked. The cost is 18 tool definitions in every session with the settings off. There's also some mis-steering: Claude's T3 instructions say "When it exposes
preview_*tools, prefer those tools" (apps/server/src/provider/T3OrchestrationInstructions.ts:43), and those tools are listed, so it may try them before falling back.Possible fixes (not yet decided)
- Filter
tools/list(and refusetools/call) by the credential's capabilities inMcpHttpServer. One server-side change would cover every provider and outside MCP clients. I haven't checked whether Effect'sMcpServersupports filtering the list per request, so this may need a custom list handler or separate toolkit registration. - Hide the tools for each provider instead: for example Claude
disallowedToolsformcp__t3-code__preview_*/device_*, and Codex's per-server tool filter. This works but has to be repeated for every adapter. - Either way, update the two
settings.tsdoc comments to describe the capability model.
Related
- #7083: the original withhold feature
- #10839: moved to the always-issued credential
- #10677: added the device setting
- #13559: the actionable preview error text you saw
- #16482: Claude prompt text for other providers' tools
- #7870: preview tools dropping out after a restart
- Discussion #6792
I didn't find a duplicate issue or an open PR that fixes this.
- A credential is always issued.
- addedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
on Oct 8, 2026 Thanks for tracing this. I reproduced the catalog mismatch with Codex 0.161.0 through both the authenticated HTTP endpoint and the native Codex API.
Your first proposed fix is supported by Effect's existing
McpSchema.EnabledWhenannotation. I have a local implementation that reads the authenticated invocation scope when tools are discovered and called. It filters the shared catalog without dependency changes or per-provider tool filters. Browser and device groups remain independent, other tools remain available, and the existing handler permission checks stay in place. Calls to hidden tools are refused before reaching their handlers.On unchanged main at
12069eefd, both access settings off still produced all 80 tools, including 21 browser tools and four device tools. On local commitf6b2b7fc, based on that revision, HTTP discovery and fresh native Codex conversations agreed:- Neither capability: 55 tools.
- Browser only: 76 tools.
- Device only: 59 tools.
- Both: 80 tools.
The real T3 manager, credential registry, HTTP registration, adapter and native process ran against isolated state. Unrelated domain services were stubbed, and only Codex was tested live. All 32 focused MCP tests passed, along with server typecheck, targeted lint/format and the server bundle build. The test command, from
apps/server, was:../../node_modules/.bin/vp test run src/mcp/McpHttpServer.test.ts src/mcp/McpDeviceToolkit.test.ts src/mcp/toolkits/worktree/registration.test.ts src/mcp/McpInvocationContext.test.tsI also checked access changes in both directions while attached: the credential and catalog stayed unchanged until idle detach/reopen, which refreshed both while preserving the native conversation and process. This implementation follows the credential's capabilities and preserves that lifecycle behavior. Model-payload token savings remain to be measured.
I'd like to contribute this fix. Would you accept a PR limited to authenticated catalog filtering, its tests, and corrections to the outdated settings comments? I'll rebase onto current main and repeat the focused checks before opening it. I'll propose broader orchestration-context improvements separately in Ideas.
Note
Written with GPT-6.1 Sol (Codex) at my request, and reviewed, edited, and approved by me.
Note
Written by Claude (Claude Code, Opus 5.5) at my request, and reviewed by me before posting.
Before submitting
Area
apps/server
Steps to reproduce
mcp__t3-code__*tools, then to callpreview_statusanddevice_list.Expected behavior
The agent does not see the
preview_*anddevice_*tools. That is what the setting's doc comment says (settings.ts):enableAgentDeviceAccessis documented to gate thedevice_*tools "the same way" (settings.ts).Actual behavior
The agent sees all 14
preview_*tools and all 4device_*tools. Calling them fails.preview_status:device_list:So the setting blocks the calls, but the tools are still offered to the agent.
Impact
Minor bug or occasional failure
Version or commit
0.0.45 (desktop)
Environment
macOS 14 (Darwin 23.6.0), Claude Code 2.1.294