# metin-release-mcp Thin [Model Context Protocol](https://modelcontextprotocol.io/) server that wraps the Phase 1 `metin-release` CLI. Each `release …` subcommand is exposed as an MCP tool. The server contains no release business logic: it shells out to the real CLI with `--json` and returns the parsed envelope verbatim. ## Install The server ships as an optional extra alongside the main CLI: ``` pip install -e '.[mcp]' ``` This installs the `mcp` Python SDK and adds a `metin-release-mcp` console script plus a `python -m metin_release_mcp` module entry. ## Running The server speaks MCP over stdio, so you wire it into an MCP-capable client (Claude Desktop, Claude Code, etc.) as a stdio command. Example client entry: ```json { "mcpServers": { "metin-release": { "command": "metin-release-mcp", "env": { "METIN_RELEASE_BINARY": "/usr/local/bin/metin-release" } } } } ``` You can also poke at it directly: - `metin-release-mcp --help` — list registered tools - `metin-release-mcp --list-tools` — dump the full tool JSON schemas - `metin-release-mcp --version` ## Tools | Tool | CLI subcommand | |---|---| | `release_inspect` | `metin-release release inspect` | | `release_build_manifest` | `metin-release release build-manifest` | | `release_sign` | `metin-release release sign` | | `release_diff_remote` | `metin-release release diff-remote` | | `release_upload_blobs` | `metin-release release upload-blobs` | | `release_promote` | `metin-release release promote` | | `release_verify_public` | `metin-release release verify-public` | | `release_publish` | `metin-release release publish` | Tool input keys match CLI flag names with `_` instead of `-` (`--base-url` → `base_url`, `--dry-run` → `dry_run`). Boolean fields correspond to argparse `store_true` flags: pass `true` to set the flag, omit or pass `false` to leave it off. ### Example invocation ```json { "name": "release_inspect", "arguments": { "source": "/srv/metin/client" } } ``` The server runs `metin-release release inspect --json --source /srv/metin/client` and returns the full JSON envelope: ```json { "ok": true, "command": "release inspect", "status": "inspected", "stats": { "source_path": "/srv/metin/client", "file_count": 9166, "total_bytes": 3523473920, "launcher_present": true, "main_exe_present": true } } ``` ## CLI resolution On every tool call the server resolves the `metin-release` binary in this order: 1. `METIN_RELEASE_BINARY` environment variable, if set and non-empty 2. `shutil.which("metin-release")` against `PATH` If neither resolves, the tool call returns a wrapper-level error envelope (`error.code = "cli_not_found"`). ## Error handling The server never invents its own release-level errors. There are three paths: - **Success** — CLI exits 0 with a valid JSON envelope → envelope returned as-is - **CLI-level failure** — CLI exits non-zero with an `{"ok": false, "error": …}` envelope → that envelope is returned as-is, plus the CLI's stderr is attached as a diagnostic text block - **Wrapper failure** — binary missing, unparseable stdout, unknown tool, invalid input → synthetic envelope with one of `cli_not_found`, `cli_unparseable_output`, `unknown_tool`, `invalid_tool_input` ## Environment variables | Variable | Purpose | |---|---| | `METIN_RELEASE_BINARY` | Override the `metin-release` binary path. | Any env vars the wrapped CLI honours (`METIN_RELEASE_MAKE_MANIFEST`, `METIN_RELEASE_SIGN_MANIFEST`) are inherited by the subprocess unchanged.