Spec-only change for Codeberg #190 — no implementation, so docs: keeps the changelog clean (code ships under feat: when the tasks are applied). Capabilities: - label-crud: repo + org label CRUD tools (create/edit/delete/get, repo-prefixed names), SDK for repo + raw-HTTP DoJSON for org, shared hex-color normalisation (6-digit), PATCH edit, safe delete with a delete_mode "safe"|"force" enum + in-use guard. - mcp-resource-label: forgejo://repo/{o}/{r}/labels{?page,limit}, /label/{id}, and forgejo://org/{org}/labels{?page,limit}. - mcp-resources-core (modified): adds a Collection-resource requirement. Adversarially reviewed by a debate team; synthesis + open questions recorded in design.md. Pre-code verification gates: org get-one endpoint, 3-digit hex acceptance. Codeberg #190
6.6 KiB
Why
forgejo-mcp exposes read and assignment label tools — list_repo_labels, list_org_labels, add_issue_labels, remove_issue_labels (all in operation/issue/issue.go) — but has no label CRUD: you cannot create, edit, or delete a repo or org label through MCP.
This surfaced 2026-05-31 (Codeberg #190) while creating the RFC - Request For Comments label for #176: the work fell back to raw curl against the Forgejo REST API because no MCP tool exists. Under the project's MCP-only policy that is a hard stop, not a minor annoyance — any project bootstrapping its own labels (RFC, triage states, priority tiers) is blocked.
Repo-label CRUD is a thin wrapper over forgejo-sdk/v3 methods that already ship in the pinned dependency; org-label CRUD has no SDK method and goes through the existing pkg/forgejo raw-HTTP DoJSON helper, exactly like fetchOrgLabels. The change also extends the additive forgejo:// resource-template surface (per mcp-resources-core) to labels, so labels become URI-addressable like the existing issue / pr / commit resources.
What Changes
- Add repo-label CRUD MCP tools, naming consistent with existing
create_*/edit_*/delete_*patterns:create_repo_label(owner, repo, name, color, description?)→ created label (incl. numericid)edit_repo_label(owner, repo, id, name?, color?, description?)→ updated label (PATCH: only supplied fields change)delete_repo_label(owner, repo, id, delete_mode?)→ success — refuses an in-use label unlessdelete_mode="force"(see below)get_repo_label(owner, repo, id)→ single label (fills the read-one gap;list_repo_labelsalready lists)
- Add org-label CRUD MCP tools (same shape, raw-HTTP
DoJSONagainst/orgs/{org}/labels[/{id}]):create_org_label(org, name, color, description?)edit_org_label(org, id, name?, color?, description?)delete_org_label(org, id, delete_mode?)→ refuses an in-use label unlessdelete_mode="force"get_org_label(org, id)→ single org label
- Safe delete:
delete_repo_label/delete_org_labelfirst count issues/PRs referencing the label. If the count is> 0they stop and report the in-use count instead of deleting;delete_mode="force"overrides and deletes anyway. (Repo count via the SDKListRepoIssuespagination filtered by label name —pkg/forgejo.DoJSONcannot read response headers, so no rawX-Total-Count; org count iterates the org's repos and is best-effort — it MAY under-count labels used in repos the token cannot read, and the refusal message says so. See design D9.) - Implement repo-label CRUD via the pinned
codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v3methods (CreateLabel,EditLabel,DeleteLabel,GetRepoLabel); org-label CRUD viapkg/forgejoDoJSON— no new dependency. - Normalise
colorto the Forgejo hex form (#rrggbb) before send so a missing#does not422(both repo and org tools). - Add additive
forgejo://resource-templates, coexisting with the tools (no tool removed):forgejo://repo/{owner}/{repo}/labels→ bounded list of repo labelsforgejo://repo/{owner}/{repo}/label/{id}→ single repo labelforgejo://org/{org}/labels→ bounded list of org labels (gate lifted now org CRUD lands here)
- Reuse
operation/resourcehelpers (ParseXxx,Bounded+EmbeddedListCap,MapForgejoError) and satisfydocs/design/output-bounding.mdfor the list resources. - Surface the new tools and resource-templates in the README tool/resource tables and
AGENTS.md.
Capabilities
New Capabilities
label-crud: Repo- and org-label create / edit / delete / get-one MCP tools. Repo labels wrapforgejo-sdk/v3; org labels usepkg/forgejoraw-HTTPDoJSON. Shared hex-color normalisation, PATCH edit semantics, and safe-delete (delete_modeenum + in-use guard).mcp-resource-label:forgejo://repo/{owner}/{repo}/labels(bounded list),forgejo://repo/{owner}/{repo}/label/{id}(single), andforgejo://org/{org}/labels(bounded org-label list) resource-templates, following themcp-resources-coreconvention.
Modified Capabilities
mcp-resources-core: Adds a Collection resource requirement.forgejo://repo/{owner}/{repo}/labelsis the first standalone collection-as-resource in the scheme (every prior resource is a single-entity fetch; lists were embedded or tool-only). Rather than let that precedent harden implicitly, this change records it normatively in the core framework: a bounded collection MAY be a resource, capped withEmbeddedListCap+ the shared sentinel, without removing the corresponding list tool. Future list entities (org labels, branches, milestones) cite this rule. No existing requirement is altered — this is a new requirement on the capability.
Existing list/assignment label tools (list_repo_labels, list_org_labels, add_issue_labels, remove_issue_labels) are unchanged.
Dependency / ordering
The mcp-resources-core delta above assumes the mcp-resource-templates change has archived (it is the home of the mcp-resources-core capability, currently unarchived). This change SHOULD archive after mcp-resource-templates; if it lands first, the core-spec delta is held until the base capability exists in openspec/specs/.
Impact
- Affected code: a label tool file under
operation/<domain>/(issue domain currently owns labels — keep them together or split alabeldomain, implementer's call) registered fromoperation/operation.go;resources*.gofor the three label templates; aParseLabel(repo) andParseOrgLabels(org list) parser inoperation/resource; an org-labelDoJSONblock inpkg/forgejomirroringfetchOrgLabels; a shared in-use-count helper. - No new external dependencies. Repo-label CRUD uses SDK methods already pinned; org-label CRUD uses the existing
pkg/forgejoDoJSONhelper; resources use the existingmcp-goresource APIs and the singletonpkg/forgejoclient. - No breaking changes. All existing tools remain; resource-unaware clients see exactly today's surface.
- Documentation: README tool table gains the eight label tools; README "Resources" section +
AGENTS.mdresource table gain the three label templates.
Out of Scope
exclusive/is_archivedlabel fields: SDK v3Labeldoes not model them (exposesid,name,color,description,urlonly); for parity the org-label raw-HTTP path also omits them in the first cut. Revisit via raw-HTTP passthrough (both paths) if a use case appears.- Implementation: this change is proposal + design + specs + tasks. Code lands when tasks are applied.