feat: issue_list tool #28

Closed
opened 2026-05-17 06:43:10 +00:00 by mathias · 0 comments
Owner

Problem

The current toolset has issue_get(owner, name, number) and issue_create(...) but no way to list issues in a repo. Agents that don't already know an issue number can't discover one without falling back to direct gitea API calls (which means asking the user for a token with read:issue scope, defeating the point of the MCP).

Concrete blocker hit 2026-05-17: working in mathias/local-dev, asked for "any fresh issues to work on" — the MCP could not answer without a number, and the credential-helper token in scope didn't have read:issue. Had to ask the user for the number manually.

Gitea API

GET /api/v1/repos/{owner}/{repo}/issues

Query params worth surfacing:

  • stateopen (default) | closed | all
  • labels — comma-separated label names
  • typeissues | pulls | all (filter to issues only by default; pulls have pr_* tools)
  • assigned_by / created_by / mentioned_by — optional user filters
  • milestones — comma-separated milestone names
  • since — ISO 8601 timestamp (for "what's new since X")
  • page / limit — pagination (cap at 50 same as repo_list)

Tool spec

tool: issue_list
params:
  owner:   string  (required, allowlisted)
  name:    string  (required)
  state:   "open" | "closed" | "all"  (default: open)
  labels:  string?  (comma-separated)
  since:   string?  (ISO 8601)
  page:    integer? (default 1)
  limit:   integer? (default 30, max 50 — match existing tool family)

returns:
  issues: [
    { number, title, state, labels: [string], assignees: [string],
      created_at, updated_at, comments, html_url }
  ]
  total:  integer  (from X-Total-Count header if present)

Body intentionally omitted from the list response (call issue_get for full body) — keeps response size sane for repos with many long issues.

Default: type=issues server-side so PRs don't leak into the list (gitea conflates them by default).

Acceptance criteria

  • issue_list registered in cmd/gitea-mcp/main.go
  • Allowlist check, same as other repo_*/issue_* tools
  • Default state=open, default type=issues (PRs excluded)
  • Pagination respects existing capLimit helper (internal/tools/tool.go)
  • Table-driven test in internal/tools/issue_list_test.go with: happy path, empty result, allowlist rejection, state filter, label filter, since filter
  • Gitea client method ListIssues(ctx, owner, name, args) in internal/gitea/issues.go
  • task check green

Out of scope

  • Issue search across multiple repos — use code_search or add issues_search later
  • Sorting — gitea default (newest first) is fine for v1
  • Issue body in list response — call issue_get for that
## Problem The current toolset has `issue_get(owner, name, number)` and `issue_create(...)` but no way to **list** issues in a repo. Agents that don't already know an issue number can't discover one without falling back to direct `gitea` API calls (which means asking the user for a token with `read:issue` scope, defeating the point of the MCP). Concrete blocker hit 2026-05-17: working in `mathias/local-dev`, asked for "any fresh issues to work on" — the MCP could not answer without a number, and the credential-helper token in scope didn't have `read:issue`. Had to ask the user for the number manually. ## Gitea API ``` GET /api/v1/repos/{owner}/{repo}/issues ``` Query params worth surfacing: - `state` — `open` (default) | `closed` | `all` - `labels` — comma-separated label names - `type` — `issues` | `pulls` | `all` (filter to issues only by default; pulls have `pr_*` tools) - `assigned_by` / `created_by` / `mentioned_by` — optional user filters - `milestones` — comma-separated milestone names - `since` — ISO 8601 timestamp (for "what's new since X") - `page` / `limit` — pagination (cap at 50 same as `repo_list`) ## Tool spec ``` tool: issue_list params: owner: string (required, allowlisted) name: string (required) state: "open" | "closed" | "all" (default: open) labels: string? (comma-separated) since: string? (ISO 8601) page: integer? (default 1) limit: integer? (default 30, max 50 — match existing tool family) returns: issues: [ { number, title, state, labels: [string], assignees: [string], created_at, updated_at, comments, html_url } ] total: integer (from X-Total-Count header if present) ``` Body intentionally omitted from the list response (call `issue_get` for full body) — keeps response size sane for repos with many long issues. Default: `type=issues` server-side so PRs don't leak into the list (gitea conflates them by default). ## Acceptance criteria - [ ] `issue_list` registered in `cmd/gitea-mcp/main.go` - [ ] Allowlist check, same as other `repo_*`/`issue_*` tools - [ ] Default `state=open`, default `type=issues` (PRs excluded) - [ ] Pagination respects existing `capLimit` helper (`internal/tools/tool.go`) - [ ] Table-driven test in `internal/tools/issue_list_test.go` with: happy path, empty result, allowlist rejection, state filter, label filter, since filter - [ ] Gitea client method `ListIssues(ctx, owner, name, args)` in `internal/gitea/issues.go` - [ ] `task check` green ## Out of scope - Issue search across multiple repos — use code_search or add `issues_search` later - Sorting — gitea default (newest first) is fine for v1 - Issue body in list response — call `issue_get` for that
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mathias/gitea-mcp#28