You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
docs(mcp): tool-call auth needs the CLI token, and get_call_run nests fields under result{} #126
Two reproducible integration gaps we hit building Speakeasy (a multilingual "AI makes the phone call for you" app) against the /mcp/openagent_oauth MCP endpoint. Both cost real debugging time and have small documentation fixes.
1. A self-driven MCP OAuth client connects and lists tools, but is Unauthorized on tools/call
Following the TypeScript OAuth example (examples/mcp-oauth-client), a client does dynamic client registration + PKCE, connects, and tools/list succeeds (returns plan_call / run_call / get_call_run). But the first tools/call (plan_call) returns Unauthorized — even though connection and discovery were authorized.
Workaround we found: tool invocation needs the account-linked bearer token that calle auth login caches at ~/.calle-mcp/cli/<hash>/token.json. Reusing that token (as a static Authorization: Bearer via the SDK authProvider) makes plan_call / run_call succeed immediately.
Suggested fix: document that placing calls requires the token from calle auth login, and note that the standalone mcp-oauth-client example authorizes tools/list but not tools/call; or clarify the scope/registration a from-scratch OAuth client needs to be authorized for the call tools.
2. get_call_run nests summary / transcript / outcome under result{}, not at the top level
The MCP doc's handoff-fields table lists summary, transcript, etc. A client reading them at the top level of structuredContent gets empty values on a COMPLETED run. The real shape is:
Suggested fix: document the result envelope (result.summary, result.transcript, result.outcome.*, result.extracted.*) in the get_call_run section, or add a normalized example response.
Priority: P1 for the authorization mismatch; P2 for the result-shape documentation.
A client that completes the advertised dynamic-registration/PKCE flow and can list tools but cannot invoke them indicates a broken or undocumented authorization boundary. The local CLI token cache is a private credential store, not a supported token-distribution API, so the documentation should not tell applications to read or copy its bearer token as the workaround.
Recommended next action:
Reproduce the TypeScript and Python OAuth examples against production with a non-call plan_call input, then compare the protected-resource metadata, requested/granted scope, audience/resource, client registration, and server authorization decision between tools/list and tools/call.
Fix the server flow or explicitly document the supported client/scopes so a standalone OAuth client receives a token authorized for the call tools. Add a live opt-in regression that proves both discovery and safe tool invocation work without exposing the token.
Keep access/refresh tokens, authorization codes, callback URLs, and token-cache paths out of logs, screenshots, examples, and model-visible output. If the standalone flow is not supported, make the example fail closed before advertising tool calls.
Update the canonical MCP guide to show the actual get_call_run envelope, including result.summary, result.transcript, result.outcome, and result.extracted, and synchronize every productized skill that renders those fields. PR fix(cursor-plugin): read get_call_run fields from result{} #129 addresses only the Cursor copy; it does not resolve the OAuth contract or the canonical/public copies.
Do not run run_call while validating authorization; it can place a real call.
Thanks for the detailed triage, this matches what we saw.
On P1 (auth): agreed that the local calle CLI token cache is a private, per-user
credential store, not a token-distribution API, and that docs should not present
reading or copying its bearer token as the supported path. We have reframed our
own docs accordingly: our recommended real-call transport is now the REST
Developer API with an API key (CALLE_TRANSPORT=rest, POST /v1/calls + GET /v1/calls/{id}), which needs no OAuth and no CLI-token reuse. The MCP/OAuth
path is kept only as a local-development convenience; when it is used, the backend
reads the CLI cache on the same machine and never copies, logs, or transmits the
token. Our request logging is allowlist-only (plan/run id, region, language,
destination count) with a regression test asserting no brief, phone, transcript,
or token is logged, and our demo screenshots use a keyless simulated mode, so no
tokens appear in logs, screenshots, or examples.
If a standalone OAuth client can be issued a token authorized for the call tools,
we would happily switch the MCP path to that and drop the CLI-cache reuse entirely.
A documented scope/audience for tool invocation (vs tools/list) is the missing
piece on our end.
On P2 (result shape): confirmed. We read the nested envelope
(result.summary, result.transcript, result.outcome, and the structured
extraction) with a top-level fallback, so a canonical guide update would let us
drop the fallback. Also noted your caution about run_call during auth
validation; we validated only with plan_call and the fake transport, no real
calls.
Completed the remaining example and documentation changes in #156, merged as 1646804. Together with #129, this covers the issue's agreed scope.
The TypeScript example completes browser OAuth when a protected request requires login after public tool discovery, retries the challenged request once, and stops on repeated denial.
The Python example pins the verified MCP SDK 2.1.1. Both examples completed fresh production OAuth and safe plan_call without reusing CLI credentials or starting a call.
The canonical guide, CLI wrapper documentation, and packaged skill instructions now read call content from the nested result object.
Validation: TypeScript typecheck and 7 e2e tests, Python compilation and 10 tests, repository checks/tests and package dry-runs, plus Linux and Windows CI all passed. The Python live probe completed OAuth and tool checks but remained running during shutdown and was stopped; a clean process exit was not verified.
The source fixes are merged. Patch changesets are included for the CLI, Codex and Claude packaged guidance; publication follows the normal release workflow. Keeping this issue closed as completed.
Activity
Ray-56 commentedon Sep 14, 2026
Priority: P1 for the authorization mismatch; P2 for the result-shape documentation.
A client that completes the advertised dynamic-registration/PKCE flow and can list tools but cannot invoke them indicates a broken or undocumented authorization boundary. The local CLI token cache is a private credential store, not a supported token-distribution API, so the documentation should not tell applications to read or copy its bearer token as the workaround.
Recommended next action:
plan_callinput, then compare the protected-resource metadata, requested/granted scope, audience/resource, client registration, and server authorization decision betweentools/listandtools/call.get_call_runenvelope, includingresult.summary,result.transcript,result.outcome, andresult.extracted, and synchronize every productized skill that renders those fields. PR fix(cursor-plugin): read get_call_run fields from result{} #129 addresses only the Cursor copy; it does not resolve the OAuth contract or the canonical/public copies.Do not run
run_callwhile validating authorization; it can place a real call.test: de-flake poll-timeout reliability test (maxWaitMs margin); docs…
yasaausman commentedon Sep 14, 2026
Thanks for the detailed triage, this matches what we saw.
On P1 (auth): agreed that the local
calleCLI token cache is a private, per-usercredential store, not a token-distribution API, and that docs should not present
reading or copying its bearer token as the supported path. We have reframed our
own docs accordingly: our recommended real-call transport is now the REST
Developer API with an API key (
CALLE_TRANSPORT=rest,POST /v1/calls+GET /v1/calls/{id}), which needs no OAuth and no CLI-token reuse. The MCP/OAuthpath is kept only as a local-development convenience; when it is used, the backend
reads the CLI cache on the same machine and never copies, logs, or transmits the
token. Our request logging is allowlist-only (plan/run id, region, language,
destination count) with a regression test asserting no brief, phone, transcript,
or token is logged, and our demo screenshots use a keyless simulated mode, so no
tokens appear in logs, screenshots, or examples.
If a standalone OAuth client can be issued a token authorized for the call tools,
we would happily switch the MCP path to that and drop the CLI-cache reuse entirely.
A documented scope/audience for tool invocation (vs
tools/list) is the missingpiece on our end.
On P2 (result shape): confirmed. We read the nested envelope
(
result.summary,result.transcript,result.outcome, and the structuredextraction) with a top-level fallback, so a canonical guide update would let us
drop the fallback. Also noted your caution about
run_callduring authvalidation; we validated only with
plan_calland the fake transport, no realcalls.
Thanks again.
JJasonSun commentedon Sep 23, 2026
Completed the remaining example and documentation changes in #156, merged as 1646804. Together with #129, this covers the issue's agreed scope.
plan_callwithout reusing CLI credentials or starting a call.resultobject.Validation: TypeScript typecheck and 7 e2e tests, Python compilation and 10 tests, repository checks/tests and package dry-runs, plus Linux and Windows CI all passed. The Python live probe completed OAuth and tool checks but remained running during shutdown and was stopped; a clean process exit was not verified.
The source fixes are merged. Patch changesets are included for the CLI, Codex and Claude packaged guidance; publication follows the normal release workflow. Keeping this issue closed as completed.