resk-mcp · Documentation

Secure Model Context Protocol layer

resk-mcp (PyPI: mcp-resk) wraps Python MCP servers with authentication, request screening, rate limiting, token budgets and audit tracking — without replacing the official Model Context Protocol SDK.

  • Version 0.1.4
  • Python 3.11+
  • MIT license
  • Import name: resk_mcp

What resk-mcp does

An MCP server is not an ordinary internal API. The caller is a language model that discovers tools at runtime and invokes them semi-autonomously, so the usual assumptions — a human on the other end, a stable capability set, structured payloads only — do not hold. resk-mcp adds the controls that assumption gap requires, on top of the official mcp Python SDK:

The library does not reimplement MCP. It subclasses FastMCP, keeps your existing tools, resources and prompts, and mounts a secured dispatcher on the FastAPI app at /mcp_secure.

How a request flows

client ── POST /mcp_secure ──▶ rate limit (slowapi)
                                  │
                                  ▼
                            Bearer token present?
                                  │
                                  ▼
                            verify_jwt_token()  ── invalid ──▶ -33000 AUTH_ERROR
                                  │
                                  ▼
                            payload shape (Pydantic)
                                  │
                                  ▼
                      detect_prompt_injection(params) ── hit ──▶ -33003
                      detect_pii(params)             ── hit ──▶ -33003
                      context_manager.is_within_limits ── over ──▶ -33002
                                  │
                                  ▼
                        dispatch: tool/<name> · resource/<name> · prompt/<name>
                                  │
                                  ▼
                        counter++ (dashboard)  →  JSON-RPC result

Detection order matters when you debug a rejection: the HTTP rate limiter runs first (it is a FastAPI dependency on the route), then authentication, then payload validation, then the injection/PII/context checks. Details are on the security model page.

Feature summary

CapabilityWhat it enforcesDetails
JWT authenticationHS256 (configurable) token on every /mcp_secure request; user_id claim requiredAuthentication
Input validationJSON-RPC envelope and params type checks with Pydanticvalidation helpers
Prompt-injection screeningKeyword and heuristic scan of string values in paramsInjection screening
PII screeningPattern scan for emails, phone numbers, card numbers and similar identifiersPII screening
Rate limitingcount/period per JWT user_id, falling back to client IPRate limiting
Token budgetsEstimated tokens per request (MAX_TOKEN_PER_REQUEST)Token budget
Schema validationOptional JSON Schema checks per tool, plus Pydantic-generated schemasSchema validation
Audit dashboardCounters per tool/resource/prompt, server info, uptime, optional loginDashboard
TLSOptional keyfile/certfile passed to uvicornTLS

Quick start

Install the package, export one secret, register a tool and start the server:

  1. Install and create .env:

    pip install mcp-resk
    
    # .env (never commit this file)
    JWT_SECRET="$(python -c 'import secrets; print(secrets.token_urlsafe(48))')"
  2. Write main.py:

    from resk_mcp import SecureMCPServer, create_jwt_token
    
    server = SecureMCPServer(name="my-secure-mcp")
    
    @server.tool(name="calculator/add")
    async def add(a: int, b: int) -> int:
        """Adds two numbers."""
        return a + b
    
    if __name__ == "__main__":
        print("Bearer", create_jwt_token(user_id="[email protected]"))
        server.run_server(port=8001)
  3. Call the secured endpoint with the token printed at startup:

    curl -s http://localhost:8001/mcp_secure \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"id":1,"method":"tool/calculator/add","params":{"a":2,"b":3}}'
  4. Enable the dashboard (DASHBOARD_AUTH_ENABLED=true plus a non-default DASHBOARD_PASSWORD) and open http://localhost:8001/dashboard to watch the interaction counters. It returns 404 while disabled — by design.

Note

create_jwt_token() reads JWT_SECRET from the environment (or config.yaml). If the secret is missing, resk_mcp.config raises at import time — that is intentional, the library refuses to start without a signing key.

HTTP surface

PathMethodPurpose
/mcp_securePOSTAuthenticated dispatcher for MCP calls — method tool/<name> (resources and prompts are registered but their POST execution is not implemented yet)
/dashboardGETWeb dashboard — 404 unless DASHBOARD_AUTH_ENABLED=true; redirects to /login without a valid session
/loginGETDashboard login page
/api/dashboard/auth/loginPOSTExchange dashboard credentials for a session cookie
/api/dashboard/auth/logoutPOSTInvalidate the dashboard session
/api/dashboard/interactionsGETCounters per tool/resource/prompt (JSON)
/api/dashboard/server-infoGETServer name, registered capabilities, uptime, auth and rate-limit settings
/GETRedirects to /login

The FastAPI-generated /docs, /redoc and /openapi.json routes are disabled by default because they publish the full tool schema. Set RESK_MCP_DOCS=true only in a trusted environment.

Scope and limitations

Being precise about the boundary is part of the design:

Where to go next

Installation

Requirements, virtualenv, first server, first authenticated call, source install.

Configuration

Every environment variable and config.yaml key, with defaults and precedence.

Security model

What is enforced, in which order, and how to harden a deployment.

API reference

Public imports, decorators, helpers, wire format and error codes.