Setu — Sarvam MCP Server
Setu — Sarvam MCP Server
Open-source Model Context Protocol server that exposes Sarvam AI's API surface as typed tools, so any MCP-capable agent (Claude Desktop, Cursor, Claude Code, or a custom LangGraph agent) can use Sarvam speech / translation / chat / document tools in minutes.
Live demo & write-up: https://sarvam-native.netlify.app/setu
Status — Milestone 7 (release-ready)
Feature-complete: all seven Sarvam tools over stdio + streamable-HTTP, each
routed through the observability/reliability middleware seam, typed end-to-end,
CI-gated (ruff, mypy --strict, 80%+ coverage, Docker build), containerised, and
packaged for PyPI with a tag-triggered release workflow (trusted publishing).
See DEMO.md for the 60-second demo storyboard. Everything runs in
mock mode with no key; set SETU_MODE=live + SARVAM_API_KEY to hit real APIs.
Architecture
MCP client (Claude Desktop / Cursor / Claude Code / LangGraph agent)
│ stdio OR streamable-HTTP (/mcp)
▼
┌──────────────────────── SETU MCP SERVER (FastMCP) ───────────────────────┐
│ Tool layer — typed Pydantic in/out │
│ setu_ping · sarvam_transcribe · sarvam_speak · sarvam_translate │
│ sarvam_transliterate · sarvam_identify_language · sarvam_chat │
│ sarvam_parse_document │
│ │ │
│ ▼ middleware seam (every call) │
│ span(start) → rate-limit(token bucket) → retry/backoff → │
│ dispatch → measure latency → compute INR cost → span attributes │
│ │ │ │
│ ┌──────┴──────┐ ┌──────┴───────┐ │
│ │ MODE: live │ │ OTel spans │ │
│ │ / mock │ │ cost+latency │ │
│ └──────┬──────┘ └──────────────┘ │
│ live │ │ mock │
│ ▼ ▼ │
│ sarvamai SDK canned fixtures (no credits burned) │
└──────────────────────┼───────────────────────────────────────────────────┘
▼
Sarvam APIs — Saaras · Bulbul · Mayura · Sarvam-30B/105B · ParseEvery Sarvam call flows through one middleware seam so every tool call is observable and priced:
Retries: exponential backoff with jitter on 5xx/network errors; 4xx never retried except 429, which honours the server's
Retry-After.Rate limiting: a client-side token bucket per API key; remaining budget is reported as a span attribute.
Cost + latency telemetry: each call is wrapped in an OpenTelemetry span carrying
setu.model,setu.endpoint,setu.latency_ms, input units, and a computedsetu.cost_inrfrom a maintained INR rate card (pricing.py). Spans export to stderr in dev (SETU_OTEL_EXPORTER=console|none).
cost_inr is also returned on every tool result.
Milestone | Scope | State |
1 | Scaffold, config, structlog, FastMCP | ✅ |
2 |
| ✅ |
3 | Retry, rate-limit, cost/latency OTel telemetry middleware | ✅ |
4 |
| ✅ |
5 | mypy --strict, 80%+ coverage gate, GitHub Actions CI | ✅ |
6 | streamable-HTTP transport, Dockerfile, compose, architecture diagram | ✅ |
7 | PyPI packaging + tag-triggered release workflow, demo storyboard | ✅ this repo |
Tools
Tool | Model | Key inputs | Output |
| — |
| server/version/mode echo |
| Saaras v3 |
|
|
| Bulbul v3 |
|
|
| Mayura v1 |
|
|
| Mayura |
|
|
| text-lid |
|
|
| Sarvam-105B/30B |
|
|
| Parse |
|
|
Every result also carries latency_ms and request_id. Try them in mock mode with
no key. To go live, set SETU_MODE=live and SARVAM_API_KEY, then install the SDK
extra: pip install -e '.[live]'. (Live sarvam_parse_document uses Sarvam's
job-based Document Digitization API, wired in a later milestone; mock is complete.)
Quickstart
Once published, install from PyPI and launch with no clone:
pip install setu-mcp # or: uvx setu-mcp
setu # stdio; or: setu --transport streamable-httpTo develop from source:
# 1. Create a virtualenv and install (editable, with dev extras)
python3 -m venv .venv && source .venv/bin/activate
pip install -e '.[dev]'
# 2. Copy the env template (defaults to mock mode — no key needed)
cp .env.example .env
# 3. Run the server over stdio
setu # or: python -m setu.server
# 4. Run the tests
pytestsetu speaks the MCP protocol on stdout, so it looks like it "hangs" — that's correct;
it's waiting for an MCP client. Logs go to stderr as JSON. Press Ctrl-C to stop.
Transports
Setu serves the same tools over two transports from one FastMCP app:
setu # stdio (local IDE / desktop agents; default)
setu --transport streamable-http # HTTP endpoint at http://127.0.0.1:8000/mcpConfigure the HTTP bind with SETU_HTTP_HOST / SETU_HTTP_PORT.
Docker
# Build and run the HTTP server (mock mode) on http://localhost:8000/mcp
docker build -t setu-mcp .
docker run --rm -p 8000:8000 setu-mcp
# Or via compose
docker compose up --build
# Live mode
docker run --rm -p 8000:8000 -e SETU_MODE=live -e SARVAM_API_KEY=sk_... setu-mcpThe image is a multi-stage build running as a non-root user; it defaults to the
streamable-HTTP transport. Override with docker run ... setu --transport stdio.
Configuration
All config is environment-driven (pydantic-settings); secrets never get hardcoded.
Variable | Default | Meaning |
|
|
|
|
| Name advertised to MCP clients. |
|
| structlog level. |
|
| Token-bucket refill rate per API key. |
|
| Max seconds to wait for a token before erroring. |
|
| Total attempts per Sarvam call (retry cap). |
|
| Span exporter: |
|
| Bind host for the streamable-HTTP transport. |
|
| Bind port for the streamable-HTTP transport. |
| — | Sarvam key; only needed in |
Connect to Claude Desktop
Find the absolute path to the
setuentrypoint inside your venv:source .venv/bin/activate which setu # e.g. /Users/you/setu-mcp/.venv/bin/setuOpen Claude Desktop → Settings → Developer → Edit Config. This opens
claude_desktop_config.json. Add Setu undermcpServers:{ "mcpServers": { "setu": { "command": "/ABSOLUTE/PATH/TO/setu-mcp/.venv/bin/setu", "env": { "SETU_MODE": "mock" } } } }(On Windows the path ends in
\.venv\Scripts\setu.exe.)Fully quit and reopen Claude Desktop. Click the tools/🔌 icon in the chat box — you should see setu listed with the
setu_pingtool.Ask Claude: "Use the setu_ping tool with message 'it works'." You should get back a typed result showing
ok: true, the server name, version, andmode: mock.
If the server doesn't appear, check Claude Desktop's MCP logs
(~/Library/Logs/Claude/mcp*.log on macOS) — Setu's JSON logs on stderr are captured there.
Layout
setu-mcp/
├─ pyproject.toml # package + tooling (ruff, mypy, pytest, coverage)
├─ Dockerfile # multi-stage, non-root, HTTP by default
├─ docker-compose.yml # one-command local run
├─ .github/workflows/ci.yml# ruff + mypy + pytest + docker build
├─ .env.example # config template (mock by default)
├─ src/setu/
│ ├─ config.py # SetuSettings + SarvamSettings (pydantic-settings)
│ ├─ logging.py # structlog → stderr, JSON, request ids
│ ├─ modes.py # live vs mock dispatch
│ ├─ errors.py # typed errors + retryable classification
│ ├─ pricing.py # INR rate card + cost estimator
│ ├─ telemetry.py # OpenTelemetry setup (stderr exporter) + tracer
│ ├─ middleware.py # token-bucket rate limiter + retry/backoff
│ ├─ sarvam_client.py # async wrapper; span→rate-limit→retry→cost seam
│ ├─ fixtures/ # deterministic mock responses
│ ├─ app.py # shared FastMCP app + Sarvam client + settings
│ ├─ tools/ # one module per tool (health, transcribe, speak,
│ │ # translate, transliterate, identify_language,
│ │ # chat, parse_document) — each typed + registered
│ └─ server.py # transport entrypoint (registers tools, runs stdio)
└─ tests/
├─ test_ping.py # health-check + registration
├─ test_tools.py # transcribe/speak (mock) + skipped live smoke test
├─ test_tools_m4.py # translate/transliterate/langID/chat/parse (mock)
└─ test_middleware.py # rate limiter, retry (5xx/4xx/429), pricing, spansReleasing
Releases are automated. Tagging a version builds the sdist + wheel, creates a GitHub Release, and publishes to PyPI via Trusted Publishing (OIDC — no token stored):
git tag v0.1.0
git push origin v0.1.0One-time PyPI setup: at pypi.org → Publishing,
add a trusted publisher for this repo (workflow release.yml, environment pypi).
License
MIT © 2026 Manasa SB
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/manasasb2000/setu-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server