# MCP server

@paxalabs/mcp gives any MCP client the Paxa API as tools: speech through the machine's speakers, speech to file, translation into Thai, and document OCR. Install it for Claude Code, Claude Desktop, Cursor, or VS Code, and turn Claude Code into a voice mode with the paxa say hook.

`@paxalabs/mcp` is the official Model Context Protocol server for the Paxa API. An agent connected to it can speak through the machine's speakers, read long content aloud as a managed playback queue, and save speech to audio files. It can also translate any language into Thai and read PDFs and images with OCR. Each tool bills the same credits as the API endpoint behind it. The source is on [GitHub](https://github.com/paxalabs/mcp).

> The server is in beta. The tool set is complete and tested end to end, and tool names may still change before 1.0. Report problems at [https://github.com/paxalabs/mcp/issues](https://github.com/paxalabs/mcp/issues).

## Install

You need an [API key](https://paxalabs.com/app/keys). New accounts include 100 free credits.

Claude Code:

```bash
claude mcp add paxa -e PAXA_API_KEY=pxa_your_key_here -- npx -y @paxalabs/mcp
```

Claude Desktop, Cursor, VS Code:

```json
{
  "mcpServers": {
    "paxa": {
      "command": "npx",
      "args": [
        "-y",
        "@paxalabs/mcp"
      ],
      "env": {
        "PAXA_API_KEY": "pxa_your_key_here"
      }
    }
  }
}
```

The JSON entry works for Claude Desktop (`claude_desktop_config.json`), Cursor (`~/.cursor/mcp.json`), VS Code, and any other client that launches stdio servers. Replace the placeholder with your key.

Claude Desktop can also install the extension bundle from the [releases page](https://github.com/paxalabs/mcp/releases). Download the `.mcpb` file, open it with Claude Desktop, and enter your key in the extension settings. The bundle carries the server and its dependencies and needs no Node.js on the machine.

## Tools

| Tool | What it does | Credits |
| --- | --- | --- |
| speak | Synthesize a short line and play it through the speakers, blocking until playback ends | 15 per 1,000 characters |
| queue_speech | Read long content aloud: splits the text, synthesizes ahead while playing, and returns at once | 15 per 1,000 characters |
| control_playback | Control the shared audio queue: `status`, `pause`, `resume`, `skip`, `clear` | Free |
| play_audio | Play a local audio file through the speakers | Free |
| text_to_speech | Synthesize speech to an audio file (mp3, opus, wav) without playing it | 15 per 1,000 characters |
| translate_to_thai | Translate any language into Thai, with formality, glossary, and context controls | 25 per 1,000 characters |
| ocr_document | OCR a local PDF, PNG, JPEG, or WebP into Markdown or structured blocks | 6.5 per page |
| list_voices | The voice roster with character notes | Free |
| list_models | Available models, limits, and pricing | Free |
| get_account | Credit balance, plan, and rate limits | Free |

All audio flows through one ordered queue and sounds never overlap. A `speak` line plays ahead of queued long-form segments, and `queue_speech` keeps a book or an article flowing by synthesizing the next segment while the current one plays. With a streaming-capable player installed, speech starts on the first bytes from the API; without one, it plays after the download completes.

## Voice mode for Claude Code

Three pieces let you walk away from Claude Code. It talks when it has news and calls you when it needs you. Install the server as above, tell Claude when to talk, and wire a hook for the moments the model is idle.

### Tell Claude when to talk

Add this to `~/.claude/CLAUDE.md`, or to one project's CLAUDE.md:

CLAUDE.md:

```md
## Voice

I have the Paxa MCP server (tools: speak, queue_speech, control_playback).
I am often away from the screen, so use voice like this:

- At the end of a turn where you did real work, call speak with a one or
  two sentence summary before writing the final message: what you did,
  what is next, and anything you need from me.
- When you need a decision from me, speak the question too.
- Keep it short and conversational. Never read code, file paths, logs, or
  long lists aloud. Those stay in text.
- Do not speak for quick back-and-forth or trivial answers.
- If I ask to hear something long, use queue_speech.
- Speak in the language I write in.
```

### Get told when Claude needs you

When Claude Code waits for a permission or an answer, the model is idle and cannot call `speak`. Claude Code fires a hook at those moments, and `paxa say` turns the hook into a spoken phrase such as "Permission needed." Put the command on your PATH with `npm install -g @paxalabs/mcp`. Then add the hook to `~/.claude/settings.json`:

settings.json:

```json
{
  "hooks": {
    "Notification": [
      {
        "matcher": "permission_prompt|idle_prompt|agent_needs_input",
        "hooks": [
          {
            "type": "command",
            "command": "paxa say"
          }
        ]
      }
    ]
  }
}
```

`paxa say` reads the key from `PAXA_API_KEY`, or from the paxa entry in `~/.claude.json` when that is unset. The built-in phrases are synthesized once per voice and kept in the user cache directory. Every later notification plays from disk with no network round trip and no credits. A `Stop` hook configured the same way speaks "Done." at the end of every turn.

`paxa say "Build finished"` also works on its own, and `--voice` picks the voice. The [README](https://github.com/paxalabs/mcp#voice-mode-for-claude-code) covers custom phrases, the `--cache` flag, and faster players on macOS.

## Environment variables

| Variable | Required | Default | Purpose |
| --- | --- | --- | --- |
| PAXA_API_KEY | Yes |  | Your API key. The server starts without it, and every tool that calls the API then fails with setup instructions the agent can relay. |
| PAXA_OUTPUT_DIR | No | Working directory | Where `text_to_speech` saves files. |
| PAXA_DEFAULT_VOICE | No | `nomyen` | Voice used when a tool call does not pick one. English text usually sounds best with an English voice (`donut`, `cookie`, `toast`, `latte`). |
| PAXA_BASE_URL | No | `https://api.paxalabs.com` | API origin override. |

## Playback support

| Platform | File player | Streaming player | Pause and resume |
| --- | --- | --- | --- |
| macOS | `afplay` (built in) | `ffplay`, `mpv`, or `mpg123` if installed | Yes |
| Linux | `ffplay`, `mpv`, `mpg123`, `paplay`, or `aplay` | `ffplay`, `mpv`, or `mpg123` | Yes |
| Windows | `ffplay` if installed, else PowerShell (wav) | `ffplay` if installed | No |

Streaming needs a player that reads from stdin. On macOS, `brew install ffmpeg` (or `mpv`) enables it. If no player is found at all, the speech tools report it and `text_to_speech` still works.

- [Load docs into a coding agent](https://paxalabs.com/docs/agents): Load the docs into a coding agent that writes against the API directly.
- [Credits, charges, and refunds](https://paxalabs.com/docs/credits): How credits are charged, refunded, and topped up.
- [List voices](https://paxalabs.com/docs/api/voices): The full voice roster the `speak` tools draw from.
