Building MCP Together: Arcade's Contribution to Secure Agent Auth

Building MCP Together: Arcade's Contribution to Secure Agent Auth

Nate Barbettini's avatar
Wils Dawson's avatar
Nate Barbettini & Wils Dawson
JULY 11, 2025
4 MIN READ
MCP
Rays decoration image
Ghost Icon

Your AI agent needs to search Gmail for that weekly report. You've built an MCP server, the tool definition, everything's wired up perfectly. One problem: there's no secure path in the protocol to get the OAuth 2.0 bearer token your agent needs to call the Gmail API.

This is the gap between MCP's design and production reality. While the protocol handles client-server authentication beautifully, it completely lacks a mechanism for servers to securely obtain third-party credentials. At Arcade.dev, we've been working to fix this fundamental limitation.

The Technical Problem: Credential Flow in Distributed Systems

Let's break down what's actually happening. Your MCP server needs to make authenticated requests to external APIs. But MCP has no secure credential gathering mechanism.

Current workarounds are all security anti-patterns for multi-user production systems:

  • Service account tokens with excessive scopes
  • Credentials hardcoded in server configs
  • Passing tokens through the MCP client (violating the principle of least privilege)
  • Client-side credential storage (hello, token exfiltration risks)

This isn't just bad UX—it's a fundamental security architecture flaw. MCP clients are often untrusted code running on user devices. They're OAuth 2.0 "public clients" that can't securely store secrets. Yet today, that's exactly what developers are forced to do.

Our Engineering Journey: Two Approaches to Secure Auth

PR #475: Adding User Interaction as a Client Capability

Our initial proposal introduced a new client capability for user interactions. The core insight: leverage the browser as a trusted security context, just like OAuth 2.0 has done successfully for 15+ years.

The implementation added a userInteraction capability:

interface UserInteractionRequest {
  method: "userInteraction";
  params: {
    prompt: string;
    url?: string;
    timeout?: number;
  };
}

This allowed servers to redirect users to secure endpoints for credential gathering, keeping sensitive data out of the client execution context entirely. The proposal sparked extensive discussion and security review with 50+ contributors examining attack vectors, CSRF protections, and state management.

But MCP 2025-06-18 shipped with  elicitation, a client capability for dynamically rendering forms and gathering data from the user. Elicitation via forms doesn't work for credentials or sensitive data, but could it be extended to enable secure user interactions?

PR #887: Extending Elicitation with URL Mode

Rather than having two similar-but-different client capabilities, we evolved our approach. PR #887 extends the elicitation framework with a new mode:

interface UrlElicitation {
  id: string;
  mode: "url";
  url: string;
  message: string;
  metadata?: {
    oauth_provider?: string;
    required_scopes?: string[];
    state?: string;
  };
}

This creates clear separation of concerns:

  • Form mode: Client-rendered UI for non-sensitive data (preferences, parameters)
  • URL mode: Direct browser navigation for sensitive flows (OAuth 2.0, payments, WebAuthn, SAML)

The security model is explicit: form elicitation flows data through the client, URL elicitation bypasses the client  entirely. But why does that matter?

Deep Dive: Why URL Elicitation Matters

Proper OAuth 2.0 Implementation

Consider implementing GitHub integration. With URL elicitation, you get proper OAuth 2.0:

# Server initiates OAuth flow
def handle_github_tool_call(params):
    if not has_valid_token(user_id):
        state = generate_secure_state()
        code_verifier = generate_code_verifier()
        
        auth_url = build_oauth_url(
            client_id=GITHUB_CLIENT_ID,
            redirect_uri=CALLBACK_URL,
            state=state,
            code_challenge=hash_verifier(code_verifier),
            scope="repo:read"
        )
        
        raise ElicitationRequired([{
            "id": f"github-auth-{state}",
            "mode": "url",
            "url": auth_url,
            "message": "Authorize GitHub access",
            "metadata": {
                "oauth_provider": "github",
                "required_scopes": ["repo:read"]
            }
        }])

Or, something other than OAuth entirely: a redirect to a payment portal, enterprise IDP login page, etc. The client just opens the URL. For the client, that means no token handling, no state management, no security responsibilities.

Respecting Security Boundaries

URL elicitation enforces proper security boundaries:

  1. Client (untrusted): Facilitates navigation, handles retry logic
  2. Server (trusted): Manages tokens, validates state, enforces scopes
  3. Auth provider (trusted): Handles user authentication, consent

This mirrors established web security patterns. The MCP client never touches credentials, preventing entire classes of attacks:

  • Token exfiltration via compromised clients
  • Scope escalation through client manipulation
  • The "confused deputy" problem

Real Implementation Benefits

From our research into production-ready MCP servers at Arcade:

// Before: Insecure token passing
const result = await mcp.callTool("search_gmail", {
  query: "weekly report",
  token: localStorage.getItem("gmail_token") // 🚨 Security nightmare
});

// After: Secure URL elicitation
try {
  const result = await mcp.callTool("search_gmail", {
    query: "weekly report"
  });
} catch (e) {
  if (e.code === "ELICITATION_REQUIRED") {
    // Client opens URL, user auths, server stores token
    window.open(e.elicitations[0].url);
      // Retry after auth completes
  }
}

This pattern mirrors what client apps already do to interact with services that require redirects for authorization.

Multi-Provider Authentication

Real agents need multiple auth providers. URL elicitation handles this elegantly:

// Server can request multiple authorizations
throw new ElicitationRequired([
  {
    id: "gmail-auth",
    mode: "url",
    url: getGoogleOAuthUrl(),
    message: "Authorize Gmail access"
  },
  {
    id: "slack-auth", 
    mode: "url",
    url: getSlackOAuthUrl(),
    message: "Connect Slack workspace"
  }
]);

What This Enables

With proper authorization, MCP servers are one step closer to being production-ready:

  • Scoped access: Request minimum necessary permissions
  • Token refresh: Handle expiry without user intervention
  • Audit trails: Track what actions were taken with which authorizations
  • Revocation: Users can revoke access anytime through the provider

The technical foundations matter. This is the difference between a demo and production infrastructure.

Implementation Timeline

PR #887 is under active review. For early adopters:

  1. Today: Arcade.dev tools already implement these patterns 
  2. Near term: URL elicitation standardizes the approach
  3. Future: The MCP ecosystem adopts these patterns in both clients and servers

Want to accelerate this?

  • Review PR #887 and stress-test the security model
  • Implement URL elicitation in your MCP server

Without a way to securely interact with the user, MCP is limited to servers that only connect to first-party APIs. By adding a mechanism that respects the security boundaries of the client (inspired by the battle-tested patterns used by OAuth), more powerful and interesting MCP servers are possible. We're excited about the future of MCP, what about you?


The Arcade.dev team has spent years hardening auth at Okta, Stormpath, and Redis. We're applying those lessons to make AI infrastructure production-ready.

Want to build AI agents that actually work in production? While we wait for authorization to land in MCP, Arcade already implements secure auth for 100+ integrations. No bot tokens, no security nightmares—just real OAuth flows that work.

Start building with Arcade → Sign Up.

SHARE THIS POST

RECENT ARTICLES

THOUGHT LEADERSHIP

Production-Ready MCP: Why Security Standards Matter for AI Tool Infrastructure

After eight years building authentication systems at Okta, followed by stints at Kong and ngrok working on developer tools and API gateways, I've seen how to build systems that are secure by default. Now at Arcade.dev, I'm watching the MCP ecosystem struggle to get there. The Model Context Protocol has incredible potential for enabling AI agents to interact with real-world systems. But there's a gap between experimental implementations and production-ready infrastructure that most developers ar

THOUGHT LEADERSHIP

The Agent Hierarchy of Needs: Why Your AI Can't Actually Do Anything (Yet)

Your AI can summarize documents you feed it, answer questions about your uploaded PDFs, and explain concepts from its training data. But ask it to pull your actual Q4 revenue from NetSuite, check real customer satisfaction scores, or update a deal in Salesforce? Suddenly it's just guessing—or worse, hallucinating numbers that sound plausible but aren't your data. This disconnect between AI's intelligence and its ability to access real data and take action is why less than 30% of AI projects hav

COMPANY NEWS

We Just Won "Overall Authentication Solution of the Year" — Here's Why It Matters for AI Builders

Arcade.dev just took home "Overall Authentication Solution of the Year" in the 8th Annual AI Breakthrough Awards. And before you roll your eyes at another tech award announcement, let me explain why this actually matters for anyone building AI agents that need to do real work. The Problem We All Keep Hitting You know that moment when your perfectly crafted AI agent suggests "I'll schedule that meeting for you" — and then... doesn't? Because it can't? Yeah, that's the wall everyone's been hit

Blog CTA Icon

Get early access to Arcade, and start building now.