Skip to content

docs(mcp): tool-call auth needs the CLI token, and get_call_run nests fields under result{} #126

Description

@yasaausman

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:

{
  "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 result envelope (result.summary, result.transcript, result.outcome.*, result.extracted.*) in the get_call_run section, 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 via calle auth login), TypeScript Streamable-HTTP MCP client.

Activity

  1. Ray-56 commented on Sep 14, 2026

    @Ray-56
    Collaborator

    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:

    1. 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.
    2. 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.
    3. 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.
    4. 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.

  2. yasaausman commented on Sep 14, 2026

    @yasaausman
    Author

    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.

    Thanks again.

  3. JJasonSun commented on Sep 23, 2026

    @JJasonSun
    Collaborator

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions