# Xcstrings Mcp

> A Rust implementation of a Model Context Protocol (MCP) server designed for working with Xcode `Localizable.xcstrings` files.

- **Type:** MCP server
- **Install:** `agentstack add mcp-lexrus-xcstrings-mcp`
- **Verified:** Pending review
- **Seller:** [lexrus](https://agentstack.voostack.com/s/lexrus)
- **Installs:** 0
- **Category:** [Integrations](https://agentstack.voostack.com/c/integrations)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [lexrus](https://github.com/lexrus)
- **Source:** https://github.com/lexrus/xcstrings_mcp

## Install

```sh
agentstack add mcp-lexrus-xcstrings-mcp
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# xcstrings-mcp

[](https://www.rust-lang.org)
[](https://opensource.org/licenses/MIT)
[](https://github.com/lexrus/xcstrings_mcp/stargazers)

A Rust implementation of a **Model Context Protocol (MCP) server** designed for working with Xcode `.xcstrings` files. It exposes the translation catalog as MCP tools and optionally serves a **lightweight web editor** (when enabled via environment variables) so teams can browse, search, and edit strings from a browser.

> **Note**: This project was created with AI assistance using tools like Codex and Claude Code. While we strive for quality, there may be issues or areas for improvement. We welcome bug reports, feature requests, and contributions via [GitHub Issues](https://github.com/lexrus/xcstrings_mcp/issues).

## Functions

This MCP server provides the following functions for managing Xcode `Localizable.xcstrings` files:

### Core Translation Functions

- **`list_translations(path, query?, limit?)`** - List translation entries with optional filtering
  - `path`: Path to the `.xcstrings` file
  - `query`: Optional case-insensitive search query to filter results
  - `limit`: Maximum number of items to return (defaults to 100, set to 0 for no limit)
  - Returns: JSON array of translation summaries including key metadata

- **`list_keys(path, query?, limit?)`** - List translation keys without loading full records
  - `path`: Path to the `.xcstrings` file
  - `query`: Optional case-insensitive search query to filter results
  - `limit`: Maximum number of items to return (defaults to 100, set to 0 for no limit)
  - Returns: JSON payload containing `keys`, `total`, `returned`, and `truncated` flags

- **`get_translation(path, key, language)`** - Fetch a single translation by key and language
  - `path`: Path to the `.xcstrings` file
  - `key`: Translation key identifier
  - `language`: Language code (e.g., "en", "fr", "es")
  - Returns: Complete translation value with variations and substitutions

- **`upsert_translation(path, key, language, value?, state?, variations?, substitutions?)`** - Create or update a translation
  - `path`: Path to the `.xcstrings` file
  - `key`: Translation key identifier
  - `language`: Language code
  - `value`: Translation text (optional)
  - `state`: Translation state (optional)
  - `variations`: Map of variation selectors to their cases (e.g., plural forms)
  - `substitutions`: Map of substitution identifiers with metadata
  - Returns: Updated translation value

- **`delete_translation(path, key, language)`** - Delete a translation for a specific language
  - `path`: Path to the `.xcstrings` file
  - `key`: Translation key identifier
  - `language`: Language code to remove
  - Returns: Success confirmation

### Key Management Functions

- **`delete_key(path, key)`** - Delete an entire translation key across all languages
  - `path`: Path to the `.xcstrings` file
  - `key`: Translation key identifier to remove completely
  - Returns: Success confirmation

- **`set_comment(path, key, comment?)`** - Set or clear the developer comment for a translation key
  - `path`: Path to the `.xcstrings` file
  - `key`: Translation key identifier
  - `comment`: Developer comment text (optional, omit to clear)
  - Returns: Success confirmation

- **`set_translation_state(path, key, language, state?)`** - Set or clear the translation state for a specific language entry
  - `path`: Path to the `.xcstrings` file
  - `key`: Translation key identifier
  - `language`: Language code for the localization to update
  - `state`: Translation state value (optional, omit to clear; defaults back to `"translated"` when a non-empty value exists)
  - Returns: Updated translation value

- **`set_extraction_state(path, key, extractionState?)`** - Set or clear the extraction state for a string key
  - `path`: Path to the `.xcstrings` file
  - `key`: Translation key identifier
  - `extractionState`: Extraction state value (optional, omit to clear)
  - Returns: Success confirmation

### Language Management Functions

- **`list_languages(path)`** - List all languages present in the xcstrings file
  - `path`: Path to the `.xcstrings` file
  - Returns: JSON array of language codes found in the catalog

- **`add_language(path, language)`** - Add a new language to the xcstrings file
  - `path`: Path to the `.xcstrings` file
  - `language`: Language code to add (e.g., "fr", "es", "de")
  - Returns: Success confirmation
  - Note: Creates placeholder entries in existing keys with `needs-translation` state so the language is immediately discoverable

- **`remove_language(path, language)`** - Remove a language from the xcstrings file
  - `path`: Path to the `.xcstrings` file
  - `language`: Language code to remove
  - Returns: Success confirmation
  - Note: Cannot remove the source language (typically "en")

- **`update_language(path, oldLanguage, newLanguage)`** - Rename/update a language code in the xcstrings file
  - `path`: Path to the `.xcstrings` file
  - `oldLanguage`: Current language code to rename
  - `newLanguage`: New language code
  - Returns: Success confirmation
  - Note: Cannot rename the source language; preserves all existing translations

- **`list_untranslated(path)`** - List untranslated keys per language
  - `path`: Path to the `.xcstrings` file
  - Returns: JSON map of language codes to arrays of untranslated keys
  - Note: A translation is considered untranslated if the value is empty/None or no localization exists

### Additional Features

- **Async-safe store** that loads and persists `Localizable.xcstrings` JSON on every change
- **Optional embedded Axum web UI** (enabled via environment variables) for browsing translations, filtering by query, editing values, plural/device variations, and managing comments
- **Translation progress tracking** with percentage display in the language dropdown (excludes keys marked as should_translate=false)
- **Automatic discovery** of `.xcstrings` files when no default path is provided, with a selector in the web UI for runtime catalog switching (when web UI is enabled)
- **Device-specific variations** support (iPhone, iPad, Mac, Apple Watch, etc.) with mutual exclusivity logic between plural and device variations
- **Inline editing** for extraction state, translation state, and substitution placeholders (including `argNum`, `formatSpecifier`, and nested plural cases)
- **JSON-first responses** from all tools to make automation and debugging easier
- **Schema-backed validation** using the vendored [`xcstrings.schema.json`](schema/xcstrings.schema.json) to keep generated catalogs consistent with Apple's format

## Prerequisites

- **Rust 1.75 or newer**: Install using Homebrew (recommended on macOS):

  ```bash
  brew install rust
  ```

  Alternatively, install using the official installer:

  ```bash
  curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  ```

  After installation, restart your terminal or run `source ~/.cargo/env` to update your PATH.

## Running the server

```bash
cargo run -- [path-to/Localizable.xcstrings]
# This will build and run the MCP server
```

```bash
cargo install --path .
# This will install `xcstrings-mcp` into `~/.cargo/bin/`
```

- `path-to/Localizable.xcstrings`: Optional. When omitted, the server scans the workspace for `.xcstrings` files and MCP tool calls must supply a `path` argument.

You can also configure the server via environment variables:

| Variable       | Description                                           | Default                |
| -------------- | ----------------------------------------------------- | ---------------------- |
| `STRINGS_PATH` | Path to the `.xcstrings` file                         | _unset_ (dynamic mode) |
| `WEB_HOST`     | Host/interface for the web UI (enables web server)   | _unset_ (disabled)     |
| `WEB_PORT`     | Port for the web UI (enables web server)             | `8787`                 |

**Note**: The web server is **disabled by default**. To enable it, you must set either `WEB_HOST` or `WEB_PORT` environment variables. When enabled, the web interface becomes available at `http://:/` (defaults to `http://127.0.0.1:8787/`).

### MCP usage

Run the binary with stdio transport (default) and wire it into an MCP-enabled client. The following tools are exposed (each expects a `path` argument pointing to the target `.xcstrings` file):

- `list_translations(path, query?, limit?)`
- `list_keys(path, query?, limit?)`
- `get_translation(path, key, language)`
- `upsert_translation(path, key, language, value?, state?, variations?)`
- `delete_translation(path, key, language)`
- `delete_key(path, key)`
- `set_comment(path, key, comment?)`
- `set_extraction_state(path, key, extractionState?)`
- `list_languages(path)`
- `add_language(path, language)`
- `remove_language(path, language)`
- `update_language(path, oldLanguage, newLanguage)`
- `list_untranslated(path)`

Each tool returns JSON payloads encoded into text content for easier consumption.

`list_translations` now returns compact summaries (`key`, `comment`, `extractionState`, `languages`, and `hasVariations`) so responses stay lightweight even for large catalogs. Use `limit` (defaults to 100, set to `0` for no limit) to page through results and pair it with `get_translation` for per-language details without flooding the client context.

When calling `upsert_translation`, you can send:

- `variations` — map selectors (e.g. `"plural"`) to their cases; each case is another translation update.
- `substitutions` — map substitution identifiers (`"arg1"`, `"device"`, etc.) to updates containing `value`, `state`, `argNum`, `formatSpecifier`, and nested `variations`.
  Missing selectors or substitutions are left untouched so you can patch individual pieces without resending the entire localization payload.

If the server starts without a default path (no CLI argument and no `STRINGS_PATH`), it scans the working tree for `.xcstrings` files and surfaces them through the web UI selector. When none are found, the UI shows a placeholder until a file appears. MCP tools still require an explicit `path` in this mode. Providing a default path pins the selector to that file and lets tool calls omit `path`.

### Integrating with AI tools

Modern MCP-aware AI clients let you register external servers through a JSON manifest.

#### Basic MCP server (web UI disabled)

```json
{
  "mcpServers": {
    "xcstrings": {
      "command": "/Users/you/.cargo/bin/xcstrings-mcp",
      "transport": "stdio"
    }
  }
}
```

Restart the client after saving so it loads the new MCP server definition. The `path` parameter is required in all tool calls when running in dynamic-path mode.

#### With web UI enabled

To enable the web UI, set the `WEB_HOST` or `WEB_PORT` environment variables:

```json
{
  "mcpServers": {
    "xcstrings": {
      "command": "/Users/you/.cargo/bin/xcstrings-mcp",
      "transport": "stdio",
      "env": {
        "WEB_PORT": "8787"
      }
    }
  }
}
```

You can customize both host and port:

```json
{
  "mcpServers": {
    "xcstrings": {
      "command": "/Users/you/.cargo/bin/xcstrings-mcp",
      "transport": "stdio",
      "env": {
        "WEB_HOST": "127.0.0.1",
        "WEB_PORT": "8787"
      }
    }
  }
}
```

#### With default xcstrings path

To run with a default localization file (letting tools omit `path`), specify the path via arguments or environment variables:

```json
{
  "mcpServers": {
    "xcstrings": {
      "command": "/Users/you/.cargo/bin/xcstrings-mcp",
      "args": ["--", "/Users/you/Projects/Localizable.xcstrings"],
      "transport": "stdio",
      "env": {
        "WEB_PORT": "8787"
      }
    }
  }
}
```

You can supply the path via `STRINGS_PATH` instead of CLI arguments if you prefer. When a default path is configured, MCP tool calls may omit the `path` parameter.

#### Claude Code

Basic setup (web UI disabled):
```bash
claude mcp add-json xcstrings '{"command":"/Users/you/.cargo/bin/xcstrings-mcp","transport":"stdio"}'
```

With web UI enabled:
```bash
claude mcp add-json xcstrings '{"command":"/Users/you/.cargo/bin/xcstrings-mcp","transport":"stdio","env":{"WEB_PORT":"8787"}}'
```

You can also add the JSON manually to `~/.claude.json`.

#### Codex

Basic setup (web UI disabled):
```ini
[mcp_servers.xcstrings_mcp]
command = "/Users/you/.cargo/bin/xcstrings-mcp"
args = ["--"]
```

With web UI enabled:
```ini
[mcp_servers.xcstrings_mcp]
command = "/Users/you/.cargo/bin/xcstrings-mcp"
args = ["--"]
env = { WEB_PORT = "8787" }
```

## Development

Install dependencies and run the full test suite:

```bash
cargo test
```

`cargo fmt --all` is recommended before submitting changes.

The repository vendors the official schema as a git submodule under `schema/`. Use `git submodule update --init --remote` to pull the latest definition when updating validation logic.

## Project layout

- `src/store.rs` – async storage layer for `.xcstrings` files.
- `src/mcp_server.rs` – MCP tool definitions exposing translation functionality.
- `src/web/mod.rs` – Axum HTTP routes and HTML/JS single page view.
- `src/main.rs` – entrypoint that launches both web and MCP services.

## Support

If you find this project useful, consider supporting me by buying me a coffee.

## License

Distributed under the [MIT License](LICENSE).

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [lexrus](https://github.com/lexrus)
- **Source:** [lexrus/xcstrings_mcp](https://github.com/lexrus/xcstrings_mcp)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: flagged — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-lexrus-xcstrings-mcp
- Seller: https://agentstack.voostack.com/s/lexrus
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
