Back to Articles

Building MCP Servers with Arcade: FastAPI Ergonomics Meet AI Tool Orchestration

[ View on GitHub ]

Building MCP Servers with Arcade: FastAPI Ergonomics Meet AI Tool Orchestration

Hook

The same Python function can become a tool in Claude Desktop, Cursor, and VS Code without changing a single line of code—even when it needs OAuth tokens or API secrets.

Context

The Model Context Protocol (MCP) from Anthropic promises to standardize how AI assistants access external tools and data sources, but the raw protocol involves significant boilerplate: managing stdio and HTTP transports, handling authentication flows, injecting secrets securely, and configuring multiple AI clients with different integration patterns. Developers familiar with building web APIs expect the ergonomics of frameworks like FastAPI—type hints, decorators, automatic validation—but MCP’s reference implementations are intentionally minimal, leaving authentication and secrets management as exercises for the implementer. Arcade MCP emerged to bridge this gap, providing a batteries-included framework that makes building production-ready MCP servers feel as intuitive as building a REST API, while solving the hard problems of credential management and multi-client compatibility that every serious tool integration eventually faces.

Technical Insight

stdio or HTTP/SSE

requires_auth

secure tokens

invokes with context

credentials never exposed

authenticated requests

responses

results

tool response

JSON-RPC

MCP Client

Claude/Cursor/VSCode

Transport Layer

MCPApp Instance

@app.tool decorators

Auth Provider

OAuth/API Keys

Context Object

Injected credentials

Tool Functions

Type-hinted Python

External APIs

Reddit/GitHub/etc

System architecture — auto-generated

Arcade MCP’s design centers on a decorator-based API that will feel immediately familiar to Python developers who’ve used FastAPI or Flask. You create an MCPApp instance and use the @app.tool decorator to expose functions as tools, with type hints and docstrings automatically generating the tool schema:

from arcade_mcp_server import MCPApp
from typing import Annotated

app = MCPApp(name="my_server", version="1.0.0")

@app.tool
def greet(name: Annotated[str, "The name of the person to greet"]) -> str:
    """Greet a person by name."""
    return f"Hello, {name}!"

This simple tool works across all MCP clients without modification. The framework’s CLI—arcade new my_server—scaffolds a complete project with proper argument parsing to support both stdio and HTTP transports, determined at runtime by command-line flags.

Where Arcade MCP differentiates itself is in authentication and secrets management. Rather than exposing credentials to LLMs or MCP clients, the framework uses context injection. Tools declare authentication requirements through decorator parameters, and the runtime injects tokens into a Context object that only your tool code can access:

from arcade_mcp_server import Context
from arcade_mcp_server.auth import Reddit

@app.tool(requires_auth=Reddit(scopes=["read"]))
async def get_posts_in_subreddit(
    context: Context, 
    subreddit: Annotated[str, "The name of the subreddit"]
) -> dict:
    """Get posts from a specific subreddit"""
    oauth_token = context.get_auth_token_or_empty()
    headers = {"Authorization": f"Bearer {oauth_token}"}
    # Make authenticated request...

The OAuth flow happens outside the MCP protocol entirely. You run arcade login once to authenticate with Reddit (or other supported providers), and the framework handles token storage and injection. The LLM only sees the tool definition and return values—never the token itself.

Secrets management follows the same pattern. Tools can declare requires_secrets=["MY_SECRET_KEY"], and the framework loads them from environment variables or .env files:

@app.tool(requires_secrets=["MY_SECRET_KEY"])
def whisper_secret(context: Context) -> str:
    """Reveal the last 4 characters of a secret"""
    secret = context.get_secret("MY_SECRET_KEY")
    return "The last 4 characters are: " + secret[-4:]

This security model is deliberate: secrets and tokens never appear in MCP messages, tool schemas, or LLM context windows. The transport layer handles authentication separately from tool execution.

The dual transport architecture means the same server code runs in fundamentally different integration modes. With stdio transport (the default), the server communicates over standard input/output, which is how Claude Desktop and CLI tools integrate. With HTTP transport, specified via command-line argument, the server exposes an HTTP/SSE endpoint for editors like Cursor and VS Code. The README explicitly notes that HTTP transport doesn’t support authenticated tools locally—those require deployment through Arcade’s platform—while stdio transport supports authentication and secrets out-of-the-box.

The arcade configure CLI commands automate client setup, generating the correct configuration files for Claude Desktop, Cursor, or VS Code without manual JSON editing. This is where the “unified developer experience” claim materializes: you write tools once, and the framework handles the integration differences between desktop apps using stdio and editors expecting HTTP endpoints.

Gotcha

The HTTP transport limitation is significant and somewhat buried in the documentation. If you’re building tools that require authentication or secrets and want to test them locally in Cursor or VS Code, you’re out of luck—the README states these tools “do not support require_auth or require_secrets unless deployed through Arcade’s platform.” This creates a dependency on Arcade’s hosted infrastructure for a complete development workflow with authenticated tools in certain clients, which may not align with teams that require fully local development or have deployment restrictions.

The Python-only implementation is a strategic constraint in a protocol designed to be language-agnostic. Teams with TypeScript, Go, or Rust codebases can’t use Arcade MCP without introducing a new language into their stack, and the MCP ecosystem has reference implementations in other languages that might be more natural fits. Additionally, with 830 stars and a relatively young project age, documentation gaps and breaking changes are realistic concerns—the framework is opinionated about authentication patterns and deployment workflows that may evolve as the MCP ecosystem matures. If you need OAuth providers beyond the documented examples (Reddit is prominently featured) or custom authentication schemes, you’ll be implementing them within Arcade’s auth framework rather than using battle-tested libraries directly.

Verdict

Use Arcade MCP if you’re building Python-based tool integrations for AI assistants and value developer ergonomics over low-level control. It’s particularly compelling when you need to support multiple MCP clients (Claude Desktop, Cursor, VS Code) without rewriting integration logic, or when your tools require OAuth authentication with supported providers and you want the security guarantees of context-injected credentials. The FastAPI-like decorator API significantly reduces boilerplate compared to raw MCP implementations, and the CLI tooling for scaffolding and configuration is genuinely useful. Skip it if you need language flexibility beyond Python, require complete local development for authenticated tools across all transports without platform dependencies, or are building simple single-tool servers where the official MCP SDK’s lower-level primitives would give you more control. Also skip if your authentication needs extend beyond the built-in providers and you’d rather integrate auth libraries directly than work within Arcade’s framework abstractions.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/ai-agents/arcadeai-arcade-mcp.svg)](https://starlog.is/api/badge-click/ai-agents/arcadeai-arcade-mcp)