Creating an MCP Server with Arcade
The arcade_mcp_server package is the secure framework to build and run servers with your Arcade . It is easiest to use with the arcade-mcp package (Arcade’s CLI) which can scaffold your with all the necessary files and dependencies, configure MCP Clients to connect to your server, deploy your server to the cloud, and more. This guide walks you through the complete process of creating a custom MCP server with Arcade.
Outcomes
Build and run a secure server with that you define.
You will Learn
- How to run servers with Arcade using the
arcade_mcp_serverpackage - How to use
arcade newfrom thearcade-mcpCLI to create your server with all necessary files and dependencies. - How to run your local Server with the Arcade CLI and register it with the so that your can find and use your .
Prerequisites
Install the Arcade CLI
In your terminal, run the following command to install the arcade-mcp package - Arcade’s CLI:
uv
uv tool install arcade-mcpThis will install the Arcade CLI as a uv tool , making it available system wide.
Create Your Server
In your terminal, run the following command to scaffold a new Server called my_server:
arcade new my_server
cd my_server/src/my_serverThis generates a Python module with the following structure:
my_server/
├── src/
│ └── my_server/
│ ├── __init__.py
│ ├── .env.example
│ └── server.py
└── pyproject.toml- server.py Main server file with MCPApp and example . It creates an
MCPApp, defines tools with@app.tool, and will start the server withapp.run()when the file is executed directly. - pyproject.toml Dependencies and configuration
- .env.example Example
.envfile containing a secret required by one of the generated inserver.py. Environments are loaded on server start, so if you update the.envfile, you will need to restart your server.
#!/usr/bin/env python3
"""my_server MCP server"""
import sys
from typing import Annotated
import httpx
from arcade_mcp_server import Context, MCPApp
from arcade_mcp_server.auth import Reddit
app = MCPApp(name="my_server", version="1.0.0", log_level="DEBUG")
@app.tool
def greet(name: Annotated[str, "The name of the person to greet"]) -> str:
"""Greet a person by name."""
return f"Hello, {name}!"
# To use this tool locally, you need to either set the secret in the .env file or as an environment variable
@app.tool(requires_secrets=["MY_SECRET_KEY"])
def whisper_secret(context: Context) -> Annotated[str, "The last 4 characters of the secret"]:
"""Reveal the last 4 characters of a secret"""
# Secrets are injected into the context at runtime.
# LLMs and MCP clients cannot see or access your secrets
# You can define secrets in a .env file.
try:
secret = context.get_secret("MY_SECRET_KEY")
except Exception as e:
return str(e)
return "The last 4 characters of the secret are: " + secret[-4:]
# To use this tool locally, you need to install the Arcade CLI (uv tool install arcade-mcp)
# and then run 'arcade login' to authenticate.
@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"""
# Normalize the subreddit name
subreddit = subreddit.lower().replace("r/", "").replace(" ", "")
# Prepare the httpx request
# OAuth token is injected into the context at runtime.
# LLMs and MCP clients cannot see or access your OAuth tokens.
oauth_token = context.get_auth_token_or_empty()
headers = {
"Authorization": f"Bearer {oauth_token}",
"User-Agent": "finally-mcp-server",
}
params = {"limit": 5}
url = f"https://oauth.reddit.com/r/{subreddit}/hot"
# Make the request
async with httpx.AsyncClient() as client:
response = await client.get(url, headers=headers, params=params)
response.raise_for_status()
# Return the response
return response.json()
# Run with specific transport
if __name__ == "__main__":
# Get transport from command line argument, default to "stdio"
# - "stdio" (default): Standard I/O for Claude Desktop, CLI tools, etc.
# Supports tools that require_auth or require_secrets out-of-the-box
# - "http": HTTPS streaming for Cursor, VS Code, etc.
# Does not support tools that require_auth or require_secrets unless the server is deployed
# using 'arcade deploy' or added in the Arcade Developer Dashboard with 'Arcade' server type
transport = sys.argv[1] if len(sys.argv) > 1 else "stdio"
# Run the server
app.run(transport=transport, host="127.0.0.1", port=8000)Setup the secrets in your environment
Secrets are sensitive strings like passwords, , or other tokens that grant access to a protected resource or API. Arcade includes the “whisper_secret” that requires a secret key to be set in your environment. If the secret is not set, the tool will return an error.
.env file
You can create a .env file at the same directory as your (server.py) and add your secret:
MY_SECRET_KEY="my-secret-value"The generated includes a .env.example file with the secret key name and example value.
You can rename it to .env to start using it.
mv .env.example .envConnect to Arcade to unlock authorized tool calling
Since the Reddit tool accesses information only available to your Reddit , you’ll need to authorize it. For this, you’ll need to create an Arcade account and connect to it from the terminal, run:
arcade loginFollow the instructions in your browser, and once you’ve finished, your terminal will be connected to your Arcade .
Run your MCP Server
Run your Server using one of the following commands in your terminal:
stdio transport (default)
uv run server.py stdioWhen using the stdio transport, clients typically launch the as
a subprocess. Because of this, the server may run in a different environment
and not have access to secrets defined in your local .env file. Please refer
to the create a tool with
secrets guide for more
information.
You should see output like this in your terminal:
2025-11-03 13:46:11.041 | DEBUG | arcade_mcp_server.mcp_app:add_tool:242 - Added tool: greet
2025-11-03 13:46:11.042 | DEBUG | arcade_mcp_server.mcp_app:add_tool:242 - Added tool: whisper_secret
2025-11-03 13:46:11.043 | DEBUG | arcade_mcp_server.mcp_app:add_tool:242 - Added tool: get_posts_in_subreddit
INFO | 13:46:11 | arcade_mcp_server.mcp_app:299 | Starting my_server v1.0.0 with 3 toolsConfigure your MCP Client(s)
Now you can connect Clients to your :
Cursor IDE
# Configure Cursor to use your MCP server with the default transport (stdio)
arcade configure cursor
# Configure Cursor to use your MCP server with the http transport
arcade configure cursor --transport httpThat’s it! Your server is running and connected to your AI assistant.
Key takeaways
- Minimal Setup Create
MCPApp, define with@app.tool, and run withapp.run() - Direct Execution Run your server file directly with
uv runorpython - Transport Flexibility Works with both stdio and HTTP
- Type Annotations Use
Annotatedfrom the builtin typing library to provide descriptions to the language model for parameters and return values - Docstrings Use docstrings to provide a description of a tool to the language model
- Command Line Arguments Pass transport type as command line argument
Next steps
- Create custom that use auth: Learn how to create tools with authorization
- Create custom that use secrets: Learn how to create tools with secrets
- Learn the capabilities of the
Contextobject: Understanding the Context object - Evaluate your : Explore how to evaluate tool performance
- Deploy your server: Learn how to deploy your MCP server