Skip to content

02 - Building Apps

Build and run an MCP server programmatically using the FastAPI-like MCPApp interface.

Running the Example

  • Run HTTP: python examples/02_building_apps.py
  • Run stdio: python examples/02_building_apps.py stdio

Source Code

#!/usr/bin/env python
"""
02_building_apps.py - Build an MCP server using MCPApp

This example shows how to build and run an MCP server programmatically
using `MCPApp` instead of relying on the arcade_mcp_server CLI.

To run (HTTP transport by default):
    python 02_building_apps.py

To run with stdio transport (for Claude Desktop):
    python 02_building_apps.py stdio
"""

import sys
from typing import Annotated

from arcade_mcp_server import Context, MCPApp

# Create the MCP application
app = MCPApp(
    name="my_mcp_server", version="0.1.0", instructions="Example MCP server built with MCPApp"
)


@app.tool
def greet(
    name: Annotated[str, "Name of the person to greet"],
) -> Annotated[str, "Greeting message"]:
    """Return a friendly greeting.

    Parameters:
        name: Person's name

    Returns:
        Greeting message.
    """
    return f"Hello, {name}!"


@app.tool
async def whoami(context: Context) -> Annotated[dict, "Basic server and user information"]:
    """Return basic information from the tool context.

    Returns:
        Dictionary with `user_id` and whether MCP features are available.
    """
    user_id = context.user_id or "anonymous"

    if context:
        await context.log.info(f"whoami called by: {user_id}")

    secret_keys = [secret.key for secret in context.secrets] if context.secrets else []
    return {
        "user_id": user_id,
        "secret_keys": secret_keys,
    }


if __name__ == "__main__":
    # Check if stdio transport was requested
    if len(sys.argv) > 1 and sys.argv[1] == "stdio":
        app.run(transport="stdio")
    else:
        # Default to HTTP transport
        app.run(host="127.0.0.1", port=8000)

MCPApp Features

1. Creating an App

from arcade_mcp_server import MCPApp

app = MCPApp(
    name="my_server",
    version="1.0.0",
    title="My MCP Server",
    instructions="This server provides utility tools",
    log_level="INFO"
)

2. Adding Tools

Method 1: Direct Tool Definition

Use the @app.tool decorator to define tools directly:

@app.tool
def my_tool(param: Annotated[str, "Description"]) -> str:
    """Tool description."""
    return f"Result: {param}"

Method 2: Importing Tools from Files

Import tools from other files and add them explicitly:

from my_tools import calculate, process_data

# Add imported tools to the app
app.add_tool(calculate)
app.add_tool(process_data)

Method 3: Importing from Packages

Import tools from Arcade packages:

from arcade_gmail.tools import list_emails

# Add package tools to the app
app.add_tool(list_emails)

This approach gives you explicit control over which tools are loaded and allows for modular organization.

For a comprehensive example of tool organization, see 06_tool_organization.md.

3. Running the Server

# Default HTTP transport
app.run()

# Specify options
app.run(
    host="0.0.0.0",
    port=8080,
    reload=True,  # Auto-reload on code changes
    transport="http"
)

# For stdio transport (Claude Desktop)
app.run(transport="stdio")

4. Using Context

Tools can access runtime context:

@app.tool
async def context_aware(context: Context, value: str) -> dict:
    """Tool that uses context features."""
    # Access user info
    user_id = context.user_id


    # Use MCP features if available
    if context:
        await context.log.info(f"Processing for user: {user_id}")

    # Access secrets
    secret_keys = list(context.secrets.keys())


    return {
        "user": user_id,
        "value": value,
        "available_secrets": secret_keys
    }

Key Concepts

  • FastAPI-like Interface: Familiar decorator-based API design
  • Programmatic Control: Build servers without CLI dependency
  • Transport Flexibility: Support for both HTTP and stdio transports
  • Context Integration: Access to user info, logging, and secrets
  • Development Features: Hot reload, debug logging, and more