Skip to main content
Glama
README.md
# levelang.app MCP Server

[![CI](https://github.com/beverage/levelang-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/beverage/levelang-mcp/actions/workflows/ci.yml)
[![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
[![MCP SDK](https://img.shields.io/badge/MCP%20SDK-1.26-purple.svg)](https://modelcontextprotocol.io/)
[![uv](https://img.shields.io/badge/uv-package%20manager-blueviolet.svg)](https://docs.astral.sh/uv/)

An [MCP](https://modelcontextprotocol.io/) server that exposes the [levelang.app](https://levelang.app) translation API to AI assistants. Unlike standard translators that always produce native-speaker complexity, levelang.app constrains translations to the learner's proficiency level.

---

## Features

- **Level-Aware Translation** — Translate text at beginner, intermediate, advanced, or fluent proficiency with grammar constraints enforced per level
- **Multi-Language Support** — French, German, Italian, Mandarin Chinese, Cantonese, with transliteration where applicable
- **Mood Control** — Casual, polite, and formal translation styles
- **Language Discovery** — Query available languages, levels, and moods dynamically from the backend
- **MCP Resources** — `levelang://languages` and `levelang://languages/{code}` for pulling language configs into context
- **Stateless Wrapper** — No database, no shared state; translates MCP tool calls into backend HTTP requests

## Quick Start

You need two things: the **MCP server URL** and an **API key**. No local setup required.

### Cursor

Add to `.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "levelang": {
      "url": "https://your-mcp-server-url/mcp",
      "headers": {
        "Authorization": "Bearer your-api-key"
      }
    }
  }
}
```

### Claude Desktop

Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):

```json
{
  "mcpServers": {
    "levelang": {
      "url": "https://your-mcp-server-url/mcp",
      "headers": {
        "Authorization": "Bearer your-api-key"
      }
    }
  }
}
```

Restart Claude Desktop. A hammer icon in the chat input indicates MCP tools are available.

### Example Usage

Once connected, ask your AI assistant things like:

> Translate "I would like to order a coffee, please" into French at the beginner level.

> What languages does Levelang support?

> Compare how "I'm worried the new rules might prevent us from finishing on time" translates into German at beginner vs advanced level.

## Server Configuration

The settings below are for **running the MCP server** (local development or self-hosting). End users connecting via URL do not need these.

All configuration is through environment variables. When running locally via stdio, these go in the `env` block of your MCP client config.

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `LEVELANG_API_BASE_URL` | No | `http://localhost:8000/api/v1` | Levelang backend URL |
| `LEVELANG_API_KEY` | Depends | — | Service key (`sk_xxx`) for backend auth |
| `MCP_TRANSPORT` | No | `stdio` | Transport: `stdio` or `streamable-http` |
| `MCP_PORT` | No | `8463` | Port when using HTTP transport |
| `MCP_API_KEYS` | No | — | Comma-separated valid API keys for HTTP auth |

`LEVELANG_API_KEY` is required when connecting to a remote backend (staging/production). It may be omitted for local development if the backend has auth disabled.

`MCP_API_KEYS` controls client authentication for the HTTP transport. When set, clients must send `Authorization: Bearer <key>` with a key from this list. When empty or unset, auth is disabled (open access). This has no effect on stdio transport.

### Local stdio Connection

For local development you can run the MCP server as a subprocess instead of connecting via URL. This requires Python 3.12+, [uv](https://docs.astral.sh/uv/), and a running [Levelang backend](https://github.com/beverage/levelang-backend).

```bash
git clone https://github.com/beverage/levelang-mcp.git
cd levelang-mcp
uv sync
```

```json
{
  "mcpServers": {
    "levelang": {
      "command": "uv",
      "args": [
        "run",
        "--directory", "/absolute/path/to/levelang-mcp",
        "python", "-m", "levelang_mcp"
      ],
      "env": {
        "LEVELANG_API_BASE_URL": "http://localhost:8000/api/v1"
      }
    }
  }
}
```

## Development

### Setup

```bash
uv sync
git config core.hooksPath .githooks
```

This enables pre-commit (auto-fix lint + format) and pre-push (lint + format check + tests) hooks.

### Running Tests

```bash
uv run pytest tests/ -v
```

### MCP Inspector

The [MCP Inspector](https://github.com/modelcontextprotocol/inspector) provides a web UI for browsing and invoking tools and resources:

```bash
npx @modelcontextprotocol/inspector uv run --directory /path/to/levelang-mcp python -m levelang_mcp
```

## Project Structure

```
src/levelang_mcp/
├── __main__.py       # Entrypoint (python -m levelang_mcp)
├── server.py         # MCP tools and resources
├── auth.py           # API-key auth middleware for HTTP transport
├── client.py         # Async HTTP client for the Levelang API
├── config.py         # Environment variable loading
└── formatting.py     # API response → human-readable text

tests/
├── test_auth.py      # Auth middleware and config tests
├── test_client.py    # HTTP client tests (mocked)
├── test_formatting.py
└── test_tools.py     # Tool integration tests (mocked)
```

## Architecture

```
MCP Client              levelang-mcp              Levelang Backend
(Claude, Cursor,   ◄── MCP/stdio ──►   (this)   ─── HTTP ──►   (FastAPI)
 Claude Code)                                     POST /translate
                                                  GET /languages/details
                                                  GET /languages/{code}
```

The MCP server is a stateless wrapper. It translates MCP tool calls into HTTP requests to the Levelang backend and formats responses as human-readable text for the LLM. It does not share code, database connections, or deployment with the backend.

## License

MIT

TDQS

A4.4/5.0

Scored across 3 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: list_languages provides configuration discovery, translate performs single-level translation, and translate_compare enables multi-level comparison. There is no overlap in functionality, and an agent can easily distinguish between them based on their specific objectives.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern: list_languages, translate, and translate_compare. The naming is predictable and readable, with no deviations or mixed conventions, making it easy for agents to understand the tool set's structure.

Tool Count4/5

With 3 tools, the count is reasonable for a language translation server, covering core operations like discovery, translation, and comparison. However, it feels slightly thin, as additional tools for managing translations or handling errors could enhance completeness, but the current set is well-scoped for basic functionality.

Completeness4/5

The tool set covers essential workflows: discovering supported languages, translating text at specific levels, and comparing translations across levels. Minor gaps exist, such as no tools for updating or deleting translations, but these are not critical for the server's purpose, and agents can work effectively with the provided operations.

Maintenance

ActivityInactive
ResponsivenessNo issues