---
title: "Creating an MCP Server with Arcade"
description: "Learn how to create, test, deploy, and publish a custom MCP Server with Arcade"
---
import { Steps, Tabs, Callout } from "nextra/components";
# Creating an MCP Server with Arcade
The `arcade_mcp_server` package is the secure framework to build and run MCP servers with your Arcade tools. It is easiest to use with the `arcade-mcp` package (Arcade's CLI) which can scaffold your MCP server 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.
Build and run a secure MCP server with tools that you define.
- The [`uv` package manager](https://docs.astral.sh/uv/getting-started/installation/)
- How to run MCP servers with Arcade tools using the [`arcade_mcp_server`](/references/mcp/python) package
- How to use `arcade new` from the `arcade-mcp` CLI to create your server project with all necessary files and dependencies.
- How to run your local MCP Server with the Arcade CLI and register it with the Arcade Engine so that your agent can find and use your tool.
## Install the Arcade CLI
In your terminal, run the following command to install the `arcade-mcp` package - Arcade's CLI:
```bash
uv tool install arcade-mcp
```
{" "}
This will install the Arcade CLI as a [uv
tool](https://docs.astral.sh/uv/guides/tools/#installing-tools), making it
available system wide.
```bash
pip install arcade-mcp
```
## Create Your Server
In your terminal, run the following command to scaffold a new MCP Server called `my_server`:
```bash
arcade new my_server
cd my_server/src/my_server
```
This generates a Python module with the following structure:
```bash
my_server/
├── src/
│ └── my_server/
│ ├── __init__.py
│ ├── .env.example
│ └── server.py
└── pyproject.toml
```
1. **server.py** Main server file with MCPApp and example tools. It creates an `MCPApp`, defines tools with `@app.tool`, and will start the server with `app.run()` when the file is executed directly.
1. **pyproject.toml** Dependencies and project configuration
1. **.env.example** Example `.env` file containing a secret required by one of the generated tools in `server.py`. Environments are loaded on server start, so **if you update the `.env` file, you will need to restart your server.**
```python filename="server.py" showLineNumbers
#!/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, API keys, or other tokens that grant access to a protected resource or API. Arcade includes the "whisper_secret" tool that requires a secret key to be set in your environment. If the secret is not set, the tool will return an error.
You can create a `.env` file at the same directory as your entrypoint file (`server.py`) and add your secret:
```env filename=".env"
MY_SECRET_KEY="my-secret-value"
```
The generated project includes a `.env.example` file with the secret key name and example value.
You can rename it to `.env` to start using it.
```bash
mv .env.example .env
```
You can set the environment variable in your terminal directly with this command:
```bash
export MY_SECRET_KEY="my-secret-value"
```
## Connect to Arcade to unlock authorized tool calling
Since the Reddit tool accesses information only available to your Reddit account, you'll need to authorize it. For this, you'll need to create an Arcade account and connect to it from the terminal, run:
```bash
arcade login
```
Follow the instructions in your browser, and once you've finished, your terminal will be connected to your Arcade account.
## Run your MCP Server
Run your MCP Server using one of the following commands in your terminal:
```bash
uv run server.py stdio
```
When using the stdio transport, MCP clients typically launch the MCP server 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](/guides/create-tools/tool-basics/create-tool-secrets) guide for more
information.
```bash
uv run server.py http
```
For HTTP transport, view your server's API docs at [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
For security reasons, Local HTTP servers do not currently support tool-level
authorization and secrets. If you need to use tool-level authorization or
secrets locally, you should use the stdio transport and configure the Arcade
API key and secrets in your MCP connection settings. Otherwise, if you intend
to expose your HTTP MCP server to the public internet with tool-level
authorization and secrets, please follow the [deploying to the cloud with
Arcade Deploy](/guides/deployment-hosting/arcade-deploy) guide or the [on-prem MCP
server](/guides/deployment-hosting/on-prem) guide for secure remote deployment.
You should see output like this in your terminal:
```bash
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 tools
```
## Configure your MCP Client(s)
Now you can connect MCP Clients to your MCP server:
```bash
# 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 http
```
```bash
# Configure VS Code to use your MCP server with the default transport (stdio)
arcade configure vscode
# Configure VS Code to use your MCP server with the http transport
arcade configure vscode --transport http
```
```bash
# Configure Claude Desktop to use your MCP server with the default transport (stdio)
arcade configure claude
# Configure Claude Desktop to use your MCP server with the http transport
arcade configure claude --transport http
```
That's it! Your MCP server is running and connected to your AI assistant.
## Key takeaways
- **Minimal Setup** Create `MCPApp`, define tools with `@app.tool`, and run with `app.run()`
- **Direct Execution** Run your server file directly with `uv run` or `python`
- **Transport Flexibility** Works with both stdio and HTTP
- **Type Annotations** Use `Annotated` from the builtin typing library to provide descriptions to the language model for parameters and return values
- **Tool 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 tools that use auth**: [Learn how to create tools with authorization](/guides/create-tools/tool-basics/create-tool-auth)
- **Create custom tools that use secrets**: [Learn how to create tools with secrets](/guides/create-tools/tool-basics/create-tool-secrets)
- **Learn the capabilities of the `Context` object**: [Understanding the Context object](/guides/create-tools/tool-basics/runtime-data-access)
- **Evaluate your tools**: [Explore how to evaluate tool performance](/guides/create-tools/evaluate-tools/why-evaluate)
- **Deploy your MCP server**: [Learn how to deploy your MCP server](/guides/deployment-hosting/arcade-deploy)