@amonstack/gitea-mcp 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -13
- package/README.zh-CN.md +52 -13
- package/dist/assets/instructions.md +63 -0
- package/dist/assets/resources/field-reference.md +48 -0
- package/dist/assets/resources/label-guide.md +47 -0
- package/dist/assets/resources/tool-cookbook.md +51 -0
- package/dist/assets/skills/gitea-create-issue/SKILL.md +21 -0
- package/dist/assets/skills/gitea-find-issues/SKILL.md +31 -0
- package/dist/assets/skills/gitea-label-issue/SKILL.md +28 -0
- package/dist/assets/skills/gitea-manage-labels/SKILL.md +22 -0
- package/dist/assets/skills/gitea-plan-milestones/SKILL.md +24 -0
- package/dist/assets/skills/gitea-resolve-repo/SKILL.md +18 -0
- package/dist/assets/skills/gitea-summarize-issue/SKILL.md +21 -0
- package/dist/assets/skills/gitea-update-issue/SKILL.md +21 -0
- package/dist/cli.js +23 -11
- package/dist/cli.js.map +1 -1
- package/dist/server.js +171 -26
- package/dist/server.js.map +1 -1
- package/dist/skills.d.ts +24 -0
- package/dist/skills.js +113 -0
- package/dist/skills.js.map +1 -0
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -18,15 +18,17 @@ The server communicates over stdio and wraps the [Gitea REST API
|
|
|
18
18
|
|
|
19
19
|
## Installation
|
|
20
20
|
|
|
21
|
-
### From
|
|
21
|
+
### From npm
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
npm set @amonstack:registry https://npm.pkg.github.com
|
|
25
|
-
npm set //npm.pkg.github.com/:_authToken YOUR_GITHUB_PAT
|
|
26
24
|
npm install -g @amonstack/gitea-mcp
|
|
27
25
|
```
|
|
28
26
|
|
|
29
|
-
|
|
27
|
+
Or run directly without global install:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx @amonstack/gitea-mcp
|
|
31
|
+
```
|
|
30
32
|
|
|
31
33
|
### Build from source
|
|
32
34
|
|
|
@@ -95,6 +97,18 @@ Add to your opencode MCP configuration:
|
|
|
95
97
|
|
|
96
98
|
If you built from source, use `node /path/to/gitea-mcp/dist/cli.js` instead.
|
|
97
99
|
|
|
100
|
+
opencode also loads native **skills** — one per action (find, create, update,
|
|
101
|
+
label, summarize, plan milestones, resolve repo) — that teach the assistant the
|
|
102
|
+
safest workflow for that action, including pre-use checks and pitfalls. Install
|
|
103
|
+
them once:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
gitea-mcp skills install # global (~/.config/opencode/skills/)
|
|
107
|
+
gitea-mcp skills install --project # this project (./.opencode/skills/)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Then restart opencode. See [AI Guidance & Skills](#ai-guidance--skills) below.
|
|
111
|
+
|
|
98
112
|
### Other MCP Clients
|
|
99
113
|
|
|
100
114
|
Any client that supports stdio-based MCP servers can use `gitea-mcp`. After
|
|
@@ -158,6 +172,42 @@ gitea-mcp
|
|
|
158
172
|
| `list_my_repos` | List repositories accessible to the authenticated user |
|
|
159
173
|
| `resolve_repo` | Auto-detect `owner` and `repo` from a local git remote URL |
|
|
160
174
|
|
|
175
|
+
## AI Guidance & Skills
|
|
176
|
+
|
|
177
|
+
The server ships guidance so assistants use the tools correctly and safely,
|
|
178
|
+
through three channels:
|
|
179
|
+
|
|
180
|
+
- **`instructions` (on connect)** — a concise strategy the server sends during the
|
|
181
|
+
MCP handshake; capable clients inject it into the system prompt automatically.
|
|
182
|
+
- **Tool descriptions** — every tool's description flags its key risk (pagination,
|
|
183
|
+
label ID-vs-name, destructive scope) and a minimal usage example.
|
|
184
|
+
- **Prompts & resources** — workflow templates (`triage_issues`,
|
|
185
|
+
`summarize_issue`, `audit_labels`, `milestone_report`) and on-demand reference
|
|
186
|
+
docs (field reference, label guide, tool cookbook) for clients that surface them.
|
|
187
|
+
|
|
188
|
+
### opencode skills
|
|
189
|
+
|
|
190
|
+
For opencode, the server ships a set of **action-scoped skills** — one per
|
|
191
|
+
workflow, so the assistant loads only the guidance it needs (and never, say,
|
|
192
|
+
delete instructions while creating). Install them with the
|
|
193
|
+
`gitea-mcp skills install` command shown in the opencode section above.
|
|
194
|
+
|
|
195
|
+
| Skill | Invoke when |
|
|
196
|
+
|-------|-------------|
|
|
197
|
+
| `gitea-find-issues` | discovering / reading / triaging issues |
|
|
198
|
+
| `gitea-create-issue` | creating an issue (after a duplicate check) |
|
|
199
|
+
| `gitea-update-issue` | editing fields, closing, clearing assignee/milestone |
|
|
200
|
+
| `gitea-label-issue` | adding / replacing / removing / clearing labels on an issue |
|
|
201
|
+
| `gitea-manage-labels` | creating or editing label definitions |
|
|
202
|
+
| `gitea-summarize-issue` | reading and summarizing an issue's discussion |
|
|
203
|
+
| `gitea-plan-milestones` | creating / editing / closing milestones |
|
|
204
|
+
| `gitea-resolve-repo` | resolving owner/repo or listing repositories |
|
|
205
|
+
|
|
206
|
+
Each skill is a short, AI-facing action flow (purpose, when to use, when not to,
|
|
207
|
+
rules, and what to check first). Destructive single-tool actions (delete issue /
|
|
208
|
+
comment / label / milestone) are intentionally left to the tool descriptions so
|
|
209
|
+
they never contaminate a creative workflow.
|
|
210
|
+
|
|
161
211
|
## Development
|
|
162
212
|
|
|
163
213
|
```bash
|
|
@@ -175,15 +225,8 @@ npm ci
|
|
|
175
225
|
| `make test-integration` | Run integration tests (needs live Gitea instance) |
|
|
176
226
|
| `make dev` | Run directly with tsx |
|
|
177
227
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
```
|
|
181
|
-
src/
|
|
182
|
-
cli.ts # Entry point
|
|
183
|
-
server.ts # MCP server setup and tool registration
|
|
184
|
-
tools.ts # Zod input schemas for all tools
|
|
185
|
-
gitea-client.ts # REST client wrapping Gitea /api/v1 endpoints
|
|
186
|
-
```
|
|
228
|
+
For the full architecture — module layout, dependency graph, core patterns, and
|
|
229
|
+
the guide to adding a new tool — see [`docs/architecture.md`](docs/architecture.md).
|
|
187
230
|
|
|
188
231
|
## License
|
|
189
232
|
|
package/README.zh-CN.md
CHANGED
|
@@ -17,15 +17,17 @@ opencode、Cursor 等)后,AI 助手即可通过自然语言在 Gitea 实例
|
|
|
17
17
|
|
|
18
18
|
## 安装
|
|
19
19
|
|
|
20
|
-
### 从
|
|
20
|
+
### 从 npm 安装
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
|
-
npm set @amonstack:registry https://npm.pkg.github.com
|
|
24
|
-
npm set //npm.pkg.github.com/:_authToken <你的 GitHub PAT>
|
|
25
23
|
npm install -g @amonstack/gitea-mcp
|
|
26
24
|
```
|
|
27
25
|
|
|
28
|
-
|
|
26
|
+
或无需全局安装,直接运行:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx @amonstack/gitea-mcp
|
|
30
|
+
```
|
|
29
31
|
|
|
30
32
|
### 从源码构建
|
|
31
33
|
|
|
@@ -94,6 +96,17 @@ node dist/cli.js
|
|
|
94
96
|
|
|
95
97
|
如果从源码构建,将 `command` 改为 `node /path/to/gitea-mcp/dist/cli.js`。
|
|
96
98
|
|
|
99
|
+
opencode 还会加载原生**技能(skills)**——按动作拆分(查找、创建、更新、打
|
|
100
|
+
标签、总结、规划里程碑、解析仓库等),教会助手针对该动作的最安全工作流,含
|
|
101
|
+
使用前检查与各类陷阱。一次性安装:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
gitea-mcp skills install # 全局(~/.config/opencode/skills/)
|
|
105
|
+
gitea-mcp skills install --project # 仅当前项目(./.opencode/skills/)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
然后重启 opencode。详见下方 [AI 引导与技能](#ai-引导与技能)。
|
|
109
|
+
|
|
97
110
|
### 其他 MCP 客户端
|
|
98
111
|
|
|
99
112
|
任何支持 stdio 方式运行 MCP 服务端的客户端都可以使用。安装完成后设置环境
|
|
@@ -157,6 +170,39 @@ gitea-mcp
|
|
|
157
170
|
| `list_my_repos` | 列出当前用户可访问的仓库 |
|
|
158
171
|
| `resolve_repo` | 从本地 git 仓库的远程地址自动检测 `owner` 和 `repo` |
|
|
159
172
|
|
|
173
|
+
## AI 引导与技能
|
|
174
|
+
|
|
175
|
+
服务端内置了引导内容,让助手正确、安全地使用工具,分三个通道:
|
|
176
|
+
|
|
177
|
+
- **`instructions`(连接时)** —— 服务端在 MCP 握手时下发一份精炼策略;支持的
|
|
178
|
+
客户端会自动注入系统提示。
|
|
179
|
+
- **工具描述** —— 每个工具的描述都标出其关键风险(分页、标签 ID 与名称、破坏性
|
|
180
|
+
作用范围)和最小用法示例。
|
|
181
|
+
- **Prompts 与 Resources** —— 工作流模板(`triage_issues`、`summarize_issue`、
|
|
182
|
+
`audit_labels`、`milestone_report`)与按需参考文档(字段参考、标签指南、工具
|
|
183
|
+
食谱),供支持的客户端使用。
|
|
184
|
+
|
|
185
|
+
### opencode 技能
|
|
186
|
+
|
|
187
|
+
对 opencode,服务端内置了一组**按动作划分的技能**——每个工作流一个,助手只
|
|
188
|
+
加载所需指引(避免例如创建时把删除说明一并带入造成幻觉)。用上面 opencode
|
|
189
|
+
小节展示的 `gitea-mcp skills install` 命令安装。
|
|
190
|
+
|
|
191
|
+
| 技能 | 何时触发 |
|
|
192
|
+
|------|----------|
|
|
193
|
+
| `gitea-find-issues` | 发现 / 读取 / 分流 issues |
|
|
194
|
+
| `gitea-create-issue` | 创建 issue(先做查重) |
|
|
195
|
+
| `gitea-update-issue` | 编辑字段、关闭、清空负责人/里程碑 |
|
|
196
|
+
| `gitea-label-issue` | 为 issue 增加 / 替换 / 移除 / 清空标签 |
|
|
197
|
+
| `gitea-manage-labels` | 创建或编辑标签定义 |
|
|
198
|
+
| `gitea-summarize-issue` | 读取并总结某 issue 的讨论 |
|
|
199
|
+
| `gitea-plan-milestones` | 创建 / 编辑 / 关闭里程碑 |
|
|
200
|
+
| `gitea-resolve-repo` | 解析 owner/repo 或列出仓库 |
|
|
201
|
+
|
|
202
|
+
每个技能都是面向 AI 的简短动作流程(目的、何时用、何时不用、规则、先检查什么)。
|
|
203
|
+
破坏性的单工具操作(删除 issue / 评论 / 标签 / 里程碑)有意仅保留在工具描述里,
|
|
204
|
+
不会污染创建类工作流。
|
|
205
|
+
|
|
160
206
|
## 二次开发
|
|
161
207
|
|
|
162
208
|
```bash
|
|
@@ -174,15 +220,8 @@ npm ci
|
|
|
174
220
|
| `make test-integration` | 运行集成测试(需要可用的 Gitea 实例) |
|
|
175
221
|
| `make dev` | 通过 tsx 直接运行 |
|
|
176
222
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
```
|
|
180
|
-
src/
|
|
181
|
-
cli.ts # 入口文件
|
|
182
|
-
server.ts # MCP 服务端搭建与工具注册
|
|
183
|
-
tools.ts # 所有工具的 Zod 输入模式定义
|
|
184
|
-
gitea-client.ts # 封装 Gitea /api/v1 端点的 REST 客户端
|
|
185
|
-
```
|
|
223
|
+
完整的架构说明(模块布局、依赖关系、核心模式,以及新增工具的指引)请参阅
|
|
224
|
+
[`docs/architecture.md`](docs/architecture.md)。
|
|
186
225
|
|
|
187
226
|
## 许可证
|
|
188
227
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Gitea MCP — usage strategy
|
|
2
|
+
|
|
3
|
+
You manage Gitea issues, comments, labels, and milestones through this MCP server.
|
|
4
|
+
Every tool returns Gitea's JSON verbatim as text. Follow these rules to use them
|
|
5
|
+
correctly.
|
|
6
|
+
|
|
7
|
+
## Resolve owner/repo FIRST (most common failure)
|
|
8
|
+
|
|
9
|
+
Most tools target one repository and need both `owner` and `repo`. They resolve in
|
|
10
|
+
this order:
|
|
11
|
+
|
|
12
|
+
1. The `owner` / `repo` arguments you pass to the call.
|
|
13
|
+
2. `GITEA_DEFAULT_OWNER` / `GITEA_DEFAULT_REPO` env vars (set at server start).
|
|
14
|
+
3. The `resolve_repo` tool (detects owner/repo from a local git remote).
|
|
15
|
+
|
|
16
|
+
If neither argument nor env var is set, the call errors. Before a batch of work on a
|
|
17
|
+
repo you have not used this session, call `resolve_repo` (or confirm the env defaults)
|
|
18
|
+
once and reuse the result — do not guess.
|
|
19
|
+
|
|
20
|
+
## Labels: IDs vs names (critical gotcha)
|
|
21
|
+
|
|
22
|
+
Label endpoints are NOT consistent — mixing them up silently fails or hits the wrong label:
|
|
23
|
+
|
|
24
|
+
- `add_issue_labels`, `replace_issue_labels` → take label **names** (`string[]`).
|
|
25
|
+
- `remove_issue_label` → takes a label **ID** (`number`). You must `list_labels` first to get IDs.
|
|
26
|
+
- `create_issue` / `update_issue` `labels` field → takes label **IDs** (`number[]`).
|
|
27
|
+
|
|
28
|
+
Always `list_labels` before any label mutation so you use the right identifier, and
|
|
29
|
+
prefer `replace_issue_labels` (name-based) when you want a known final set.
|
|
30
|
+
|
|
31
|
+
## Comments use comment IDs, not issue numbers
|
|
32
|
+
|
|
33
|
+
`update_comment` and `delete_comment` take the comment `id`, NOT the issue `index`.
|
|
34
|
+
`list_comments` returns each comment's `id`. Never reuse an issue number where a
|
|
35
|
+
comment id is required.
|
|
36
|
+
|
|
37
|
+
## Pagination
|
|
38
|
+
|
|
39
|
+
List endpoints are 1-based: `page` starts at 1, `limit` max 100. To fetch everything,
|
|
40
|
+
page forward until a page returns fewer than `limit` items (or an empty page). Do not
|
|
41
|
+
assume one page is complete.
|
|
42
|
+
|
|
43
|
+
## Destructive operations — confirm before running
|
|
44
|
+
|
|
45
|
+
These are irreversible on most Gitea instances (no trash/recycle):
|
|
46
|
+
|
|
47
|
+
- `delete_issue`, `delete_label`, `delete_milestone`, `delete_comment`
|
|
48
|
+
- `clear_issue_labels`, `replace_issue_labels` (replaces the ENTIRE label set)
|
|
49
|
+
|
|
50
|
+
Confirm the target id/index and scope with the user before calling. For labels,
|
|
51
|
+
`replace_issue_labels` overwrites — read current labels first if any must survive.
|
|
52
|
+
|
|
53
|
+
## Search vs list
|
|
54
|
+
|
|
55
|
+
- `list_issues` — one repo, paginated, filterable by state/labels.
|
|
56
|
+
- `search_issues` — across ALL repos the token can see, by keyword/type/state. Use it
|
|
57
|
+
for "find issues about X" or duplicate detection, not for listing one repo.
|
|
58
|
+
|
|
59
|
+
## Error format
|
|
60
|
+
|
|
61
|
+
Failed calls throw `Gitea API error (<status>): <body>`. Read the status: 401/403 →
|
|
62
|
+
token scope/expiry; 404 → wrong owner/repo or no permission; 409 → conflict;
|
|
63
|
+
422 → validation. Do not retry blindly on 4xx.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Gitea object field reference
|
|
2
|
+
|
|
3
|
+
The shapes of the JSON objects these tools return (verbatim from the Gitea API).
|
|
4
|
+
Use this to read results correctly and pick the right identifier.
|
|
5
|
+
|
|
6
|
+
## Issue
|
|
7
|
+
- `id` (number) — internal id. NOT used by tools; tools use `number`.
|
|
8
|
+
- `number` (number) — the issue index shown in the URL (#42). This is the `index`
|
|
9
|
+
passed to get_issue / update_issue / delete_issue / comments tools.
|
|
10
|
+
- `title` (string), `body` (string?), `state` ("open" | "closed")
|
|
11
|
+
- `html_url`, `url` (string)
|
|
12
|
+
- `comments` (number) — comment COUNT, not the comments themselves
|
|
13
|
+
- `labels` (Label[])
|
|
14
|
+
- `assignee` (User?), `assignees` (User[]?)
|
|
15
|
+
- `milestone` (Milestone?)
|
|
16
|
+
- `repository` (Repository) — present on search_issues results
|
|
17
|
+
- `created_at`, `updated_at` (string ISO); `closed_at` (string?, when closed)
|
|
18
|
+
|
|
19
|
+
## Label
|
|
20
|
+
- `id` (number) — used by remove_issue_label / update_label / delete_label
|
|
21
|
+
- `name` (string) — used by add_issue_labels / replace_issue_labels
|
|
22
|
+
- `color` (string) — 6-digit hex, with or without "#"
|
|
23
|
+
- `description` (string?)
|
|
24
|
+
|
|
25
|
+
## User
|
|
26
|
+
- `id` (number), `login` (string), `full_name` (string?), `avatar_url` (string),
|
|
27
|
+
`email` (string?)
|
|
28
|
+
|
|
29
|
+
## Milestone
|
|
30
|
+
- `id` (number) — used by get/update/delete_milestone; also the `milestone` value on
|
|
31
|
+
create/update_issue
|
|
32
|
+
- `title` (string), `description` (string?), `state` ("open" | "closed")
|
|
33
|
+
- `open_issues` (number), `closed_issues` (number) — counts for progress
|
|
34
|
+
- `due_on` (string?, ISO)
|
|
35
|
+
|
|
36
|
+
## Comment
|
|
37
|
+
- `id` (number) — used by update_comment / delete_comment. NOT the issue number.
|
|
38
|
+
- `body` (string) — Markdown
|
|
39
|
+
- `html_url` (string), `created_at`, `updated_at` (string ISO)
|
|
40
|
+
- `user` (User) — comment author
|
|
41
|
+
|
|
42
|
+
## Repository (embedded on issues)
|
|
43
|
+
- `id` (number), `full_name` (string "owner/repo"), `name` (string),
|
|
44
|
+
`owner` ({ login: string })
|
|
45
|
+
|
|
46
|
+
## Repo (from list_my_repos)
|
|
47
|
+
- `id`, `full_name`, `name`, `owner` (User), `description?`, `html_url`,
|
|
48
|
+
`default_branch?`, `created_at`, `updated_at`
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Label management guide
|
|
2
|
+
|
|
3
|
+
Labels are the most error-prone area because endpoints mix **names** and **ids**.
|
|
4
|
+
|
|
5
|
+
## The name-vs-id matrix (memorize)
|
|
6
|
+
|
|
7
|
+
| Operation | Identifier | Type |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `add_issue_labels` | name | `string[]` |
|
|
10
|
+
| `replace_issue_labels` | name | `string[]` |
|
|
11
|
+
| `remove_issue_label` | **id** | `number` |
|
|
12
|
+
| `create_issue` / `update_issue` `.labels` | **id** | `number[]` |
|
|
13
|
+
| `update_label` / `delete_label` | **id** | `number` |
|
|
14
|
+
|
|
15
|
+
**Workflow rule:** call `list_labels` first and keep the `id`↔`name` map for the
|
|
16
|
+
whole session. Translate before any call that needs the other form.
|
|
17
|
+
|
|
18
|
+
## Conventions
|
|
19
|
+
|
|
20
|
+
- **Color:** 6-digit hex, with or without `#`. Keep a consistent palette per repo.
|
|
21
|
+
- **Names are unique** per repo. Rename with `update_label` (by id); never recreate
|
|
22
|
+
to rename.
|
|
23
|
+
- **Scoped/exclusive labels** use `scope/name` (e.g. `priority/low`, `priority/high`).
|
|
24
|
+
Within a scope, labels are mutually exclusive — adding `priority/high` removes
|
|
25
|
+
`priority/low`. Use this intentionally for single-value dimensions (priority,
|
|
26
|
+
type, status); use plain labels for multi-value dimensions (topic, team).
|
|
27
|
+
|
|
28
|
+
## Safe operations
|
|
29
|
+
|
|
30
|
+
- **Add one label without disturbing others** → `add_issue_labels(["name"])`
|
|
31
|
+
(additive, by name).
|
|
32
|
+
- **Remove one label** → `remove_issue_label(id)` (by id; get it from the issue's
|
|
33
|
+
`labels`).
|
|
34
|
+
- **Set an exact known set** → `replace_issue_labels(["a","b"])` — but read the
|
|
35
|
+
current labels first and confirm, since it removes everything not listed.
|
|
36
|
+
- **Bulk cleanup** → `delete_label(id)` removes the label from EVERY issue; only
|
|
37
|
+
after user confirmation.
|
|
38
|
+
|
|
39
|
+
## Anti-patterns
|
|
40
|
+
|
|
41
|
+
- Passing a label **name** to `remove_issue_label` (needs id) — hits the wrong id
|
|
42
|
+
or 404s.
|
|
43
|
+
- Passing a label **id** to `add_issue_labels` (needs name) — 404s.
|
|
44
|
+
- Using `update_issue({ labels })` to "add" a label — it REPLACES the whole set.
|
|
45
|
+
Use `add_issue_labels` instead.
|
|
46
|
+
- Deleting a label to "remove it from one issue" — that removes it from all issues.
|
|
47
|
+
Use `remove_issue_label` for one issue.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Tool cookbook — task → tool recipes
|
|
2
|
+
|
|
3
|
+
Quick recipes for common goals. Always resolve owner/repo first (explicit args,
|
|
4
|
+
`GITEA_DEFAULT_OWNER`/`REPO`, or `resolve_repo`).
|
|
5
|
+
|
|
6
|
+
## Discover where to work
|
|
7
|
+
- One local repo → `resolve_repo({})` once, reuse `{owner, repo}`.
|
|
8
|
+
- Across all repos → `list_my_repos({ page: 1, limit: 20 })`, page as needed.
|
|
9
|
+
|
|
10
|
+
## Read / report
|
|
11
|
+
- One issue's full picture → `get_issue` then `list_comments` (mind: list_comments
|
|
12
|
+
is one default page; long threads may be truncated).
|
|
13
|
+
- All open issues in a repo → `list_issues({ state: "open", page: 1, limit: 50 })`,
|
|
14
|
+
page until a page returns < 50.
|
|
15
|
+
- Issues across repos by keyword / duplicate check → `search_issues({ query, type:
|
|
16
|
+
"issues" })`.
|
|
17
|
+
- Milestone progress → `list_milestones({ state: "all" })` (default omits closed!).
|
|
18
|
+
|
|
19
|
+
## Create
|
|
20
|
+
- New issue with labels → `list_labels` (to get ids) → `create_issue({ title, body,
|
|
21
|
+
labels: [ids] })`. Or create then `add_issue_labels([names])`.
|
|
22
|
+
|
|
23
|
+
## Edit (non-destructive)
|
|
24
|
+
- Change title/body/assignee → `update_issue` (PATCH; only given fields change).
|
|
25
|
+
- Add ONE label → `add_issue_labels(["name"])` (additive, by name).
|
|
26
|
+
- Remove ONE label → `remove_issue_label(id)` (by id).
|
|
27
|
+
|
|
28
|
+
## Destructive (confirm first)
|
|
29
|
+
- Close an issue → `update_issue({ state: "closed" })` (preferred over delete).
|
|
30
|
+
- Delete an issue → `delete_issue` (irreversible).
|
|
31
|
+
- Replace all labels → `replace_issue_labels(["a","b"])` (overwrites whole set).
|
|
32
|
+
- Clear all labels → `clear_issue_labels`.
|
|
33
|
+
- Delete a label → `delete_label(id)` (removes from EVERY issue).
|
|
34
|
+
- Delete a milestone → prefer `update_milestone({ state: "closed" })`; `delete_milestone`
|
|
35
|
+
detaches its issues (they keep existing, milestone becomes null).
|
|
36
|
+
|
|
37
|
+
## Pagination pattern (all list tools)
|
|
38
|
+
```
|
|
39
|
+
page = 1
|
|
40
|
+
loop:
|
|
41
|
+
res = list_X({ ..., page, limit })
|
|
42
|
+
process(res)
|
|
43
|
+
if res.length < limit: break
|
|
44
|
+
page += 1
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Error triage
|
|
48
|
+
- 401/403 → token scope/expiry. Ask user; don't loop.
|
|
49
|
+
- 404 → wrong owner/repo or no permission.
|
|
50
|
+
- 409 → conflict (e.g. duplicate label name).
|
|
51
|
+
- 422 → validation (bad color, malformed date).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gitea-create-issue
|
|
3
|
+
description: Invoke to CREATE / FILE / OPEN a new Gitea issue. The flow checks for an existing duplicate first, then creates. Do NOT invoke to edit/close (gitea-update-issue), delete (no skill — destructive), read/find (gitea-find-issues), or attach labels (gitea-label-issue).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# gitea-create-issue
|
|
7
|
+
|
|
8
|
+
Open one new issue after ruling out a duplicate. Tools: `search_issues`, `create_issue`.
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
- Resolve `owner`+`repo`: pass explicitly, else `GITEA_DEFAULT_OWNER`/`GITEA_DEFAULT_REPO`, else `resolve_repo` (gitea-resolve-repo). Never guess — wrong values create the issue in the wrong repo.
|
|
12
|
+
|
|
13
|
+
## Flow
|
|
14
|
+
1. DUPLICATE CHECK (always first): `search_issues({ query: "<title keywords>", type: "issues" })`. If a match exists, comment on it (gitea-summarize-issue, then `create_comment`) instead of creating a duplicate. Do NOT skip this — creation does not de-duplicate.
|
|
15
|
+
2. CREATE: `create_issue({ title, body?, assignees? })`.
|
|
16
|
+
|
|
17
|
+
## create_issue
|
|
18
|
+
- RULES: `title` required; `body` is Markdown. Use `assignees` (array); do NOT also set the deprecated single `assignee`.
|
|
19
|
+
- Do NOT pass `labels` or `milestone` here: `labels` expect IDs (error-prone) and milestone needs an id. Create WITHOUT them, then attach labels by name via `add_issue_labels` (gitea-label-issue) and set the milestone via `update_issue` (gitea-update-issue).
|
|
20
|
+
- CHECK FIRST: confirm no duplicate (step 1) and that `owner`/`repo` are correct before writing.
|
|
21
|
+
- CHECK AFTER: the returned object's `number` is the new issue index.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gitea-find-issues
|
|
3
|
+
description: Invoke to DISCOVER or READ Gitea issues — listing one repo's issues, cross-repo keyword search, reading a single issue, or triage. Do NOT invoke to create/edit/close/delete (gitea-create-issue / gitea-update-issue), to manage labels (gitea-label-issue), or to read the comment discussion (gitea-summarize-issue).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# gitea-find-issues
|
|
7
|
+
|
|
8
|
+
Read-only issue discovery. Tools: `list_issues`, `search_issues`, `get_issue`.
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
- Resolve `owner`+`repo` for `list_issues`/`get_issue`: pass explicitly, else `GITEA_DEFAULT_OWNER`/`GITEA_DEFAULT_REPO`, else `resolve_repo` (gitea-resolve-repo). Never guess — wrong values 404 or silently target the wrong repo.
|
|
12
|
+
- Paginate 1-based, `limit` ≤ 100. A page is final ONLY when it returns fewer than `limit` items. Always set `limit` (default page size is server-controlled).
|
|
13
|
+
|
|
14
|
+
## Choose the tool
|
|
15
|
+
- ONE repo's issues, filtered by state/labels → `list_issues`.
|
|
16
|
+
- ACROSS repos by keyword, or duplicate check → `search_issues` (global; no owner/repo).
|
|
17
|
+
- ONE issue's full detail → `get_issue`.
|
|
18
|
+
|
|
19
|
+
## list_issues
|
|
20
|
+
- RULES: `labels` = comma-separated NAMES, AND-matched; a mistyped or non-existent name returns EMPTY with no error. Results include pull requests (no `type` filter here); identify a PR by a non-null `pull_request` field in the JSON.
|
|
21
|
+
- CHECK FIRST: confirm label names via `list_labels` (gitea-manage-labels) before filtering.
|
|
22
|
+
- CHECK AFTER: if `length === limit`, fetch the next page before treating the list as complete.
|
|
23
|
+
|
|
24
|
+
## search_issues
|
|
25
|
+
- RULES: GLOBAL — no owner/repo; each result carries its own `repository`. Returns issues AND PRs by default; set `type: "issues"` to exclude PRs (`type: "pulls"` for PRs only). `labels` = names. `query` matches title + body.
|
|
26
|
+
- CHECK FIRST: set `type` deliberately.
|
|
27
|
+
- CHECK AFTER: page fully if completeness matters.
|
|
28
|
+
|
|
29
|
+
## get_issue
|
|
30
|
+
- RULES: pass `index` = the issue `number` (URL #42 → 42), never the internal `id`.
|
|
31
|
+
- NOTE: the `comments` field is a COUNT only — to read the discussion use `list_comments` (gitea-summarize-issue).
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gitea-label-issue
|
|
3
|
+
description: Invoke to ATTACH labels to ONE Gitea issue — add, replace, remove one, or clear all. Covers the label name-vs-id pitfall. Do NOT invoke to create/edit/delete the repo's label definitions (gitea-manage-labels) or to create/edit the issue (gitea-create-issue / gitea-update-issue).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# gitea-label-issue
|
|
7
|
+
|
|
8
|
+
Manage the labels ON one issue. Tools: `list_labels`, `add_issue_labels`, `replace_issue_labels`, `remove_issue_label`, `clear_issue_labels`.
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
- Resolve `owner`+`repo`: pass explicitly, else `GITEA_DEFAULT_OWNER`/`GITEA_DEFAULT_REPO`, else `resolve_repo` (gitea-resolve-repo).
|
|
12
|
+
|
|
13
|
+
## CRITICAL — name vs id (the endpoints are inconsistent)
|
|
14
|
+
- BY NAME (string[]): `add_issue_labels`, `replace_issue_labels`.
|
|
15
|
+
- BY ID (number): `remove_issue_label` (param `id`).
|
|
16
|
+
Resolve names→ids via `list_labels` when you need an id.
|
|
17
|
+
|
|
18
|
+
## Choose the tool
|
|
19
|
+
- ADD labels (keep existing) → `add_issue_labels({ index, labels: [names] })`.
|
|
20
|
+
- SET the exact label set (overwrites all) → `replace_issue_labels({ index, labels: [names] })`.
|
|
21
|
+
- REMOVE ONE label → `remove_issue_label({ index, id })` (id, not name).
|
|
22
|
+
- REMOVE ALL → `clear_issue_labels({ index })`.
|
|
23
|
+
|
|
24
|
+
## Rules
|
|
25
|
+
- `index` = issue `number` (URL #N → N).
|
|
26
|
+
- Adding a non-existent NAME → 404. Confirm names via `list_labels` first.
|
|
27
|
+
- Scoped/exclusive labels (`scope/name`) are mutually exclusive within a scope — adding one may replace another in the same scope.
|
|
28
|
+
- CHECK FIRST: for `replace`, `get_issue` (gitea-find-issues) to see current labels so you don't drop one unintentionally; for `remove`, `list_labels` to resolve the id.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gitea-manage-labels
|
|
3
|
+
description: Invoke to CREATE or EDIT a Gitea label in the repo's taxonomy (the set of available label definitions). Do NOT invoke to attach labels to an issue (gitea-label-issue) or to delete a label (destructive — no skill).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# gitea-manage-labels
|
|
7
|
+
|
|
8
|
+
Create or edit the repo's label definitions. Tools: `list_labels`, `create_label`, `update_label`.
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
- Resolve `owner`+`repo`: pass explicitly, else `GITEA_DEFAULT_OWNER`/`GITEA_DEFAULT_REPO`, else `resolve_repo` (gitea-resolve-repo).
|
|
12
|
+
|
|
13
|
+
## list_labels
|
|
14
|
+
- Enumerate available labels (name, id, color, description). Use to resolve names→ids and to check name uniqueness before create. Paginate 1-based, `limit` ≤ 100.
|
|
15
|
+
|
|
16
|
+
## create_label
|
|
17
|
+
- RULES: `name` required and UNIQUE per repo (duplicate → conflict). `color` = 6-digit hex, with or without leading `#` (e.g. `#ff0000` or `ff0000`). `description` optional.
|
|
18
|
+
- CHECK FIRST: `list_labels` to avoid a name collision.
|
|
19
|
+
|
|
20
|
+
## update_label
|
|
21
|
+
- RULES: identifies the label by `id` (number). PATCH — only `name` / `color` / `description` you pass change; omit = unchanged. `color` stays 6-digit hex.
|
|
22
|
+
- NOTE: renaming or recoloring a label updates it on EVERY issue that currently has it.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gitea-plan-milestones
|
|
3
|
+
description: Invoke to CREATE, EDIT, or CLOSE a Gitea milestone, or to list/inspect milestones for planning. Do NOT invoke to delete a milestone (destructive — no skill) or to set an issue's milestone (that is gitea-update-issue).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# gitea-plan-milestones
|
|
7
|
+
|
|
8
|
+
Create / edit / close milestones. Tools: `list_milestones`, `get_milestone`, `create_milestone`, `update_milestone`.
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
- Resolve `owner`+`repo`: pass explicitly, else `GITEA_DEFAULT_OWNER`/`GITEA_DEFAULT_REPO`, else `resolve_repo` (gitea-resolve-repo).
|
|
12
|
+
|
|
13
|
+
## list_milestones — DEFAULT-OPEN TRAP
|
|
14
|
+
- Default returns ONLY open milestones. Pass `state: "all"` or `state: "closed"` to see the rest, or you will silently miss closed/completed milestones. Paginate 1-based, `limit` ≤ 100.
|
|
15
|
+
|
|
16
|
+
## create_milestone
|
|
17
|
+
- RULES: `title` required; `description` optional; `due_on` = ISO 8601 (e.g. `2025-12-31T23:59:59Z`). Returns the new milestone including its `id`.
|
|
18
|
+
|
|
19
|
+
## update_milestone
|
|
20
|
+
- RULES: identifies by `id` (number). PATCH — only `title` / `description` / `due_on` / `state` you pass change; omit = unchanged. State is `open` | `closed`.
|
|
21
|
+
- NOTE: CLOSING a milestone does NOT close its issues — they stay open. Close issues separately (gitea-update-issue) before or after.
|
|
22
|
+
|
|
23
|
+
## get_milestone
|
|
24
|
+
- Read one milestone by `id`: includes `open_issues` / `closed_issues` counts for progress (progress = closed / (open + closed)).
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gitea-resolve-repo
|
|
3
|
+
description: Invoke to RESOLVE which owner/repo the other gitea tools should target (auto-detect from a git remote) or to LIST repositories the token can access. Run FIRST when the target repository is unknown. Do NOT invoke for issue/comment/label/milestone work once owner/repo are known.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# gitea-resolve-repo
|
|
7
|
+
|
|
8
|
+
Discover the target repository. Tools: `resolve_repo`, `list_my_repos`.
|
|
9
|
+
|
|
10
|
+
## resolve_repo
|
|
11
|
+
- Reads the `origin` remote of the git repo at `path` (default: current directory) and parses owner/repo from SSH (`git@gitea.example:owner/repo.git`) or HTTPS (`https://gitea.example/owner/repo.git`) URLs.
|
|
12
|
+
- Throws if there is no `origin` remote or the URL is unparseable. Do NOT guess owner/repo from context — call this, or ask the user.
|
|
13
|
+
|
|
14
|
+
## list_my_repos
|
|
15
|
+
- Lists repositories the token can access. GLOBAL. Returns large objects — paginate (`limit` ≤ 100) and read only the fields you need (name, full_name, owner.login).
|
|
16
|
+
|
|
17
|
+
## Resolution order (every repo-scoped tool)
|
|
18
|
+
explicit `owner`/`repo` args → `GITEA_DEFAULT_OWNER`/`GITEA_DEFAULT_REPO` → `resolve_repo`. If none resolve, the tool throws `owner and repo are required`. Wrong values 404 or silently target the wrong repo — never guess.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gitea-summarize-issue
|
|
3
|
+
description: Invoke to READ and SUMMARIZE an issue's discussion — the issue body plus its comment thread. Do NOT invoke to create/edit an issue (gitea-create-issue / gitea-update-issue), post or edit a comment (no skill — single tool), or manage labels/milestones.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# gitea-summarize-issue
|
|
7
|
+
|
|
8
|
+
Read an issue and its discussion, then synthesize. Tools: `get_issue`, `list_comments`.
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
- Resolve `owner`+`repo`: pass explicitly, else `GITEA_DEFAULT_OWNER`/`GITEA_DEFAULT_REPO`, else `resolve_repo` (gitea-resolve-repo).
|
|
12
|
+
|
|
13
|
+
## Flow
|
|
14
|
+
1. `get_issue({ index })` — `index` = issue `number` (URL #N → N), never the internal `id`. The `comments` field is only a COUNT.
|
|
15
|
+
2. `list_comments({ index })` — returns the comment thread, oldest-first.
|
|
16
|
+
|
|
17
|
+
## list_comments — TRUNCATION TRAP
|
|
18
|
+
- This tool passes NO page/limit and returns at most the server's default page size. For long threads the list is SILENTLY TRUNCATED. If the `comments` count (from `get_issue`) exceeds the number returned, warn that part of the thread is missing and do NOT claim a complete summary.
|
|
19
|
+
|
|
20
|
+
## Synthesis
|
|
21
|
+
- Report the issue state (open/closed), the body's ask, then the thread's consensus / decision / open questions. Attribute by author. Flag unresolved disagreement explicitly — do not paper over it.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gitea-update-issue
|
|
3
|
+
description: Invoke to EDIT fields, CLOSE, REOPEN, or CLEAR assignee/milestone on ONE existing Gitea issue. Do NOT invoke to create (gitea-create-issue), delete (destructive — no skill), find/read (gitea-find-issues), or change a single label (gitea-label-issue).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# gitea-update-issue
|
|
7
|
+
|
|
8
|
+
Edit one issue's fields or change its state. Tools: `get_issue`, `update_issue`.
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
- Resolve `owner`+`repo`: pass explicitly, else `GITEA_DEFAULT_OWNER`/`GITEA_DEFAULT_REPO`, else `resolve_repo` (gitea-resolve-repo).
|
|
12
|
+
- There is NO optimistic locking — always read current state first.
|
|
13
|
+
|
|
14
|
+
## Flow
|
|
15
|
+
1. READ CURRENT: `get_issue({ index })` — `index` = issue `number` (URL #N → N), never the internal `id`.
|
|
16
|
+
2. APPLY: `update_issue({ index, ...changedFields })`.
|
|
17
|
+
|
|
18
|
+
## update_issue
|
|
19
|
+
- RULES: PATCH — only fields you pass change; omit = unchanged. To CLEAR a field pass its EMPTY form, do NOT omit it: `milestone: 0` clears the milestone, `assignees: []` clears assignees. Use `assignees` (array), not the deprecated `assignee`. State values are `open` | `closed`.
|
|
20
|
+
- NOT FOR single-label changes: `labels` here are IDs and REPLACE the entire set — to add/remove one label use `add_issue_labels` / `remove_issue_label` (gitea-label-issue).
|
|
21
|
+
- CHECK FIRST: `get_issue` for current values if state may have changed since your last read; state the intended change to the user before writing.
|
package/dist/cli.js
CHANGED
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { runServer } from "./server.js";
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
const argv = process.argv.slice(2);
|
|
4
|
+
if (argv[0] === "skills") {
|
|
5
|
+
// `gitea-mcp skills ...` manages the bundled opencode skill. It needs no Gitea
|
|
6
|
+
// credentials, so it is dispatched before the env-var guard below.
|
|
7
|
+
const { runSkillsCommand } = await import("./skills.js");
|
|
8
|
+
runSkillsCommand(argv.slice(1)).catch((err) => {
|
|
9
|
+
console.error("Fatal error:", err);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
const baseUrl = process.env.GITEA_BASE_URL;
|
|
15
|
+
const token = process.env.GITEA_TOKEN;
|
|
16
|
+
const defaultOwner = process.env.GITEA_DEFAULT_OWNER;
|
|
17
|
+
const defaultRepo = process.env.GITEA_DEFAULT_REPO;
|
|
18
|
+
if (!baseUrl || !token) {
|
|
19
|
+
console.error("GITEA_BASE_URL and GITEA_TOKEN environment variables are required");
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
runServer(baseUrl, token, defaultOwner, defaultRepo).catch((err) => {
|
|
23
|
+
console.error("Fatal error:", err);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
});
|
|
10
26
|
}
|
|
11
|
-
runServer(baseUrl, token, defaultOwner, defaultRepo).catch((err) => {
|
|
12
|
-
console.error("Fatal error:", err);
|
|
13
|
-
process.exit(1);
|
|
14
|
-
});
|
|
15
27
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;IACzB,+EAA+E;IAC/E,mEAAmE;IACnE,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;IACzD,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QAC5C,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;QACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC;KAAM,CAAC;IACN,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAC3C,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IACtC,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;IACrD,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IAEnD,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CAAC,mEAAmE,CAAC,CAAC;QACnF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,SAAS,CAAC,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACjE,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;QACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
|