mirror of
				https://gitea.com/gitea/gitea-mcp.git
				synced 2025-11-03 20:01:50 +00:00 
			
		
		
		
	Compare commits
	
		
			17 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					058d4cd07f | ||
| 
						 | 
					ba64780c2f | ||
| 
						 | 
					6930c8ee30 | ||
| 
						 | 
					f08720a625 | ||
| 
						 | 
					f6b45fdf6e | ||
| 
						 | 
					98f908d5a1 | ||
| 
						 | 
					e4aa29b0f9 | ||
| 
						 | 
					32eaf86426 | ||
| 
						 | 
					8c028ec48b | ||
| 
						 | 
					88471b5de0 | ||
| 
						 | 
					e9840cf6c0 | ||
| 
						 | 
					95ab3a4b73 | ||
| 
						 | 
					de311344cd | ||
| 
						 | 
					d7addd56c4 | ||
| 
						 | 
					dc3e120e97 | ||
| 
						 | 
					f33b04a3df | ||
| 
						 | 
					ba07925969 | 
@@ -66,6 +66,8 @@ jobs:
 | 
				
			|||||||
            linux/amd64
 | 
					            linux/amd64
 | 
				
			||||||
            linux/arm64
 | 
					            linux/arm64
 | 
				
			||||||
          push: true
 | 
					          push: true
 | 
				
			||||||
 | 
					          build-args: |
 | 
				
			||||||
 | 
					            VERSION=${{ steps.meta.outputs.REPO_VERSION }}
 | 
				
			||||||
          tags: |
 | 
					          tags: |
 | 
				
			||||||
            ${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}-server:${{ steps.meta.outputs.REPO_VERSION }}
 | 
					            ${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}-server:${{ steps.meta.outputs.REPO_VERSION }}
 | 
				
			||||||
            ${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}-server:${{ env.DOCKER_LATEST }}
 | 
					            ${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}-server:${{ env.DOCKER_LATEST }}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										71
									
								
								CLAUDE.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								CLAUDE.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
				
			|||||||
 | 
					# CLAUDE.md
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Development Commands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Build**: `make build` - Build the gitea-mcp binary
 | 
				
			||||||
 | 
					**Install**: `make install` - Build and install to GOPATH/bin
 | 
				
			||||||
 | 
					**Clean**: `make clean` - Remove build artifacts
 | 
				
			||||||
 | 
					**Test**: `go test ./...` - Run all tests
 | 
				
			||||||
 | 
					**Hot reload**: `make dev` - Start development server with hot reload (requires air)
 | 
				
			||||||
 | 
					**Dependencies**: `make vendor` - Tidy and verify module dependencies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Architecture Overview
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This is a **Gitea MCP (Model Context Protocol) Server** written in Go that provides MCP tools for interacting with Gitea repositories, issues, pull requests, users, and more.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Core Components**:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `main.go` + `cmd/cmd.go`: CLI entry point and flag parsing
 | 
				
			||||||
 | 
					- `operation/operation.go`: Main server setup and tool registration
 | 
				
			||||||
 | 
					- `pkg/tool/tool.go`: Tool registry with read/write categorization
 | 
				
			||||||
 | 
					- `operation/*/`: Individual tool modules (user, repo, issue, pull, search, wiki, etc.)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Transport Modes**:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- **stdio** (default): Standard input/output for MCP clients
 | 
				
			||||||
 | 
					- **http**: HTTP server mode on configurable port (default 8080)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Authentication**:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Global token via `--token` flag or `GITEA_ACCESS_TOKEN` env var
 | 
				
			||||||
 | 
					- HTTP mode supports per-request Bearer token override in Authorization header
 | 
				
			||||||
 | 
					- Token precedence: HTTP Authorization header > CLI flag > environment variable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Tool Organization**:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Tools are categorized as read-only or write operations
 | 
				
			||||||
 | 
					- `--read-only` flag exposes only read tools
 | 
				
			||||||
 | 
					- Tool modules register via `Tool.RegisterRead()` and `Tool.RegisterWrite()`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Key Configuration**:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Default Gitea host: `https://gitea.com` (override with `--host` or `GITEA_HOST`)
 | 
				
			||||||
 | 
					- Environment variables can override CLI flags: `MCP_MODE`, `GITEA_READONLY`, `GITEA_DEBUG`, `GITEA_INSECURE`
 | 
				
			||||||
 | 
					- Logs are written to `~/.gitea-mcp/gitea-mcp.log` with rotation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Available Tools
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The server provides 40+ MCP tools covering:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- **User**: get_my_user_info, get_user_orgs, search_users
 | 
				
			||||||
 | 
					- **Repository**: create_repo, fork_repo, list_my_repos, search_repos
 | 
				
			||||||
 | 
					- **Branches/Tags**: create_branch, delete_branch, list_branches, create_tag, list_tags
 | 
				
			||||||
 | 
					- **Files**: get_file_content, create_file, update_file, delete_file, get_dir_content
 | 
				
			||||||
 | 
					- **Issues**: create_issue, list_repo_issues, create_issue_comment, edit_issue
 | 
				
			||||||
 | 
					- **Pull Requests**: create_pull_request, list_repo_pull_requests, get_pull_request_by_index
 | 
				
			||||||
 | 
					- **Releases**: create_release, list_releases, get_latest_release
 | 
				
			||||||
 | 
					- **Wiki**: create_wiki_page, update_wiki_page, list_wiki_pages
 | 
				
			||||||
 | 
					- **Search**: search_repos, search_users, search_org_teams
 | 
				
			||||||
 | 
					- **Version**: get_gitea_mcp_server_version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Common Development Patterns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Testing**: Use `go test ./operation -run TestFunctionName` for specific tests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Token Context**: HTTP requests use `pkg/context.TokenContextKey` for request-scoped token access
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Flag Access**: All packages access configuration via global variables in `pkg/flag/flag.go`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Graceful Shutdown**: HTTP mode implements graceful shutdown with 10-second timeout on SIGTERM/SIGINT
 | 
				
			||||||
							
								
								
									
										32
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								README.md
									
									
									
									
									
								
							@@ -133,25 +133,16 @@ To configure the MCP server for Gitea, add the following to your MCP configurati
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- **sse mode**
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```json
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  "mcpServers": {
 | 
					 | 
				
			||||||
    "gitea": {
 | 
					 | 
				
			||||||
      "url": "http://localhost:8080/sse"
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- **http mode**
 | 
					- **http mode**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```json
 | 
					```json
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "mcpServers": {
 | 
					  "mcpServers": {
 | 
				
			||||||
    "gitea": {
 | 
					    "gitea": {
 | 
				
			||||||
      "url": "http://localhost:8080/mcp"
 | 
					      "url": "http://localhost:8080/mcp",
 | 
				
			||||||
 | 
					      "headers": {
 | 
				
			||||||
 | 
					        "Authorization": "Bearer <your personal access token>"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -208,17 +199,28 @@ The Gitea MCP Server supports the following tools:
 | 
				
			|||||||
|  get_pull_request_by_index   | Pull Request |             Get a pull request by its index              |
 | 
					|  get_pull_request_by_index   | Pull Request |             Get a pull request by its index              |
 | 
				
			||||||
|   list_repo_pull_requests    | Pull Request |          List all pull requests in a repository          |
 | 
					|   list_repo_pull_requests    | Pull Request |          List all pull requests in a repository          |
 | 
				
			||||||
|     create_pull_request      | Pull Request |                Create a new pull request                 |
 | 
					|     create_pull_request      | Pull Request |                Create a new pull request                 |
 | 
				
			||||||
 | 
					| create_pull_request_reviewer | Pull Request |           Add reviewers to a pull request                |
 | 
				
			||||||
|         search_users         |     User     |                     Search for users                     |
 | 
					|         search_users         |     User     |                     Search for users                     |
 | 
				
			||||||
|       search_org_teams       | Organization |           Search for teams in an organization            |
 | 
					|       search_org_teams       | Organization |           Search for teams in an organization            |
 | 
				
			||||||
 | 
					|        list_org_labels       | Organization |            List labels defined at organization level     |
 | 
				
			||||||
 | 
					|        create_org_label      | Organization |                  Create a label in an organization       |
 | 
				
			||||||
 | 
					|         edit_org_label       | Organization |                  Edit a label in an organization         |
 | 
				
			||||||
 | 
					|       delete_org_label       | Organization |                 Delete a label in an organization        |
 | 
				
			||||||
|         search_repos         |  Repository  |                 Search for repositories                  |
 | 
					|         search_repos         |  Repository  |                 Search for repositories                  |
 | 
				
			||||||
| get_gitea_mcp_server_version |    Server    |         Get the version of the Gitea MCP Server          |
 | 
					| get_gitea_mcp_server_version |    Server    |         Get the version of the Gitea MCP Server          |
 | 
				
			||||||
 | 
					|       list_wiki_pages        |     Wiki     |          List all wiki pages in a repository            |
 | 
				
			||||||
 | 
					|        get_wiki_page         |     Wiki     |       Get a wiki page content and metadata              |
 | 
				
			||||||
 | 
					|      get_wiki_revisions      |     Wiki     |        Get revisions history of a wiki page             |
 | 
				
			||||||
 | 
					|      create_wiki_page        |     Wiki     |                 Create a new wiki page                   |
 | 
				
			||||||
 | 
					|      update_wiki_page        |     Wiki     |             Update an existing wiki page                 |
 | 
				
			||||||
 | 
					|      delete_wiki_page        |     Wiki     |                  Delete a wiki page                      |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 🐛 Debugging
 | 
					## 🐛 Debugging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
To enable debug mode, add the `-d` flag when running the Gitea MCP Server with sse mode:
 | 
					To enable debug mode, add the `-d` flag when running the Gitea MCP Server with http mode:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```sh
 | 
					```sh
 | 
				
			||||||
./gitea-mcp -t sse [--port 8080] --token <your personal access token> -d
 | 
					./gitea-mcp -t http [--port 8080] --token <your personal access token> -d
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 🛠 Troubleshooting
 | 
					## 🛠 Troubleshooting
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										166
									
								
								README.zh-cn.md
									
									
									
									
									
								
							
							
						
						
									
										166
									
								
								README.zh-cn.md
									
									
									
									
									
								
							@@ -14,9 +14,9 @@
 | 
				
			|||||||
  - [什么是 MCP?](#什么是-mcp)
 | 
					  - [什么是 MCP?](#什么是-mcp)
 | 
				
			||||||
  - [🚧 安装](#-安装)
 | 
					  - [🚧 安装](#-安装)
 | 
				
			||||||
    - [在 VS Code 中使用](#在-vs-code-中使用)
 | 
					    - [在 VS Code 中使用](#在-vs-code-中使用)
 | 
				
			||||||
    - [📥 下载官方 Gitea MCP 二进制版本](#-下载官方-gitea-mcp-二进制版本)
 | 
					    - [📥 下载官方二进制版本](#-下载官方二进制版本)
 | 
				
			||||||
    - [🔧 从源代码构建](#-从源代码构建)
 | 
					    - [🔧 从源码构建](#-从源码构建)
 | 
				
			||||||
    - [📁 添加到 PATH](#-添加到-path)
 | 
					    - [📁 加入 PATH](#-加入-path)
 | 
				
			||||||
  - [🚀 使用](#-使用)
 | 
					  - [🚀 使用](#-使用)
 | 
				
			||||||
  - [✅ 可用工具](#-可用工具)
 | 
					  - [✅ 可用工具](#-可用工具)
 | 
				
			||||||
  - [🐛 调试](#-调试)
 | 
					  - [🐛 调试](#-调试)
 | 
				
			||||||
@@ -24,23 +24,23 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## 什么是 Gitea?
 | 
					## 什么是 Gitea?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Gitea 是一个由社区管理的轻量级代码托管解决方案,使用 Go 语言编写。它以 MIT 许可证发布。Gitea 提供 Git 托管,包括仓库查看器、问题追踪、拉取请求等功能。
 | 
					Gitea 是一个由社区管理的轻量级代码托管解决方案,使用 Go 语言编写,采用 MIT 许可证。Gitea 提供 Git 托管,包括仓库浏览、问题追踪、拉取请求等功能。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 什么是 MCP?
 | 
					## 什么是 MCP?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Model Context Protocol (MCP) 是一种协议,允许通过聊天界面整合各种工具和系统。它能够无缝执行命令和管理仓库、用户和其他资源。
 | 
					Model Context Protocol (MCP) 是一种协议,允许通过聊天界面整合各种工具和系统。它能够无缝执行命令并管理仓库、用户及其他资源。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 🚧 安装
 | 
					## 🚧 安装
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### 在 VS Code 中使用
 | 
					### 在 VS Code 中使用
 | 
				
			||||||
 | 
					
 | 
				
			||||||
要快速安装,请使用本 README 顶部的单击安装按钮之一。
 | 
					要快速安装,请使用本 README 顶部的安装按钮。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
要手动安装,请将以下 JSON 块添加到 VS Code 的用户设置 (JSON) 文件中。您可以通过按 `Ctrl + Shift + P` 并输入 `Preferences: Open User Settings (JSON)` 来完成此操作。
 | 
					如需手动安装,请将以下 JSON 块添加到 VS Code 的用户设置 (JSON) 文件。可通过按 `Ctrl + Shift + P` 并输入 `Preferences: Open User Settings (JSON)`。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
或者,您可以将其添加到工作区中的 `.vscode/mcp.json` 文件中。这将允许您与他人共享配置。
 | 
					也可添加到工作区的 `.vscode/mcp.json` 文件,方便与他人共享配置。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
> 请注意,`.vscode/mcp.json` 文件中不需要 `mcp` 键。
 | 
					> `.vscode/mcp.json` 文件不需要 `mcp` 键。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```json
 | 
					```json
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -73,22 +73,22 @@ Model Context Protocol (MCP) 是一种协议,允许通过聊天界面整合各
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### 📥 下载官方 Gitea MCP 二进制版本
 | 
					### 📥 下载官方二进制版本
 | 
				
			||||||
 | 
					
 | 
				
			||||||
您可以从[官方 Gitea MCP 二进制版本](https://gitea.com/gitea/gitea-mcp/releases)下载官方版本。
 | 
					可在 [官方 Gitea MCP 二进制版本](https://gitea.com/gitea/gitea-mcp/releases) 下载。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### 🔧 从源代码构建
 | 
					### 🔧 从源码构建
 | 
				
			||||||
 | 
					
 | 
				
			||||||
您可以使用 Git 克隆仓库来下载源代码:
 | 
					可用 Git 下载源码:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```bash
 | 
					```bash
 | 
				
			||||||
git clone https://gitea.com/gitea/gitea-mcp.git
 | 
					git clone https://gitea.com/gitea/gitea-mcp.git
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
在构建之前,请确保您已安装以下内容:
 | 
					构建前请先安装:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- make
 | 
					- make
 | 
				
			||||||
- Golang (建议使用 Go 1.24 或更高版本)
 | 
					- Golang(建议 Go 1.24 及以上)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
然后运行:
 | 
					然后运行:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -96,9 +96,9 @@ git clone https://gitea.com/gitea/gitea-mcp.git
 | 
				
			|||||||
make install
 | 
					make install
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### 📁 添加到 PATH
 | 
					### 📁 加入 PATH
 | 
				
			||||||
 | 
					
 | 
				
			||||||
构建后,将二进制文件 gitea-mcp 复制到系统 PATH 中包含的目录。例如:
 | 
					安装后,将 gitea-mcp 可执行文件复制到系统 PATH 目录,例如:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```bash
 | 
					```bash
 | 
				
			||||||
cp gitea-mcp /usr/local/bin/
 | 
					cp gitea-mcp /usr/local/bin/
 | 
				
			||||||
@@ -106,8 +106,8 @@ cp gitea-mcp /usr/local/bin/
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## 🚀 使用
 | 
					## 🚀 使用
 | 
				
			||||||
 | 
					
 | 
				
			||||||
此示例适用于 Cursor,您也可以在 VSCode 中使用插件。
 | 
					此示例适用于 Cursor,也可在 VSCode 使用插件。  
 | 
				
			||||||
要配置 Gitea 的 MCP 服务器,请将以下内容添加到您的 MCP 配置文件中:
 | 
					要配置 Gitea MCP 服务器,请将以下内容添加到 MCP 配置文件:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- **stdio 模式**
 | 
					- **stdio 模式**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -133,25 +133,16 @@ cp gitea-mcp /usr/local/bin/
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- **sse 模式**
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```json
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  "mcpServers": {
 | 
					 | 
				
			||||||
    "gitea": {
 | 
					 | 
				
			||||||
      "url": "http://localhost:8080/sse"
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- **http 模式**
 | 
					- **http 模式**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```json
 | 
					```json
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "mcpServers": {
 | 
					  "mcpServers": {
 | 
				
			||||||
    "gitea": {
 | 
					    "gitea": {
 | 
				
			||||||
      "url": "http://localhost:8080/mcp"
 | 
					      "url": "http://localhost:8080/mcp",
 | 
				
			||||||
 | 
					      "headers": {
 | 
				
			||||||
 | 
					        "Authorization": "Bearer <your personal access token>"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -160,10 +151,10 @@ cp gitea-mcp /usr/local/bin/
 | 
				
			|||||||
**默认日志路径**: `$HOME/.gitea-mcp/gitea-mcp.log`
 | 
					**默认日志路径**: `$HOME/.gitea-mcp/gitea-mcp.log`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
> [!注意]
 | 
					> [!注意]
 | 
				
			||||||
> 您可以通过命令行参数或环境变量提供您的 Gitea 主机和访问令牌。
 | 
					> 可通过命令行参数或环境变量提供 Gitea 主机和访问令牌。  
 | 
				
			||||||
> 命令行参数具有最高优先级
 | 
					> 命令行参数优先。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
一切设置完成后,请尝试在您的 MCP 兼容聊天框中输入以下内容:
 | 
					一切设置完成后,可在 MCP 聊天框输入:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```text
 | 
					```text
 | 
				
			||||||
列出我所有的仓库
 | 
					列出我所有的仓库
 | 
				
			||||||
@@ -173,61 +164,72 @@ cp gitea-mcp /usr/local/bin/
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Gitea MCP 服务器支持以下工具:
 | 
					Gitea MCP 服务器支持以下工具:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
|             工具             |   范围   |             描述             |
 | 
					|             工具             |   范围   |            描述            |
 | 
				
			||||||
| :--------------------------: | :------: | :--------------------------: |
 | 
					| :--------------------------: | :------: | :------------------------: |
 | 
				
			||||||
|       get_my_user_info       |   用户   |     获取已认证用户的信息     |
 | 
					|       get_my_user_info       |   用户   |     获取已认证用户信息     |
 | 
				
			||||||
|        get_user_orgs         |   用户   |   获取已认证用户关联的组织   |
 | 
					|        get_user_orgs         |   用户   |   获取已认证用户关联组织   |
 | 
				
			||||||
|         create_repo          |   仓库   |        创建一个新仓库        |
 | 
					|         create_repo          |   仓库   |         创建新仓库         |
 | 
				
			||||||
|          fork_repo           |   仓库   |         复刻一个仓库         |
 | 
					|          fork_repo           |   仓库   |          复刻仓库          |
 | 
				
			||||||
|        list_my_repos         |   仓库   | 列出已认证用户拥有的所有仓库 |
 | 
					|        list_my_repos         |   仓库   |      列出用户所有仓库      |
 | 
				
			||||||
|        create_branch         |   分支   |        创建一个新分支        |
 | 
					|        create_branch         |   分支   |         创建新分支         |
 | 
				
			||||||
|        delete_branch         |   分支   |         删除一个分支         |
 | 
					|        delete_branch         |   分支   |          删除分支          |
 | 
				
			||||||
|        list_branches         |   分支   |     列出仓库中的所有分支     |
 | 
					|        list_branches         |   分支   |        列出所有分支        |
 | 
				
			||||||
|        create_release        | 版本发布 |      创建一个新版本发布      |
 | 
					|        create_release        | 版本发布 |       创建新版本发布       |
 | 
				
			||||||
|        delete_release        | 版本发布 |       删除一个版本发布       |
 | 
					|        delete_release        | 版本发布 |        删除版本发布        |
 | 
				
			||||||
|         get_release          | 版本发布 |       获取一个版本发布       |
 | 
					|         get_release          | 版本发布 |        获取版本发布        |
 | 
				
			||||||
|      get_latest_release      | 版本发布 |      获取最新的版本发布      |
 | 
					|      get_latest_release      | 版本发布 |      获取最新版本发布      |
 | 
				
			||||||
|        list_releases         | 版本发布 |       列出所有版本发布       |
 | 
					|        list_releases         | 版本发布 |      列出所有版本发布      |
 | 
				
			||||||
|          create_tag          |   标签   |        创建一个新标签        |
 | 
					|          create_tag          |   标签   |         创建新标签         |
 | 
				
			||||||
|          delete_tag          |   标签   |         删除一个标签         |
 | 
					|          delete_tag          |   标签   |          删除标签          |
 | 
				
			||||||
|           get_tag            |   标签   |         获取一个标签         |
 | 
					|           get_tag            |   标签   |          获取标签          |
 | 
				
			||||||
|          list_tags           |   标签   |         列出所有标签         |
 | 
					|          list_tags           |   标签   |        列出所有标签        |
 | 
				
			||||||
|      list_repo_commits       |   提交   |     列出仓库中的所有提交     |
 | 
					|      list_repo_commits       |   提交   |        列出所有提交        |
 | 
				
			||||||
|       get_file_content       |   文件   |    获取文件的内容和元数据    |
 | 
					|       get_file_content       |   文件   |    获取文件内容和元数据    |
 | 
				
			||||||
|        get_dir_content       |   文件   |      获取目录的内容列表      |
 | 
					|       get_dir_content        |   文件   |      获取目录内容列表      |
 | 
				
			||||||
|         create_file          |   文件   |        创建一个新文件        |
 | 
					|         create_file          |   文件   |         创建新文件         |
 | 
				
			||||||
|         update_file          |   文件   |         更新现有文件         |
 | 
					|         update_file          |   文件   |        更新现有文件        |
 | 
				
			||||||
|         delete_file          |   文件   |         删除一个文件         |
 | 
					|         delete_file          |   文件   |          删除文件          |
 | 
				
			||||||
|      get_issue_by_index      |   问题   |       根据索引获取问题       |
 | 
					|      get_issue_by_index      |   问题   |       按索引获取问题       |
 | 
				
			||||||
|       list_repo_issues       |   问题   |     列出仓库中的所有问题     |
 | 
					|       list_repo_issues       |   问题   |        列出所有问题        |
 | 
				
			||||||
|         create_issue         |   问题   |        创建一个新问题        |
 | 
					|         create_issue         |   问题   |         创建新问题         |
 | 
				
			||||||
|     create_issue_comment     |   问题   |       在问题上创建评论       |
 | 
					|     create_issue_comment     |   问题   |      在问题上创建评论      |
 | 
				
			||||||
|          edit_issue          |   问题   |         编辑一个问题         |
 | 
					|          edit_issue          |   问题   |          编辑问题          |
 | 
				
			||||||
|      edit_issue_comment      |   问题   |      在问题上编辑评论         |
 | 
					|      edit_issue_comment      |   问题   |        编辑问题评论        |
 | 
				
			||||||
| get_issue_comments_by_index  |   问题   |     根据索引获取问题的评论     |
 | 
					| get_issue_comments_by_index  |   问题   |     按索引获取问题评论     |
 | 
				
			||||||
|  get_pull_request_by_index   | 拉取请求 |     根据索引获取拉取请求     |
 | 
					|  get_pull_request_by_index   | 拉取请求 |     按索引获取拉取请求     |
 | 
				
			||||||
|   list_repo_pull_requests    | 拉取请求 |   列出仓库中的所有拉取请求   |
 | 
					|   list_repo_pull_requests    | 拉取请求 |      列出所有拉取请求      |
 | 
				
			||||||
|     create_pull_request      | 拉取请求 |      创建一个新拉取请求      |
 | 
					|     create_pull_request      | 拉取请求 |       创建新拉取请求       |
 | 
				
			||||||
|         search_users         |   用户   |           搜索用户           |
 | 
					| create_pull_request_reviewer | 拉取请求 |     为拉取请求添加审查者   |
 | 
				
			||||||
|       search_org_teams       |   组织   |       搜索组织中的团队       |
 | 
					|         search_users         |   用户   |          搜索用户          |
 | 
				
			||||||
|         search_repos         |   仓库   |           搜索仓库           |
 | 
					|       search_org_teams       |   组织   |        搜索组织团队        |
 | 
				
			||||||
| get_gitea_mcp_server_version |   服务器    |        获取 Gitea MCP 服务器的版本         |
 | 
					|       list_org_labels        |   组织   |        列出组织标签        |
 | 
				
			||||||
 | 
					|       create_org_label       |   组织   |        创建组织标签        |
 | 
				
			||||||
 | 
					|        edit_org_label        |   组织   |        编辑组织标签        |
 | 
				
			||||||
 | 
					|       delete_org_label       |   组织   |        删除组织标签        |
 | 
				
			||||||
 | 
					|         search_repos         |   仓库   |          搜索仓库          |
 | 
				
			||||||
 | 
					| get_gitea_mcp_server_version |  服务器  | 获取 Gitea MCP 服务器版本  |
 | 
				
			||||||
 | 
					|       list_wiki_pages        |   Wiki   |     列出所有 Wiki 页面     |
 | 
				
			||||||
 | 
					|        get_wiki_page         |   Wiki   | 获取 Wiki 页面内容和元数据 |
 | 
				
			||||||
 | 
					|      get_wiki_revisions      |   Wiki   |     获取 Wiki 修订历史     |
 | 
				
			||||||
 | 
					|       create_wiki_page       |   Wiki   |      创建新 Wiki 页面      |
 | 
				
			||||||
 | 
					|       update_wiki_page       |   Wiki   |     更新现有 Wiki 页面     |
 | 
				
			||||||
 | 
					|       delete_wiki_page       |   Wiki   |       删除 Wiki 页面       |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 🐛 调试
 | 
					## 🐛 调试
 | 
				
			||||||
 | 
					
 | 
				
			||||||
要启用调试模式,请在使用 sse 模式运行 Gitea MCP 服务器时添加 `-d` 标志:
 | 
					启用调试模式时,请在 http 模式运行 Gitea MCP 服务器时加上 `-d` 标志:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```sh
 | 
					```sh
 | 
				
			||||||
./gitea-mcp -t sse [--port 8080] --token <your personal access token> -d
 | 
					./gitea-mcp -t http [--port 8080] --token <your personal access token> -d
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 🛠 疑难排解
 | 
					## 🛠 疑难排解
 | 
				
			||||||
 | 
					
 | 
				
			||||||
如果您遇到任何问题,以下是一些常见的疑难排解步骤:
 | 
					如遇问题,可参考以下步骤:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
1. **检查您的 PATH**: 确保 `gitea-mcp` 二进制文件位于系统 PATH 中包含的目录中。
 | 
					1. **检查 PATH**:确保 `gitea-mcp` 可执行文件已在系统 PATH 目录中。
 | 
				
			||||||
2. **验证依赖项**: 确保您已安装所有所需的依赖项,例如 `make` 和 `Golang`。
 | 
					2. **验证依赖**:确认已安装 `make` 和 `Golang` 等必要依赖。
 | 
				
			||||||
3. **检查配置**: 仔细检查您的 MCP 配置文件是否有任何错误或遗漏的信息。
 | 
					3. **检查配置**:仔细检查 MCP 配置文件是否有错误或遗漏。
 | 
				
			||||||
4. **查看日志**: 检查日志中是否有任何错误消息或警告,可以提供有关问题的更多信息。
 | 
					4. **查看日志**:检查日志消息或警告以获取更多信息。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
享受通过聊天探索和管理您的 Gitea 仓库的乐趣!
 | 
					享受通过聊天探索和管理您的 Gitea 仓库!
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										158
									
								
								README.zh-tw.md
									
									
									
									
									
								
							
							
						
						
									
										158
									
								
								README.zh-tw.md
									
									
									
									
									
								
							@@ -14,9 +14,9 @@
 | 
				
			|||||||
  - [什麼是 MCP?](#什麼是-mcp)
 | 
					  - [什麼是 MCP?](#什麼是-mcp)
 | 
				
			||||||
  - [🚧 安裝](#-安裝)
 | 
					  - [🚧 安裝](#-安裝)
 | 
				
			||||||
    - [在 VS Code 中使用](#在-vs-code-中使用)
 | 
					    - [在 VS Code 中使用](#在-vs-code-中使用)
 | 
				
			||||||
    - [📥 下載官方 Gitea MCP 二進位版本](#-下載官方-gitea-mcp-二進位版本)
 | 
					    - [📥 下載官方二進位版本](#-下載官方二進位版本)
 | 
				
			||||||
    - [🔧 從源代碼構建](#-從源代碼構建)
 | 
					    - [🔧 從原始碼建置](#-從原始碼建置)
 | 
				
			||||||
    - [📁 添加到 PATH](#-添加到-path)
 | 
					    - [📁 加入 PATH](#-加入-path)
 | 
				
			||||||
  - [🚀 使用](#-使用)
 | 
					  - [🚀 使用](#-使用)
 | 
				
			||||||
  - [✅ 可用工具](#-可用工具)
 | 
					  - [✅ 可用工具](#-可用工具)
 | 
				
			||||||
  - [🐛 調試](#-調試)
 | 
					  - [🐛 調試](#-調試)
 | 
				
			||||||
@@ -24,23 +24,23 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## 什麼是 Gitea?
 | 
					## 什麼是 Gitea?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Gitea 是一個由社群管理的輕量級代碼託管解決方案,使用 Go 語言編寫。它以 MIT 許可證發布。Gitea 提供 Git 託管,包括倉庫查看器、問題追蹤、拉取請求等功能。
 | 
					Gitea 是一個由社群管理的輕量級程式碼託管解決方案,使用 Go 語言編寫,採用 MIT 授權。Gitea 提供 Git 託管,包括倉庫瀏覽、議題追蹤、拉取請求等功能。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 什麼是 MCP?
 | 
					## 什麼是 MCP?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Model Context Protocol (MCP) 是一種協議,允許通過聊天界面整合各種工具和系統。它能夠無縫執行命令和管理倉庫、用戶和其他資源。
 | 
					Model Context Protocol (MCP) 是一種協議,允許透過聊天介面整合各種工具與系統。它能夠無縫執行命令並管理倉庫、使用者及其他資源。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 🚧 安裝
 | 
					## 🚧 安裝
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### 在 VS Code 中使用
 | 
					### 在 VS Code 中使用
 | 
				
			||||||
 | 
					
 | 
				
			||||||
要快速安裝,請使用本 README 頂部的單擊安裝按鈕之一。
 | 
					欲快速安裝,請使用本 README 頂部的安裝按鈕。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
要手動安裝,請將以下 JSON 塊添加到 VS Code 的用戶設置 (JSON) 文件中。您可以通過按 `Ctrl + Shift + P` 並輸入 `Preferences: Open User Settings (JSON)` 來完成此操作。
 | 
					如需手動安裝,請將下列 JSON 區塊加入 VS Code 的使用者設定 (JSON) 檔案。可按 `Ctrl + Shift + P` 並輸入 `Preferences: Open User Settings (JSON)`。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
或者,您可以將其添加到工作區中的 `.vscode/mcp.json` 文件中。這將允許您與他人共享配置。
 | 
					也可加入至工作區的 `.vscode/mcp.json` 檔案,方便與他人共享設定。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
> 請注意,`.vscode/mcp.json` 文件中不需要 `mcp` 鍵。
 | 
					> `.vscode/mcp.json` 檔案不需 `mcp` 鍵。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```json
 | 
					```json
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -49,7 +49,7 @@ Model Context Protocol (MCP) 是一種協議,允許通過聊天界面整合各
 | 
				
			|||||||
      {
 | 
					      {
 | 
				
			||||||
        "type": "promptString",
 | 
					        "type": "promptString",
 | 
				
			||||||
        "id": "gitea_token",
 | 
					        "id": "gitea_token",
 | 
				
			||||||
        "description": "Gitea 個人訪問令牌",
 | 
					        "description": "Gitea 個人存取令牌",
 | 
				
			||||||
        "password": true
 | 
					        "password": true
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
@@ -73,32 +73,32 @@ Model Context Protocol (MCP) 是一種協議,允許通過聊天界面整合各
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### 📥 下載官方 Gitea MCP 二進位版本
 | 
					### 📥 下載官方二進位版本
 | 
				
			||||||
 | 
					
 | 
				
			||||||
您可以從[官方 Gitea MCP 二進位版本](https://gitea.com/gitea/gitea-mcp/releases)下載官方版本。
 | 
					可至 [官方 Gitea MCP 二進位版本](https://gitea.com/gitea/gitea-mcp/releases) 下載。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### 🔧 從源代碼構建
 | 
					### 🔧 從原始碼建置
 | 
				
			||||||
 | 
					
 | 
				
			||||||
您可以使用 Git 克隆倉庫來下載源代碼:
 | 
					可用 Git 下載原始碼:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```bash
 | 
					```bash
 | 
				
			||||||
git clone https://gitea.com/gitea/gitea-mcp.git
 | 
					git clone https://gitea.com/gitea/gitea-mcp.git
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
在構建之前,請確保您已安裝以下內容:
 | 
					建置前請先安裝:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- make
 | 
					- make
 | 
				
			||||||
- Golang (建議使用 Go 1.24 或更高版本)
 | 
					- Golang(建議 Go 1.24 以上)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
然後運行:
 | 
					然後執行:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```bash
 | 
					```bash
 | 
				
			||||||
make install
 | 
					make install
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### 📁 添加到 PATH
 | 
					### 📁 加入 PATH
 | 
				
			||||||
 | 
					
 | 
				
			||||||
安裝後,將二進制文件 gitea-mcp 複製到系統 PATH 中包含的目錄。例如:
 | 
					安裝後,將 gitea-mcp 執行檔複製到系統 PATH 目錄,例如:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```bash
 | 
					```bash
 | 
				
			||||||
cp gitea-mcp /usr/local/bin/
 | 
					cp gitea-mcp /usr/local/bin/
 | 
				
			||||||
@@ -106,8 +106,8 @@ cp gitea-mcp /usr/local/bin/
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## 🚀 使用
 | 
					## 🚀 使用
 | 
				
			||||||
 | 
					
 | 
				
			||||||
此示例適用於 Cursor,您也可以在 VSCode 中使用插件。
 | 
					此範例適用於 Cursor,也可在 VSCode 使用插件。  
 | 
				
			||||||
要配置 Gitea 的 MCP 伺服器,請將以下內容添加到您的 MCP 配置文件中:
 | 
					欲設定 Gitea MCP 伺服器,請將下列內容加入 MCP 設定檔:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- **stdio 模式**
 | 
					- **stdio 模式**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -133,25 +133,16 @@ cp gitea-mcp /usr/local/bin/
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- **sse 模式**
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```json
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  "mcpServers": {
 | 
					 | 
				
			||||||
    "gitea": {
 | 
					 | 
				
			||||||
      "url": "http://localhost:8080/sse"
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- **http 模式**
 | 
					- **http 模式**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```json
 | 
					```json
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "mcpServers": {
 | 
					  "mcpServers": {
 | 
				
			||||||
    "gitea": {
 | 
					    "gitea": {
 | 
				
			||||||
      "url": "http://localhost:8080/mcp"
 | 
					      "url": "http://localhost:8080/mcp",
 | 
				
			||||||
 | 
					      "headers": {
 | 
				
			||||||
 | 
					        "Authorization": "Bearer <your personal access token>"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -160,10 +151,10 @@ cp gitea-mcp /usr/local/bin/
 | 
				
			|||||||
**預設日誌路徑**: `$HOME/.gitea-mcp/gitea-mcp.log`
 | 
					**預設日誌路徑**: `$HOME/.gitea-mcp/gitea-mcp.log`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
> [!注意]
 | 
					> [!注意]
 | 
				
			||||||
> 您可以通過命令列參數或環境變數提供您的 Gitea 主機和訪問令牌。
 | 
					> 可用命令列參數或環境變數提供 Gitea 主機與存取令牌。  
 | 
				
			||||||
> 命令列參數具有最高優先權
 | 
					> 命令列參數優先。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
一切設置完成後,請嘗試在您的 MCP 兼容聊天框中輸入以下內容:
 | 
					一切設定完成後,可在 MCP 聊天框輸入:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```text
 | 
					```text
 | 
				
			||||||
列出我所有的倉庫
 | 
					列出我所有的倉庫
 | 
				
			||||||
@@ -171,63 +162,74 @@ cp gitea-mcp /usr/local/bin/
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## ✅ 可用工具
 | 
					## ✅ 可用工具
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Gitea MCP 伺服器支持以下工具:
 | 
					Gitea MCP 伺服器支援以下工具:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
|             工具             |   範圍   |             描述             |
 | 
					|             工具             |   範圍   |             描述             |
 | 
				
			||||||
| :--------------------------: | :------: | :--------------------------: |
 | 
					| :--------------------------: | :------: | :--------------------------: |
 | 
				
			||||||
|       get_my_user_info       |   用戶   |     獲取已認證用戶的信息     |
 | 
					|       get_my_user_info       |   用戶   |      取得已認證用戶資訊      |
 | 
				
			||||||
|        get_user_orgs         |   用戶   |    取得已認證用戶所屬組織    |
 | 
					|        get_user_orgs         |   用戶   |    取得已認證用戶所屬組織    |
 | 
				
			||||||
|         create_repo          |   倉庫   |        創建一個新倉庫        |
 | 
					|         create_repo          |   倉庫   |          創建新倉庫          |
 | 
				
			||||||
|          fork_repo           |   倉庫   |         復刻一個倉庫         |
 | 
					|          fork_repo           |   倉庫   |           復刻倉庫           |
 | 
				
			||||||
|        list_my_repos         |   倉庫   | 列出已認證用戶擁有的所有倉庫 |
 | 
					|        list_my_repos         |   倉庫   |       列出用戶所有倉庫       |
 | 
				
			||||||
|        create_branch         |   分支   |        創建一個新分支        |
 | 
					|        create_branch         |   分支   |          創建新分支          |
 | 
				
			||||||
|        delete_branch         |   分支   |         刪除一個分支         |
 | 
					|        delete_branch         |   分支   |           刪除分支           |
 | 
				
			||||||
|        list_branches         |   分支   |     列出倉庫中的所有分支     |
 | 
					|        list_branches         |   分支   |         列出所有分支         |
 | 
				
			||||||
|        create_release        | 版本發布 |      創建一個新版本發布      |
 | 
					|        create_release        | 版本發布 |        創建新版本發布        |
 | 
				
			||||||
|        delete_release        | 版本發布 |       刪除一個版本發布       |
 | 
					|        delete_release        | 版本發布 |         刪除版本發布         |
 | 
				
			||||||
|         get_release          | 版本發布 |       獲取一個版本發布       |
 | 
					|         get_release          | 版本發布 |         取得版本發布         |
 | 
				
			||||||
|      get_latest_release      | 版本發布 |      獲取最新的版本發布      |
 | 
					|      get_latest_release      | 版本發布 |       取得最新版本發布       |
 | 
				
			||||||
|        list_releases         | 版本發布 |       列出所有版本發布       |
 | 
					|        list_releases         | 版本發布 |       列出所有版本發布       |
 | 
				
			||||||
|          create_tag          |   標籤   |        創建一個新標籤        |
 | 
					|          create_tag          |   標籤   |          創建新標籤          |
 | 
				
			||||||
|          delete_tag          |   標籤   |         刪除一個標籤         |
 | 
					|          delete_tag          |   標籤   |           刪除標籤           |
 | 
				
			||||||
|           get_tag            |   標籤   |         獲取一個標籤         |
 | 
					|           get_tag            |   標籤   |           取得標籤           |
 | 
				
			||||||
|          list_tags           |   標籤   |         列出所有標籤         |
 | 
					|          list_tags           |   標籤   |         列出所有標籤         |
 | 
				
			||||||
|      list_repo_commits       |   提交   |     列出倉庫中的所有提交     |
 | 
					|      list_repo_commits       |   提交   |         列出所有提交         |
 | 
				
			||||||
|       get_file_content       |   文件   |    獲取文件的內容和元數據    |
 | 
					|       get_file_content       |   文件   |    取得文件內容與中繼資料    |
 | 
				
			||||||
|        get_dir_content       |   文件   |      獲取目錄的內容列表      |
 | 
					|       get_dir_content        |   文件   |       取得目錄內容列表       |
 | 
				
			||||||
|         create_file          |   文件   |        創建一個新文件        |
 | 
					|         create_file          |   文件   |          創建新文件          |
 | 
				
			||||||
|         update_file          |   文件   |         更新現有文件         |
 | 
					|         update_file          |   文件   |         更新現有文件         |
 | 
				
			||||||
|         delete_file          |   文件   |         刪除一個文件         |
 | 
					|         delete_file          |   文件   |           刪除文件           |
 | 
				
			||||||
|      get_issue_by_index      |   問題   |       根據索引獲取問題       |
 | 
					|      get_issue_by_index      |   問題   |        依索引取得問題        |
 | 
				
			||||||
|       list_repo_issues       |   問題   |     列出倉庫中的所有問題     |
 | 
					|       list_repo_issues       |   問題   |         列出所有問題         |
 | 
				
			||||||
|         create_issue         |   問題   |        創建一個新問題        |
 | 
					|         create_issue         |   問題   |          創建新問題          |
 | 
				
			||||||
|     create_issue_comment     |   問題   |       在問題上創建評論       |
 | 
					|     create_issue_comment     |   問題   |       在問題上創建評論       |
 | 
				
			||||||
|          edit_issue          |   問題   |         編輯一個問題         |
 | 
					|          edit_issue          |   問題   |           編輯問題           |
 | 
				
			||||||
|      edit_issue_comment      |   問題   |      在問題上編輯評論         |
 | 
					|      edit_issue_comment      |   問題   |         編輯問題評論         |
 | 
				
			||||||
| get_issue_comments_by_index  |   问题   |     根據索引獲取問題的評論     |
 | 
					| get_issue_comments_by_index  |   問題   |      依索引取得問題評論      |
 | 
				
			||||||
|  get_pull_request_by_index   | 拉取請求 |     根據索引獲取拉取請求     |
 | 
					|  get_pull_request_by_index   | 拉取請求 |      依索引取得拉取請求      |
 | 
				
			||||||
|   list_repo_pull_requests    | 拉取請求 |   列出倉庫中的所有拉取請求   |
 | 
					|   list_repo_pull_requests    | 拉取請求 |       列出所有拉取請求       |
 | 
				
			||||||
|     create_pull_request      | 拉取請求 |      創建一個新拉取請求      |
 | 
					|     create_pull_request      | 拉取請求 |        創建新拉取請求        |
 | 
				
			||||||
|         search_users         |   用戶   |           搜索用戶           |
 | 
					| create_pull_request_reviewer | 拉取請求 |      為拉取請求添加審查者    |
 | 
				
			||||||
|       search_org_teams       |   組織   |       搜索組織中的團隊       |
 | 
					|         search_users         |   用戶   |           搜尋用戶           |
 | 
				
			||||||
|         search_repos         |   倉庫   |           搜索倉庫           |
 | 
					|       search_org_teams       |   組織   |         搜尋組織團隊         |
 | 
				
			||||||
| get_gitea_mcp_server_version |   伺服器    |        獲取 Gitea MCP 伺服器的版本         |
 | 
					|       list_org_labels        |   組織   |         列出組織標籤         |
 | 
				
			||||||
 | 
					|       create_org_label       |   組織   |         創建組織標籤         |
 | 
				
			||||||
 | 
					|        edit_org_label        |   組織   |         編輯組織標籤         |
 | 
				
			||||||
 | 
					|       delete_org_label       |   組織   |         刪除組織標籤         |
 | 
				
			||||||
 | 
					|         search_repos         |   倉庫   |           搜尋倉庫           |
 | 
				
			||||||
 | 
					| get_gitea_mcp_server_version |  伺服器  |  取得 Gitea MCP 伺服器版本   |
 | 
				
			||||||
 | 
					|       list_wiki_pages        |   Wiki   |      列出所有 Wiki 頁面      |
 | 
				
			||||||
 | 
					|        get_wiki_page         |   Wiki   | 取得 Wiki 頁面內容與中繼資料 |
 | 
				
			||||||
 | 
					|      get_wiki_revisions      |   Wiki   |      取得 Wiki 修訂歷史      |
 | 
				
			||||||
 | 
					|       create_wiki_page       |   Wiki   |       創建新 Wiki 頁面       |
 | 
				
			||||||
 | 
					|       update_wiki_page       |   Wiki   |      更新現有 Wiki 頁面      |
 | 
				
			||||||
 | 
					|       delete_wiki_page       |   Wiki   |        刪除 Wiki 頁面        |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 🐛 調試
 | 
					## 🐛 調試
 | 
				
			||||||
 | 
					
 | 
				
			||||||
要啟用調試模式,請在使用 sse 模式運行 Gitea MCP 伺服器時添加 `-d` 旗標:
 | 
					啟用調試模式時,請在 http 模式執行 Gitea MCP 伺服器時加上 `-d` 旗標:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```sh
 | 
					```sh
 | 
				
			||||||
./gitea-mcp -t sse [--port 8080] --token <your personal access token> -d
 | 
					./gitea-mcp -t http [--port 8080] --token <your personal access token> -d
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 🛠 疑難排解
 | 
					## 🛠 疑難排解
 | 
				
			||||||
 | 
					
 | 
				
			||||||
如果您遇到任何問題,以下是一些常見的疑難排解步驟:
 | 
					如遇問題,可參考以下步驟:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
1. **檢查您的 PATH**: 確保 `gitea-mcp` 二進制文件位於系統 PATH 中包含的目錄中。
 | 
					1. **檢查 PATH**:確保 `gitea-mcp` 執行檔已在系統 PATH 目錄中。
 | 
				
			||||||
2. **驗證依賴項**: 確保您已安裝所有所需的依賴項,例如 `make` 和 `Golang`。
 | 
					2. **驗證依賴**:確認已安裝 `make` 與 `Golang` 等必要依賴。
 | 
				
			||||||
3. **檢查配置**: 仔細檢查您的 MCP 配置文件是否有任何錯誤或遺漏的信息。
 | 
					3. **檢查設定**:仔細檢查 MCP 設定檔是否有錯誤或遺漏。
 | 
				
			||||||
4. **查看日誌**: 檢查日誌中是否有任何錯誤消息或警告,可以提供有關問題的更多信息。
 | 
					4. **查看日誌**:檢查日誌訊息或警告以獲取更多資訊。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
享受通過聊天探索和管理您的 Gitea 倉庫的樂趣!
 | 
					享受透過聊天探索與管理您的 Gitea 倉庫!
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,13 +21,13 @@ func init() {
 | 
				
			|||||||
		&flagPkg.Mode,
 | 
							&flagPkg.Mode,
 | 
				
			||||||
		"t",
 | 
							"t",
 | 
				
			||||||
		"stdio",
 | 
							"stdio",
 | 
				
			||||||
		"Transport type (stdio, sse or http)",
 | 
							"Transport type (stdio or http)",
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	flag.StringVar(
 | 
						flag.StringVar(
 | 
				
			||||||
		&flagPkg.Mode,
 | 
							&flagPkg.Mode,
 | 
				
			||||||
		"transport",
 | 
							"transport",
 | 
				
			||||||
		"stdio",
 | 
							"stdio",
 | 
				
			||||||
		"Transport type (stdio, sse or http)",
 | 
							"Transport type (stdio or http)",
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	flag.StringVar(
 | 
						flag.StringVar(
 | 
				
			||||||
		&host,
 | 
							&host,
 | 
				
			||||||
@@ -39,7 +39,7 @@ func init() {
 | 
				
			|||||||
		&port,
 | 
							&port,
 | 
				
			||||||
		"port",
 | 
							"port",
 | 
				
			||||||
		8080,
 | 
							8080,
 | 
				
			||||||
		"see or http port",
 | 
							"http port",
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	flag.StringVar(
 | 
						flag.StringVar(
 | 
				
			||||||
		&token,
 | 
							&token,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								config.json
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								config.json
									
									
									
									
									
								
							@@ -2,11 +2,11 @@
 | 
				
			|||||||
  "mcpServers": {
 | 
					  "mcpServers": {
 | 
				
			||||||
    "gitea": {
 | 
					    "gitea": {
 | 
				
			||||||
      "command": "gitea-mcp",
 | 
					      "command": "gitea-mcp",
 | 
				
			||||||
      "args": {
 | 
					      "args": [
 | 
				
			||||||
        "-t": "stdio",
 | 
					        "-t", "stdio",
 | 
				
			||||||
        "--host": "https://gitea.com",
 | 
					        "--host", "https://gitea.com",
 | 
				
			||||||
        "--token": "<your personal access token>"
 | 
					        "--token", "<your personal access token>"
 | 
				
			||||||
      },
 | 
					      ]
 | 
				
			||||||
      "env": {
 | 
					      "env": {
 | 
				
			||||||
        "GITEA_HOST": "https://gitea.com",
 | 
					        "GITEA_HOST": "https://gitea.com",
 | 
				
			||||||
        "GITEA_ACCESS_TOKEN": "<your personal access token>"
 | 
					        "GITEA_ACCESS_TOKEN": "<your personal access token>"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								go.mod
									
									
									
									
									
								
							@@ -3,8 +3,8 @@ module gitea.com/gitea/gitea-mcp
 | 
				
			|||||||
go 1.24.0
 | 
					go 1.24.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	code.gitea.io/sdk/gitea v0.21.0
 | 
						code.gitea.io/sdk/gitea v0.22.1
 | 
				
			||||||
	github.com/mark3labs/mcp-go v0.36.0
 | 
						github.com/mark3labs/mcp-go v0.42.0
 | 
				
			||||||
	go.uber.org/zap v1.27.0
 | 
						go.uber.org/zap v1.27.0
 | 
				
			||||||
	gopkg.in/natefinch/lumberjack.v2 v2.2.1
 | 
						gopkg.in/natefinch/lumberjack.v2 v2.2.1
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -18,12 +18,12 @@ require (
 | 
				
			|||||||
	github.com/google/uuid v1.6.0 // indirect
 | 
						github.com/google/uuid v1.6.0 // indirect
 | 
				
			||||||
	github.com/hashicorp/go-version v1.7.0 // indirect
 | 
						github.com/hashicorp/go-version v1.7.0 // indirect
 | 
				
			||||||
	github.com/invopop/jsonschema v0.13.0 // indirect
 | 
						github.com/invopop/jsonschema v0.13.0 // indirect
 | 
				
			||||||
	github.com/mailru/easyjson v0.7.7 // indirect
 | 
						github.com/mailru/easyjson v0.9.1 // indirect
 | 
				
			||||||
	github.com/spf13/cast v1.9.2 // indirect
 | 
						github.com/spf13/cast v1.10.0 // indirect
 | 
				
			||||||
	github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
 | 
						github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
 | 
				
			||||||
	github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
 | 
						github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
 | 
				
			||||||
	go.uber.org/multierr v1.11.0 // indirect
 | 
						go.uber.org/multierr v1.11.0 // indirect
 | 
				
			||||||
	golang.org/x/crypto v0.40.0 // indirect
 | 
						golang.org/x/crypto v0.43.0 // indirect
 | 
				
			||||||
	golang.org/x/sys v0.34.0 // indirect
 | 
						golang.org/x/sys v0.37.0 // indirect
 | 
				
			||||||
	gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
						gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										29
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								go.sum
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
				
			|||||||
code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4=
 | 
					code.gitea.io/sdk/gitea v0.22.1 h1:7K05KjRORyTcTYULQ/AwvlVS6pawLcWyXZcTr7gHFyA=
 | 
				
			||||||
code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA=
 | 
					code.gitea.io/sdk/gitea v0.22.1/go.mod h1:yyF5+GhljqvA30sRDreoyHILruNiy4ASufugzYg0VHM=
 | 
				
			||||||
github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs=
 | 
					github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs=
 | 
				
			||||||
github.com/42wim/httpsig v1.2.3/go.mod h1:nZq9OlYKDrUBhptd77IHx4/sZZD+IxTBADvAPI9G/EM=
 | 
					github.com/42wim/httpsig v1.2.3/go.mod h1:nZq9OlYKDrUBhptd77IHx4/sZZD+IxTBADvAPI9G/EM=
 | 
				
			||||||
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
 | 
					github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
 | 
				
			||||||
@@ -22,21 +22,20 @@ github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKe
 | 
				
			|||||||
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 | 
					github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 | 
				
			||||||
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
 | 
					github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
 | 
				
			||||||
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
 | 
					github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
 | 
				
			||||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 | 
					 | 
				
			||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 | 
					github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 | 
				
			||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 | 
					github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 | 
				
			||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 | 
					github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 | 
				
			||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 | 
					github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 | 
				
			||||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
 | 
					github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=
 | 
				
			||||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 | 
					github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
 | 
				
			||||||
github.com/mark3labs/mcp-go v0.36.0 h1:rIZaijrRYPeSbJG8/qNDe0hWlGrCJ7FWHNMz2SQpTis=
 | 
					github.com/mark3labs/mcp-go v0.42.0 h1:gk/8nYJh8t3yroCAOBhNbYsM9TCKvkM13I5t5Hfu6Ls=
 | 
				
			||||||
github.com/mark3labs/mcp-go v0.36.0/go.mod h1:T7tUa2jO6MavG+3P25Oy/jR7iCeJPHImCZHRymCn39g=
 | 
					github.com/mark3labs/mcp-go v0.42.0/go.mod h1:YnJfOL382MIWDx1kMY+2zsRHU/q78dBg9aFb8W6Thdw=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
					github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
					github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
 | 
					github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
 | 
					github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
 | 
				
			||||||
github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
 | 
					github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
 | 
				
			||||||
github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
 | 
					github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
 | 
				
			||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
 | 
					github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
 | 
				
			||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 | 
					github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 | 
				
			||||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
 | 
					github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
 | 
				
			||||||
@@ -52,18 +51,18 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
 | 
				
			|||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
					golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
					golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
 | 
					golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
 | 
				
			||||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
 | 
					golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
 | 
				
			||||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
 | 
					golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
 | 
				
			||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
					golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
				
			||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
					golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
 | 
					golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
 | 
				
			||||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 | 
					golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
 | 
				
			||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
					golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
				
			||||||
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
 | 
					golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
 | 
				
			||||||
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
 | 
					golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
 | 
				
			||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
					golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
				
			||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
					golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
					golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -140,7 +140,11 @@ func GetIssueByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
 | 
				
			|||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("index is required"))
 | 
							return to.ErrorResult(fmt.Errorf("index is required"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	issue, _, err := gitea.Client().GetIssue(owner, repo, int64(index))
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						issue, _, err := client.GetIssue(owner, repo, int64(index))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("get %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
 | 
							return to.ErrorResult(fmt.Errorf("get %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -177,7 +181,11 @@ func ListRepoIssuesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
 | 
				
			|||||||
			PageSize: int(pageSize),
 | 
								PageSize: int(pageSize),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	issues, _, err := gitea.Client().ListRepoIssues(owner, repo, opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						issues, _, err := client.ListRepoIssues(owner, repo, opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("get %v/%v/issues err: %v", owner, repo, err))
 | 
							return to.ErrorResult(fmt.Errorf("get %v/%v/issues err: %v", owner, repo, err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -202,7 +210,11 @@ func CreateIssueFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
 | 
				
			|||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("body is required"))
 | 
							return to.ErrorResult(fmt.Errorf("body is required"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	issue, _, err := gitea.Client().CreateIssue(owner, repo, gitea_sdk.CreateIssueOption{
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						issue, _, err := client.CreateIssue(owner, repo, gitea_sdk.CreateIssueOption{
 | 
				
			||||||
		Title: title,
 | 
							Title: title,
 | 
				
			||||||
		Body:  body,
 | 
							Body:  body,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
@@ -234,7 +246,11 @@ func CreateIssueCommentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
 | 
				
			|||||||
	opt := gitea_sdk.CreateIssueCommentOption{
 | 
						opt := gitea_sdk.CreateIssueCommentOption{
 | 
				
			||||||
		Body: body,
 | 
							Body: body,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	issueComment, _, err := gitea.Client().CreateIssueComment(owner, repo, int64(index), opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						issueComment, _, err := client.CreateIssueComment(owner, repo, int64(index), opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("create %v/%v/issue/%v/comment err: %v", owner, repo, int64(index), err))
 | 
							return to.ErrorResult(fmt.Errorf("create %v/%v/issue/%v/comment err: %v", owner, repo, int64(index), err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -280,7 +296,11 @@ func EditIssueFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRes
 | 
				
			|||||||
		opt.State = ptr.To(gitea_sdk.StateType(state))
 | 
							opt.State = ptr.To(gitea_sdk.StateType(state))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	issue, _, err := gitea.Client().EditIssue(owner, repo, int64(index), opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						issue, _, err := client.EditIssue(owner, repo, int64(index), opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("edit %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
 | 
							return to.ErrorResult(fmt.Errorf("edit %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -309,7 +329,11 @@ func EditIssueCommentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
 | 
				
			|||||||
	opt := gitea_sdk.EditIssueCommentOption{
 | 
						opt := gitea_sdk.EditIssueCommentOption{
 | 
				
			||||||
		Body: body,
 | 
							Body: body,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	issueComment, _, err := gitea.Client().EditIssueComment(owner, repo, int64(commentID), opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						issueComment, _, err := client.EditIssueComment(owner, repo, int64(commentID), opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("edit %v/%v/issues/comments/%v err: %v", owner, repo, int64(commentID), err))
 | 
							return to.ErrorResult(fmt.Errorf("edit %v/%v/issues/comments/%v err: %v", owner, repo, int64(commentID), err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -332,7 +356,11 @@ func GetIssueCommentsByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*m
 | 
				
			|||||||
		return to.ErrorResult(fmt.Errorf("index is required"))
 | 
							return to.ErrorResult(fmt.Errorf("index is required"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	opt := gitea_sdk.ListIssueCommentOptions{}
 | 
						opt := gitea_sdk.ListIssueCommentOptions{}
 | 
				
			||||||
	issue, _, err := gitea.Client().ListIssueComments(owner, repo, int64(index), opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						issue, _, err := client.ListIssueComments(owner, repo, int64(index), opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("get %v/%v/issues/%v/comments err: %v", owner, repo, int64(index), err))
 | 
							return to.ErrorResult(fmt.Errorf("get %v/%v/issues/%v/comments err: %v", owner, repo, int64(index), err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,10 @@ const (
 | 
				
			|||||||
	ReplaceIssueLabelsToolName = "replace_issue_labels"
 | 
						ReplaceIssueLabelsToolName = "replace_issue_labels"
 | 
				
			||||||
	ClearIssueLabelsToolName   = "clear_issue_labels"
 | 
						ClearIssueLabelsToolName   = "clear_issue_labels"
 | 
				
			||||||
	RemoveIssueLabelToolName   = "remove_issue_label"
 | 
						RemoveIssueLabelToolName   = "remove_issue_label"
 | 
				
			||||||
 | 
					    ListOrgLabelsToolName      = "list_org_labels"
 | 
				
			||||||
 | 
					    CreateOrgLabelToolName     = "create_org_label"
 | 
				
			||||||
 | 
					    EditOrgLabelToolName       = "edit_org_label"
 | 
				
			||||||
 | 
					    DeleteOrgLabelToolName     = "delete_org_label"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
@@ -110,6 +114,43 @@ var (
 | 
				
			|||||||
		mcp.WithNumber("index", mcp.Required(), mcp.Description("issue index")),
 | 
							mcp.WithNumber("index", mcp.Required(), mcp.Description("issue index")),
 | 
				
			||||||
		mcp.WithNumber("label_id", mcp.Required(), mcp.Description("label ID to remove")),
 | 
							mcp.WithNumber("label_id", mcp.Required(), mcp.Description("label ID to remove")),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ListOrgLabelsTool = mcp.NewTool(
 | 
				
			||||||
 | 
					        ListOrgLabelsToolName,
 | 
				
			||||||
 | 
					        mcp.WithDescription("Lists labels defined at organization level"),
 | 
				
			||||||
 | 
					        mcp.WithString("org", mcp.Required(), mcp.Description("organization name")),
 | 
				
			||||||
 | 
					        mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1)),
 | 
				
			||||||
 | 
					        mcp.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CreateOrgLabelTool = mcp.NewTool(
 | 
				
			||||||
 | 
					        CreateOrgLabelToolName,
 | 
				
			||||||
 | 
					        mcp.WithDescription("Creates a new label for an organization"),
 | 
				
			||||||
 | 
					        mcp.WithString("org", mcp.Required(), mcp.Description("organization name")),
 | 
				
			||||||
 | 
					        mcp.WithString("name", mcp.Required(), mcp.Description("label name")),
 | 
				
			||||||
 | 
					        mcp.WithString("color", mcp.Required(), mcp.Description("label color (hex code, e.g., #RRGGBB)")),
 | 
				
			||||||
 | 
					        mcp.WithString("description", mcp.Description("label description")),
 | 
				
			||||||
 | 
					        mcp.WithBoolean("exclusive", mcp.Description("whether the label is exclusive"), mcp.DefaultBool(false)),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    EditOrgLabelTool = mcp.NewTool(
 | 
				
			||||||
 | 
					        EditOrgLabelToolName,
 | 
				
			||||||
 | 
					        mcp.WithDescription("Edits an existing organization label"),
 | 
				
			||||||
 | 
					        mcp.WithString("org", mcp.Required(), mcp.Description("organization name")),
 | 
				
			||||||
 | 
					        mcp.WithNumber("id", mcp.Required(), mcp.Description("label ID")),
 | 
				
			||||||
 | 
					        mcp.WithString("name", mcp.Description("new label name")),
 | 
				
			||||||
 | 
					        mcp.WithString("color", mcp.Description("new label color (hex code, e.g., #RRGGBB)")),
 | 
				
			||||||
 | 
					        mcp.WithString("description", mcp.Description("new label description")),
 | 
				
			||||||
 | 
					        mcp.WithBoolean("exclusive", mcp.Description("whether the label is exclusive")),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DeleteOrgLabelTool = mcp.NewTool(
 | 
				
			||||||
 | 
					        DeleteOrgLabelToolName,
 | 
				
			||||||
 | 
					        mcp.WithDescription("Deletes an organization label by ID"),
 | 
				
			||||||
 | 
					        mcp.WithString("org", mcp.Required(), mcp.Description("organization name")),
 | 
				
			||||||
 | 
					        mcp.WithNumber("id", mcp.Required(), mcp.Description("label ID")),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
@@ -149,6 +190,22 @@ func init() {
 | 
				
			|||||||
		Tool:    RemoveIssueLabelTool,
 | 
							Tool:    RemoveIssueLabelTool,
 | 
				
			||||||
		Handler: RemoveIssueLabelFn,
 | 
							Handler: RemoveIssueLabelFn,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					    Tool.RegisterRead(server.ServerTool{
 | 
				
			||||||
 | 
					        Tool:    ListOrgLabelsTool,
 | 
				
			||||||
 | 
					        Handler: ListOrgLabelsFn,
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    Tool.RegisterWrite(server.ServerTool{
 | 
				
			||||||
 | 
					        Tool:    CreateOrgLabelTool,
 | 
				
			||||||
 | 
					        Handler: CreateOrgLabelFn,
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    Tool.RegisterWrite(server.ServerTool{
 | 
				
			||||||
 | 
					        Tool:    EditOrgLabelTool,
 | 
				
			||||||
 | 
					        Handler: EditOrgLabelFn,
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    Tool.RegisterWrite(server.ServerTool{
 | 
				
			||||||
 | 
					        Tool:    DeleteOrgLabelTool,
 | 
				
			||||||
 | 
					        Handler: DeleteOrgLabelFn,
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ListRepoLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
					func ListRepoLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
@@ -176,7 +233,11 @@ func ListRepoLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
 | 
				
			|||||||
			PageSize: int(pageSize),
 | 
								PageSize: int(pageSize),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	labels, _, err := gitea.Client().ListRepoLabels(owner, repo, opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						labels, _, err := client.ListRepoLabels(owner, repo, opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("list %v/%v/labels err: %v", owner, repo, err))
 | 
							return to.ErrorResult(fmt.Errorf("list %v/%v/labels err: %v", owner, repo, err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -198,7 +259,11 @@ func GetRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
 | 
				
			|||||||
		return to.ErrorResult(fmt.Errorf("label ID is required"))
 | 
							return to.ErrorResult(fmt.Errorf("label ID is required"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	label, _, err := gitea.Client().GetRepoLabel(owner, repo, int64(id))
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						label, _, err := client.GetRepoLabel(owner, repo, int64(id))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("get %v/%v/label/%v err: %v", owner, repo, int64(id), err))
 | 
							return to.ErrorResult(fmt.Errorf("get %v/%v/label/%v err: %v", owner, repo, int64(id), err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -231,7 +296,11 @@ func CreateRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
 | 
				
			|||||||
		Description: description,
 | 
							Description: description,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	label, _, err := gitea.Client().CreateLabel(owner, repo, opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						label, _, err := client.CreateLabel(owner, repo, opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("create %v/%v/label err: %v", owner, repo, err))
 | 
							return to.ErrorResult(fmt.Errorf("create %v/%v/label err: %v", owner, repo, err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -264,7 +333,11 @@ func EditRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
 | 
				
			|||||||
		opt.Description = ptr.To(description)
 | 
							opt.Description = ptr.To(description)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	label, _, err := gitea.Client().EditLabel(owner, repo, int64(id), opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						label, _, err := client.EditLabel(owner, repo, int64(id), opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("edit %v/%v/label/%v err: %v", owner, repo, int64(id), err))
 | 
							return to.ErrorResult(fmt.Errorf("edit %v/%v/label/%v err: %v", owner, repo, int64(id), err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -286,7 +359,11 @@ func DeleteRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
 | 
				
			|||||||
		return to.ErrorResult(fmt.Errorf("label ID is required"))
 | 
							return to.ErrorResult(fmt.Errorf("label ID is required"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, err := gitea.Client().DeleteLabel(owner, repo, int64(id))
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, err = client.DeleteLabel(owner, repo, int64(id))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("delete %v/%v/label/%v err: %v", owner, repo, int64(id), err))
 | 
							return to.ErrorResult(fmt.Errorf("delete %v/%v/label/%v err: %v", owner, repo, int64(id), err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -324,7 +401,11 @@ func AddIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
 | 
				
			|||||||
		Labels: labels,
 | 
							Labels: labels,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	issueLabels, _, err := gitea.Client().AddIssueLabels(owner, repo, int64(index), opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						issueLabels, _, err := client.AddIssueLabels(owner, repo, int64(index), opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("add labels to %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
 | 
							return to.ErrorResult(fmt.Errorf("add labels to %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -362,7 +443,11 @@ func ReplaceIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
 | 
				
			|||||||
		Labels: labels,
 | 
							Labels: labels,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	issueLabels, _, err := gitea.Client().ReplaceIssueLabels(owner, repo, int64(index), opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						issueLabels, _, err := client.ReplaceIssueLabels(owner, repo, int64(index), opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("replace labels on %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
 | 
							return to.ErrorResult(fmt.Errorf("replace labels on %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -384,7 +469,11 @@ func ClearIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
 | 
				
			|||||||
		return to.ErrorResult(fmt.Errorf("issue index is required"))
 | 
							return to.ErrorResult(fmt.Errorf("issue index is required"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, err := gitea.Client().ClearIssueLabels(owner, repo, int64(index))
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, err = client.ClearIssueLabels(owner, repo, int64(index))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("clear labels on %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
 | 
							return to.ErrorResult(fmt.Errorf("clear labels on %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -410,9 +499,138 @@ func RemoveIssueLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
 | 
				
			|||||||
		return to.ErrorResult(fmt.Errorf("label ID is required"))
 | 
							return to.ErrorResult(fmt.Errorf("label ID is required"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, err := gitea.Client().DeleteIssueLabel(owner, repo, int64(index), int64(labelID))
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, err = client.DeleteIssueLabel(owner, repo, int64(index), int64(labelID))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("remove label %v from %v/%v/issue/%v err: %v", int64(labelID), owner, repo, int64(index), err))
 | 
							return to.ErrorResult(fmt.Errorf("remove label %v from %v/%v/issue/%v err: %v", int64(labelID), owner, repo, int64(index), err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return to.TextResult("Label removed successfully")
 | 
						return to.TextResult("Label removed successfully")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ListOrgLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
					    log.Debugf("Called ListOrgLabelsFn")
 | 
				
			||||||
 | 
					    org, ok := req.GetArguments()["org"].(string)
 | 
				
			||||||
 | 
					    if !ok {
 | 
				
			||||||
 | 
					        return to.ErrorResult(fmt.Errorf("org is required"))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    page, ok := req.GetArguments()["page"].(float64)
 | 
				
			||||||
 | 
					    if !ok {
 | 
				
			||||||
 | 
					        page = 1
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pageSize, ok := req.GetArguments()["pageSize"].(float64)
 | 
				
			||||||
 | 
					    if !ok {
 | 
				
			||||||
 | 
					        pageSize = 100
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    opt := gitea_sdk.ListOrgLabelsOptions{
 | 
				
			||||||
 | 
					        ListOptions: gitea_sdk.ListOptions{
 | 
				
			||||||
 | 
					            Page:     int(page),
 | 
				
			||||||
 | 
					            PageSize: int(pageSize),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
					    if err != nil {
 | 
				
			||||||
 | 
					        return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    labels, _, err := client.ListOrgLabels(org, opt)
 | 
				
			||||||
 | 
					    if err != nil {
 | 
				
			||||||
 | 
					        return to.ErrorResult(fmt.Errorf("list %v/labels err: %v", org, err))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return to.TextResult(labels)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func CreateOrgLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
					    log.Debugf("Called CreateOrgLabelFn")
 | 
				
			||||||
 | 
					    org, ok := req.GetArguments()["org"].(string)
 | 
				
			||||||
 | 
					    if !ok {
 | 
				
			||||||
 | 
					        return to.ErrorResult(fmt.Errorf("org is required"))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    name, ok := req.GetArguments()["name"].(string)
 | 
				
			||||||
 | 
					    if !ok {
 | 
				
			||||||
 | 
					        return to.ErrorResult(fmt.Errorf("name is required"))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    color, ok := req.GetArguments()["color"].(string)
 | 
				
			||||||
 | 
					    if !ok {
 | 
				
			||||||
 | 
					        return to.ErrorResult(fmt.Errorf("color is required"))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    description, _ := req.GetArguments()["description"].(string)
 | 
				
			||||||
 | 
					    exclusive, _ := req.GetArguments()["exclusive"].(bool)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    opt := gitea_sdk.CreateOrgLabelOption{
 | 
				
			||||||
 | 
					        Name:        name,
 | 
				
			||||||
 | 
					        Color:       color,
 | 
				
			||||||
 | 
					        Description: description,
 | 
				
			||||||
 | 
					        Exclusive:   exclusive,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
					    if err != nil {
 | 
				
			||||||
 | 
					        return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    label, _, err := client.CreateOrgLabel(org, opt)
 | 
				
			||||||
 | 
					    if err != nil {
 | 
				
			||||||
 | 
					        return to.ErrorResult(fmt.Errorf("create %v/labels err: %v", org, err))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return to.TextResult(label)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func EditOrgLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
					    log.Debugf("Called EditOrgLabelFn")
 | 
				
			||||||
 | 
					    org, ok := req.GetArguments()["org"].(string)
 | 
				
			||||||
 | 
					    if !ok {
 | 
				
			||||||
 | 
					        return to.ErrorResult(fmt.Errorf("org is required"))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    id, ok := req.GetArguments()["id"].(float64)
 | 
				
			||||||
 | 
					    if !ok {
 | 
				
			||||||
 | 
					        return to.ErrorResult(fmt.Errorf("label ID is required"))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    opt := gitea_sdk.EditOrgLabelOption{}
 | 
				
			||||||
 | 
					    if name, ok := req.GetArguments()["name"].(string); ok {
 | 
				
			||||||
 | 
					        opt.Name = ptr.To(name)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if color, ok := req.GetArguments()["color"].(string); ok {
 | 
				
			||||||
 | 
					        opt.Color = ptr.To(color)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if description, ok := req.GetArguments()["description"].(string); ok {
 | 
				
			||||||
 | 
					        opt.Description = ptr.To(description)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if exclusive, ok := req.GetArguments()["exclusive"].(bool); ok {
 | 
				
			||||||
 | 
					        opt.Exclusive = ptr.To(exclusive)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
					    if err != nil {
 | 
				
			||||||
 | 
					        return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    label, _, err := client.EditOrgLabel(org, int64(id), opt)
 | 
				
			||||||
 | 
					    if err != nil {
 | 
				
			||||||
 | 
					        return to.ErrorResult(fmt.Errorf("edit %v/labels/%v err: %v", org, int64(id), err))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return to.TextResult(label)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func DeleteOrgLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
					    log.Debugf("Called DeleteOrgLabelFn")
 | 
				
			||||||
 | 
					    org, ok := req.GetArguments()["org"].(string)
 | 
				
			||||||
 | 
					    if !ok {
 | 
				
			||||||
 | 
					        return to.ErrorResult(fmt.Errorf("org is required"))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    id, ok := req.GetArguments()["id"].(float64)
 | 
				
			||||||
 | 
					    if !ok {
 | 
				
			||||||
 | 
					        return to.ErrorResult(fmt.Errorf("label ID is required"))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
					    if err != nil {
 | 
				
			||||||
 | 
					        return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    _, err = client.DeleteOrgLabel(org, int64(id))
 | 
				
			||||||
 | 
					    if err != nil {
 | 
				
			||||||
 | 
					        return to.ErrorResult(fmt.Errorf("delete %v/labels/%v err: %v", org, int64(id), err))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return to.TextResult("Label deleted successfully")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,7 +1,13 @@
 | 
				
			|||||||
package operation
 | 
					package operation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/signal"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gitea.com/gitea/gitea-mcp/operation/issue"
 | 
						"gitea.com/gitea/gitea-mcp/operation/issue"
 | 
				
			||||||
@@ -11,6 +17,8 @@ import (
 | 
				
			|||||||
	"gitea.com/gitea/gitea-mcp/operation/search"
 | 
						"gitea.com/gitea/gitea-mcp/operation/search"
 | 
				
			||||||
	"gitea.com/gitea/gitea-mcp/operation/user"
 | 
						"gitea.com/gitea/gitea-mcp/operation/user"
 | 
				
			||||||
	"gitea.com/gitea/gitea-mcp/operation/version"
 | 
						"gitea.com/gitea/gitea-mcp/operation/version"
 | 
				
			||||||
 | 
						"gitea.com/gitea/gitea-mcp/operation/wiki"
 | 
				
			||||||
 | 
						mcpContext "gitea.com/gitea/gitea-mcp/pkg/context"
 | 
				
			||||||
	"gitea.com/gitea/gitea-mcp/pkg/flag"
 | 
						"gitea.com/gitea/gitea-mcp/pkg/flag"
 | 
				
			||||||
	"gitea.com/gitea/gitea-mcp/pkg/log"
 | 
						"gitea.com/gitea/gitea-mcp/pkg/log"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -41,9 +49,42 @@ func RegisterTool(s *server.MCPServer) {
 | 
				
			|||||||
	// Version Tool
 | 
						// Version Tool
 | 
				
			||||||
	s.AddTools(version.Tool.Tools()...)
 | 
						s.AddTools(version.Tool.Tools()...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Wiki Tool
 | 
				
			||||||
 | 
						s.AddTools(wiki.Tool.Tools()...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	s.DeleteTools("")
 | 
						s.DeleteTools("")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// parseBearerToken extracts the Bearer token from an Authorization header.
 | 
				
			||||||
 | 
					// Returns the token and true if valid, empty string and false otherwise.
 | 
				
			||||||
 | 
					func parseBearerToken(authHeader string) (string, bool) {
 | 
				
			||||||
 | 
						const bearerPrefix = "Bearer "
 | 
				
			||||||
 | 
						if len(authHeader) < len(bearerPrefix) || !strings.HasPrefix(authHeader, bearerPrefix) {
 | 
				
			||||||
 | 
							return "", false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						token := strings.TrimSpace(authHeader[len(bearerPrefix):])
 | 
				
			||||||
 | 
						if token == "" {
 | 
				
			||||||
 | 
							return "", false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return token, true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getContextWithToken(ctx context.Context, r *http.Request) context.Context {
 | 
				
			||||||
 | 
						authHeader := r.Header.Get("Authorization")
 | 
				
			||||||
 | 
						if authHeader == "" {
 | 
				
			||||||
 | 
							return ctx
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						token, ok := parseBearerToken(authHeader)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return ctx
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return context.WithValue(ctx, mcpContext.TokenContextKey, token)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Run() error {
 | 
					func Run() error {
 | 
				
			||||||
	mcpServer = newMCPServer(flag.Version)
 | 
						mcpServer = newMCPServer(flag.Version)
 | 
				
			||||||
	RegisterTool(mcpServer)
 | 
						RegisterTool(mcpServer)
 | 
				
			||||||
@@ -54,27 +95,37 @@ func Run() error {
 | 
				
			|||||||
		); err != nil {
 | 
							); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	case "sse":
 | 
					 | 
				
			||||||
		sseServer := server.NewSSEServer(
 | 
					 | 
				
			||||||
			mcpServer,
 | 
					 | 
				
			||||||
		)
 | 
					 | 
				
			||||||
		log.Infof("Gitea MCP SSE server listening on :%d", flag.Port)
 | 
					 | 
				
			||||||
		if err := sseServer.Start(fmt.Sprintf(":%d", flag.Port)); err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	case "http":
 | 
						case "http":
 | 
				
			||||||
		httpServer := server.NewStreamableHTTPServer(
 | 
							httpServer := server.NewStreamableHTTPServer(
 | 
				
			||||||
			mcpServer,
 | 
								mcpServer,
 | 
				
			||||||
			server.WithLogger(log.New()),
 | 
								server.WithLogger(log.New()),
 | 
				
			||||||
			server.WithHeartbeatInterval(30*time.Second),
 | 
								server.WithHeartbeatInterval(30*time.Second),
 | 
				
			||||||
			server.WithStateLess(true),
 | 
								server.WithHTTPContextFunc(getContextWithToken),
 | 
				
			||||||
		)
 | 
							)
 | 
				
			||||||
		log.Infof("Gitea MCP HTTP server listening on :%d", flag.Port)
 | 
							log.Infof("Gitea MCP HTTP server listening on :%d", flag.Port)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Graceful shutdown setup
 | 
				
			||||||
 | 
							sigCh := make(chan os.Signal, 1)
 | 
				
			||||||
 | 
							signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
 | 
				
			||||||
 | 
							shutdownDone := make(chan struct{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							go func() {
 | 
				
			||||||
 | 
								<-sigCh
 | 
				
			||||||
 | 
								log.Infof("Shutdown signal received, gracefully stopping HTTP server...")
 | 
				
			||||||
 | 
								shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 | 
				
			||||||
 | 
								defer cancel()
 | 
				
			||||||
 | 
								if err := httpServer.Shutdown(shutdownCtx); err != nil {
 | 
				
			||||||
 | 
									log.Errorf("HTTP server shutdown error: %v", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								close(shutdownDone)
 | 
				
			||||||
 | 
							}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err := httpServer.Start(fmt.Sprintf(":%d", flag.Port)); err != nil {
 | 
							if err := httpServer.Start(fmt.Sprintf(":%d", flag.Port)); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							<-shutdownDone // Wait for shutdown to finish
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return fmt.Errorf("invalid transport type: %s. Must be 'stdio', 'sse' or 'http'", flag.Mode)
 | 
							return fmt.Errorf("invalid transport type: %s. Must be 'stdio' or 'http'", flag.Mode)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										81
									
								
								operation/operation_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								operation/operation_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,81 @@
 | 
				
			|||||||
 | 
					package operation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestParseBearerToken(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name      string
 | 
				
			||||||
 | 
							header    string
 | 
				
			||||||
 | 
							wantToken string
 | 
				
			||||||
 | 
							wantOK    bool
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:      "valid token",
 | 
				
			||||||
 | 
								header:    "Bearer validtoken",
 | 
				
			||||||
 | 
								wantToken: "validtoken",
 | 
				
			||||||
 | 
								wantOK:    true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:      "token with spaces trimmed",
 | 
				
			||||||
 | 
								header:    "Bearer   spacedToken ",
 | 
				
			||||||
 | 
								wantToken: "spacedToken",
 | 
				
			||||||
 | 
								wantOK:    true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:      "lowercase bearer should fail",
 | 
				
			||||||
 | 
								header:    "bearer lowercase",
 | 
				
			||||||
 | 
								wantToken: "",
 | 
				
			||||||
 | 
								wantOK:    false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:      "bearer with no token",
 | 
				
			||||||
 | 
								header:    "Bearer ",
 | 
				
			||||||
 | 
								wantToken: "",
 | 
				
			||||||
 | 
								wantOK:    false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:      "bearer with only spaces",
 | 
				
			||||||
 | 
								header:    "Bearer     ",
 | 
				
			||||||
 | 
								wantToken: "",
 | 
				
			||||||
 | 
								wantOK:    false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:      "missing space after Bearer",
 | 
				
			||||||
 | 
								header:    "Bearertoken",
 | 
				
			||||||
 | 
								wantToken: "",
 | 
				
			||||||
 | 
								wantOK:    false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:      "different auth type",
 | 
				
			||||||
 | 
								header:    "Basic dXNlcjpwYXNz",
 | 
				
			||||||
 | 
								wantToken: "",
 | 
				
			||||||
 | 
								wantOK:    false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:      "empty header",
 | 
				
			||||||
 | 
								header:    "",
 | 
				
			||||||
 | 
								wantToken: "",
 | 
				
			||||||
 | 
								wantOK:    false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:      "token with internal spaces",
 | 
				
			||||||
 | 
								header:    "Bearer token with spaces",
 | 
				
			||||||
 | 
								wantToken: "token with spaces",
 | 
				
			||||||
 | 
								wantOK:    true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tt := range tests {
 | 
				
			||||||
 | 
							t.Run(tt.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								gotToken, gotOK := parseBearerToken(tt.header)
 | 
				
			||||||
 | 
								if gotToken != tt.wantToken {
 | 
				
			||||||
 | 
									t.Errorf("parseBearerToken() token = %q, want %q", gotToken, tt.wantToken)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if gotOK != tt.wantOK {
 | 
				
			||||||
 | 
									t.Errorf("parseBearerToken() ok = %v, want %v", gotOK, tt.wantOK)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -17,9 +17,10 @@ import (
 | 
				
			|||||||
var Tool = tool.New()
 | 
					var Tool = tool.New()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	GetPullRequestByIndexToolName = "get_pull_request_by_index"
 | 
						GetPullRequestByIndexToolName     = "get_pull_request_by_index"
 | 
				
			||||||
	ListRepoPullRequestsToolName  = "list_repo_pull_requests"
 | 
						ListRepoPullRequestsToolName      = "list_repo_pull_requests"
 | 
				
			||||||
	CreatePullRequestToolName     = "create_pull_request"
 | 
						CreatePullRequestToolName         = "create_pull_request"
 | 
				
			||||||
 | 
						CreatePullRequestReviewerToolName = "create_pull_request_reviewer"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
@@ -53,6 +54,16 @@ var (
 | 
				
			|||||||
		mcp.WithString("head", mcp.Required(), mcp.Description("pull request head")),
 | 
							mcp.WithString("head", mcp.Required(), mcp.Description("pull request head")),
 | 
				
			||||||
		mcp.WithString("base", mcp.Required(), mcp.Description("pull request base")),
 | 
							mcp.WithString("base", mcp.Required(), mcp.Description("pull request base")),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CreatePullRequestReviewerTool = mcp.NewTool(
 | 
				
			||||||
 | 
							CreatePullRequestReviewerToolName,
 | 
				
			||||||
 | 
							mcp.WithDescription("create pull request reviewer"),
 | 
				
			||||||
 | 
							mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
 | 
				
			||||||
 | 
							mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
 | 
				
			||||||
 | 
							mcp.WithNumber("index", mcp.Required(), mcp.Description("pull request index")),
 | 
				
			||||||
 | 
							mcp.WithArray("reviewers", mcp.Description("list of reviewer usernames"), mcp.Items(map[string]interface{}{"type": "string"})),
 | 
				
			||||||
 | 
							mcp.WithArray("team_reviewers", mcp.Description("list of team reviewer names"), mcp.Items(map[string]interface{}{"type": "string"})),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
@@ -68,6 +79,10 @@ func init() {
 | 
				
			|||||||
		Tool:    CreatePullRequestTool,
 | 
							Tool:    CreatePullRequestTool,
 | 
				
			||||||
		Handler: CreatePullRequestFn,
 | 
							Handler: CreatePullRequestFn,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
						Tool.RegisterWrite(server.ServerTool{
 | 
				
			||||||
 | 
							Tool:    CreatePullRequestReviewerTool,
 | 
				
			||||||
 | 
							Handler: CreatePullRequestReviewerFn,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GetPullRequestByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
					func GetPullRequestByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
@@ -84,7 +99,11 @@ func GetPullRequestByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp
 | 
				
			|||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("index is required"))
 | 
							return to.ErrorResult(fmt.Errorf("index is required"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	pr, _, err := gitea.Client().GetPullRequest(owner, repo, int64(index))
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pr, _, err := client.GetPullRequest(owner, repo, int64(index))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("get %v/%v/pr/%v err: %v", owner, repo, int64(index), err))
 | 
							return to.ErrorResult(fmt.Errorf("get %v/%v/pr/%v err: %v", owner, repo, int64(index), err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -125,7 +144,11 @@ func ListRepoPullRequestsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.
 | 
				
			|||||||
			PageSize: int(pageSize),
 | 
								PageSize: int(pageSize),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	pullRequests, _, err := gitea.Client().ListRepoPullRequests(owner, repo, opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pullRequests, _, err := client.ListRepoPullRequests(owner, repo, opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("list %v/%v/pull_requests err: %v", owner, repo, err))
 | 
							return to.ErrorResult(fmt.Errorf("list %v/%v/pull_requests err: %v", owner, repo, err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -159,7 +182,11 @@ func CreatePullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Cal
 | 
				
			|||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("base is required"))
 | 
							return to.ErrorResult(fmt.Errorf("base is required"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	pr, _, err := gitea.Client().CreatePullRequest(owner, repo, gitea_sdk.CreatePullRequestOption{
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pr, _, err := client.CreatePullRequest(owner, repo, gitea_sdk.CreatePullRequestOption{
 | 
				
			||||||
		Title: title,
 | 
							Title: title,
 | 
				
			||||||
		Body:  body,
 | 
							Body:  body,
 | 
				
			||||||
		Head:  head,
 | 
							Head:  head,
 | 
				
			||||||
@@ -171,3 +198,65 @@ func CreatePullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Cal
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return to.TextResult(pr)
 | 
						return to.TextResult(pr)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func CreatePullRequestReviewerFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
						log.Debugf("Called CreatePullRequestReviewerFn")
 | 
				
			||||||
 | 
						owner, ok := req.GetArguments()["owner"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("owner is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						repo, ok := req.GetArguments()["repo"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("repo is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						index, ok := req.GetArguments()["index"].(float64)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("index is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var reviewers []string
 | 
				
			||||||
 | 
						if reviewersArg, exists := req.GetArguments()["reviewers"]; exists {
 | 
				
			||||||
 | 
							if reviewersSlice, ok := reviewersArg.([]interface{}); ok {
 | 
				
			||||||
 | 
								for _, reviewer := range reviewersSlice {
 | 
				
			||||||
 | 
									if reviewerStr, ok := reviewer.(string); ok {
 | 
				
			||||||
 | 
										reviewers = append(reviewers, reviewerStr)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var teamReviewers []string
 | 
				
			||||||
 | 
						if teamReviewersArg, exists := req.GetArguments()["team_reviewers"]; exists {
 | 
				
			||||||
 | 
							if teamReviewersSlice, ok := teamReviewersArg.([]interface{}); ok {
 | 
				
			||||||
 | 
								for _, teamReviewer := range teamReviewersSlice {
 | 
				
			||||||
 | 
									if teamReviewerStr, ok := teamReviewer.(string); ok {
 | 
				
			||||||
 | 
										teamReviewers = append(teamReviewers, teamReviewerStr)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = client.CreateReviewRequests(owner, repo, int64(index), gitea_sdk.PullReviewRequestOptions{
 | 
				
			||||||
 | 
							Reviewers:     reviewers,
 | 
				
			||||||
 | 
							TeamReviewers: teamReviewers,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("create review requests for %v/%v/pr/%v err: %v", owner, repo, int64(index), err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Return a success message instead of the Response object which contains non-serializable functions
 | 
				
			||||||
 | 
						successMsg := map[string]interface{}{
 | 
				
			||||||
 | 
							"message":        "Successfully created review requests",
 | 
				
			||||||
 | 
							"reviewers":      reviewers,
 | 
				
			||||||
 | 
							"team_reviewers": teamReviewers,
 | 
				
			||||||
 | 
							"pr_index":       int64(index),
 | 
				
			||||||
 | 
							"repository":     fmt.Sprintf("%s/%s", owner, repo),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return to.TextResult(successMsg)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -76,7 +76,11 @@ func CreateBranchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	oldBranch, _ := req.GetArguments()["old_branch"].(string)
 | 
						oldBranch, _ := req.GetArguments()["old_branch"].(string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, _, err := gitea.Client().CreateBranch(owner, repo, gitea_sdk.CreateBranchOption{
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, _, err = client.CreateBranch(owner, repo, gitea_sdk.CreateBranchOption{
 | 
				
			||||||
		BranchName:    branch,
 | 
							BranchName:    branch,
 | 
				
			||||||
		OldBranchName: oldBranch,
 | 
							OldBranchName: oldBranch,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
@@ -101,7 +105,11 @@ func DeleteBranchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
 | 
				
			|||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("branch is required"))
 | 
							return to.ErrorResult(fmt.Errorf("branch is required"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_, _, err := gitea.Client().DeleteRepoBranch(owner, repo, branch)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, _, err = client.DeleteRepoBranch(owner, repo, branch)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("delete branch error: %v", err))
 | 
							return to.ErrorResult(fmt.Errorf("delete branch error: %v", err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -125,7 +133,11 @@ func ListBranchesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
 | 
				
			|||||||
			PageSize: 100,
 | 
								PageSize: 100,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	branches, _, err := gitea.Client().ListRepoBranches(owner, repo, opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						branches, _, err := client.ListRepoBranches(owner, repo, opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("list branches error: %v", err))
 | 
							return to.ErrorResult(fmt.Errorf("list branches error: %v", err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -63,7 +63,11 @@ func ListRepoCommitsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
 | 
				
			|||||||
		SHA:  sha,
 | 
							SHA:  sha,
 | 
				
			||||||
		Path: path,
 | 
							Path: path,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	commits, _, err := gitea.Client().ListRepoCommits(owner, repo, opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						commits, _, err := client.ListRepoCommits(owner, repo, opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("list repo commits err: %v", err))
 | 
							return to.ErrorResult(fmt.Errorf("list repo commits err: %v", err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -64,7 +64,7 @@ var (
 | 
				
			|||||||
		mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
 | 
							mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
 | 
				
			||||||
		mcp.WithString("filePath", mcp.Required(), mcp.Description("file path")),
 | 
							mcp.WithString("filePath", mcp.Required(), mcp.Description("file path")),
 | 
				
			||||||
		mcp.WithString("sha", mcp.Required(), mcp.Description("sha is the SHA for the file that already exists")),
 | 
							mcp.WithString("sha", mcp.Required(), mcp.Description("sha is the SHA for the file that already exists")),
 | 
				
			||||||
		mcp.WithString("content", mcp.Required(), mcp.Description("file content, base64 encoded")),
 | 
							mcp.WithString("content", mcp.Required(), mcp.Description("file content")),
 | 
				
			||||||
		mcp.WithString("message", mcp.Required(), mcp.Description("commit message")),
 | 
							mcp.WithString("message", mcp.Required(), mcp.Description("commit message")),
 | 
				
			||||||
		mcp.WithString("branch_name", mcp.Required(), mcp.Description("branch name")),
 | 
							mcp.WithString("branch_name", mcp.Required(), mcp.Description("branch name")),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
@@ -124,7 +124,11 @@ func GetFileContentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
 | 
				
			|||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("filePath is required"))
 | 
							return to.ErrorResult(fmt.Errorf("filePath is required"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	content, _, err := gitea.Client().GetContents(owner, repo, ref, filePath)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						content, _, err := client.GetContents(owner, repo, ref, filePath)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("get file err: %v", err))
 | 
							return to.ErrorResult(fmt.Errorf("get file err: %v", err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -184,7 +188,11 @@ func GetDirContentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
 | 
				
			|||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("filePath is required"))
 | 
							return to.ErrorResult(fmt.Errorf("filePath is required"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	content, _, err := gitea.Client().ListContents(owner, repo, ref, filePath)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						content, _, err := client.ListContents(owner, repo, ref, filePath)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("get dir content err: %v", err))
 | 
							return to.ErrorResult(fmt.Errorf("get dir content err: %v", err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -216,7 +224,11 @@ func CreateFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, _, err := gitea.Client().CreateFile(owner, repo, filePath, opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, _, err = client.CreateFile(owner, repo, filePath, opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("create file err: %v", err))
 | 
							return to.ErrorResult(fmt.Errorf("create file err: %v", err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -253,7 +265,11 @@ func UpdateFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
 | 
				
			|||||||
			BranchName: branchName,
 | 
								BranchName: branchName,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_, _, err := gitea.Client().UpdateFile(owner, repo, filePath, opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, _, err = client.UpdateFile(owner, repo, filePath, opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("update file err: %v", err))
 | 
							return to.ErrorResult(fmt.Errorf("update file err: %v", err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -287,7 +303,11 @@ func DeleteFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
		SHA: sha,
 | 
							SHA: sha,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_, err := gitea.Client().DeleteFile(owner, repo, filePath, opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, err = client.DeleteFile(owner, repo, filePath, opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("delete file err: %v", err))
 | 
							return to.ErrorResult(fmt.Errorf("delete file err: %v", err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -134,7 +134,11 @@ func CreateReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
 | 
				
			|||||||
	isPreRelease, _ := req.GetArguments()["is_pre_release"].(bool)
 | 
						isPreRelease, _ := req.GetArguments()["is_pre_release"].(bool)
 | 
				
			||||||
	body, _ := req.GetArguments()["body"].(string)
 | 
						body, _ := req.GetArguments()["body"].(string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, _, err := gitea.Client().CreateRelease(owner, repo, gitea_sdk.CreateReleaseOption{
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, _, err = client.CreateRelease(owner, repo, gitea_sdk.CreateReleaseOption{
 | 
				
			||||||
		TagName:      tagName,
 | 
							TagName:      tagName,
 | 
				
			||||||
		Target:       target,
 | 
							Target:       target,
 | 
				
			||||||
		Title:        title,
 | 
							Title:        title,
 | 
				
			||||||
@@ -164,7 +168,11 @@ func DeleteReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
 | 
				
			|||||||
		return nil, fmt.Errorf("id is required")
 | 
							return nil, fmt.Errorf("id is required")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, err := gitea.Client().DeleteRelease(owner, repo, int64(id))
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, err = client.DeleteRelease(owner, repo, int64(id))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("delete release error: %v", err)
 | 
							return nil, fmt.Errorf("delete release error: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -187,7 +195,11 @@ func GetReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
 | 
				
			|||||||
		return nil, fmt.Errorf("id is required")
 | 
							return nil, fmt.Errorf("id is required")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	release, _, err := gitea.Client().GetRelease(owner, repo, int64(id))
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						release, _, err := client.GetRelease(owner, repo, int64(id))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("get release error: %v", err)
 | 
							return nil, fmt.Errorf("get release error: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -206,7 +218,11 @@ func GetLatestReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
 | 
				
			|||||||
		return nil, fmt.Errorf("repo is required")
 | 
							return nil, fmt.Errorf("repo is required")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	release, _, err := gitea.Client().GetLatestRelease(owner, repo)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						release, _, err := client.GetLatestRelease(owner, repo)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("get latest release error: %v", err)
 | 
							return nil, fmt.Errorf("get latest release error: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -237,7 +253,11 @@ func ListReleasesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
 | 
				
			|||||||
	page, _ := req.GetArguments()["page"].(float64)
 | 
						page, _ := req.GetArguments()["page"].(float64)
 | 
				
			||||||
	pageSize, _ := req.GetArguments()["pageSize"].(float64)
 | 
						pageSize, _ := req.GetArguments()["pageSize"].(float64)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	releases, _, err := gitea.Client().ListReleases(owner, repo, gitea_sdk.ListReleasesOptions{
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						releases, _, err := client.ListReleases(owner, repo, gitea_sdk.ListReleasesOptions{
 | 
				
			||||||
		ListOptions: gitea_sdk.ListOptions{
 | 
							ListOptions: gitea_sdk.ListOptions{
 | 
				
			||||||
			Page:     int(page),
 | 
								Page:     int(page),
 | 
				
			||||||
			PageSize: int(pageSize),
 | 
								PageSize: int(pageSize),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,7 +27,7 @@ const (
 | 
				
			|||||||
var (
 | 
					var (
 | 
				
			||||||
	CreateRepoTool = mcp.NewTool(
 | 
						CreateRepoTool = mcp.NewTool(
 | 
				
			||||||
		CreateRepoToolName,
 | 
							CreateRepoToolName,
 | 
				
			||||||
		mcp.WithDescription("Create repository"),
 | 
							mcp.WithDescription("Create repository in personal account or organization"),
 | 
				
			||||||
		mcp.WithString("name", mcp.Required(), mcp.Description("Name of the repository to create")),
 | 
							mcp.WithString("name", mcp.Required(), mcp.Description("Name of the repository to create")),
 | 
				
			||||||
		mcp.WithString("description", mcp.Description("Description of the repository to create")),
 | 
							mcp.WithString("description", mcp.Description("Description of the repository to create")),
 | 
				
			||||||
		mcp.WithBoolean("private", mcp.Description("Whether the repository is private")),
 | 
							mcp.WithBoolean("private", mcp.Description("Whether the repository is private")),
 | 
				
			||||||
@@ -38,6 +38,7 @@ var (
 | 
				
			|||||||
		mcp.WithString("license", mcp.Description("License to use")),
 | 
							mcp.WithString("license", mcp.Description("License to use")),
 | 
				
			||||||
		mcp.WithString("readme", mcp.Description("Readme of the repository to create")),
 | 
							mcp.WithString("readme", mcp.Description("Readme of the repository to create")),
 | 
				
			||||||
		mcp.WithString("default_branch", mcp.Description("DefaultBranch of the repository (used when initializes and in template)")),
 | 
							mcp.WithString("default_branch", mcp.Description("DefaultBranch of the repository (used when initializes and in template)")),
 | 
				
			||||||
 | 
							mcp.WithString("organization", mcp.Description("Organization name to create repository in (optional - defaults to personal account)")),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ForkRepoTool = mcp.NewTool(
 | 
						ForkRepoTool = mcp.NewTool(
 | 
				
			||||||
@@ -120,6 +121,7 @@ func CreateRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
 | 
				
			|||||||
	license, _ := req.GetArguments()["license"].(string)
 | 
						license, _ := req.GetArguments()["license"].(string)
 | 
				
			||||||
	readme, _ := req.GetArguments()["readme"].(string)
 | 
						readme, _ := req.GetArguments()["readme"].(string)
 | 
				
			||||||
	defaultBranch, _ := req.GetArguments()["default_branch"].(string)
 | 
						defaultBranch, _ := req.GetArguments()["default_branch"].(string)
 | 
				
			||||||
 | 
						organization, _ := req.GetArguments()["organization"].(string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	opt := gitea_sdk.CreateRepoOption{
 | 
						opt := gitea_sdk.CreateRepoOption{
 | 
				
			||||||
		Name:          name,
 | 
							Name:          name,
 | 
				
			||||||
@@ -133,9 +135,22 @@ func CreateRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
 | 
				
			|||||||
		Readme:        readme,
 | 
							Readme:        readme,
 | 
				
			||||||
		DefaultBranch: defaultBranch,
 | 
							DefaultBranch: defaultBranch,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	repo, _, err := gitea.Client().CreateRepo(opt)
 | 
					
 | 
				
			||||||
 | 
						var repo *gitea_sdk.Repository
 | 
				
			||||||
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("create repo err: %v", err))
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if organization != "" {
 | 
				
			||||||
 | 
							repo, _, err = client.CreateOrgRepo(organization, opt)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return to.ErrorResult(fmt.Errorf("create organization repository '%s' in '%s' err: %v", name, organization, err))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							repo, _, err = client.CreateRepo(opt)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return to.ErrorResult(fmt.Errorf("create repository '%s' err: %v", name, err))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return to.TextResult(repo)
 | 
						return to.TextResult(repo)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -164,7 +179,11 @@ func ForkRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResu
 | 
				
			|||||||
		Organization: organizationPtr,
 | 
							Organization: organizationPtr,
 | 
				
			||||||
		Name:         namePtr,
 | 
							Name:         namePtr,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_, _, err := gitea.Client().CreateFork(user, repo, opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, _, err = client.CreateFork(user, repo, opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("fork repository error: %v", err))
 | 
							return to.ErrorResult(fmt.Errorf("fork repository error: %v", err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -187,7 +206,11 @@ func ListMyReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
 | 
				
			|||||||
			PageSize: int(pageSize),
 | 
								PageSize: int(pageSize),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	repos, _, err := gitea.Client().ListMyRepos(opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						repos, _, err := client.ListMyRepos(opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("list my repositories error: %v", err))
 | 
							return to.ErrorResult(fmt.Errorf("list my repositories error: %v", err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -102,7 +102,11 @@ func CreateTagFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRes
 | 
				
			|||||||
	target, _ := req.GetArguments()["target"].(string)
 | 
						target, _ := req.GetArguments()["target"].(string)
 | 
				
			||||||
	message, _ := req.GetArguments()["message"].(string)
 | 
						message, _ := req.GetArguments()["message"].(string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, _, err := gitea.Client().CreateTag(owner, repo, gitea_sdk.CreateTagOption{
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, _, err = client.CreateTag(owner, repo, gitea_sdk.CreateTagOption{
 | 
				
			||||||
		TagName: tagName,
 | 
							TagName: tagName,
 | 
				
			||||||
		Target:  target,
 | 
							Target:  target,
 | 
				
			||||||
		Message: message,
 | 
							Message: message,
 | 
				
			||||||
@@ -129,7 +133,11 @@ func DeleteTagFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRes
 | 
				
			|||||||
		return nil, fmt.Errorf("tag_name is required")
 | 
							return nil, fmt.Errorf("tag_name is required")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, err := gitea.Client().DeleteTag(owner, repo, tagName)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, err = client.DeleteTag(owner, repo, tagName)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("delete tag error: %v", err)
 | 
							return nil, fmt.Errorf("delete tag error: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -152,7 +160,11 @@ func GetTagFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult
 | 
				
			|||||||
		return nil, fmt.Errorf("tag_name is required")
 | 
							return nil, fmt.Errorf("tag_name is required")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tag, _, err := gitea.Client().GetTag(owner, repo, tagName)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						tag, _, err := client.GetTag(owner, repo, tagName)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("get tag error: %v", err)
 | 
							return nil, fmt.Errorf("get tag error: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -173,7 +185,11 @@ func ListTagsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResu
 | 
				
			|||||||
	page, _ := req.GetArguments()["page"].(float64)
 | 
						page, _ := req.GetArguments()["page"].(float64)
 | 
				
			||||||
	pageSize, _ := req.GetArguments()["pageSize"].(float64)
 | 
						pageSize, _ := req.GetArguments()["pageSize"].(float64)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tags, _, err := gitea.Client().ListRepoTags(owner, repo, gitea_sdk.ListRepoTagsOptions{
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						tags, _, err := client.ListRepoTags(owner, repo, gitea_sdk.ListRepoTagsOptions{
 | 
				
			||||||
		ListOptions: gitea_sdk.ListOptions{
 | 
							ListOptions: gitea_sdk.ListOptions{
 | 
				
			||||||
			Page:     int(page),
 | 
								Page:     int(page),
 | 
				
			||||||
			PageSize: int(pageSize),
 | 
								PageSize: int(pageSize),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -94,7 +94,11 @@ func SearchUsersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
 | 
				
			|||||||
			PageSize: int(pageSize),
 | 
								PageSize: int(pageSize),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	users, _, err := gitea.Client().SearchUsers(opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						users, _, err := client.SearchUsers(opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("search users err: %v", err))
 | 
							return to.ErrorResult(fmt.Errorf("search users err: %v", err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -128,7 +132,11 @@ func SearchOrgTeamsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
 | 
				
			|||||||
			PageSize: int(pageSize),
 | 
								PageSize: int(pageSize),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	teams, _, err := gitea.Client().SearchOrgTeams(org, &opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						teams, _, err := client.SearchOrgTeams(org, &opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("search organization teams error: %v", err))
 | 
							return to.ErrorResult(fmt.Errorf("search organization teams error: %v", err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -178,7 +186,11 @@ func SearchReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
 | 
				
			|||||||
			PageSize: int(pageSize),
 | 
								PageSize: int(pageSize),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	repos, _, err := gitea.Client().SearchRepos(opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						repos, _, err := client.SearchRepos(opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("search repos error: %v", err))
 | 
							return to.ErrorResult(fmt.Errorf("search repos error: %v", err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -79,7 +79,11 @@ func getIntArg(req mcp.CallToolRequest, name string, def int) int {
 | 
				
			|||||||
// Logs invocation, fetches current user info from gitea, wraps result for MCP.
 | 
					// Logs invocation, fetches current user info from gitea, wraps result for MCP.
 | 
				
			||||||
func GetUserInfoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
					func GetUserInfoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
	log.Debugf("[User] Called GetUserInfoFn")
 | 
						log.Debugf("[User] Called GetUserInfoFn")
 | 
				
			||||||
	user, _, err := gitea.Client().GetMyUserInfo()
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						user, _, err := client.GetMyUserInfo()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("get user info err: %v", err))
 | 
							return to.ErrorResult(fmt.Errorf("get user info err: %v", err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -100,7 +104,11 @@ func GetUserOrgsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
 | 
				
			|||||||
			PageSize: pageSize,
 | 
								PageSize: pageSize,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	orgs, _, err := gitea.Client().ListMyOrgs(opt)
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						orgs, _, err := client.ListMyOrgs(opt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return to.ErrorResult(fmt.Errorf("get user orgs err: %v", err))
 | 
							return to.ErrorResult(fmt.Errorf("get user orgs err: %v", err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										363
									
								
								operation/wiki/wiki.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										363
									
								
								operation/wiki/wiki.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,363 @@
 | 
				
			|||||||
 | 
					package wiki
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.com/gitea/gitea-mcp/pkg/flag"
 | 
				
			||||||
 | 
						"gitea.com/gitea/gitea-mcp/pkg/gitea"
 | 
				
			||||||
 | 
						"gitea.com/gitea/gitea-mcp/pkg/log"
 | 
				
			||||||
 | 
						"gitea.com/gitea/gitea-mcp/pkg/to"
 | 
				
			||||||
 | 
						"gitea.com/gitea/gitea-mcp/pkg/tool"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/mark3labs/mcp-go/mcp"
 | 
				
			||||||
 | 
						"github.com/mark3labs/mcp-go/server"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var Tool = tool.New()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						ListWikiPagesToolName    = "list_wiki_pages"
 | 
				
			||||||
 | 
						GetWikiPageToolName      = "get_wiki_page"
 | 
				
			||||||
 | 
						GetWikiRevisionsToolName = "get_wiki_revisions"
 | 
				
			||||||
 | 
						CreateWikiPageToolName   = "create_wiki_page"
 | 
				
			||||||
 | 
						UpdateWikiPageToolName   = "update_wiki_page"
 | 
				
			||||||
 | 
						DeleteWikiPageToolName   = "delete_wiki_page"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						ListWikiPagesTool = mcp.NewTool(
 | 
				
			||||||
 | 
							ListWikiPagesToolName,
 | 
				
			||||||
 | 
							mcp.WithDescription("List all wiki pages in a repository"),
 | 
				
			||||||
 | 
							mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
 | 
				
			||||||
 | 
							mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						GetWikiPageTool = mcp.NewTool(
 | 
				
			||||||
 | 
							GetWikiPageToolName,
 | 
				
			||||||
 | 
							mcp.WithDescription("Get a wiki page content and metadata"),
 | 
				
			||||||
 | 
							mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
 | 
				
			||||||
 | 
							mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
 | 
				
			||||||
 | 
							mcp.WithString("pageName", mcp.Required(), mcp.Description("wiki page name")),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						GetWikiRevisionsTool = mcp.NewTool(
 | 
				
			||||||
 | 
							GetWikiRevisionsToolName,
 | 
				
			||||||
 | 
							mcp.WithDescription("Get revisions history of a wiki page"),
 | 
				
			||||||
 | 
							mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
 | 
				
			||||||
 | 
							mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
 | 
				
			||||||
 | 
							mcp.WithString("pageName", mcp.Required(), mcp.Description("wiki page name")),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CreateWikiPageTool = mcp.NewTool(
 | 
				
			||||||
 | 
							CreateWikiPageToolName,
 | 
				
			||||||
 | 
							mcp.WithDescription("Create a new wiki page"),
 | 
				
			||||||
 | 
							mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
 | 
				
			||||||
 | 
							mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
 | 
				
			||||||
 | 
							mcp.WithString("title", mcp.Required(), mcp.Description("wiki page title")),
 | 
				
			||||||
 | 
							mcp.WithString("content_base64", mcp.Required(), mcp.Description("page content, base64 encoded")),
 | 
				
			||||||
 | 
							mcp.WithString("message", mcp.Description("commit message (optional)")),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						UpdateWikiPageTool = mcp.NewTool(
 | 
				
			||||||
 | 
							UpdateWikiPageToolName,
 | 
				
			||||||
 | 
							mcp.WithDescription("Update an existing wiki page"),
 | 
				
			||||||
 | 
							mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
 | 
				
			||||||
 | 
							mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
 | 
				
			||||||
 | 
							mcp.WithString("pageName", mcp.Required(), mcp.Description("current wiki page name")),
 | 
				
			||||||
 | 
							mcp.WithString("title", mcp.Description("new page title (optional)")),
 | 
				
			||||||
 | 
							mcp.WithString("content_base64", mcp.Required(), mcp.Description("page content, base64 encoded")),
 | 
				
			||||||
 | 
							mcp.WithString("message", mcp.Description("commit message (optional)")),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DeleteWikiPageTool = mcp.NewTool(
 | 
				
			||||||
 | 
							DeleteWikiPageToolName,
 | 
				
			||||||
 | 
							mcp.WithDescription("Delete a wiki page"),
 | 
				
			||||||
 | 
							mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
 | 
				
			||||||
 | 
							mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
 | 
				
			||||||
 | 
							mcp.WithString("pageName", mcp.Required(), mcp.Description("wiki page name to delete")),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						Tool.RegisterRead(server.ServerTool{
 | 
				
			||||||
 | 
							Tool:    ListWikiPagesTool,
 | 
				
			||||||
 | 
							Handler: ListWikiPagesFn,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						Tool.RegisterRead(server.ServerTool{
 | 
				
			||||||
 | 
							Tool:    GetWikiPageTool,
 | 
				
			||||||
 | 
							Handler: GetWikiPageFn,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						Tool.RegisterRead(server.ServerTool{
 | 
				
			||||||
 | 
							Tool:    GetWikiRevisionsTool,
 | 
				
			||||||
 | 
							Handler: GetWikiRevisionsFn,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						Tool.RegisterWrite(server.ServerTool{
 | 
				
			||||||
 | 
							Tool:    CreateWikiPageTool,
 | 
				
			||||||
 | 
							Handler: CreateWikiPageFn,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						Tool.RegisterWrite(server.ServerTool{
 | 
				
			||||||
 | 
							Tool:    UpdateWikiPageTool,
 | 
				
			||||||
 | 
							Handler: UpdateWikiPageFn,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						Tool.RegisterWrite(server.ServerTool{
 | 
				
			||||||
 | 
							Tool:    DeleteWikiPageTool,
 | 
				
			||||||
 | 
							Handler: DeleteWikiPageFn,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ListWikiPagesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
						log.Debugf("Called ListWikiPagesFn")
 | 
				
			||||||
 | 
						owner, ok := req.GetArguments()["owner"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("owner is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						repo, ok := req.GetArguments()["repo"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("repo is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Use direct HTTP request because SDK does not support yet wikis
 | 
				
			||||||
 | 
						result, err := makeWikiAPIRequest(ctx, client, "GET", fmt.Sprintf("repos/%s/%s/wiki/pages", url.QueryEscape(owner), url.QueryEscape(repo)), nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("list wiki pages err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return to.TextResult(result)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetWikiPageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
						log.Debugf("Called GetWikiPageFn")
 | 
				
			||||||
 | 
						owner, ok := req.GetArguments()["owner"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("owner is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						repo, ok := req.GetArguments()["repo"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("repo is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pageName, ok := req.GetArguments()["pageName"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("pageName is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result, err := makeWikiAPIRequest(ctx, client, "GET", fmt.Sprintf("repos/%s/%s/wiki/page/%s", url.QueryEscape(owner), url.QueryEscape(repo), url.QueryEscape(pageName)), nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get wiki page err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return to.TextResult(result)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetWikiRevisionsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
						log.Debugf("Called GetWikiRevisionsFn")
 | 
				
			||||||
 | 
						owner, ok := req.GetArguments()["owner"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("owner is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						repo, ok := req.GetArguments()["repo"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("repo is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pageName, ok := req.GetArguments()["pageName"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("pageName is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result, err := makeWikiAPIRequest(ctx, client, "GET", fmt.Sprintf("repos/%s/%s/wiki/revisions/%s", url.QueryEscape(owner), url.QueryEscape(repo), url.QueryEscape(pageName)), nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get wiki revisions err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return to.TextResult(result)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func CreateWikiPageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
						log.Debugf("Called CreateWikiPageFn")
 | 
				
			||||||
 | 
						owner, ok := req.GetArguments()["owner"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("owner is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						repo, ok := req.GetArguments()["repo"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("repo is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						title, ok := req.GetArguments()["title"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("title is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						contentBase64, ok := req.GetArguments()["content_base64"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("content_base64 is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						message, _ := req.GetArguments()["message"].(string)
 | 
				
			||||||
 | 
						if message == "" {
 | 
				
			||||||
 | 
							message = fmt.Sprintf("Create wiki page '%s'", title)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						requestBody := map[string]string{
 | 
				
			||||||
 | 
							"title":          title,
 | 
				
			||||||
 | 
							"content_base64": contentBase64,
 | 
				
			||||||
 | 
							"message":        message,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result, err := makeWikiAPIRequest(ctx, client, "POST", fmt.Sprintf("repos/%s/%s/wiki/new", url.QueryEscape(owner), url.QueryEscape(repo)), requestBody)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("create wiki page err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return to.TextResult(result)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func UpdateWikiPageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
						log.Debugf("Called UpdateWikiPageFn")
 | 
				
			||||||
 | 
						owner, ok := req.GetArguments()["owner"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("owner is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						repo, ok := req.GetArguments()["repo"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("repo is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pageName, ok := req.GetArguments()["pageName"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("pageName is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						contentBase64, ok := req.GetArguments()["content_base64"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("content_base64 is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						requestBody := map[string]string{
 | 
				
			||||||
 | 
							"content_base64": contentBase64,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If title is given, use it. Otherwise, keep current page name
 | 
				
			||||||
 | 
						if title, ok := req.GetArguments()["title"].(string); ok && title != "" {
 | 
				
			||||||
 | 
							requestBody["title"] = title
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// Utiliser pageName comme fallback pour éviter "unnamed"
 | 
				
			||||||
 | 
							requestBody["title"] = pageName
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if message, ok := req.GetArguments()["message"].(string); ok && message != "" {
 | 
				
			||||||
 | 
							requestBody["message"] = message
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							requestBody["message"] = fmt.Sprintf("Update wiki page '%s'", pageName)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result, err := makeWikiAPIRequest(ctx, client, "PATCH", fmt.Sprintf("repos/%s/%s/wiki/page/%s", url.QueryEscape(owner), url.QueryEscape(repo), url.QueryEscape(pageName)), requestBody)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("update wiki page err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return to.TextResult(result)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func DeleteWikiPageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
						log.Debugf("Called DeleteWikiPageFn")
 | 
				
			||||||
 | 
						owner, ok := req.GetArguments()["owner"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("owner is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						repo, ok := req.GetArguments()["repo"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("repo is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pageName, ok := req.GetArguments()["pageName"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("pageName is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client, err := gitea.ClientFromContext(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = makeWikiAPIRequest(ctx, client, "DELETE", fmt.Sprintf("repos/%s/%s/wiki/page/%s", url.QueryEscape(owner), url.QueryEscape(repo), url.QueryEscape(pageName)), nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("delete wiki page err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return to.TextResult(map[string]string{"message": "Wiki page deleted successfully"})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Helper function to make HTTP requests to Gitea Wiki API
 | 
				
			||||||
 | 
					func makeWikiAPIRequest(ctx context.Context, client interface{}, method, path string, body interface{}) (interface{}, error) {
 | 
				
			||||||
 | 
						// Use flags to get base URL and token
 | 
				
			||||||
 | 
						apiURL := fmt.Sprintf("%s/api/v1/%s", flag.Host, path)
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						httpClient := &http.Client{}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						var reqBody io.Reader
 | 
				
			||||||
 | 
						if body != nil {
 | 
				
			||||||
 | 
							bodyBytes, err := json.Marshal(body)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, fmt.Errorf("failed to marshal request body: %w", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							reqBody = strings.NewReader(string(bodyBytes))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req, err := http.NewRequestWithContext(ctx, method, apiURL, reqBody)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed to create request: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req.Header.Set("Authorization", fmt.Sprintf("token %s", flag.Token))
 | 
				
			||||||
 | 
						req.Header.Set("Accept", "application/json")
 | 
				
			||||||
 | 
						if body != nil {
 | 
				
			||||||
 | 
							req.Header.Set("Content-Type", "application/json")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp, err := httpClient.Do(req)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed to make request: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if resp.StatusCode < 200 || resp.StatusCode >= 300 {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("API request failed with status %d", resp.StatusCode)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if method == "DELETE" {
 | 
				
			||||||
 | 
							return map[string]string{"message": "success"}, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var result interface{}
 | 
				
			||||||
 | 
						if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed to decode response: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return result, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										7
									
								
								pkg/context/context.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								pkg/context/context.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					package context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type contextKey string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						TokenContextKey = contextKey("token")
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
@@ -1,52 +1,47 @@
 | 
				
			|||||||
package gitea
 | 
					package gitea
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
	"crypto/tls"
 | 
						"crypto/tls"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"sync"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"gitea.com/gitea/gitea-mcp/pkg/flag"
 | 
					 | 
				
			||||||
	"gitea.com/gitea/gitea-mcp/pkg/log"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/sdk/gitea"
 | 
						"code.gitea.io/sdk/gitea"
 | 
				
			||||||
 | 
						mcpContext "gitea.com/gitea/gitea-mcp/pkg/context"
 | 
				
			||||||
 | 
						"gitea.com/gitea/gitea-mcp/pkg/flag"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					func NewClient(token string) (*gitea.Client, error) {
 | 
				
			||||||
	client     *gitea.Client
 | 
						httpClient := &http.Client{
 | 
				
			||||||
	clientOnce sync.Once
 | 
							Transport: http.DefaultTransport,
 | 
				
			||||||
)
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Client() *gitea.Client {
 | 
						opts := []gitea.ClientOption{
 | 
				
			||||||
	clientOnce.Do(func() {
 | 
							gitea.SetToken(token),
 | 
				
			||||||
		var err error
 | 
						}
 | 
				
			||||||
		if client != nil {
 | 
						if flag.Insecure {
 | 
				
			||||||
			return
 | 
							httpClient.Transport.(*http.Transport).TLSClientConfig = &tls.Config{
 | 
				
			||||||
 | 
								InsecureSkipVerify: true,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						opts = append(opts, gitea.SetHTTPClient(httpClient))
 | 
				
			||||||
 | 
						if flag.Debug {
 | 
				
			||||||
 | 
							opts = append(opts, gitea.SetDebugMode())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						client, err := gitea.NewClient(flag.Host, opts...)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("create gitea client err: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		httpClient := &http.Client{
 | 
						// Set user agent for the client
 | 
				
			||||||
			Transport: http.DefaultTransport,
 | 
						client.SetUserAgent(fmt.Sprintf("gitea-mcp-server/%s", flag.Version))
 | 
				
			||||||
		}
 | 
						return client, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		opts := []gitea.ClientOption{
 | 
					
 | 
				
			||||||
			gitea.SetToken(flag.Token),
 | 
					func ClientFromContext(ctx context.Context) (*gitea.Client, error) {
 | 
				
			||||||
		}
 | 
						token, ok := ctx.Value(mcpContext.TokenContextKey).(string)
 | 
				
			||||||
		if flag.Insecure {
 | 
						if !ok {
 | 
				
			||||||
			httpClient.Transport.(*http.Transport).TLSClientConfig = &tls.Config{
 | 
							token = flag.Token
 | 
				
			||||||
				InsecureSkipVerify: true,
 | 
						}
 | 
				
			||||||
			}
 | 
						return NewClient(token)
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		opts = append(opts, gitea.SetHTTPClient(httpClient))
 | 
					 | 
				
			||||||
		if flag.Debug {
 | 
					 | 
				
			||||||
			opts = append(opts, gitea.SetDebugMode())
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		client, err = gitea.NewClient(flag.Host, opts...)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			log.Fatalf("create gitea client err: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Set user agent for the client
 | 
					 | 
				
			||||||
		client.SetUserAgent(fmt.Sprintf("gitea-mcp-server/%s", flag.Version))
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	return client
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,7 +48,7 @@ func Default() *zap.Logger {
 | 
				
			|||||||
			MaxAge:     30,
 | 
								MaxAge:     30,
 | 
				
			||||||
		}))
 | 
							}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if flag.Mode == "http" || flag.Mode == "sse" {
 | 
							if flag.Mode == "http" {
 | 
				
			||||||
			wss = append(wss, zapcore.AddSync(os.Stdout))
 | 
								wss = append(wss, zapcore.AddSync(os.Stdout))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user