lexrus/xcstrings_mcp
If you are the rightful owner of xcstrings_mcp and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.
A Rust-based MCP server for managing Xcode Localizable.xcstrings files, offering both a toolset and a web UI for translation management.
xcstrings-mcp
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.

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 filteringpath: Path to the.xcstringsfilequery: Optional case-insensitive search query to filter resultslimit: 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 recordspath: Path to the.xcstringsfilequery: Optional case-insensitive search query to filter resultslimit: Maximum number of items to return (defaults to 100, set to 0 for no limit)- Returns: JSON payload containing
keys,total,returned, andtruncatedflags
-
get_translation(path, key, language)- Fetch a single translation by key and languagepath: Path to the.xcstringsfilekey: Translation key identifierlanguage: 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 translationpath: Path to the.xcstringsfilekey: Translation key identifierlanguage: Language codevalue: 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 languagepath: Path to the.xcstringsfilekey: Translation key identifierlanguage: Language code to remove- Returns: Success confirmation
Key Management Functions
-
delete_key(path, key)- Delete an entire translation key across all languagespath: Path to the.xcstringsfilekey: Translation key identifier to remove completely- Returns: Success confirmation
-
set_comment(path, key, comment?)- Set or clear the developer comment for a translation keypath: Path to the.xcstringsfilekey: Translation key identifiercomment: 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 entrypath: Path to the.xcstringsfilekey: Translation key identifierlanguage: Language code for the localization to updatestate: 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 keypath: Path to the.xcstringsfilekey: Translation key identifierextractionState: Extraction state value (optional, omit to clear)- Returns: Success confirmation
Language Management Functions
-
list_languages(path)- List all languages present in the xcstrings filepath: Path to the.xcstringsfile- Returns: JSON array of language codes found in the catalog
-
add_language(path, language)- Add a new language to the xcstrings filepath: Path to the.xcstringsfilelanguage: Language code to add (e.g., "fr", "es", "de")- Returns: Success confirmation
- Note: Creates placeholder entries in existing keys with
needs-translationstate so the language is immediately discoverable
-
remove_language(path, language)- Remove a language from the xcstrings filepath: Path to the.xcstringsfilelanguage: 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 filepath: Path to the.xcstringsfileoldLanguage: Current language code to renamenewLanguage: New language code- Returns: Success confirmation
- Note: Cannot rename the source language; preserves all existing translations
-
list_untranslated(path)- List untranslated keys per languagepath: Path to the.xcstringsfile- 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.xcstringsJSON 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
.xcstringsfiles 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 to keep generated catalogs consistent with Apple's format
Prerequisites
-
Rust 1.75 or newer: Install using Homebrew (recommended on macOS):
brew install rustAlternatively, install using the official installer:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shAfter installation, restart your terminal or run
source ~/.cargo/envto update your PATH.
Running the server
cargo run -- [path-to/Localizable.xcstrings]
# This will build and run the MCP server
cargo install --path .
# This will install `xcstrings-mcp` into `~/.cargo/bin/`
path-to/Localizable.xcstrings: Optional. When omitted, the server scans the workspace for.xcstringsfiles and MCP tool calls must supply apathargument.
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://<host>:<port>/ (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 containingvalue,state,argNum,formatSpecifier, and nestedvariations. 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)
{
"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:
{
"mcpServers": {
"xcstrings": {
"command": "/Users/you/.cargo/bin/xcstrings-mcp",
"transport": "stdio",
"env": {
"WEB_PORT": "8787"
}
}
}
}
You can customize both host and port:
{
"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:
{
"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):
claude mcp add-json xcstrings '{"command":"/Users/you/.cargo/bin/xcstrings-mcp","transport":"stdio"}'
With web UI enabled:
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):
[mcp_servers.xcstrings_mcp]
command = "/Users/you/.cargo/bin/xcstrings-mcp"
args = ["--"]
With web UI enabled:
[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:
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.xcstringsfiles.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 .