mcp-server

nathanjclark/mcp-server

3.3

If you are the rightful owner of mcp-server and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to henry@mcphub.com.

The Shuttle MCP Server is a comprehensive Model Context Protocol server built using Rust, Axum, and Shuttle, designed for production-ready deployment with advanced features like OAuth 2.1 authentication and AI integration.

Shuttle MCP Server

A complete Model Context Protocol (MCP) server built with Rust, Axum, and Shuttle. This template provides everything you need to build a production-ready MCP server with OAuth 2.1 authentication, database integration, AI tools, and a clean registry-based architecture.

πŸš€ What You Get

  • πŸ” OAuth 2.1 Authentication - Secure authentication via Auth0
  • πŸ—„οΈ PostgreSQL Database - Managed database with automatic migrations
  • πŸ€– AI Integration - AI-powered tools via the rig crate
  • πŸ“Š Built-in Tools - Text processing, database queries, timestamps, and more
  • πŸ”§ Registry System - Centralized management of tools, resources, and prompts
  • ⚑ Full MCP Compliance - Complete JSON-RPC 2.0 implementation with proper authentication
  • πŸš€ One-Click Deploy - Deploy to Shuttle with a single command

πŸ—οΈ Architecture Overview

This server implements the complete MCP specification with authentication:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    JSON-RPC 2.0     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   MCP Client    β”‚ ────────────────────▢│  Shuttle MCP    β”‚
β”‚ (Claude, etc.)  β”‚                      β”‚     Server      β”‚
β”‚                 β”‚ ◄──── Tools ─────────│                 β”‚
β”‚                 β”‚ ◄── Resources ───────│  πŸ” OAuth 2.1   β”‚
β”‚                 β”‚ ◄─── Prompts ────────│  πŸ—„οΈ PostgreSQL  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                      β”‚  πŸ€– AI Tools    β”‚
                                         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Authentication Flow

  • Public Methods: initialize, notifications/initialized, exit
  • Protected Methods: All tools, resources, and prompts require authentication
  • Security: Users authenticate via OAuth 2.1 before accessing any functionality

πŸ“‹ Prerequisites

  1. Rust - Install from rustup.rs

  2. Shuttle CLI - Recommended installation method:

    # Linux/macOS
    curl -sSfL https://www.shuttle.dev/install | bash
    
    # Windows (PowerShell)
    # iwr https://www.shuttle.dev/install-win | iex
    
    # Alternative: Using Cargo
    # cargo install cargo-shuttle
    
  3. Auth0 Account - Free at auth0.com

  4. Docker

πŸš€ Quick Start

1. Clone and Setup

git clone <this-repo-url>
cd mcp-server

2. Configure Auth0

  1. Go to Applications and click "Create Application"
  2. Choose "Regular Web Application"
  3. Go to connections and enable Google Social Connection
  4. Note your domain, client ID, and client secret from the application's settings

3. Create Secrets

# Create Secrets.toml in project root
cat > Secrets.toml << EOF
AUTH0_DOMAIN = 'your-tenant.auth0.com'
AUTH0_CLIENT_ID = 'your-client-id'
AUTH0_CLIENT_SECRET = 'your-client-secret'
AUTH0_CALLBACK_URL = 'http://localhost:8000/auth/callback'
SESSION_JWT_SECRET = 'your-very-long-random-secret-key-at-least-32-chars'
OPENAI_API_KEY = 'sk-your-openai-api-key'  # Optional
EOF

4. Run Locally

shuttle run

5. Test Your Server

The easiest way to test your MCP server is using the official MCP Inspector - a visual testing tool designed specifically for MCP development.

Using MCP Inspector (Recommended)

The MCP Inspector provides a complete testing interface with no installation required:

# Test the Shuttle server directly
npx @modelcontextprotocol/inspector shuttle run
  • Copy session token from CLI ouput after running the above command
  • Open MCP Inspector in the Browser
  • Choose Streamable HTTP as the transport type.
  • Set URL as "http://localhost:8000/mcp"
  • Click "Configuration" and then enter the copied session token into the "Proxy Session Token" field.
  • Click Connect
  • Go the "Auth" tab, select guided or quick auth flow and follow the steps.

The Inspector provides:

  • Visual Interface: Interactive UI for testing tools, resources, and prompts
  • Authentication Support: Built-in bearer token authentication for testing protected methods
  • Real-time Debugging: Monitor JSON-RPC messages and server responses
  • Export Configuration: Generate mcp.json files for client integration
Basic Testing with curl

For quick verification, you can test public endpoints:

# Test MCP endpoint (initialize is public - no auth required)
curl -X POST http://localhost:8000/mcp \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "initialize",
    "params": {
      "protocolVersion": "2024-11-05",
      "capabilities": {},
      "clientInfo": {"name": "test-client", "version": "1.0.0"}
    },
    "id": 1
  }'

# Test authentication flow (will redirect to Auth0)
curl -I http://localhost:8000/auth/login

Note: Testing protected MCP methods (tools, resources, prompts) requires authentication, which is much easier to handle with the MCP Inspector's built-in auth support.

🌐 Deploy to Production

1. Setup Shuttle

shuttle login

2. Deploy

shuttle deploy

Note: This deployment will not fully work as it does not yet have the required secrets. However, we need the deployment url in order to confugure the secrets - seee the next step.

3. Update Auth0 Settings

In your Auth0 application:

  • Allowed Callback URLs: https://your-mcp-server.shuttleapp.dev/auth/callback
  • Allowed Logout URLs: https://your-mcp-server.shuttleapp.dev/

4. Update Production Secrets

# Update Secrets.toml
AUTH0_CALLBACK_URL = 'https://your-mcp-server.shuttleapp.dev/auth/callback'
# Keep other secrets the same

5. Re-Deploy

shuttle deploy

πŸ”Œ API Endpoints

Authentication

OAuth 2.1 Endpoints

EndpointMethodDescription
/.well-known/oauth-authorization-serverGETOAuth server metadata (RFC8414)
/authorizeGETAuthorization endpoint with PKCE support
/tokenPOSTToken endpoint for access tokens
/registerPOSTDynamic client registration (RFC7591)

OAuth Flow

  1. Client registers using /register endpoint
  2. Client initiates auth flow via /authorize with PKCE
  3. User authenticates and authorizes
  4. Client exchanges code for token via /token
  5. Client uses access token for MCP requests

Auth0 Callback Endpoint

This endpoint handles the Auth0 callback as part of the OAuth flow:

  • GET /auth/callback - Handle Auth0 callback and bind users to OAuth authorization codes

MCP Protocol

  • POST /mcp - Main MCP JSON-RPC 2.0 endpoint

πŸ› οΈ Available MCP Capabilities

Core Protocol Methods

MethodAuthenticationDescription
initialize❌ PublicExchange capabilities and server info
notifications/initialized❌ PublicComplete MCP handshake
tools/listβœ… RequiredList available tools with schemas
tools/callβœ… RequiredExecute tools
resources/listβœ… RequiredList available data resources
resources/readβœ… RequiredRead resource content
prompts/listβœ… RequiredList available prompt templates
prompts/getβœ… RequiredGet specific prompts

Built-in Tools

ToolDescriptionArguments
text_lengthGet character counttext: string
text_transformTransform text casetext: string, transform: enum
text_searchSearch for patternstext: string, pattern: string
timestampGet current UTC timeNone
ai_completeComplete text promptsprompt: string
ai_summarizeSummarize long texttext: string
user_statsGet database statisticsNone

Built-in Resources

ResourceDescriptionContent Type
user://statsUser statistics from databaseapplication/json

Built-in Prompts

PromptDescriptionArguments
code_reviewGenerate code review promptscode: string, language?: string
explain_errorGenerate error explanation promptserror: string

πŸ”§ Extending Your Server

Adding New Tools

  1. Implement the tool in src/tools/:
// src/tools/my_tools.rs
pub fn calculate_fibonacci(n: u32) -> u64 {
    match n {
        0 => 0,
        1 => 1,
        _ => calculate_fibonacci(n - 1) + calculate_fibonacci(n - 2)
    }
}
  1. Register in the registry (src/registries.rs):
Tool {
    name: "fibonacci",
    description: "Calculate Fibonacci number",
},
  1. Add the handler in src/mcp.rs (handle_tool_call function):
"fibonacci" => {
    let n = arguments.get("n").and_then(|v| v.as_u64()).unwrap_or(0) as u32;
    let result = crate::tools::my_tools::calculate_fibonacci(n);
    Ok(serde_json::json!({
        "content": [{
            "type": "text",
            "text": format!("Fibonacci({}) = {}", n, result)
        }]
    }))
}
  1. Add the schema in get_tool_schema function:
"fibonacci" => serde_json::json!({
    "type": "object",
    "properties": {
        "n": {
            "type": "integer",
            "description": "The position in Fibonacci sequence",
            "minimum": 0
        }
    },
    "required": ["n"]
}),

Adding New Resources

  1. Register the resource (src/registries.rs):
Resource {
    uri: "system://health",
    name: "System Health",
    description: "Current system health metrics",
    mime_type: "application/json",
},
  1. Add the handler in handle_resource_read function (src/mcp.rs):
"system://health" => {
    let health_data = serde_json::json!({
        "status": "healthy",
        "uptime": "2h 30m",
        "memory_usage": "45%"
    });
    Ok(serde_json::json!({
        "contents": [{
            "uri": uri,
            "mimeType": resource.mime_type,
            "text": serde_json::to_string_pretty(&health_data).unwrap()
        }]
    }))
}

Adding New Prompts

  1. Register the prompt (src/registries.rs):
Prompt {
    name: "write_tests",
    description: "Generate unit tests for code",
    arguments: vec![
        PromptArgument {
            name: "code",
            description: "The code to test",
            required: true,
        },
        PromptArgument {
            name: "framework",
            description: "Testing framework",
            required: false,
        },
    ],
},
  1. Add the handler in handle_prompt_get function (src/mcp.rs):
"write_tests" => {
    let code = arguments.and_then(|args| args.get("code"))
        .and_then(|v| v.as_str()).unwrap_or("// No code provided");
    let framework = arguments.and_then(|args| args.get("framework"))
        .and_then(|v| v.as_str()).unwrap_or("jest");

    Ok(serde_json::json!({
        "messages": [{
            "role": "user",
            "content": {
                "type": "text",
                "text": format!("Write {} unit tests for this code:\n\n{}", framework, code)
            }
        }]
    }))
}

πŸ“ Project Structure

src/
β”œβ”€β”€ main.rs              # Application entry point and routing
β”œβ”€β”€ auth/                # Authentication system
β”‚   β”œβ”€β”€ mod.rs           # Module exports
β”‚   β”œβ”€β”€ handlers.rs      # OAuth and session handlers
β”‚   β”œβ”€β”€ middleware.rs    # Authentication middleware and helpers
β”‚   └── models.rs        # User and auth data models
β”œβ”€β”€ database.rs          # Database initialization and migrations
β”œβ”€β”€ mcp.rs              # MCP JSON-RPC protocol implementation
β”œβ”€β”€ registries.rs        # Central registries for tools, resources, and prompts
└── tools/              # Tool implementations
    β”œβ”€β”€ mod.rs          # Tool module exports
    β”œβ”€β”€ ai.rs           # OpenAI integration tools
    β”œβ”€β”€ db.rs           # Database query tools
    β”œβ”€β”€ text.rs         # Text processing utilities
    └── utils.rs        # General utility tools
migrations/             # Database migration files

πŸ“š Learn More

πŸ“ License

This project is licensed under the MIT License - see the file for details.