Problem
agent-adaptor currently exposes transport/auth/required fields on mcp.Server, but it cannot express a provider-neutral per-tool approval mode. Codex supports this natively:
[mcp_servers.yangxia-agent-ui.tools.yangxia_ui_open]
approval_mode = "approve"
Without that projection, an Agent using the normal Ask policy maps to Codex approvalPolicy: "on-request". Codex sends mcpServer/elicitation/request for the tool call, while codex/appserver.Client deliberately rejects every unsolicited server request. The model observes user rejected MCP tool call and a run-scoped UI tool never executes.
Using global AutoApprove/approvalPolicy: "never" is not an acceptable workaround: it changes shell, file, permission, and unrelated third-party MCP behavior. A generic server-request auto-accept callback is also unnecessarily broad and easy to misuse.
This was reproduced with agent-adaptor v1.1.3-0.20260812201432-6432e5e4f727 and Codex 0.146: a bearer-bound loopback run service repeatedly called yangxia_ui_open; every call was declined before the MCP endpoint received it.
Proposed provider-neutral API
Extend the MCP declaration itself, so approval authority is coupled to one exact MCP server and exact tool names:
package driver
type MCPToolApprovalMode string
const (
MCPToolApprovalPrompt MCPToolApprovalMode = "prompt"
MCPToolApprovalApprove MCPToolApprovalMode = "approve"
)
type MCPToolPolicy struct {
ApprovalMode MCPToolApprovalMode
}
type MCPServerSpec struct {
// existing fields...
Tools map[string]MCPToolPolicy
}
Ergonomic construction:
srv := mcp.HTTP(
"yangxia-agent-ui",
loopbackURL,
mcp.WithBearerTokenEnv("YANGXIA_AGENT_UI_RUN_TOKEN"),
mcp.Required("run-scoped A2UI bridge"),
mcp.WithToolApproval("yangxia_ui_open", mcp.ToolApprovalApprove),
mcp.WithToolApproval("yangxia_ui_patch", mcp.ToolApprovalApprove),
mcp.WithToolApproval("yangxia_ui_wait_action", mcp.ToolApprovalApprove),
mcp.WithToolApproval("yangxia_ui_close", mcp.ToolApprovalApprove),
)
Suggested public names are illustrative; the important contract is:
- the override lives on the exact
MCPServerSpec, not on the Agent/global policy;
- tool names are explicit and normalized/sorted;
- only closed enum values are accepted;
- duplicate declarations for one tool conflict unless byte-for-byte equivalent;
- the field participates in MCP/runtime/profile/session compatibility fingerprints;
- no secret or bearer value is stored in the policy;
- unsupported drivers fail closed when a non-default override is requested; they must not silently drop it.
Codex projection
For Codex, render:
[mcp_servers.<server>.tools.<tool>]
approval_mode = "prompt" | "approve"
This uses Codex's existing per-tool policy and avoids handling mcpServer/elicitation/request in agent-adaptor for auto-approved tools. The existing app-server behavior (reject all unsolicited server requests) can remain unchanged for command/file/permission requests and for MCP tools that still require a prompt.
The projection must work for both construction-time WithMCP and call-scoped RunServiceProvider attachments. Call-scoped policies must be removed during detach/profile restoration exactly like the corresponding server.
Security model
A host that can construct a RunServiceProvider is already trusted to attach executable/remote MCP capability to the run. The new field does not make arbitrary model tools trusted: it lets that host narrowly declare which tools on that exact attached server do not need an additional provider UI prompt.
Required negative boundaries:
- another MCP server with the same tool name remains unchanged;
- another tool on the same server remains unchanged;
- shell, file-change,
request_permissions, URL/auth elicitation, and unknown app-server methods remain unchanged;
- literal secrets, headers, URLs, or arguments never enter the approval policy;
- profile/runtime fingerprint changes when the allowlist changes;
- an unsupported provider returns a pre-launch capability/config error rather than executing without the requested policy.
For extra defense, hosts should combine approve with an authenticated run-scoped endpoint. In the motivating case the service is exact loopback, bearer-bound to one run, and each tool validates surface/run/binding authority; the approval override is not a replacement for those checks.
Tests / acceptance
- Public API/golden tests cover stable clone, normalization, ordering, duplicate detection, and JSON/TOML-independent fingerprinting.
- Codex profile test renders exactly four requested tool tables and no server-wide
default_tools_approval_mode.
- Exact fake Codex app-server run under global
on-request proves an approved tool reaches MCP without mcpServer/elicitation/request.
- A fifth tool on the same server still requests approval and is rejected by the current client.
- Same tool name on a different server still requests approval.
- Command/file/permission server requests remain rejected and do not inherit MCP policy.
- RunService attach/detach and resume tests prove policy is call-scoped, stable across endpoint secret rotation, and changes compatibility when the tool policy changes.
- Claude/Cursor/CodeBuddy either map an equivalent exact per-tool primitive or fail pre-launch; never silently downgrade.
Non-goals
- Globally disabling approvals.
- Auto-accepting arbitrary
mcpServer/elicitation/request payloads.
- Treating
Required, ReuseKey, server name, loopback URL, or bearer presence alone as trust.
- Making approval decisions from model-provided arguments.
Problem
agent-adaptorcurrently exposes transport/auth/required fields onmcp.Server, but it cannot express a provider-neutral per-tool approval mode. Codex supports this natively:Without that projection, an Agent using the normal
Askpolicy maps to CodexapprovalPolicy: "on-request". Codex sendsmcpServer/elicitation/requestfor the tool call, whilecodex/appserver.Clientdeliberately rejects every unsolicited server request. The model observesuser rejected MCP tool calland a run-scoped UI tool never executes.Using global
AutoApprove/approvalPolicy: "never"is not an acceptable workaround: it changes shell, file, permission, and unrelated third-party MCP behavior. A generic server-request auto-accept callback is also unnecessarily broad and easy to misuse.This was reproduced with agent-adaptor
v1.1.3-0.20260812201432-6432e5e4f727and Codex 0.146: a bearer-bound loopback run service repeatedly calledyangxia_ui_open; every call was declined before the MCP endpoint received it.Proposed provider-neutral API
Extend the MCP declaration itself, so approval authority is coupled to one exact MCP server and exact tool names:
Ergonomic construction:
Suggested public names are illustrative; the important contract is:
MCPServerSpec, not on the Agent/global policy;Codex projection
For Codex, render:
This uses Codex's existing per-tool policy and avoids handling
mcpServer/elicitation/requestin agent-adaptor for auto-approved tools. The existing app-server behavior (reject all unsolicited server requests) can remain unchanged for command/file/permission requests and for MCP tools that still require a prompt.The projection must work for both construction-time
WithMCPand call-scopedRunServiceProviderattachments. Call-scoped policies must be removed during detach/profile restoration exactly like the corresponding server.Security model
A host that can construct a
RunServiceProvideris already trusted to attach executable/remote MCP capability to the run. The new field does not make arbitrary model tools trusted: it lets that host narrowly declare which tools on that exact attached server do not need an additional provider UI prompt.Required negative boundaries:
request_permissions, URL/auth elicitation, and unknown app-server methods remain unchanged;For extra defense, hosts should combine
approvewith an authenticated run-scoped endpoint. In the motivating case the service is exact loopback, bearer-bound to one run, and each tool validates surface/run/binding authority; the approval override is not a replacement for those checks.Tests / acceptance
default_tools_approval_mode.on-requestproves an approved tool reaches MCP withoutmcpServer/elicitation/request.Non-goals
mcpServer/elicitation/requestpayloads.Required,ReuseKey, server name, loopback URL, or bearer presence alone as trust.