121 lines
3.5 KiB
Markdown
121 lines
3.5 KiB
Markdown
# 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.
|