Skip to content

Commit ea353c6

Browse files
committed
docs(mcp-tools): document error envelope + idempotency
- Replace ad-hoc string error-codes table with structured envelope shape + full code enumeration with retryable column. - Add migration note for v1 -> v2 consumers. - Add Idempotency section (all current tools idempotent; future write tools must document). - Update repo_status, ecosystem_activity, and pin_drift JSON examples to show envelope in per-item errors.
1 parent 6661467 commit ea353c6

File tree

1 file changed

+52
-16
lines changed

1 file changed

+52
-16
lines changed

docs/mcp-tools.md

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,50 @@ All tools are **read-only** (`readOnlyHint: true`). Pass **`format: "json"`** fo
2525

2626
## JSON responses
2727

28-
Payloads are minified (`JSON.stringify`, no pretty-print). `MCP_JSON_FORMAT_VERSION` is **`"1"`**. Optional fields are omitted when empty/null.
28+
Payloads are minified (`JSON.stringify`, no pretty-print). `MCP_JSON_FORMAT_VERSION` is **`"2"`**. Optional fields are omitted when empty/null.
29+
30+
### Error envelope
31+
32+
Tool-level failures return a top-level `error` object:
33+
34+
```jsonc
35+
{
36+
"error": {
37+
"code": "NOT_FOUND",
38+
"message": "PR Rethunk-AI/github-mcp#42 not found.",
39+
"retryable": false,
40+
"suggestedFix": "Verify the PR number." // optional
41+
}
42+
}
43+
```
44+
45+
Per-item failures (inside arrays like `repos[]` or `pins[]`) follow the same envelope shape in the item's `error` field. The batch does not fail as a whole when a per-item failure occurs.
46+
47+
Agents can decide programmatically whether to retry (e.g. exponential backoff on `retryable: true`) vs. surface the problem to the user (`retryable: false`).
2948

3049
### Error codes
3150

32-
| Code | Meaning |
33-
|------|---------|
34-
| `github_auth_missing` | No `GITHUB_TOKEN`/`GH_TOKEN` and `gh auth token` failed. |
35-
| `not_found` | Repository, PR, or workflow run does not exist. |
36-
| `no_ci_runs` | No workflow runs found for the given ref/PR. |
37-
| `org_not_found` | GitHub organization does not exist or is inaccessible. |
38-
| `local_repo_no_remote` | Local path has no resolvable GitHub `origin` remote. |
39-
| `graphql_error` | Upstream GitHub GraphQL error (message included). |
40-
| `compare_failed` | `base...head` comparison failed (bad ref, etc.). |
41-
| `query_failed` | General API failure (message included). |
42-
| `ci_diagnosis_failed` | Failed to resolve or diagnose the CI run. |
43-
| `unsupported_language` | `module_pin_hint` was called with a `language` other than `"go"`. |
51+
| Code | Meaning | Retryable |
52+
|------|---------|-----------|
53+
| `AUTH_MISSING` | No `GITHUB_TOKEN`/`GH_TOKEN` and `gh auth token` failed. | no |
54+
| `AUTH_FAILED` | GitHub rejected the token (HTTP 401). | no |
55+
| `NOT_FOUND` | Repository, PR, org, ref, or workflow run does not exist (HTTP 404). | no |
56+
| `PERMISSION_DENIED` | Token lacks scopes or repo access (HTTP 403, not rate limit). | no |
57+
| `RATE_LIMITED` | GitHub rate limit exhausted (HTTP 403 + `x-ratelimit-remaining: 0`). `suggestedFix` includes the reset time. | **yes** |
58+
| `VALIDATION` | Request failed GitHub's input validation (HTTP 422). | no |
59+
| `UPSTREAM_FAILURE` | GitHub 5xx or GraphQL-level error. | **yes** |
60+
| `NO_CI_RUNS` | No workflow runs found for the given ref/PR (`ci_diagnosis`). | no |
61+
| `COMPARE_FAILED` | Reserved: `base...head` comparison failure distinct from a 404. | no |
62+
| `LOCAL_REPO_NO_REMOTE` | Local path has no resolvable GitHub `origin` remote. | no |
63+
| `UNSUPPORTED_LANGUAGE` | `module_pin_hint` was called with a `language` other than `"go"`. | no |
64+
| `AMBIGUOUS_REPO` | Reserved: pin source does not encode which GitHub repo a value belongs to. | no |
65+
| `INTERNAL` | Unrecognized/unexpected failure. | no |
66+
67+
### Idempotency
68+
69+
All current tools are **read-only** (`readOnlyHint: true`) and therefore idempotent: calling any tool twice with the same arguments is equivalent to calling it once. There is no server-side state mutation. Safe to retry transparently on `RATE_LIMITED` or `UPSTREAM_FAILURE`.
70+
71+
Future write-capable tools (e.g. a proposed `release_create`) will document their idempotency semantics explicitly in this section.
4472

4573
---
4674

@@ -69,7 +97,8 @@ Payloads are minified (`JSON.stringify`, no pretty-print). `MCP_JSON_FORMAT_VERS
6997
"draftPRs": 1,
7098
"openIssues": 12,
7199
"local": { "branch": "feature", "dirty": 2, "ahead": 1, "behind": 0 }
72-
// "error": "not_found" — on per-repo failure (does not fail the batch)
100+
// "error": { "code": "NOT_FOUND", "message": "...", "retryable": false }
101+
// — on per-repo failure (does not fail the batch)
73102
}]
74103
}
75104
```
@@ -302,7 +331,9 @@ The `attention` array is sorted by urgency: failing CI repos first, then by stal
302331
- `scripts/versions.env`: shell `KEY=VALUE` lines whose key ends in `_REF`, `_SHA`, or `_VERSION` and whose value is a 40-char hex SHA. These are always reported under `skipped` with `reason: "ambiguous_repo"` because the file does not encode which GitHub repo each key belongs to.
303332
- `package.json`: `dependencies`/`devDependencies` whose version is a GitHub shorthand (`owner/repo#ref`) or HTTPS GitHub URL.
304333

305-
`behindBy: -1` signals an error resolving that pin (e.g. upstream repo not found).
334+
`behindBy: -1` signals an error resolving that pin. When this happens the pin entry also carries an `error: { code, message, retryable, suggestedFix? }` field explaining why (e.g. `NOT_FOUND`, `RATE_LIMITED`, `UPSTREAM_FAILURE`).
335+
336+
`skipped[].reason` remains a free-text parser-level code (`ambiguous_repo`, `ambiguous_ref`, `not_github`, `ls_tree_no_sha`, `ls_tree_failed`) — it describes a pin that couldn't be parsed at all, not a GitHub-side error.
306337

307338
---
308339

@@ -326,7 +357,12 @@ The `attention` array is sorted by urgency: failing CI repos first, then by stal
326357
"since": "2026-04-10T17:19:40Z",
327358
"repos": [
328359
{ "owner": "Rethunk-Tech", "repo": "bastion-satcom", "commitCount": 12 },
329-
{ "owner": "Rethunk-AI", "repo": "some-lib", "commitCount": 0, "error": "not_found" }
360+
{
361+
"owner": "Rethunk-AI",
362+
"repo": "some-lib",
363+
"commitCount": 0,
364+
"error": { "code": "NOT_FOUND", "message": "...", "retryable": false }
365+
}
330366
],
331367
"commits": [{
332368
"owner": "Rethunk-Tech",

0 commit comments

Comments
 (0)