Repository navigation
docs(mcp): tool-call auth needs the CLI token, and get_call_run nests fields under result{} #126
Description
Activity
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_callinput, then compare the protected-resource metadata, requested/granted scope, audience/resource, client registration, and server authorization decision betweentools/listandtools/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_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.- Reproduce the TypeScript and Python OAuth examples against production with a non-call
- added a commit that references this issue
on 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-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 (vstools/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 aboutrun_callduring auth
validation; we validated only withplan_calland the fake transport, no real
calls.Thanks again.
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_callwithout reusing CLI credentials or starting a call. - The canonical guide, CLI wrapper documentation, and packaged skill instructions now read call content from the nested
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.
Two reproducible integration gaps we hit building Speakeasy (a multilingual "AI makes the phone call for you" app) against the
/mcp/openagent_oauthMCP endpoint. Both cost real debugging time and have small documentation fixes.1. A self-driven MCP OAuth client connects and lists tools, but is
Unauthorizedontools/callFollowing the TypeScript OAuth example (
examples/mcp-oauth-client), a client does dynamic client registration + PKCE, connects, andtools/listsucceeds (returnsplan_call/run_call/get_call_run). But the firsttools/call(plan_call) returnsUnauthorized— even though connection and discovery were authorized.Workaround we found: tool invocation needs the account-linked bearer token that
calle auth logincaches at~/.calle-mcp/cli/<hash>/token.json. Reusing that token (as a staticAuthorization: Bearervia the SDKauthProvider) makesplan_call/run_callsucceed immediately.Suggested fix: document that placing calls requires the token from
calle auth login, and note that the standalonemcp-oauth-clientexample authorizestools/listbut nottools/call; or clarify the scope/registration a from-scratch OAuth client needs to be authorized for the call tools.2.
get_call_runnestssummary/transcript/outcomeunderresult{}, not at the top levelThe MCP doc's handoff-fields table lists
summary,transcript, etc. A client reading them at the top level ofstructuredContentgets empty values on aCOMPLETEDrun. The real shape is:{ "status": "COMPLETED", "result": { "summary": "…", "transcript": "[00:00:00] BOT: Hi. …", "outcome": { "task_completed": true, "completion_confidence": { "score": 0.86, "label": "high" }, "evidence": [ … ] }, "extracted": { "calling": { "duration_seconds": 26, "calls": [ … ] }, "to_phones": [ … ] } } }A synthetic example with this shape (reserved fictional data): https://github.com/yasaausman/Speakeasy/blob/main/docs/sample-run.json
Suggested fix: document the
resultenvelope (result.summary,result.transcript,result.outcome.*,result.extracted.*) in theget_call_runsection, or add a normalized example response.Environment: endpoint
https://seleven-mcp-sg.airudder.com/mcp/openagent_oauth,@modelcontextprotocol/sdk^1.29,@call-e/cli(auth viacalle auth login), TypeScript Streamable-HTTP MCP client.