Skip to content
Scalekit Docs
Talk to an Engineer Dashboard

FastMCP integration quickstart 🚀

This guide shows you how Scalekit’s provider secures any FastMCP server. We’ll register a sample server as a protected resource, configure a local FastMCP runtime, and use Scalekit as the Authentication server. We will able confirm that every request from MCP Client carries the right scopes and validate them on the MCP Server.

Use this quickstart to experience a working reference for authorizing any FastMCP project with Scalekit. We walk through a simple Todo MCP application. The Application will allow adding tasks and completing them. You can use this to understand the authenticatin pattern for MCP and apply it in your own tools.

Prerequisites

  • A Scalekit account with permission to manage MCP servers
  • Python 3.11+ installed locally
  • Familiarity with OAuth scopes (todo:read, todo:write) and basic terminal commands
Review the FastMCP authorization flowDiagram
  1. Create a protected resource so Scalekit can issue scoped tokens that FastMCP will validate on every request.

    1. Go to Dashboard > MCP Servers > Add MCP Server.
    2. Enter a descriptive name (for example, FastMCP Todo Server).
    3. Set Server URL to http://localhost:3002/ (keep the trailing slash).
    4. Create or link the scopes below, then click Save.

    Register FastMCP server

    ScopeDescriptionRequired
    todo:readGrants read access to todo tasksYes
    todo:writeAllows creating, updating, or deleting tasksYes
  2. Prepare a fresh directory and virtual environment so you can keep the FastMCP dependencies isolated.

    Terminal
    mkdir -p fastmcp-todo
    cd fastmcp-todo
    python3 -m venv venv
    source venv/bin/activate
  3. Add dependencies and configuration templates

    Section titled “Add dependencies and configuration templates”

    Create the support files that FastMCP and Scalekit expect, then install the required libraries.

    Terminal
    cat <<'EOF' > requirements.txt
    fastmcp>=2.13.0.2
    python-dotenv>=1.0.0
    EOF
    pip install -r requirements.txt
    cat <<'EOF' > env.example
    PORT=3002
    SCALEKIT_ENVIRONMENT_URL=https://your-environment-url.scalekit.com
    SCALEKIT_CLIENT_ID=your_client_id
    SCALEKIT_RESOURCE_ID=mcp_server_id
    MCP_URL=http://localhost:3002/
    EOF
  4. Copy the following code into server.py. It registers the Scalekit provider, defines an in-memory todo store, and exposes CRUD tools guarded by the todo:read and todo:write scopes.

    server.py
    """Scalekit-authenticated FastMCP server providing in-memory CRUD tools for todos.
    This example mirrors the greeting-fastmcp project structure while exposing
    todo management capabilities through FastMCP tools.
    """
    import os
    import uuid
    from dataclasses import dataclass, asdict
    from typing import Optional
    from dotenv import load_dotenv
    from fastmcp import FastMCP
    from fastmcp.server.auth.providers.scalekit import ScalekitProvider
    from fastmcp.server.dependencies import AccessToken, get_access_token
    load_dotenv()
    mcp = FastMCP(
    "Todo Server",
    stateless_http=True,
    auth=ScalekitProvider(
    environment_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"),
    client_id=os.getenv("SCALEKIT_CLIENT_ID"),
    resource_id=os.getenv("SCALEKIT_RESOURCE_ID"),
    # FastMCP appends /mcp automatically; keep base URL with trailing slash only.
    mcp_url=os.getenv("MCP_URL"),
    ),
    )
    @dataclass
    class TodoItem:
    id: str
    title: str
    description: Optional[str]
    completed: bool = False
    def to_dict(self) -> dict:
    return asdict(self)
    _TODO_STORE: dict[str, TodoItem] = {}
    def _require_scope(scope: str) -> Optional[str]:
    token: AccessToken = get_access_token()
    if scope not in token.scopes:
    return f"Insufficient permissions: `{scope}` scope required."
    return None
    @mcp.tool
    def create_todo(title: str, description: Optional[str] = None) -> dict:
    error = _require_scope("todo:write")
    if error:
    return {"error": error}
    todo = TodoItem(id=str(uuid.uuid4()), title=title, description=description)
    _TODO_STORE[todo.id] = todo
    return {"todo": todo.to_dict()}
    @mcp.tool
    def list_todos(completed: Optional[bool] = None) -> dict:
    error = _require_scope("todo:read")
    if error:
    return {"error": error}
    todos = [
    todo.to_dict()
    for todo in _TODO_STORE.values()
    if completed is None or todo.completed == completed
    ]
    return {"todos": todos}
    @mcp.tool
    def get_todo(todo_id: str) -> dict:
    error = _require_scope("todo:read")
    if error:
    return {"error": error}
    todo = _TODO_STORE.get(todo_id)
    if todo is None:
    return {"error": f"Todo `{todo_id}` not found."}
    return {"todo": todo.to_dict()}
    @mcp.tool
    def update_todo(
    todo_id: str,
    title: Optional[str] = None,
    description: Optional[str] = None,
    completed: Optional[bool] = None,
    ) -> dict:
    error = _require_scope("todo:write")
    if error:
    return {"error": error}
    todo = _TODO_STORE.get(todo_id)
    if todo is None:
    return {"error": f"Todo `{todo_id}` not found."}
    if title is not None:
    todo.title = title
    if description is not None:
    todo.description = description
    if completed is not None:
    todo.completed = completed
    return {"todo": todo.to_dict()}
    @mcp.tool
    def delete_todo(todo_id: str) -> dict:
    error = _require_scope("todo:write")
    if error:
    return {"error": error}
    todo = _TODO_STORE.pop(todo_id, None)
    if todo is None:
    return {"error": f"Todo `{todo_id}` not found."}
    return {"deleted": todo_id}
    if __name__ == "__main__":
    mcp.run(transport="http", port=int(os.getenv("PORT", "3002")))
  5. Copy the environment template, populate the values from the Scalekit dashboard, and review them with your security process.

    Terminal
    cp env.example .env
    open .env
    VariableDescription
    SCALEKIT_ENVIRONMENT_URLYour Scalekit environment URL from Dashboard -> Settings
    SCALEKIT_CLIENT_IDScalekit Client ID Dashboard -> Settings
    SCALEKIT_RESOURCE_IDThe resource identifier assigned to your MCP server in Scalekit res_xxxxxx.
    MCP_URLThe base public URL you registered (keep the trailing slash, e.g., http://localhost:3002/).
    PORTLocal port for the FastMCP HTTP transport (defaults to 3002).
  6. Start the server so it can accept authenticated MCP requests at /mcp.

    Terminal
    source venv/bin/activate
    python server.py

    When the server boots successfully, you’ll see FastMCP announce the HTTP transport and listen on http://localhost:3002/, ready to enforce Scalekit-issued tokens.

    Run MCP server

  7. Use any MCP-compatible client to exercise the todo tools with scoped tokens. During development, the MCP Inspector demonstrates how the Scalekit provider enforces scopes end-to-end.

    Terminal
    npx @modelcontextprotocol/inspector@latest

    In the Inspector UI, point the client to http://localhost:3002/mcp and click connect. Run any tool (the example exposes create_todo, list_todos, get_todo, update_todo, delete_todo).

    MCP Inspector

Once you’re satisfied with the quickstart example, extend server.py with your own FastMCP tools or replace the in-memory store with your production data source. Scalekit’s provider handles authentication for any toolset you add. Continue with the MCP OAuth 2.1 implementation guide to enforce additional grant types, rotate credentials, and automate client onboarding.

You can get the full code example at GitHub.