What happened?
Summary
Two FastMCP servers with otherwise-identical configuration behave differently under Claude Desktop's MCP OAuth flow. A server built with
create_proxy() (returning FastMCPProxy) triggers a per-first-tool-use "Authentication required to use this tool" confirmation dialog in Claude
Desktop after successful OAuth-at-connect. A plain FastMCP server with the same Okta OIDC auth wiring does not. I've isolated the trigger to the
FastMCPProxy / ProxyProvider shape by elimination.
Expected behavior
After a user authenticates once at connection time via OAuth, subsequent tool calls in that session should use the cached token silently — as plain
FastMCP servers do.
Actual behavior
On a FastMCPProxy server:
- User clicks "Connect" in Claude Desktop → browser OAuth flow → success.
- User sends a message that invokes any tool → Claude Desktop shows:
▎ Claude wants to use <tool_name> from
▎ Request {}
▎ Authentication required to use this tool
- User clicks through → tool runs.
- Subsequent tool calls in the same session do not re-prompt.
The per-first-tool prompt is persistent across disconnect/quit/reopen/reconnect cycles and is unaffected by Claude Desktop's "Always allow"
permission setting for that tool.
Reproduction
Two deployed servers on fastmcp.cloud, both behind the same Okta OIDC app using identical MultiAuth(server=OIDCProxy,
verifiers=[IntrospectionTokenVerifier]) wiring:
Server A — FastMCPProxy (reproduces the prompt):
mcp = create_proxy(
MCPConfigTransportSubclass(build_proxy_config(BACKENDS_PATH)),
name="mcp-docs-server",
instructions="...",
auth=_create_auth(),
transforms=[CodeMode(discovery_tools=[...])],
)
Server B — plain FastMCP (no prompt):
mcp = FastMCP(
"moon-d1-mcp",
instructions="...",
auth=_create_auth(),
)
@mcp.tool()
async def moon_list_features(...): ...
Both servers are connected to the same Claude Desktop, same account, same browser session. The tool call on A prompts; the tool call on B does not.
What I've ruled out
- CodeMode transform — disabled it temporarily; prompt still fired on directly-invoked proxied backend tools (cloudflare_*).
- fastmcp version — tested both directions. Upgrading Server B from 3.1.1 to 3.2.4 didn't introduce the prompt. Downgrading Server A from 3.2.4 to
3.1.1 didn't remove it. Version is not the cause.
- Tool metadata / annotations — fastmcp inspect --format mcp output for both servers shows annotations: null and _meta: { fastmcp: { tags: [] } } on
every tool. No requiresAuth-style fields on either side.
- Backend 401 + WWW-Authenticate propagation — I patched this separately by subclassing MCPConfigTransport to set forward_incoming_headers = False
after _create_proxy(); that fixed a different-but-related issue where AWS/Cloudflare docs backends were 401'ing on the forwarded Okta token and
triggering a separate per-backend OAuth dance. That fix is in place and the symptom under discussion remains.
- Claude Desktop per-tool consent model — "Always allow" in the tool permissions UI does not suppress the prompt, so it isn't standard per-tool
consent.
Diagnostic observation
fastmcp.cloud server logs show no incoming HTTP request at the moment the "Authentication required" dialog appears — the prompt is emitted
client-side, pre-emptively, before any tools/call reaches the server. This suggests Claude Desktop's MCP client is deciding tool-level auth is
required based on something it read from the server's initialize / tools/list response, rather than reacting to a runtime 401.
Environment
- fastmcp: 3.2.4
- mcp SDK: 1.27.0
- Deploy target: fastmcp.cloud (streamable-http, stateless)
- Client: Claude Desktop (macOS), current version
- Auth: OIDCProxy wrapping Okta, MultiAuth(server=..., verifiers=[IntrospectionTokenVerifier])
Hypothesis
Something in FastMCPProxy / ProxyProvider's capability-advertisement or initialize-response shape is cueing Claude Desktop to treat tool calls as
needing their own auth step, even when the server's Okta gate already holds a valid session token. Since we've excluded version, metadata, transform,
and config-level differences, this points at the create_proxy() code path itself.
Example Code
Version Information
FastMCP version: 3.2.4
MCP version: 1.27.0
Python version: 3.13.6
What happened?
Summary
Two FastMCP servers with otherwise-identical configuration behave differently under Claude Desktop's MCP OAuth flow. A server built with
create_proxy() (returning FastMCPProxy) triggers a per-first-tool-use "Authentication required to use this tool" confirmation dialog in Claude
Desktop after successful OAuth-at-connect. A plain FastMCP server with the same Okta OIDC auth wiring does not. I've isolated the trigger to the
FastMCPProxy / ProxyProvider shape by elimination.
Expected behavior
After a user authenticates once at connection time via OAuth, subsequent tool calls in that session should use the cached token silently — as plain
FastMCP servers do.
Actual behavior
On a FastMCPProxy server:
▎ Claude wants to use <tool_name> from
▎ Request {}
▎ Authentication required to use this tool
The per-first-tool prompt is persistent across disconnect/quit/reopen/reconnect cycles and is unaffected by Claude Desktop's "Always allow"
permission setting for that tool.
Reproduction
Two deployed servers on fastmcp.cloud, both behind the same Okta OIDC app using identical MultiAuth(server=OIDCProxy,
verifiers=[IntrospectionTokenVerifier]) wiring:
Server A — FastMCPProxy (reproduces the prompt):
mcp = create_proxy(
MCPConfigTransportSubclass(build_proxy_config(BACKENDS_PATH)),
name="mcp-docs-server",
instructions="...",
auth=_create_auth(),
transforms=[CodeMode(discovery_tools=[...])],
)
Server B — plain FastMCP (no prompt):
mcp = FastMCP(
"moon-d1-mcp",
instructions="...",
auth=_create_auth(),
)
@mcp.tool()
async def moon_list_features(...): ...
Both servers are connected to the same Claude Desktop, same account, same browser session. The tool call on A prompts; the tool call on B does not.
What I've ruled out
3.1.1 didn't remove it. Version is not the cause.
every tool. No requiresAuth-style fields on either side.
after _create_proxy(); that fixed a different-but-related issue where AWS/Cloudflare docs backends were 401'ing on the forwarded Okta token and
triggering a separate per-backend OAuth dance. That fix is in place and the symptom under discussion remains.
consent.
Diagnostic observation
fastmcp.cloud server logs show no incoming HTTP request at the moment the "Authentication required" dialog appears — the prompt is emitted
client-side, pre-emptively, before any tools/call reaches the server. This suggests Claude Desktop's MCP client is deciding tool-level auth is
required based on something it read from the server's initialize / tools/list response, rather than reacting to a runtime 401.
Environment
Hypothesis
Something in FastMCPProxy / ProxyProvider's capability-advertisement or initialize-response shape is cueing Claude Desktop to treat tool calls as
needing their own auth step, even when the server's Okta gate already holds a valid session token. Since we've excluded version, metadata, transform,
and config-level differences, this points at the create_proxy() code path itself.
Example Code
Version Information