mcp-server-toolkit

May 31, 2026 · View on GitHub

The batteries-included starter for production Model Context Protocol servers.

CI License: MIT Language Last commit Python MCP

MCP became the default integration layer for agents, and most reference servers are still toys: a single tool, no auth, no observability. This toolkit is the opinionated alternative that ships an MCP 1.0 compliant server with the plumbing already wired in. Write a tool once and reach it over stdio for a local agent and over streamable HTTP for a remote one, with the same handler, schema validation, auth, rate limiting, and tracing on every call.

Built by Sarma Linux.


Architecture

graph TD
  Client[MCP client<br/>desktop / IDE / remote agent]
  Client -->|stdio JSON-RPC| Stdio[stdio transport]
  Client -->|streamable HTTP| HTTP[FastAPI HTTP transport]
  HTTP --> Auth[auth: API key / OAuth 2.1<br/>+ per-client rate limit]
  Stdio --> Proto[MCP protocol dispatch<br/>initialize / tools.list / tools.call]
  Auth --> Proto
  Proto --> Reg[tool registry<br/>schema validation + spans]
  Reg --> P1[plugin: filesystem]
  Reg --> P2[plugin: sarmalink]
  Reg --> P3[your plugins]
  Reg -->|OTLP| OTEL[OpenTelemetry collector]
  P2 -->|api.sarmalink.ai| SLAI[SarmaLink-AI]

  classDef ext fill:#a78bfa,stroke:#a78bfa,color:#fff
  class SLAI,OTEL ext

Quick start

git clone https://github.com/sarmakska/mcp-server-toolkit.git
cd mcp-server-toolkit
uv sync
cp .env.example .env
uv run mcp-toolkit run --transport stdio

That boots an MCP server over stdio with the bundled plugins registered. Point a desktop client or IDE at it (see the Quick-Start wiki page), or drive it by hand:

printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18"}}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
  | uv run mcp-toolkit run --transport stdio

For remote use, switch to HTTP and call the MCP endpoint:

uv run mcp-toolkit run --transport http --port 8000
curl -s localhost:8000/mcp -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

What is in the box

  • MCP 1.0 compliant. Full initialize handshake with protocol version negotiation (2025-06-18, 2025-03-26, 2024-11-05), notifications/initialized, ping, tools/list, and tools/call with content blocks and structured results. The protocol layer is shared, so both transports behave identically.
  • Two transports, one code path. stdio (JSON-RPC 2.0) for local agents, streamable HTTP (FastAPI) at POST /mcp for remote deployment. A tool written once is reachable over both.
  • Auth built in. API key (constant-time comparison) or OAuth 2.1 bearer tokens validated against the issuer's JWKS with issuer and audience checks, selected by MCP_AUTH. A mcp-toolkit login command runs the OAuth 2.1 PKCE flow to obtain a token against a hosted server.
  • Schema validation. The registry derives a JSON Schema from each handler's type hints and validates every call's arguments against it. Declare an output_schema and the return value is validated too, then surfaced as MCP structuredContent.
  • Per-client rate limiting. Token bucket keyed by client identity, configurable with MCP_RATE_LIMIT_RPS.
  • Span export. Every tool call runs inside an OpenTelemetry span with name, argument count, duration, and error type. Set an OTLP endpoint and spans export; otherwise structured JSON logs still flow to stderr.
  • Two example plugins. A sandboxed filesystem plugin and a sarmalink plugin that wraps an external API end to end.
  • Container image. Reproducible uv-based multi-stage build that runs as a non-root user with a health check.

Plugin authoring

A plugin is a Python module that registers handlers with @registry.tool. The input schema comes from the type hints.

from mcp_toolkit.registry import registry

@registry.tool("search_docs", description="Search internal docs")
async def search_docs(query: str, limit: int = 10) -> dict:
    return {"results": [...]}

query is required (no default) and typed as a string; limit is optional and typed as an integer. The generated schema reflects both. See Plugin-Authoring.

Configuration

Env varPurposeDefault
MCP_TRANSPORTstdio or httpstdio
MCP_HTTP_HOST / MCP_HTTP_PORTHTTP bind address0.0.0.0:8000
MCP_AUTHnone, api_key, oauthnone
MCP_API_KEYkey for api_key modeunset
MCP_OAUTH_ISSUER / MCP_OAUTH_AUDIENCEOAuth resource server checksunset
MCP_OAUTH_JWKS_URIexplicit JWKS endpoint (else discovered)unset
MCP_RATE_LIMIT_RPS / MCP_RATE_LIMIT_BURSTper-client rate limit0 (off)
MCP_FS_ROOTfilesystem plugin sandbox root~/mcp-data
MCP_OTEL_ENDPOINTOTLP collector URL for span exportunset
MCP_SARMALINK_API_KEYkey for the sarmalink pluginunset

All settings use the MCP_ prefix and can be supplied via a .env file. See .env.example.

Deployment

docker build -t mcp-toolkit .
docker run -p 8000:8000 --env-file .env mcp-toolkit

The image runs the HTTP transport as a non-root user with a built-in health check. Front it with TLS termination (your platform's load balancer, or Caddy on a VPS). See Deployment.

When to use this, when not to

Use this when you are building an MCP server that needs to ship: you want auth, schema validation, tracing, rate limiting, and both transports without writing the plumbing, and you want the same plugin code to run locally over stdio and in production over HTTP. It suits internal tool gateways, remote MCP servers for a team, and bridges that wrap an existing API as MCP tools.

Do not reach for this if you only need a throwaway single-tool stdio server for one local agent, where the reference SDK example is lighter. It is also not a managed product: you host and operate it yourself.

Documentation

Full docs live in the wiki: architecture, quick start, plugin authoring, auth modes, observability, and deployment. The bundled sarmalink plugin is a working end-to-end example of a tool that calls an external API. See also ARCHITECTURE.md, ROADMAP.md, and CHANGELOG.md.

License

MIT. Built by Sarma Linux.


More open source by Sarma

Part of a portfolio of twelve production-shaped open-source repositories built and maintained by Sarma.

RepositoryWhat it is
Sarmalink-aiMulti-provider OpenAI-compatible AI gateway with 14-engine failover and intent-based plugin auto-routing
agent-orchestratorDurable multi-agent workflows in TypeScript with deterministic replay and Inspector UI
voice-agent-starterSub-second full-duplex voice agent loop. WebRTC, mediasoup, pluggable STT / LLM / TTS
ai-eval-runnerEvals as code. Python, DuckDB, FastAPI viewer, regression mode for CI
mcp-server-toolkitProduction Model Context Protocol server starter (Python / FastAPI)
local-llm-routerOpenAI-compatible proxy that routes to Ollama or cloud providers based on policy
rag-over-pdfMinimal end-to-end RAG starter for PDF corpora
receipt-scannerVision OCR for receipts with Zod-validated JSON output
webhook-to-emailWebhook receiver that forwards events to email via Resend
k8s-ops-toolkitHelm chart for shipping Next.js to Kubernetes with full observability stack
terraform-stackVercel + Supabase + Cloudflare + DigitalOcean modules in one Terraform repo
staff-portalOpen-source HR / ops portal: leave, attendance, expenses, kiosk mode

Engineering essays at sarmalinux.com/blog · All projects at sarmalinux.com/open-source