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)
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
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem
The current toolset has
issue_get(owner, name, number)andissue_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 directgiteaAPI calls (which means asking the user for a token withread:issuescope, 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 haveread:issue. Had to ask the user for the number manually.Gitea API
Query params worth surfacing:
state—open(default) |closed|alllabels— comma-separated label namestype—issues|pulls|all(filter to issues only by default; pulls havepr_*tools)assigned_by/created_by/mentioned_by— optional user filtersmilestones— comma-separated milestone namessince— ISO 8601 timestamp (for "what's new since X")page/limit— pagination (cap at 50 same asrepo_list)Tool spec
Body intentionally omitted from the list response (call
issue_getfor full body) — keeps response size sane for repos with many long issues.Default:
type=issuesserver-side so PRs don't leak into the list (gitea conflates them by default).Acceptance criteria
issue_listregistered incmd/gitea-mcp/main.gorepo_*/issue_*toolsstate=open, defaulttype=issues(PRs excluded)capLimithelper (internal/tools/tool.go)internal/tools/issue_list_test.gowith: happy path, empty result, allowlist rejection, state filter, label filter, since filterListIssues(ctx, owner, name, args)ininternal/gitea/issues.gotask checkgreenOut of scope
issues_searchlaterissue_getfor that