Add missing MCP tools: label create/edit/delete + richer label_list, issue dependencies, typed not-found for file_read #56

Open
opened 2026-07-12 19:50:17 +00:00 by mathias · 0 comments
Owner

Problem

The gitea MCP surface has gaps that force manual UI steps and block automation in the middle of real work. This session hit three concretely; this issue enumerates the full set of tools needed so intended work-actions are doable end-to-end through the MCP, not half-through-then-stuck.

The triggering case: swedsl was created from template with zero labels defined. issue_label silently can't apply status:dispatch because the label doesn't exist, and there is no create-label tool — so a repo cannot be made dispatch-eligible through the MCP at all. That's a hard stop mid-workflow.

Gaps confirmed this session

  1. Label create / managelabel_list exists (names only) but there is no create, edit, or delete. Cannot bootstrap a repo's labels, cannot fix a color/description. This is the blocker.
  2. label_list is thin — returns names only; no id/color/description, so callers can't tell whether a label needs creating vs. recoloring, and can't resolve the label id that some Gitea label-apply paths need.
  3. file_read on a missing path returns opaque Error occurred during tool execution — indistinguishable from a transient failure. A clean typed not-found is needed so callers can branch on "absent" vs "error" (I had to do a control read of a known-present file to disambiguate).
  4. No issue-dependency tooling — Gitea has a native issue-dependency API; none of it is surfaced. Forces dependencies to live in issue prose (see dispatch#24). Blocks structured dependency enforcement.

Tools to add

Labels (priority — unblocks dispatch)

  • label_create(owner, repo, name, color, description?, exclusive?) — idempotent-friendly: if a label of that name exists, return it rather than erroring (or a create_if_missing flag). Support scoped/exclusive labels (scope/name, the dual-color exclusive kind).
  • label_edit(owner, repo, id|name, {color?, description?, name?}) — for fixing an existing label.
  • label_delete(owner, repo, id|name).
  • Extend label_list to return {id, name, color, description, exclusive} per label, not bare names. (Several apply-paths key on id.)
  • Consider label_ensure(owner, repo, labels[]) — batch create-if-missing, since the common need is "make sure these N labels exist." This is exactly what dispatch#25 (watcher self-ensures its labels) will call.

Issue dependencies (unblocks dispatch#24)

  • issue_dependency_add(owner, repo, number, depends_on_number) — set "this issue is blocked by that one" via Gitea's native dependency API.
  • issue_dependency_remove(owner, repo, number, depends_on_number).
  • issue_dependency_list(owner, repo, number) — returns blocked-by and blocking sets, with state, so a caller (the watcher) can gate on them structurally instead of parsing prose.

Error semantics (cross-cutting)

  • file_read (and siblings) should return a typed not-found distinct from execution errors — a structured {not_found: true} or a stable error code — so callers branch cleanly instead of string-matching an opaque message.

Nice-to-have (lower priority, name them so they're not forgotten)

  • milestone_list / milestone_create — if we start grouping the #10–#18 chains under milestones.
  • label_list filtering by scope prefix (status:) — cheap once list returns structured data.

Acceptance criteria

  1. label_create creates a label and is idempotent (second call with same name → returns existing, no error). Covered by a test against a scratch repo.
  2. label_list returns id + color + description + exclusive for every label.
  3. label_ensure([...]) on a repo missing some/all → creates only the missing, returns the full resolved set.
  4. issue_dependency_add + issue_dependency_list round-trip: add #A as a blocker of #B, list on #B shows #A as blocked-by with its state.
  5. file_read on an absent path returns a typed not-found, not a generic execution error; on a present path unchanged.
  6. Follow the mcp-builder skill conventions (intent-named verbs per ADR-0012 in swedsl/mcp-interface-design territory — align tool naming with the existing issue_*, repo_*, label_* families).
  7. Existing tools unchanged; additions only.

Why now / impact

  • Immediate: unblocks labelling swedsl #10/#15 for dispatch without a UI detour.
  • dispatch#25 (watcher self-ensures labels) needs label_ensure/label_create to exist on the client — this is its upstream dependency.
  • dispatch#24 (watcher honors dependencies) is far cleaner against a native issue_dependency_* API than against prose-parsing.

So this issue is the tooling substrate for both open dispatch issues. Recommend doing the Labels section first (small, unblocks the live workflow), then dependencies, then error semantics.

Interim workaround

Create status:dispatch manually in the Gitea UI (Settings → Labels) per repo until label_create lands.

## Problem The gitea MCP surface has gaps that force manual UI steps and block automation in the middle of real work. This session hit three concretely; this issue enumerates the full set of tools needed so intended work-actions are doable end-to-end through the MCP, not half-through-then-stuck. The triggering case: swedsl was created from template with **zero labels defined**. `issue_label` silently can't apply `status:dispatch` because the label doesn't exist, and there is **no create-label tool** — so a repo cannot be made dispatch-eligible through the MCP at all. That's a hard stop mid-workflow. ## Gaps confirmed this session 1. **Label create / manage** — `label_list` exists (names only) but there is no create, edit, or delete. Cannot bootstrap a repo's labels, cannot fix a color/description. **This is the blocker.** 2. **`label_list` is thin** — returns names only; no id/color/description, so callers can't tell whether a label needs creating vs. recoloring, and can't resolve the label *id* that some Gitea label-apply paths need. 3. **`file_read` on a missing path** returns opaque `Error occurred during tool execution` — indistinguishable from a transient failure. A clean typed not-found is needed so callers can branch on "absent" vs "error" (I had to do a control read of a known-present file to disambiguate). 4. **No issue-dependency tooling** — Gitea has a native issue-dependency API; none of it is surfaced. Forces dependencies to live in issue prose (see dispatch#24). Blocks structured dependency enforcement. ## Tools to add ### Labels (priority — unblocks dispatch) - `label_create(owner, repo, name, color, description?, exclusive?)` — idempotent-friendly: if a label of that name exists, return it rather than erroring (or a `create_if_missing` flag). Support scoped/exclusive labels (`scope/name`, the dual-color exclusive kind). - `label_edit(owner, repo, id|name, {color?, description?, name?})` — for fixing an existing label. - `label_delete(owner, repo, id|name)`. - **Extend `label_list`** to return `{id, name, color, description, exclusive}` per label, not bare names. (Several apply-paths key on id.) - Consider `label_ensure(owner, repo, labels[])` — batch create-if-missing, since the common need is "make sure these N labels exist." This is exactly what dispatch#25 (watcher self-ensures its labels) will call. ### Issue dependencies (unblocks dispatch#24) - `issue_dependency_add(owner, repo, number, depends_on_number)` — set "this issue is blocked by that one" via Gitea's native dependency API. - `issue_dependency_remove(owner, repo, number, depends_on_number)`. - `issue_dependency_list(owner, repo, number)` — returns blocked-by and blocking sets, with state, so a caller (the watcher) can gate on them structurally instead of parsing prose. ### Error semantics (cross-cutting) - `file_read` (and siblings) should return a **typed not-found** distinct from execution errors — a structured `{not_found: true}` or a stable error code — so callers branch cleanly instead of string-matching an opaque message. ## Nice-to-have (lower priority, name them so they're not forgotten) - `milestone_list` / `milestone_create` — if we start grouping the #10–#18 chains under milestones. - `label_list` filtering by scope prefix (`status:`) — cheap once list returns structured data. ## Acceptance criteria 1. `label_create` creates a label and is idempotent (second call with same name → returns existing, no error). Covered by a test against a scratch repo. 2. `label_list` returns id + color + description + exclusive for every label. 3. `label_ensure([...])` on a repo missing some/all → creates only the missing, returns the full resolved set. 4. `issue_dependency_add` + `issue_dependency_list` round-trip: add #A as a blocker of #B, list on #B shows #A as blocked-by with its state. 5. `file_read` on an absent path returns a typed not-found, not a generic execution error; on a present path unchanged. 6. Follow the mcp-builder skill conventions (intent-named verbs per ADR-0012 in swedsl/mcp-interface-design territory — align tool naming with the existing `issue_*`, `repo_*`, `label_*` families). 7. Existing tools unchanged; additions only. ## Why now / impact - **Immediate:** unblocks labelling swedsl #10/#15 for dispatch without a UI detour. - **dispatch#25** (watcher self-ensures labels) needs `label_ensure`/`label_create` to exist on the client — this is its upstream dependency. - **dispatch#24** (watcher honors dependencies) is far cleaner against a native `issue_dependency_*` API than against prose-parsing. So this issue is the tooling substrate for both open dispatch issues. Recommend doing the **Labels** section first (small, unblocks the live workflow), then dependencies, then error semantics. ## Interim workaround Create `status:dispatch` manually in the Gitea UI (Settings → Labels) per repo until `label_create` lands.
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mathias/gitea-mcp#56