Skip to main content
Glama

Setu — Sarvam MCP Server

CI Python License: MIT Coverage

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 · Parse

Every 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 computed setu.cost_inr from 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 setu_ping over stdio

2

sarvam_transcribe + sarvam_speak, mock/live dispatch

3

Retry, rate-limit, cost/latency OTel telemetry middleware

4

translate, chat, transliterate, identify_language, parse_document

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

setu_ping

message

server/version/mode echo

sarvam_transcribe

Saaras v3

audio_base64|audio_url, language_code=auto, mode=codemix

text, language, confidence, cost_inr

sarvam_speak

Bulbul v3

text, target_language_code, speaker

audio_base64 (WAV), chars, cost_inr

sarvam_translate

Mayura v1

input, target_language_code, source_language_code=auto

translated_text, source_language_code, cost_inr

sarvam_transliterate

Mayura

input, target_language_code, spoken_form

transliterated_text, cost_inr

sarvam_identify_language

text-lid

input

language_code, script_code, cost_inr

sarvam_chat

Sarvam-105B/30B

messages, model, tools, tool_choice

content, tool_calls, usage, cost_inr

sarvam_parse_document

Parse

file_base64|file_url

pages, fields, tables, cost_inr

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-http

To 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
pytest

setu 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/mcp

Configure 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-mcp

The 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

SETU_MODE

mock

mock serves fixtures (no credits); live calls Sarvam (milestone 2+).

SETU_SERVER_NAME

setu

Name advertised to MCP clients.

SETU_LOG_LEVEL

INFO

structlog level.

SETU_RATE_LIMIT_PER_MINUTE

60

Token-bucket refill rate per API key.

SETU_RATE_LIMIT_TIMEOUT_S

10

Max seconds to wait for a token before erroring.

SETU_MAX_RETRIES

3

Total attempts per Sarvam call (retry cap).

SETU_OTEL_EXPORTER

console

Span exporter: console (stderr) or none.

SETU_HTTP_HOST

127.0.0.1

Bind host for the streamable-HTTP transport.

SETU_HTTP_PORT

8000

Bind port for the streamable-HTTP transport.

SARVAM_API_KEY

Sarvam key; only needed in live mode. Get one at dashboard.sarvam.ai.

Connect to Claude Desktop

  1. Find the absolute path to the setu entrypoint inside your venv:

    source .venv/bin/activate
    which setu      # e.g. /Users/you/setu-mcp/.venv/bin/setu
  2. Open Claude Desktop → Settings → Developer → Edit Config. This opens claude_desktop_config.json. Add Setu under mcpServers:

    {
      "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.)

  3. Fully quit and reopen Claude Desktop. Click the tools/🔌 icon in the chat box — you should see setu listed with the setu_ping tool.

  4. 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, and mode: 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, spans

Releasing

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.0

One-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

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