Skip to content

callOnce() should forward the OAuth-suppression option (disableOAuth from #198) #201

@feniix

Description

@feniix

Summary

callOnce() is the one-shot library convenience exported from mcporter:

// src/runtime.ts (current main)
export async function callOnce(params: {
  server: string;
  toolName: string;
  args?: Record<string, unknown>;
  configPath?: string;
}): Promise<unknown>;

It drops every CallOptions knob the underlying runtime.callTool exposes. Once #198 lands disableOAuth on Runtime.callTool (and listTools / listResources / readResource), callOnce will be the only public call surface that can't suppress the interactive OAuth flow — headless callers will have to drop down to createRuntime + callTool + close just to set one boolean.

Why this matters

The README explicitly points callOnce at headless use cases (line 257 today):

callOnce automatically discovers the selected server (...), handles OAuth prompts, and closes transports when it finishes. It is ideal for manual runs or wiring MCPorter directly into an agent tool hook.

"Agent tool hook" is non-interactive. Today on main, if the cached token is expired or missing, callOnce reaches openExternal and tries to spawn a browser from a context the user can't see — the exact failure mode #197 reports and #198 closes for Runtime.callTool. The convenience function the README promotes for headless work cannot enforce headless safety.

Security / operational impact

Strictly additive and safe-by-default:

  • disableOAuth defaults to undefined → behavior identical to today.
  • disableOAuth: true is more restrictive than the default — suppresses interactive OAuth at the transport layer and short-circuits the unauthorized-fallback path that could otherwise re-enter OAuth.
  • Falls back to cached tokens via the same on-disk store the CLI's --no-oauth path (added in fix(runtime): preserve disableOAuth across headless paths #198) uses. Missing or expired token → server returns 401, call fails cleanly. No hanging browser launch.

The flag only narrows what the runtime is allowed to do; no scenario widens an attacker's reach.

Proposed API

Add one optional field; forward it:

 export async function callOnce(params: {
   server: string;
   toolName: string;
   args?: Record<string, unknown>;
   configPath?: string;
+  disableOAuth?: boolean;
 }): Promise<unknown> {
   const runtime = await createRuntime({ configPath: params.configPath });
   try {
     return await runtime.callTool(params.server, params.toolName, {
       args: params.args,
+      disableOAuth: params.disableOAuth,
     });
   } finally {
     await runtime.close(params.server);
   }
 }

Forward only disableOAuth. Per #198's design, disableOAuth: true already implies cached-token use, so explicit allowCachedAuth isn't required. CallOptions.timeoutMs and ConnectOptions.oauthSessionOptions are deliberately left to the full Runtime surface — callers needing them should not be on the convenience function. Adding timeoutMs later is a reasonable separable expansion.

README follow-up

Update the README line above in the same PR to mention disableOAuth and cross-reference the CLI's --no-oauth flag.

Backward compatibility

Strictly additive. Every existing caller keeps working with the same default behavior.

Scope

callOnce only. Not in scope: revisiting callOnce's broader design, forwarding the full CallOptions / ConnectOptions surface.

Related & dependency

Becomes actionable when #198 lands; could also be folded into #198's PR.

Priority

P3 / impact:auth-provider — same class as #199. The functional workaround (createRuntime + callTool + close) will exist once #198 lands; the gap is that the README documents a use case its convenience API cannot fully support.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low-risk cleanup, docs, polish, ergonomics, or speculative feature.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:auth-providerThis issue is about auth, provider routing, model choice, or SecretRef resolution.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions