Commit Graph

192 Commits

Author SHA1 Message Date
torben 5a79cb7d9e fix(ci): update artifact upload path in build workflow 2026-07-19 11:00:56 +02:00
torben d2e0d1e3b7 feat(ci): add build and push container image workflow 2026-07-19 10:56:12 +02:00
torben 6dbc2c3cce fix(ci): correct spelling of 'build-essential' in CI dependencies 2026-07-19 10:47:54 +02:00
torben 7d90afd456 fix(ci): add build-essentials to CI dependencies 2026-07-19 10:43:38 +02:00
torben c7128a3996 fix(ci): ensure correct branch checkout in build workflow 2026-07-19 10:41:45 +02:00
torben 8b4ac7f8aa feat(ci): add initial build and test workflow configuration 2026-07-19 10:38:45 +02:00
torben cd7c29271a fix: add missing descriptions for various tools
Fixes interoperability with vscode, Perplexity, Mistral and others.
2026-07-19 08:40:39 +02:00
jermey bbde7ee110 feat(issue): add milestones and type filters to list_issues (#199)
## Summary

`list_issues` could not be scoped to a milestone, and the milestone was
absent from its list output. To find which milestone an issue belongs to,
callers had to read every issue individually — expensive on repositories
with hundreds of issues.

## Changes

- `list_issues`: add `milestones` (name or ID filter) and `type`
  (`issues`/`pulls`) parameters, wiring the SDK's
  `ListIssueOption.Milestones` and `ListIssueOption.Type`.
- Include the `milestone` `{id, title}` field in `slimIssues` (the list
  output), consistent with the existing `slimIssue` (single-issue) output.
- Tests: extend `Test_listRepoIssuesFn_filters` to assert the `milestones`
  and `type` query params, and add `Test_listRepoIssuesFn_includesMilestone`
  for the milestone field in list output.

This is a natural continuation of #164 (which added `labels`/`since`/`before`
to `list_issues`); `milestones` and `type` are supported by the SDK but were
not yet wired through.

## Testing

- `go build ./...` — ok
- `go vet ./operation/issue/` — ok
- `go test ./...` — all pass
- `make fmt` — clean

---
This PR was written with the help of Claude Opus 4.8

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/199
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: jermey <186737+jermey@noreply.gitea.com>
Co-committed-by: jermey <186737+jermey@noreply.gitea.com>
2026-05-31 22:19:41 +00:00
Sandro Santilli 3af6250589 Add example configuration for OpenCode (#198)
It took me some time so it's nice if the next person finds easier instructions here.

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/198
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Sandro Santilli <strk@kbt.io>
Co-committed-by: Sandro Santilli <strk@kbt.io>
2026-05-28 17:46:35 +00:00
Lunny Xiao a35fd26112 replace gitea sdk (#197)
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/197
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
2026-05-28 02:41:43 +00:00
Jason 4d7a33e57e Add a sample config for Mistral's Vibe agent config (#194)
Note: There seems to be some nascent work on
https://github.com/mistralai/mistral-vibe to make it easier to pass
environment variables into the config and eliminate hardcoding. But,
it hasn't landed yet, as far as I can tell.

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/194
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason <jro@codegrinder.com>
Co-committed-by: Jason <jro@codegrinder.com>
2026-05-19 17:33:19 +00:00
silverwind 371a06403a Simplify codebase (#195)
Net **-650 LOC** by removing duplication and dead noise. All tests pass.

### Duplication & helpers
- Extracted shared slim helpers (`UserLogin`, `UserLogins`, `LabelNames`, `BodyWithAttachments`, `UserDetail`, `Repo`/`Repos`, `Label`/`Labels`) into `pkg/slim`. Deleted the 4 copies that lived in `issue/`, `pull/`, `search/`, `repo/` (plus duplicate `slimUserDetail`/`slimRepo`/`slimLabels` across packages).
- Added `params.GetOptionalBoolPtr` and `params.GetOptionalStringPtr`. Replaced 18 awkward `new(localVar); if !ok { = nil }` patterns across `repo/`, `pull/`, `issue/`, `label/`, `milestone/`, `search/`.
- Extracted `pullRequestReviewerFn` for the 99%-identical `createPullRequestReviewerFn`/`deletePullRequestReviewerFn` pair.

### Dead code & noise
- Deleted **122** `log.Debugf("Called X")` narration lines (`zap.AddCaller` already records the caller) and pruned 19 unused `log` imports.
- Removed the unused `log.Logger` wrapper; the mcp-go server now uses `log.Default().Sugar()` directly (matches `util.Logger`).
- Deleted dead `s.DeleteTools("")` — confirmed no-op in mcp-go.
- Stripped WHAT-narration comments per project guidance.

### Correctness & consistency
- Fixed `log.Errorf(err.Error())` format-string bug in `pkg/to/to.go` — a `%` in the error would have been interpreted as a directive.
- Standardized `to.TextResult`/`to.ErrorResult` usage; `release.go`, `tag.go`, `branch.go` were bypassing the helpers in 9 sites (skipping the wrapper's debug/error logging).
- Made `params.GetString` reject empty strings; dropped 21 redundant `err != nil || x == ""` checks in `operation/actions/`.
- Replaced raw `args["org"].(string)` in `ListOrgReposFn` with `params.GetString` to match the rest of the codebase.

### Performance
- **Cached `*gitea.Client` by host+token via `sync.Map`** + shared `*http.Transport` via `sync.Once` for both SDK and raw REST paths. Eliminates the SDK's `/api/v1/version` preflight on every tool call and enables connection keep-alive across requests.
- Gated `to.TextResult` debug log behind `flag.Debug` to skip the `string(bytes)` allocation when debug is off.
- Hoisted `8192` and `60s` magic numbers in `pkg/gitea/rest.go` into named constants.

---
This PR was written with the help of Claude Opus 4.7

---------

Co-authored-by: silverwind <silv3rwind@gmail.com>
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/195
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-19 08:25:36 +00:00
silverwind e36137f5a1 Add org.opencontainers.image.source label (#193)
Two image-label fixes:

- Add `org.opencontainers.image.source` so tools like renovate can retrieve release notes from the source repo.
- Re-declare `ARG VERSION` in the final stage. `ARG` after `FROM` is stage-scoped, so the builder-stage `VERSION` never reached the final stage and `org.opencontainers.image.version` expanded to an empty string in published images.

Closes #192

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/193
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-17 18:10:54 +00:00
silverwind 2e67d5ebf3 Trim tool schemas, add param aliases, new PR methods (#191)
- Tool list size reduced by 26.6% (43,032 → 31,599 bytes on the `tools/list` JSON-RPC response).
- Trim redundant tool/param descriptions; shared description constants for `owner`/`repo`/`page`/`per_page`.
- Schemas now use github-mcp-server param names directly: `issue_number` (was `index` on issue tools), `pull_number` (was `index` on PR tools), `path` (was `filePath`), `query` (was `keyword` on user/repo search), `per_page` (was `perPage`).
- New PR read methods `get_files` and `get_status`; new PR write method `update_branch` (update PR branch from base).
- `list_org_repos` now uses `per_page` (was `pageSize`).
- `milestone_write` accepts `update` and `edit`.
- `create_branch` `old_branch` is optional; Gitea defaults to the repo default branch.
- Fix `list_commits` handler to honour optional `page`/`per_page` schema (was erroring out when callers omitted them).

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/191
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
v1.3.0
2026-05-14 06:24:51 +00:00
silverwind a77b54acdd fix(milestone): persist due_on on create and edit (#189)
Fixes https://gitea.com/gitea/gitea-mcp/issues/187

The `due_on` argument was declared in the `milestone_write` schema but never read by `createMilestoneFn` or `editMilestoneFn`, so `opt.Deadline` was always nil and the field was silently dropped. This reuses the existing `params.GetOptionalTime` helper that already handles the analogous `deadline` field on issues and pull requests.

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/189
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-12 00:21:18 +00:00
silverwind 9275c5a0e1 Update golangci-lint and tighten lint config (#190)
Bump `golangci-lint` to v2.12.2 and pin `govulncheck` to v1.3.0. Align `.golangci.yml` with the gitea repo's config: enable `revive` `var-naming` (with `skip-package-name-checks`) and drop the test-file exclusion for `errcheck`/`staticcheck`/`unparam`. Fix the now-surfaced `errcheck` violations in test handlers by discarding return values to match the existing codebase pattern.

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/190
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-12 00:04:36 +00:00
Martin Mikula bcefbaa9c1 feat: add --tools flag to filter exposed MCP tools (#167)
Adds `-O`/`-tools` CLI flag and `GITEA_TOOLS` environment variable
accepting a comma-separated list of tool names. When set, only the
listed tools are exposed to MCP clients, which lets AI agents trim
their tool context. Composes with `--read-only`. Unknown names are
logged at startup so typos surface instead of failing silently.

Co-Authored-By: silverwind <me@silverwind.io>
Co-Authored-By: Claude (Opus 4.7) <noreply@anthropic.com>
2026-05-10 12:07:16 +02:00
Skyf0l cd82f6f207 Add package listing and management tools (#170)
Adds `package_read` and `package_write` MCP tools for the Gitea
Packages API.

- `package_read` (read): `list`, `list_versions`, `get`
- `package_write` (write): `delete`

Package names containing slashes (e.g. container image paths like
`my-repo/my-image`) are accepted raw or pre-encoded and URL-encoded
correctly without double-encoding.

Co-Authored-By: silverwind <me@silverwind.io>
Co-Authored-By: Claude (Opus 4.7) <noreply@anthropic.com>
2026-05-10 11:42:01 +02:00
unpossible 329a97d5d2 Add tool annotations and PR close/reopen support (#174)
Add MCP `ToolAnnotation` metadata (Title, ReadOnlyHint, DestructiveHint)
to all registered tools so MCP hosts (VS Code, Claude, Cursor) get
accurate per-tool hints. A shared `pkg/annotation` package exposes
`ReadOnly`, `Write`, and `Destructive` helpers for consistency.

Add `close` and `reopen` methods to `pull_request_write` so PR state
can be toggled without going through the generic `update` path.

Co-Authored-By: silverwind <me@silverwind.io>
Co-Authored-By: Claude (Opus 4.7) <noreply@anthropic.com>
2026-05-10 11:25:22 +02:00
Dennis Gaida 4c45b42cb5 feat(config): support GITEA_ACCESS_TOKEN_FILE for Docker secrets (#186)
I don't like secrets just being added via environment variables. Add support for the `_FILE` environment variable convention used by Docker secrets.

When `GITEA_ACCESS_TOKEN_FILE` is set, the token is read from the file at that path (e.g. `/run/secrets/gitea_token`). Trailing newlines are stripped to handle the typical Docker secrets file format on both Linux and Windows.

Token resolution precedence (highest to lowest):

  1. `--token` / `-T` CLI flag
  2. `GITEA_ACCESS_TOKEN` env var
  3. `GITEA_ACCESS_TOKEN_FILE` env var

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/186
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Dennis Gaida <gitea@mail.gaida.biz>
Co-committed-by: Dennis Gaida <gitea@mail.gaida.biz>
v1.2.0
2026-05-09 23:04:50 +00:00
silverwind 7759c7f327 Inline issue/comment attachments in body (#183)
The Gitea API returns an `assets` array on issue and comment responses, but the SDK structs drop it — so attachments are invisible to MCP agents.

Append each attachment as a `[name](url)` markdown link at the end of the body, mirroring how GitHub embeds attachments inline (which `github-mcp-server` preserves as-is).

**Coverage:**
- `issue_read get` — issue body attachments
- `issue_read get_comments` — issue and PR conversation comment attachments (same endpoint)
- `pull_request_read get` — PR description attachments (Gitea's `/pulls/` endpoint omits `assets`, so a follow-up best-effort call to `/issues/{n}/assets` surfaces them; PRs are issues internally)

PR review summaries and line-comment reviews don't support attachments per the Gitea API spec, so nothing to do there.

Closes https://gitea.com/gitea/gitea-mcp/issues/182

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/183
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-08 05:44:53 +00:00
silverwind 5867f2f472 Replace AGENTS.md with concise coding guidelines (#185)
Replace AGENTS.md with minimal list of instructions to save context, the rest can be discovered easily.

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/185
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-07 21:33:05 +00:00
Philipp Horstenkamp 26f826d25c Update dev container version to use the same version as the app to go 1.26 (#180)
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/180
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Philipp Horstenkamp <182049+philipp-horstenkamp@noreply.gitea.com>
Co-committed-by: Philipp Horstenkamp <182049+philipp-horstenkamp@noreply.gitea.com>
2026-04-24 22:38:55 +00:00
silverwind baf792b061 Use golangci-lint fmt to format code (#178)
Use `golangci-lint fmt` to format code, replacing the previous gofumpt-based formatter. https://github.com/daixiang0/gci is used to order the imports.

Mirrors https://github.com/go-gitea/gitea/pull/37194.

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/178
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-04-17 22:38:13 +00:00
silverwind 08128b9471 Add notification_read and notification_write tools (#172)
Add notification management via two new MCP tools using the method-dispatch pattern:

- `notification_read`: list notifications (global or repo-scoped, with status/subject_type/since/before filters) and get single notification thread by ID
- `notification_write`: mark single notification as read, mark all notifications as read (global or repo-scoped)

---
This PR was written with the help of Claude Opus 4.6

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/172
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-04-02 16:47:24 +00:00
pengu 133fe487cd Add missing ref and deadline fields to issue responses (#169)
## Summary

### Read (issue responses)
- Add `ref` (branch) field to issue responses when non-empty
- Add `deadline` (due_date) field to issue responses when non-nil
- Applied to `slimIssue()`, `slimIssues()` in `operation/issue/slim.go` and `slimIssues()` in `operation/search/slim.go`

### Write (issue_write)
- Add `ref` parameter to `issue_write` tool for both `create` and `update` methods
- Allows setting the branch reference on issues via the MCP, consistent with the SDK's `CreateIssueOption.Ref` and `EditIssueOption.Ref` fields

Fixes #168

## Test plan
- [ ] `go test ./...` passes
- [ ] Verify `issue_read` returns `ref` when a branch is assigned to an issue
- [ ] Verify `issue_read` returns `deadline` when a due date is set
- [ ] Verify `list_issues` and `search_issues` include these fields
- [ ] Verify `issue_write` with method `update` can set `ref` on an issue
- [ ] Verify `issue_write` with method `create` can set `ref` on a new issue

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/169
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: pengu <jeremy@wenjy.fr>
Co-committed-by: pengu <jeremy@wenjy.fr>
v1.1.0
2026-03-26 18:37:57 +00:00
silverwind 05682e2afa Document server-side pagination limit in README (#166)
The maximum effective page size for paginated tools is determined by the Gitea server's [`[api].MAX_RESPONSE_ITEMS`](https://github.com/go-gitea/gitea/blob/ffa626b585225d62718f39e1b5fcc00416b0b7e4/custom/conf/app.example.ini#L2471-L2472) setting (default: 50). Requesting a `perPage` value higher than this limit will be silently capped by the server.

Fixes https://gitea.com/gitea/gitea-mcp/issues/165

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/166
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-03-26 17:15:33 +00:00
silverwind a5dd03c7f0 Add missing tool parameters from Gitea SDK (#164)
Expose additional parameters that the Gitea SDK supports but were not yet wired through the MCP tool definitions.

**Motivation:** Systematic comparison against github-mcp-server and the Gitea SDK revealed several supported parameters that were missing from tool schemas.

- `list_issues`: `labels`, `since`, `before` filters
- `issue_write`: `labels` and `deadline` on create, `deadline`/`remove_deadline` on update
- `pull_request_write`: `labels`/`deadline` on create/update, `remove_deadline` on update, `force_merge`/`merge_when_checks_succeed`/`head_commit_id` on merge
- `list_branches`: `page`/`perPage` pagination
- `create_repo`: `trust_model`, `object_format_name`
- `label_write`: `is_archived` on create/edit

**Testing:** Added tests for issue list filters, issue create labels/deadline, PR create labels/deadline, and PR merge new params.

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/164
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-03-26 07:00:14 +00:00
silverwind 9056a5ef27 Add get_commit, get_repository_tree, and search_issues tools (#162)
Add three new read-only tools inspired by the GitHub MCP server:

- `get_commit`: Get details of a specific commit by SHA, branch, or tag
- `get_repository_tree`: Get the file tree of a repository with optional recursive traversal, pagination, and ref support
- `search_issues`: Search issues and pull requests across all accessible repositories with filters for state, type, labels, and owner

---------

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/162
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-03-24 17:12:58 +00:00
silverwind c8004e9198 Update mcp-go to v0.45.0 (#163)
Update github.com/mark3labs/mcp-go from v0.44.0 to v0.45.0.

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/163
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-03-24 17:07:40 +00:00
tomholford 6a3ce66e09 feat(pull): add draft parameter for creating/updating draft PRs (#159)
## Summary

The Gitea API has no native `draft` field on `CreatePullRequestOption` or `EditPullRequestOption`. Instead, Gitea treats PRs whose title starts with a WIP prefix (e.g. `WIP:`, `[WIP]`) as drafts. This adds a `draft` boolean parameter to the `pull_request_write` tool so MCP clients can create/update draft PRs without knowing about the WIP prefix convention.

## Changes

- Add `draft` boolean parameter to `PullRequestWriteTool` schema, supported on `create` and `update` methods
- Add `applyDraftPrefix()` helper that handles both default Gitea WIP prefixes (`WIP:`, `[WIP]`) case-insensitively
- When `draft=true` and no prefix exists, prepend `WIP: `; when a prefix already exists, preserve the title as-is (no normalization)
- When `draft=false`, strip any recognized WIP prefix
- On `update`, if `draft` is set without `title`, auto-fetch the current PR title via GET
- Add tests: 12 unit tests for `applyDraftPrefix`, 5 integration tests for create, 4 for edit

---------

Co-authored-by: tomholford <tomholford@users.noreply.github.com>
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/159
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: tomholford <128995+tomholford@noreply.gitea.com>
Co-committed-by: tomholford <128995+tomholford@noreply.gitea.com>
v1.0.2
2026-03-23 11:53:00 +00:00
silverwind 0bdf8f5bb3 wiki: accept plain text content instead of content_base64 (#156)
The `wiki_write` tool exposed the Gitea API's `content_base64` parameter directly, requiring callers to provide base64-encoded content. This caused LLM agents to incorrectly infer that other tools like `create_or_update_file` also require base64 encoding, corrupting files with literal base64 strings.

Rename the parameter to `content` (plain text) and handle base64 encoding internally, matching the pattern already used by `create_or_update_file`. Also added test coverage for this.

Closes #151

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/156
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-03-14 01:49:40 +00:00
silverwind 7bf54b9e83 Use debug.ReadBuildInfo to determine version for go run (#155)
*Written by Claude on behalf of @silverwind*

When installed via `go run module@version` or `go install module@version`, the Go toolchain embeds the module version in the binary. Use `debug.ReadBuildInfo` to read it as a fallback when `Version` has not been set via ldflags, so that `go run gitea.com/gitea/gitea-mcp@latest` will reports the latest tag version instead of `dev`.

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/155
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-03-13 19:56:42 +00:00
silverwind c57e4c2e57 fix: prevent silent write loss on 301 redirects (#154)
When a Gitea repo is renamed, the API returns a 301 redirect. Go's default `http.Client` follows 301/302/303 redirects by changing the HTTP method from PATCH/POST/PUT to GET and dropping the request body. This causes mutating API calls (edit PR, create issue, etc.) to silently appear to succeed while no write actually occurs — the client receives the current resource data via the redirected GET and returns it as if the edit worked.

## Fix

Add a `CheckRedirect` function to both HTTP clients (SDK client in `gitea.go` and REST client in `rest.go`) that returns `http.ErrUseLastResponse` for non-GET/HEAD methods. This surfaces the redirect as an error instead of silently downgrading the request. GET/HEAD reads continue to follow redirects normally.

## Tests

- `TestCheckRedirect`: table-driven unit tests for all HTTP methods + redirect limit
- `TestDoJSON_RepoRenameRedirect`: regression test with `httptest` server proving PATCH to a 301 endpoint returns an error instead of silently succeeding
- `TestDoJSON_GETRedirectFollowed`: verifies GET reads still follow 301 redirects

*This PR was authored by Claude.*

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/154
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
v1.0.1
2026-03-13 17:45:59 +00:00
silverwind 22fc663387 Partially revert #149: remove Content-Type middleware, keep ErrServerClosed fix (#150)
Reverts the Content-Type middleware and custom server/mux plumbing from #149, keeping only the `http.ErrServerClosed` fix which is a legitimate bug.

The middleware is unnecessary because rmcp's `post_message` already returns early for 202/204 before any Content-Type validation. Both Go MCP SDKs (`modelcontextprotocol/go-sdk` used by GitHub's MCP server, and `mark3labs/mcp-go` used here) intentionally omit Content-Type on 202 responses per the MCP spec. See #148 for the full analysis.

*PR written by Claude on behalf of @silverwind*

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/150
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-03-13 09:31:26 +00:00
Bo-Yi Wu e0abd256a3 feat(mcp): add MCP tool to list organization repositories (#152)
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/152
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2026-03-12 05:46:07 +00:00
Mutex 73263e74d0 http: set Content-Type for 202/204 streamable responses (#149)
Fixes: #148
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/149
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Mutex <gitea314@libertyprime.org>
Co-committed-by: Mutex <gitea314@libertyprime.org>
2026-03-09 13:07:27 +00:00
silverwind bba612d238 Consolidate tools from 110 to 45 using method dispatch (#143)
Consolidate 110 individual MCP tools down to 45 using a method dispatch pattern, aligning tool names with the GitHub MCP server conventions.

**Motivation:** LLMs work better with fewer, well-organized tools. The method dispatch pattern (used by GitHub's MCP server) groups related operations under read/write tools with a `method` parameter.

**Changes:**
- Group related tools into `_read`/`_write` pairs with method dispatch (e.g. `issue_read`, `issue_write`, `pull_request_read`, `pull_request_write`)
- Rename tools to match GitHub MCP naming (`get_file_contents`, `create_or_update_file`, `list_issues`, `list_pull_requests`, etc.)
- Rename `pageSize` to `perPage` for GitHub MCP compat
- Move issue label ops (`add_labels`, `remove_label`, etc.) into `issue_write`
- Merge `create_file`/`update_file` into `create_or_update_file` with optional `sha`
- Make `delete_file` require `sha`
- Add `get_labels` method to `issue_read`
- Add shared helpers: `GetInt64Slice`, `GetStringSlice`, `GetPagination` in params package
- Unexport all dispatch handler functions
- Fix: pass assignees/milestone in `CreateIssue`, bounds check in `GetFileContent`

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/143
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
v1.0.0
2026-03-06 19:12:15 +00:00
silverwind c3db4fb65f feat: slim tool responses (#141)
Reduce token usage by slimming tool responses. Instead of returning full Gitea SDK objects (with nested user/repo objects, avatars, permissions, etc.), each operation now has a colocated `slim.go` that extracts only the fields an LLM needs. List endpoints return even fewer fields than single-item endpoints.

Other changes:
- Add `params` helpers to DRY parameter extraction across 40+ handlers
- Remove `{"Result": ...}` wrapper for flatter responses
- Reduce default pageSize from 100 to 30

Fixes: https://gitea.com/gitea/gitea-mcp/issues/128

*Created by Claude on behalf of @silverwind*

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/141
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-03-05 05:56:23 +00:00
silverwind 9ce5604e4c Improve CLI help text and flags (#139)
## Summary
- Replace default `flag.Usage` with custom 2-column layout using `text/tabwriter`
- Add short and long aliases for all CLI flags
- Add environment variables section with sorted entries
- Handle `-version` flag in `Execute()`

## Sample output
```
Usage: gitea-mcp [options]

Options:
  -t, -transport <type>   Transport type: stdio or http (default: stdio)
  -H, -host <url>         Gitea host URL (default: https://gitea.com)
  -p, -port <number>      HTTP server port (default: 8080)
  -T, -token <token>      Personal access token
  -r, -read-only          Expose only read-only tools
  -d, -debug              Enable debug mode
  -k, -insecure           Ignore TLS certificate errors
  -v, -version            Print version and exit

Environment variables:
  GITEA_ACCESS_TOKEN   Provide access token
  GITEA_DEBUG          Set to 'true' for debug mode
  GITEA_HOST           Override Gitea host URL
  GITEA_INSECURE       Set to 'true' to ignore TLS errors
  GITEA_READONLY       Set to 'true' for read-only mode
  MCP_MODE             Override transport mode
```

*Created by Claude on behalf of @silverwind*

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/139
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-02-26 18:49:37 +00:00
silverwind 653781a199 fix: replace goreleaser-action with direct go install
The goreleaser/goreleaser-action@v7 JS wrapper was crashing in CI
before goreleaser could run. Install goreleaser via `go install`
instead to avoid the action's compatibility issues with the runner.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
v0.9.0
2026-02-26 02:09:18 +01:00
silverwind 67a1e1e7fe feat: accept string values for all numeric input parameters (#138)
## Summary

- MCP clients may send numbers as strings. This adds `ToInt64` and `GetOptionalInt` helpers to `pkg/params` and replaces all raw `.(float64)` type assertions across operation handlers to accept both `float64` and string inputs.

## Test plan

- [x] Verify `go test ./...` passes
- [x] Test with an MCP client that sends numeric parameters as strings

*Created by Claude on behalf of @silverwind*

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/138
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-02-25 23:28:14 +00:00
silverwind 4a2935d898 feat: add title parameter to merge_pull_request tool (#134)
## Summary
- Add missing `title` parameter to `merge_pull_request` tool for custom merge commit titles
- Use `params.GetIndex()` for consistent index parameter handling (supports both string and number inputs)
- Add test for `MergePullRequestFn`

Closes #120

## Test plan
- [x] `go test ./operation/pull/ -run TestMergePullRequestFn` passes
- [x] All existing tests pass (`go test ./...`)
- [x] Build succeeds (`make build`)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/134
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <silverwind@noreply.gitea.com>
Co-committed-by: silverwind <silverwind@noreply.gitea.com>
2026-02-25 19:05:09 +00:00
silverwind 6540693771 fix: parse Authorization header case-insensitively and support token format (#137)
## Summary
- Make auth header parsing RFC 7235 compliant by comparing the scheme case-insensitively (`bearer`, `BEARER`, etc. all work now)
- Add support for Gitea-style `token <value>` format in addition to `Bearer <value>`

Fixes https://gitea.com/gitea/gitea-mcp/issues/59

---
*This PR was authored by Claude.*

---------

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/137
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-02-25 19:04:14 +00:00
silverwind 3b9236695c fix: add missing required attributes to search tool schemas (#135)
## Summary
- Add `mcp.Required()` to `keyword` in `search_repos` and `search_users` tool schemas
- Add `mcp.Required()` to `org` and `query` in `search_org_teams` tool schema
- Add test verifying required fields are set on all search tool schemas
- Fixes MCP clients failing with `keyword is required` because the schema didn't declare the field as required

Closes #115

---
*This PR was authored by Claude.*

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/135
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <silverwind@noreply.gitea.com>
Co-committed-by: silverwind <silverwind@noreply.gitea.com>
2026-02-25 19:00:17 +00:00
silverwind 723a30ae23 fix: replace deprecated SDK calls in timetracking (#136)
## Summary
- Replace `client.GetMyStopwatches()` with `client.ListMyStopwatches(ListStopwatchesOptions{})`
- Replace `client.GetMyTrackedTimes()` with `client.ListMyTrackedTimes(ListTrackedTimesOptions{})`
- Fixes staticcheck SA1019 lint errors for deprecated API usage

## Test plan
- [x] `make lint` passes with 0 issues
- [x] `go test ./...` passes

*Created by Claude on behalf of @silverwind*

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/136
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-02-25 03:28:59 +00:00
techknowlogick 7bbe015ea7 Bump to go 1.26 2026-02-22 17:13:24 +00:00
silverwind bb9470a259 chore: update Go and Actions dependencies (#132)
## Summary
- Update Go dependencies: gitea SDK v0.22.1→v0.23.2, mcp-go v0.42.0→v0.44.0, zap v1.27.0→v1.27.1, go-version v1.7.0→v1.8.0, x/crypto v0.43.0→v0.48.0, x/sys v0.37.0→v0.41.0
- Update Actions: checkout v4→v6, setup-go v5→v6, build-push-action v5→v6, goreleaser-action v6→v7

## Test plan
- [x] `go test ./...` passes
- [x] `make build` succeeds

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/132
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-02-22 17:10:25 +00:00
silverwind 8728c04748 chore: add golangci-lint, bump Go to 1.26, fix all lint issues (#133)
## Summary
- Add `.golangci.yml` with linter configuration matching the main gitea repo
- Add `lint`, `lint-fix`, `lint-go`, `lint-go-fix`, and `security-check` Makefile targets
- Add `tidy` Makefile target (extracts min Go version from `go.mod` for `-compat` flag)
- Bump minimum Go version to 1.26
- Update golangci-lint to v2.10.1
- Replace `golang/govulncheck-action` with `make security-check` in CI
- Add `make lint` step to CI
- Fix all lint issues across the codebase (formatting, `errors.New` vs `fmt.Errorf`, `any` vs `interface{}`, unused returns, stuttering names, Go 1.26 `new(expr)`, etc.)
- Remove unused `pkg/ptr` package (inlined by Go 1.26 `new(expr)`)
- Remove dead linter exclusions (staticcheck, gocritic, testifylint, dupl)

## Test plan
- [x] `make lint` passes
- [x] `go test ./...` passes
- [x] `make build` succeeds

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/133
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-02-22 17:10:04 +00:00
silverwind 4d5fa3ab2c feat: accept string or number for index parameters (#131)
This change makes index parameters more flexible by accepting both numeric and string values. LLM agents often pass issue/PR indices as strings (e.g., "123") since they appear as string identifiers in URLs and CLI contexts. The implementation:

- Created `pkg/params` package with `GetIndex()` helper function
- Updated 25+ tool functions across issue, pull, label, and timetracking operations
- Improved error messages to say "must be a valid integer" instead of misleading "is required"
- Added comprehensive tests for both numeric and string inputs

Based on #122 by @jamespharaoh with review feedback applied (replaced custom `contains()` test helper with `strings.Contains`). Verified working in Claude Code.

Fixes: https://gitea.com/gitea/gitea-mcp/issues/121
Fixes: https://gitea.com/gitea/gitea-mcp/issues/122
---------

Co-authored-by: James Pharaoh <james@pharaoh.uk>
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/131
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
v0.8.1
2026-02-20 23:47:22 +00:00