mirror of
				https://gitea.com/gitea/gitea-mcp.git
				synced 2025-11-04 04:11:50 +00:00 
			
		
		
		
	Compare commits
	
		
			28 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					de311344cd | ||
| 
						 | 
					d7addd56c4 | ||
| 
						 | 
					dc3e120e97 | ||
| 
						 | 
					f33b04a3df | ||
| 
						 | 
					ba07925969 | ||
| 
						 | 
					5c2ff6dcb2 | ||
| 
						 | 
					feaedaf604 | ||
| 
						 | 
					a601d6b698 | ||
| 
						 | 
					62cb6e7830 | ||
| 
						 | 
					9fff996294 | ||
| 
						 | 
					4c3f5149d8 | ||
| 
						 | 
					eb6b5a8f92 | ||
| 
						 | 
					1d9bdb5b44 | ||
| 
						 | 
					093cddbcb6 | ||
| 
						 | 
					5dbfe21127 | ||
| 
						 | 
					b85a523983 | ||
| 
						 | 
					da08718e24 | ||
| 
						 | 
					44ea8969f4 | ||
| 
						 | 
					94aa8dc572 | ||
| 
						 | 
					05194ffc1c | ||
| 
						 | 
					5c329129f8 | ||
| 
						 | 
					52ccf92761 | ||
| 
						 | 
					061ea86b0b | ||
| 
						 | 
					f14b60fe56 | ||
| 
						 | 
					94782a85b6 | ||
| 
						 | 
					e94dd26b30 | ||
| 
						 | 
					da49bdeb96 | ||
| 
						 | 
					3f61299f72 | 
							
								
								
									
										52
									
								
								.air.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								.air.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					root = "."
 | 
				
			||||||
 | 
					testdata_dir = "testdata"
 | 
				
			||||||
 | 
					tmp_dir = "tmp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[build]
 | 
				
			||||||
 | 
					  args_bin = ["-t", "http"]
 | 
				
			||||||
 | 
					  bin = "./gitea-mcp"
 | 
				
			||||||
 | 
					  cmd = "make build"
 | 
				
			||||||
 | 
					  delay = 1000
 | 
				
			||||||
 | 
					  exclude_dir = ["assets", "tmp", "vendor", "testdata"]
 | 
				
			||||||
 | 
					  exclude_file = []
 | 
				
			||||||
 | 
					  exclude_regex = ["_test.go"]
 | 
				
			||||||
 | 
					  exclude_unchanged = false
 | 
				
			||||||
 | 
					  follow_symlink = false
 | 
				
			||||||
 | 
					  full_bin = ""
 | 
				
			||||||
 | 
					  include_dir = []
 | 
				
			||||||
 | 
					  include_ext = ["go", "tpl", "tmpl", "html"]
 | 
				
			||||||
 | 
					  include_file = []
 | 
				
			||||||
 | 
					  kill_delay = "0s"
 | 
				
			||||||
 | 
					  log = "build-errors.log"
 | 
				
			||||||
 | 
					  poll = false
 | 
				
			||||||
 | 
					  poll_interval = 0
 | 
				
			||||||
 | 
					  post_cmd = []
 | 
				
			||||||
 | 
					  pre_cmd = []
 | 
				
			||||||
 | 
					  rerun = false
 | 
				
			||||||
 | 
					  rerun_delay = 500
 | 
				
			||||||
 | 
					  send_interrupt = false
 | 
				
			||||||
 | 
					  stop_on_error = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[color]
 | 
				
			||||||
 | 
					  app = ""
 | 
				
			||||||
 | 
					  build = "yellow"
 | 
				
			||||||
 | 
					  main = "magenta"
 | 
				
			||||||
 | 
					  runner = "green"
 | 
				
			||||||
 | 
					  watcher = "cyan"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[log]
 | 
				
			||||||
 | 
					  main_only = false
 | 
				
			||||||
 | 
					  silent = false
 | 
				
			||||||
 | 
					  time = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[misc]
 | 
				
			||||||
 | 
					  clean_on_exit = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[proxy]
 | 
				
			||||||
 | 
					  app_port = 0
 | 
				
			||||||
 | 
					  enabled = false
 | 
				
			||||||
 | 
					  proxy_port = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[screen]
 | 
				
			||||||
 | 
					  clear_on_rebuild = false
 | 
				
			||||||
 | 
					  keep_scroll = true
 | 
				
			||||||
@@ -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 }}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -2,3 +2,4 @@
 | 
				
			|||||||
gitea-mcp
 | 
					gitea-mcp
 | 
				
			||||||
gitea-mcp.exe
 | 
					gitea-mcp.exe
 | 
				
			||||||
*.log
 | 
					*.log
 | 
				
			||||||
 | 
					tmp
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								.vscode/mcp.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.vscode/mcp.json
									
									
									
									
										vendored
									
									
								
							@@ -21,7 +21,7 @@
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "servers": {
 | 
					  "servers": {
 | 
				
			||||||
    "gitea": {
 | 
					    "gitea-mcp-stdio": {
 | 
				
			||||||
      "type": "stdio",
 | 
					      "type": "stdio",
 | 
				
			||||||
      "command": "gitea-mcp",
 | 
					      "command": "gitea-mcp",
 | 
				
			||||||
      "args": ["-t", "stdio"],
 | 
					      "args": ["-t", "stdio"],
 | 
				
			||||||
@@ -30,6 +30,10 @@
 | 
				
			|||||||
            "GITEA_ACCESS_TOKEN": "${input:gitea-token}",
 | 
					            "GITEA_ACCESS_TOKEN": "${input:gitea-token}",
 | 
				
			||||||
            "GITEA_INSECURE": "${input:gitea-insecure}"
 | 
					            "GITEA_INSECURE": "${input:gitea-insecure}"
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "gitea-mcp-http": {
 | 
				
			||||||
 | 
					      "type": "http",
 | 
				
			||||||
 | 
					      "url": "http://localhost:8080/mcp",
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										39
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								Dockerfile
									
									
									
									
									
								
							@@ -1,39 +1,32 @@
 | 
				
			|||||||
 | 
					# syntax=docker/dockerfile:1.4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Build stage
 | 
					# Build stage
 | 
				
			||||||
FROM golang:1.24-bullseye AS builder
 | 
					FROM --platform=$BUILDPLATFORM golang:1.24-alpine AS builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ARG VERSION
 | 
					ARG VERSION=dev
 | 
				
			||||||
 | 
					ARG TARGETOS
 | 
				
			||||||
 | 
					ARG TARGETARCH
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Set the working directory
 | 
					 | 
				
			||||||
WORKDIR /app
 | 
					WORKDIR /app
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Copy go.mod and go.sum files
 | 
					 | 
				
			||||||
COPY go.mod go.sum ./
 | 
					COPY go.mod go.sum ./
 | 
				
			||||||
 | 
					RUN --mount=type=cache,target=/go/pkg/mod \
 | 
				
			||||||
 | 
					    go mod download
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Download dependencies
 | 
					 | 
				
			||||||
RUN go mod download
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Copy the source code
 | 
					 | 
				
			||||||
COPY . .
 | 
					COPY . .
 | 
				
			||||||
 | 
					RUN --mount=type=cache,target=/go/pkg/mod \
 | 
				
			||||||
RUN CGO_ENABLED=0 go build -ldflags="-s -w -X main.Version=${VERSION}" -o gitea-mcp
 | 
					    --mount=type=cache,target=/root/.cache/go-build \
 | 
				
			||||||
 | 
					    CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH:-amd64} \
 | 
				
			||||||
 | 
					    go build -trimpath -ldflags="-s -w -X main.Version=${VERSION}" -o gitea-mcp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Final stage
 | 
					# Final stage
 | 
				
			||||||
FROM debian:bullseye-slim
 | 
					FROM gcr.io/distroless/static-debian12:nonroot
 | 
				
			||||||
 | 
					 | 
				
			||||||
ENV GITEA_MODE=stdio
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
WORKDIR /app
 | 
					WORKDIR /app
 | 
				
			||||||
 | 
					COPY --from=builder --chown=nonroot:nonroot /app/gitea-mcp .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Install ca-certificates for HTTPS requests
 | 
					USER nonroot:nonroot
 | 
				
			||||||
RUN apt-get update && \
 | 
					 | 
				
			||||||
    apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Create a non-root user
 | 
					LABEL org.opencontainers.image.version="${VERSION}"
 | 
				
			||||||
RUN useradd -r -u 1000 -m gitea-mcp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
COPY --from=builder --chown=1000:1000 /app/gitea-mcp .
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Use the non-root user
 | 
					 | 
				
			||||||
USER gitea-mcp
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
CMD ["/app/gitea-mcp"]
 | 
					CMD ["/app/gitea-mcp"]
 | 
				
			||||||
							
								
								
									
										15
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								README.md
									
									
									
									
									
								
							@@ -54,7 +54,7 @@ Optionally, you can add it to a file called `.vscode/mcp.json` in your workspace
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "servers": {
 | 
					    "servers": {
 | 
				
			||||||
      "github": {
 | 
					      "gitea-mcp": {
 | 
				
			||||||
        "command": "docker",
 | 
					        "command": "docker",
 | 
				
			||||||
        "args": [
 | 
					        "args": [
 | 
				
			||||||
          "run",
 | 
					          "run",
 | 
				
			||||||
@@ -139,7 +139,10 @@ To configure the MCP server for Gitea, add the following to your MCP configurati
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "mcpServers": {
 | 
					  "mcpServers": {
 | 
				
			||||||
    "gitea": {
 | 
					    "gitea": {
 | 
				
			||||||
      "url": "http://localhost:8080/sse"
 | 
					      "url": "http://localhost:8080/sse",
 | 
				
			||||||
 | 
					      "headers": {
 | 
				
			||||||
 | 
					        "Authorization": "Bearer <your personal access token>"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -151,7 +154,10 @@ To configure the MCP server for Gitea, add the following to your MCP configurati
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "mcpServers": {
 | 
					  "mcpServers": {
 | 
				
			||||||
    "gitea": {
 | 
					    "gitea": {
 | 
				
			||||||
      "url": "http://localhost:8080/mcp"
 | 
					      "url": "http://localhost:8080/mcp",
 | 
				
			||||||
 | 
					      "headers": {
 | 
				
			||||||
 | 
					        "Authorization": "Bearer <your personal access token>"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -194,6 +200,7 @@ The Gitea MCP Server supports the following tools:
 | 
				
			|||||||
|          list_tags           |     Tag      |              List all tags in a repository               |
 | 
					|          list_tags           |     Tag      |              List all tags in a repository               |
 | 
				
			||||||
|      list_repo_commits       |    Commit    |             List all commits in a repository             |
 | 
					|      list_repo_commits       |    Commit    |             List all commits in a repository             |
 | 
				
			||||||
|       get_file_content       |     File     |          Get the content and metadata of a file          |
 | 
					|       get_file_content       |     File     |          Get the content and metadata of a file          |
 | 
				
			||||||
 | 
					|        get_dir_content       |     File     |           Get a list of entries in a directory           |
 | 
				
			||||||
|         create_file          |     File     |                    Create a new file                     |
 | 
					|         create_file          |     File     |                    Create a new file                     |
 | 
				
			||||||
|         update_file          |     File     |                 Update an existing file                  |
 | 
					|         update_file          |     File     |                 Update an existing file                  |
 | 
				
			||||||
|         delete_file          |     File     |                      Delete a file                       |
 | 
					|         delete_file          |     File     |                      Delete a file                       |
 | 
				
			||||||
@@ -202,6 +209,8 @@ The Gitea MCP Server supports the following tools:
 | 
				
			|||||||
|         create_issue         |    Issue     |                    Create a new issue                    |
 | 
					|         create_issue         |    Issue     |                    Create a new issue                    |
 | 
				
			||||||
|     create_issue_comment     |    Issue     |               Create a comment on an issue               |
 | 
					|     create_issue_comment     |    Issue     |               Create a comment on an issue               |
 | 
				
			||||||
|          edit_issue          |    Issue     |                       Edit a issue                       |
 | 
					|          edit_issue          |    Issue     |                       Edit a issue                       |
 | 
				
			||||||
 | 
					|      edit_issue_comment      |    Issue     |                Edit a comment on an issue                |
 | 
				
			||||||
 | 
					| get_issue_comments_by_index  |    Issue     |          Get comments of an issue by its index           |
 | 
				
			||||||
|  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                 |
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -54,7 +54,7 @@ Model Context Protocol (MCP) 是一种协议,允许通过聊天界面整合各
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "servers": {
 | 
					    "servers": {
 | 
				
			||||||
      "github": {
 | 
					      "gitea-mcp": {
 | 
				
			||||||
        "command": "docker",
 | 
					        "command": "docker",
 | 
				
			||||||
        "args": [
 | 
					        "args": [
 | 
				
			||||||
          "run",
 | 
					          "run",
 | 
				
			||||||
@@ -139,7 +139,10 @@ cp gitea-mcp /usr/local/bin/
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "mcpServers": {
 | 
					  "mcpServers": {
 | 
				
			||||||
    "gitea": {
 | 
					    "gitea": {
 | 
				
			||||||
      "url": "http://localhost:8080/sse"
 | 
					      "url": "http://localhost:8080/sse",
 | 
				
			||||||
 | 
					      "headers": {
 | 
				
			||||||
 | 
					        "Authorization": "Bearer <your personal access token>"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -151,7 +154,10 @@ cp gitea-mcp /usr/local/bin/
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "mcpServers": {
 | 
					  "mcpServers": {
 | 
				
			||||||
    "gitea": {
 | 
					    "gitea": {
 | 
				
			||||||
      "url": "http://localhost:8080/mcp"
 | 
					      "url": "http://localhost:8080/mcp",
 | 
				
			||||||
 | 
					      "headers": {
 | 
				
			||||||
 | 
					        "Authorization": "Bearer <your personal access token>"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -194,6 +200,7 @@ Gitea MCP 服务器支持以下工具:
 | 
				
			|||||||
|          list_tags           |   标签   |         列出所有标签         |
 | 
					|          list_tags           |   标签   |         列出所有标签         |
 | 
				
			||||||
|      list_repo_commits       |   提交   |     列出仓库中的所有提交     |
 | 
					|      list_repo_commits       |   提交   |     列出仓库中的所有提交     |
 | 
				
			||||||
|       get_file_content       |   文件   |    获取文件的内容和元数据    |
 | 
					|       get_file_content       |   文件   |    获取文件的内容和元数据    |
 | 
				
			||||||
 | 
					|        get_dir_content       |   文件   |      获取目录的内容列表      |
 | 
				
			||||||
|         create_file          |   文件   |        创建一个新文件        |
 | 
					|         create_file          |   文件   |        创建一个新文件        |
 | 
				
			||||||
|         update_file          |   文件   |         更新现有文件         |
 | 
					|         update_file          |   文件   |         更新现有文件         |
 | 
				
			||||||
|         delete_file          |   文件   |         删除一个文件         |
 | 
					|         delete_file          |   文件   |         删除一个文件         |
 | 
				
			||||||
@@ -202,6 +209,8 @@ Gitea MCP 服务器支持以下工具:
 | 
				
			|||||||
|         create_issue         |   问题   |        创建一个新问题        |
 | 
					|         create_issue         |   问题   |        创建一个新问题        |
 | 
				
			||||||
|     create_issue_comment     |   问题   |       在问题上创建评论       |
 | 
					|     create_issue_comment     |   问题   |       在问题上创建评论       |
 | 
				
			||||||
|          edit_issue          |   问题   |         编辑一个问题         |
 | 
					|          edit_issue          |   问题   |         编辑一个问题         |
 | 
				
			||||||
 | 
					|      edit_issue_comment      |   问题   |      在问题上编辑评论         |
 | 
				
			||||||
 | 
					| 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      | 拉取请求 |      创建一个新拉取请求      |
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -54,7 +54,7 @@ Model Context Protocol (MCP) 是一種協議,允許通過聊天界面整合各
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "servers": {
 | 
					    "servers": {
 | 
				
			||||||
      "github": {
 | 
					      "gitea-mcp": {
 | 
				
			||||||
        "command": "docker",
 | 
					        "command": "docker",
 | 
				
			||||||
        "args": [
 | 
					        "args": [
 | 
				
			||||||
          "run",
 | 
					          "run",
 | 
				
			||||||
@@ -139,7 +139,10 @@ cp gitea-mcp /usr/local/bin/
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "mcpServers": {
 | 
					  "mcpServers": {
 | 
				
			||||||
    "gitea": {
 | 
					    "gitea": {
 | 
				
			||||||
      "url": "http://localhost:8080/sse"
 | 
					      "url": "http://localhost:8080/sse",
 | 
				
			||||||
 | 
					      "headers": {
 | 
				
			||||||
 | 
					        "Authorization": "Bearer <your personal access token>"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -151,7 +154,10 @@ cp gitea-mcp /usr/local/bin/
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "mcpServers": {
 | 
					  "mcpServers": {
 | 
				
			||||||
    "gitea": {
 | 
					    "gitea": {
 | 
				
			||||||
      "url": "http://localhost:8080/mcp"
 | 
					      "url": "http://localhost:8080/mcp",
 | 
				
			||||||
 | 
					      "headers": {
 | 
				
			||||||
 | 
					        "Authorization": "Bearer <your personal access token>"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -194,6 +200,7 @@ Gitea MCP 伺服器支持以下工具:
 | 
				
			|||||||
|          list_tags           |   標籤   |         列出所有標籤         |
 | 
					|          list_tags           |   標籤   |         列出所有標籤         |
 | 
				
			||||||
|      list_repo_commits       |   提交   |     列出倉庫中的所有提交     |
 | 
					|      list_repo_commits       |   提交   |     列出倉庫中的所有提交     |
 | 
				
			||||||
|       get_file_content       |   文件   |    獲取文件的內容和元數據    |
 | 
					|       get_file_content       |   文件   |    獲取文件的內容和元數據    |
 | 
				
			||||||
 | 
					|        get_dir_content       |   文件   |      獲取目錄的內容列表      |
 | 
				
			||||||
|         create_file          |   文件   |        創建一個新文件        |
 | 
					|         create_file          |   文件   |        創建一個新文件        |
 | 
				
			||||||
|         update_file          |   文件   |         更新現有文件         |
 | 
					|         update_file          |   文件   |         更新現有文件         |
 | 
				
			||||||
|         delete_file          |   文件   |         刪除一個文件         |
 | 
					|         delete_file          |   文件   |         刪除一個文件         |
 | 
				
			||||||
@@ -202,6 +209,8 @@ Gitea MCP 伺服器支持以下工具:
 | 
				
			|||||||
|         create_issue         |   問題   |        創建一個新問題        |
 | 
					|         create_issue         |   問題   |        創建一個新問題        |
 | 
				
			||||||
|     create_issue_comment     |   問題   |       在問題上創建評論       |
 | 
					|     create_issue_comment     |   問題   |       在問題上創建評論       |
 | 
				
			||||||
|          edit_issue          |   問題   |         編輯一個問題         |
 | 
					|          edit_issue          |   問題   |         編輯一個問題         |
 | 
				
			||||||
 | 
					|      edit_issue_comment      |   問題   |      在問題上編輯評論         |
 | 
				
			||||||
 | 
					| 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      | 拉取請求 |      創建一個新拉取請求      |
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -80,8 +80,8 @@ func init() {
 | 
				
			|||||||
		flagPkg.Token = os.Getenv("GITEA_ACCESS_TOKEN")
 | 
							flagPkg.Token = os.Getenv("GITEA_ACCESS_TOKEN")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if os.Getenv("GITEA_MODE") != "" {
 | 
						if os.Getenv("MCP_MODE") != "" {
 | 
				
			||||||
		flagPkg.Mode = os.Getenv("GITEA_MODE")
 | 
							flagPkg.Mode = os.Getenv("MCP_MODE")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if os.Getenv("GITEA_READONLY") == "true" {
 | 
						if os.Getenv("GITEA_READONLY") == "true" {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										14
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								go.mod
									
									
									
									
									
								
							@@ -4,20 +4,26 @@ go 1.24.0
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	code.gitea.io/sdk/gitea v0.21.0
 | 
						code.gitea.io/sdk/gitea v0.21.0
 | 
				
			||||||
	github.com/mark3labs/mcp-go v0.30.0
 | 
						github.com/mark3labs/mcp-go v0.36.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
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/42wim/httpsig v1.2.3 // indirect
 | 
						github.com/42wim/httpsig v1.2.3 // indirect
 | 
				
			||||||
 | 
						github.com/bahlo/generic-list-go v0.2.0 // indirect
 | 
				
			||||||
 | 
						github.com/buger/jsonparser v1.1.1 // indirect
 | 
				
			||||||
	github.com/davidmz/go-pageant v1.0.2 // indirect
 | 
						github.com/davidmz/go-pageant v1.0.2 // indirect
 | 
				
			||||||
	github.com/go-fed/httpsig v1.1.0 // indirect
 | 
						github.com/go-fed/httpsig v1.1.0 // indirect
 | 
				
			||||||
	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/spf13/cast v1.8.0 // indirect
 | 
						github.com/invopop/jsonschema v0.13.0 // indirect
 | 
				
			||||||
 | 
						github.com/mailru/easyjson v0.7.7 // indirect
 | 
				
			||||||
 | 
						github.com/spf13/cast v1.9.2 // 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.38.0 // indirect
 | 
						golang.org/x/crypto v0.40.0 // indirect
 | 
				
			||||||
	golang.org/x/sys v0.33.0 // indirect
 | 
						golang.org/x/sys v0.34.0 // indirect
 | 
				
			||||||
 | 
						gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										33
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								go.sum
									
									
									
									
									
								
							@@ -2,6 +2,10 @@ code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4=
 | 
				
			|||||||
code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA=
 | 
					code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA=
 | 
				
			||||||
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/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
 | 
				
			||||||
 | 
					github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
 | 
				
			||||||
 | 
					github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
 | 
				
			||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
					github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
				
			||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
					github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
				
			||||||
github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0=
 | 
					github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0=
 | 
				
			||||||
@@ -16,20 +20,27 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 | 
				
			|||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
					github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
				
			||||||
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
 | 
					github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
 | 
				
			||||||
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/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/mark3labs/mcp-go v0.30.0 h1:Taz7fiefkxY/l8jz1nA90V+WdM2eoMtlvwfWforVYbo=
 | 
					github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
 | 
				
			||||||
github.com/mark3labs/mcp-go v0.30.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4=
 | 
					github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 | 
				
			||||||
 | 
					github.com/mark3labs/mcp-go v0.36.0 h1:rIZaijrRYPeSbJG8/qNDe0hWlGrCJ7FWHNMz2SQpTis=
 | 
				
			||||||
 | 
					github.com/mark3labs/mcp-go v0.36.0/go.mod h1:T7tUa2jO6MavG+3P25Oy/jR7iCeJPHImCZHRymCn39g=
 | 
				
			||||||
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.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk=
 | 
					github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
 | 
				
			||||||
github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
 | 
					github.com/spf13/cast v1.9.2/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/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
 | 
				
			||||||
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
 | 
					github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
 | 
				
			||||||
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
 | 
					github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
 | 
				
			||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
 | 
					go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
 | 
				
			||||||
@@ -41,21 +52,23 @@ 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.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
 | 
					golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
 | 
				
			||||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
 | 
					golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
 | 
				
			||||||
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.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
 | 
					golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
 | 
				
			||||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 | 
					golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 | 
				
			||||||
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.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
 | 
					golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
 | 
				
			||||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
 | 
					golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
 | 
				
			||||||
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=
 | 
				
			||||||
 | 
					gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 | 
				
			||||||
 | 
					gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
 | 
					gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
 | 
				
			||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
 | 
					gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
					gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,8 @@ const (
 | 
				
			|||||||
	CreateIssueToolName             = "create_issue"
 | 
						CreateIssueToolName             = "create_issue"
 | 
				
			||||||
	CreateIssueCommentToolName      = "create_issue_comment"
 | 
						CreateIssueCommentToolName      = "create_issue_comment"
 | 
				
			||||||
	EditIssueToolName               = "edit_issue"
 | 
						EditIssueToolName               = "edit_issue"
 | 
				
			||||||
 | 
						EditIssueCommentToolName        = "edit_issue_comment"
 | 
				
			||||||
 | 
						GetIssueCommentsByIndexToolName = "get_issue_comments_by_index"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
@@ -52,6 +54,7 @@ var (
 | 
				
			|||||||
		mcp.WithString("title", mcp.Required(), mcp.Description("issue title")),
 | 
							mcp.WithString("title", mcp.Required(), mcp.Description("issue title")),
 | 
				
			||||||
		mcp.WithString("body", mcp.Required(), mcp.Description("issue body")),
 | 
							mcp.WithString("body", mcp.Required(), mcp.Description("issue body")),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	CreateIssueCommentTool = mcp.NewTool(
 | 
						CreateIssueCommentTool = mcp.NewTool(
 | 
				
			||||||
		CreateIssueCommentToolName,
 | 
							CreateIssueCommentToolName,
 | 
				
			||||||
		mcp.WithDescription("create issue comment"),
 | 
							mcp.WithDescription("create issue comment"),
 | 
				
			||||||
@@ -60,6 +63,7 @@ var (
 | 
				
			|||||||
		mcp.WithNumber("index", mcp.Required(), mcp.Description("repository issue index")),
 | 
							mcp.WithNumber("index", mcp.Required(), mcp.Description("repository issue index")),
 | 
				
			||||||
		mcp.WithString("body", mcp.Required(), mcp.Description("issue comment body")),
 | 
							mcp.WithString("body", mcp.Required(), mcp.Description("issue comment body")),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	EditIssueTool = mcp.NewTool(
 | 
						EditIssueTool = mcp.NewTool(
 | 
				
			||||||
		EditIssueToolName,
 | 
							EditIssueToolName,
 | 
				
			||||||
		mcp.WithDescription("edit issue"),
 | 
							mcp.WithDescription("edit issue"),
 | 
				
			||||||
@@ -72,6 +76,23 @@ var (
 | 
				
			|||||||
		mcp.WithNumber("milestone", mcp.Description("milestone number")),
 | 
							mcp.WithNumber("milestone", mcp.Description("milestone number")),
 | 
				
			||||||
		mcp.WithString("state", mcp.Description("issue state, one of open, closed, all")),
 | 
							mcp.WithString("state", mcp.Description("issue state, one of open, closed, all")),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						EditIssueCommentTool = mcp.NewTool(
 | 
				
			||||||
 | 
							EditIssueCommentToolName,
 | 
				
			||||||
 | 
							mcp.WithDescription("edit issue comment"),
 | 
				
			||||||
 | 
							mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
 | 
				
			||||||
 | 
							mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
 | 
				
			||||||
 | 
							mcp.WithNumber("commentID", mcp.Required(), mcp.Description("id of issue comment")),
 | 
				
			||||||
 | 
							mcp.WithString("body", mcp.Required(), mcp.Description("issue comment body")),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						GetIssueCommentsByIndexTool = mcp.NewTool(
 | 
				
			||||||
 | 
							GetIssueCommentsByIndexToolName,
 | 
				
			||||||
 | 
							mcp.WithDescription("get issue comment by index"),
 | 
				
			||||||
 | 
							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("repository issue index")),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
@@ -95,6 +116,14 @@ func init() {
 | 
				
			|||||||
		Tool:    EditIssueTool,
 | 
							Tool:    EditIssueTool,
 | 
				
			||||||
		Handler: EditIssueFn,
 | 
							Handler: EditIssueFn,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
						Tool.RegisterWrite(server.ServerTool{
 | 
				
			||||||
 | 
							Tool:    EditIssueCommentTool,
 | 
				
			||||||
 | 
							Handler: EditIssueCommentFn,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						Tool.RegisterRead(server.ServerTool{
 | 
				
			||||||
 | 
							Tool:    GetIssueCommentsByIndexTool,
 | 
				
			||||||
 | 
							Handler: GetIssueCommentsByIndexFn,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GetIssueByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
					func GetIssueByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
@@ -111,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))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -148,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))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -173,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,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
@@ -205,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))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -251,10 +296,74 @@ 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))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return to.TextResult(issue)
 | 
						return to.TextResult(issue)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func EditIssueCommentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
						log.Debugf("Called EditIssueCommentFn")
 | 
				
			||||||
 | 
						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"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						commentID, ok := req.GetArguments()["commentID"].(float64)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("comment ID is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						body, ok := req.GetArguments()["body"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("body is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						opt := gitea_sdk.EditIssueCommentOption{
 | 
				
			||||||
 | 
							Body: body,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						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 {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("edit %v/%v/issues/comments/%v err: %v", owner, repo, int64(commentID), err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return to.TextResult(issueComment)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetIssueCommentsByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
						log.Debugf("Called GetIssueCommentsByIndexFn")
 | 
				
			||||||
 | 
						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"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						opt := gitea_sdk.ListIssueCommentOptions{}
 | 
				
			||||||
 | 
						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 {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get %v/%v/issues/%v/comments err: %v", owner, repo, int64(index), err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return to.TextResult(issue)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										454
									
								
								operation/label/label.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										454
									
								
								operation/label/label.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,454 @@
 | 
				
			|||||||
 | 
					package label
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.com/gitea/gitea-mcp/pkg/gitea"
 | 
				
			||||||
 | 
						"gitea.com/gitea/gitea-mcp/pkg/log"
 | 
				
			||||||
 | 
						"gitea.com/gitea/gitea-mcp/pkg/ptr"
 | 
				
			||||||
 | 
						"gitea.com/gitea/gitea-mcp/pkg/to"
 | 
				
			||||||
 | 
						"gitea.com/gitea/gitea-mcp/pkg/tool"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gitea_sdk "code.gitea.io/sdk/gitea"
 | 
				
			||||||
 | 
						"github.com/mark3labs/mcp-go/mcp"
 | 
				
			||||||
 | 
						"github.com/mark3labs/mcp-go/server"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var Tool = tool.New()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						ListRepoLabelsToolName     = "list_repo_labels"
 | 
				
			||||||
 | 
						GetRepoLabelToolName       = "get_repo_label"
 | 
				
			||||||
 | 
						CreateRepoLabelToolName    = "create_repo_label"
 | 
				
			||||||
 | 
						EditRepoLabelToolName      = "edit_repo_label"
 | 
				
			||||||
 | 
						DeleteRepoLabelToolName    = "delete_repo_label"
 | 
				
			||||||
 | 
						AddIssueLabelsToolName     = "add_issue_labels"
 | 
				
			||||||
 | 
						ReplaceIssueLabelsToolName = "replace_issue_labels"
 | 
				
			||||||
 | 
						ClearIssueLabelsToolName   = "clear_issue_labels"
 | 
				
			||||||
 | 
						RemoveIssueLabelToolName   = "remove_issue_label"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						ListRepoLabelsTool = mcp.NewTool(
 | 
				
			||||||
 | 
							ListRepoLabelsToolName,
 | 
				
			||||||
 | 
							mcp.WithDescription("Lists all labels for a given repository"),
 | 
				
			||||||
 | 
							mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
 | 
				
			||||||
 | 
							mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
 | 
				
			||||||
 | 
							mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1)),
 | 
				
			||||||
 | 
							mcp.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						GetRepoLabelTool = mcp.NewTool(
 | 
				
			||||||
 | 
							GetRepoLabelToolName,
 | 
				
			||||||
 | 
							mcp.WithDescription("Gets a single label by its ID for a repository"),
 | 
				
			||||||
 | 
							mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
 | 
				
			||||||
 | 
							mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
 | 
				
			||||||
 | 
							mcp.WithNumber("id", mcp.Required(), mcp.Description("label ID")),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CreateRepoLabelTool = mcp.NewTool(
 | 
				
			||||||
 | 
							CreateRepoLabelToolName,
 | 
				
			||||||
 | 
							mcp.WithDescription("Creates a new label for a repository"),
 | 
				
			||||||
 | 
							mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
 | 
				
			||||||
 | 
							mcp.WithString("repo", mcp.Required(), mcp.Description("repository 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")),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						EditRepoLabelTool = mcp.NewTool(
 | 
				
			||||||
 | 
							EditRepoLabelToolName,
 | 
				
			||||||
 | 
							mcp.WithDescription("Edits an existing label in a repository"),
 | 
				
			||||||
 | 
							mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
 | 
				
			||||||
 | 
							mcp.WithString("repo", mcp.Required(), mcp.Description("repository 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")),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DeleteRepoLabelTool = mcp.NewTool(
 | 
				
			||||||
 | 
							DeleteRepoLabelToolName,
 | 
				
			||||||
 | 
							mcp.WithDescription("Deletes a label from a repository"),
 | 
				
			||||||
 | 
							mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
 | 
				
			||||||
 | 
							mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
 | 
				
			||||||
 | 
							mcp.WithNumber("id", mcp.Required(), mcp.Description("label ID")),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						AddIssueLabelsTool = mcp.NewTool(
 | 
				
			||||||
 | 
							AddIssueLabelsToolName,
 | 
				
			||||||
 | 
							mcp.WithDescription("Adds one or more labels to an issue"),
 | 
				
			||||||
 | 
							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("issue index")),
 | 
				
			||||||
 | 
							mcp.WithArray("labels", mcp.Required(), mcp.Description("array of label IDs to add"), mcp.Items(map[string]interface{}{"type": "number"})),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ReplaceIssueLabelsTool = mcp.NewTool(
 | 
				
			||||||
 | 
							ReplaceIssueLabelsToolName,
 | 
				
			||||||
 | 
							mcp.WithDescription("Replaces all labels on an issue"),
 | 
				
			||||||
 | 
							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("issue index")),
 | 
				
			||||||
 | 
							mcp.WithArray("labels", mcp.Required(), mcp.Description("array of label IDs to replace with"), mcp.Items(map[string]interface{}{"type": "number"})),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ClearIssueLabelsTool = mcp.NewTool(
 | 
				
			||||||
 | 
							ClearIssueLabelsToolName,
 | 
				
			||||||
 | 
							mcp.WithDescription("Removes all labels from an issue"),
 | 
				
			||||||
 | 
							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("issue index")),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RemoveIssueLabelTool = mcp.NewTool(
 | 
				
			||||||
 | 
							RemoveIssueLabelToolName,
 | 
				
			||||||
 | 
							mcp.WithDescription("Removes a single label from an issue"),
 | 
				
			||||||
 | 
							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("issue index")),
 | 
				
			||||||
 | 
							mcp.WithNumber("label_id", mcp.Required(), mcp.Description("label ID to remove")),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						Tool.RegisterRead(server.ServerTool{
 | 
				
			||||||
 | 
							Tool:    ListRepoLabelsTool,
 | 
				
			||||||
 | 
							Handler: ListRepoLabelsFn,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						Tool.RegisterRead(server.ServerTool{
 | 
				
			||||||
 | 
							Tool:    GetRepoLabelTool,
 | 
				
			||||||
 | 
							Handler: GetRepoLabelFn,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						Tool.RegisterWrite(server.ServerTool{
 | 
				
			||||||
 | 
							Tool:    CreateRepoLabelTool,
 | 
				
			||||||
 | 
							Handler: CreateRepoLabelFn,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						Tool.RegisterWrite(server.ServerTool{
 | 
				
			||||||
 | 
							Tool:    EditRepoLabelTool,
 | 
				
			||||||
 | 
							Handler: EditRepoLabelFn,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						Tool.RegisterWrite(server.ServerTool{
 | 
				
			||||||
 | 
							Tool:    DeleteRepoLabelTool,
 | 
				
			||||||
 | 
							Handler: DeleteRepoLabelFn,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						Tool.RegisterWrite(server.ServerTool{
 | 
				
			||||||
 | 
							Tool:    AddIssueLabelsTool,
 | 
				
			||||||
 | 
							Handler: AddIssueLabelsFn,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						Tool.RegisterWrite(server.ServerTool{
 | 
				
			||||||
 | 
							Tool:    ReplaceIssueLabelsTool,
 | 
				
			||||||
 | 
							Handler: ReplaceIssueLabelsFn,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						Tool.RegisterWrite(server.ServerTool{
 | 
				
			||||||
 | 
							Tool:    ClearIssueLabelsTool,
 | 
				
			||||||
 | 
							Handler: ClearIssueLabelsFn,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						Tool.RegisterWrite(server.ServerTool{
 | 
				
			||||||
 | 
							Tool:    RemoveIssueLabelTool,
 | 
				
			||||||
 | 
							Handler: RemoveIssueLabelFn,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ListRepoLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
						log.Debugf("Called ListRepoLabelsFn")
 | 
				
			||||||
 | 
						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"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						page, ok := req.GetArguments()["page"].(float64)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							page = 1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pageSize, ok := req.GetArguments()["pageSize"].(float64)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							pageSize = 100
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opt := gitea_sdk.ListLabelsOptions{
 | 
				
			||||||
 | 
							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.ListRepoLabels(owner, repo, opt)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("list %v/%v/labels err: %v", owner, repo, err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return to.TextResult(labels)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
						log.Debugf("Called GetRepoLabelFn")
 | 
				
			||||||
 | 
						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"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						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))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						label, _, err := client.GetRepoLabel(owner, repo, int64(id))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get %v/%v/label/%v err: %v", owner, repo, int64(id), err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return to.TextResult(label)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func CreateRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
						log.Debugf("Called CreateRepoLabelFn")
 | 
				
			||||||
 | 
						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"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						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) // Optional
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opt := gitea_sdk.CreateLabelOption{
 | 
				
			||||||
 | 
							Name:        name,
 | 
				
			||||||
 | 
							Color:       color,
 | 
				
			||||||
 | 
							Description: description,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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 {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("create %v/%v/label err: %v", owner, repo, err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return to.TextResult(label)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func EditRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
						log.Debugf("Called EditRepoLabelFn")
 | 
				
			||||||
 | 
						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"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						id, ok := req.GetArguments()["id"].(float64)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("label ID is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opt := gitea_sdk.EditLabelOption{}
 | 
				
			||||||
 | 
						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)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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 {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("edit %v/%v/label/%v err: %v", owner, repo, int64(id), err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return to.TextResult(label)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func DeleteRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
						log.Debugf("Called DeleteRepoLabelFn")
 | 
				
			||||||
 | 
						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"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						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.DeleteLabel(owner, repo, int64(id))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("delete %v/%v/label/%v err: %v", owner, repo, int64(id), err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return to.TextResult("Label deleted successfully")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func AddIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
						log.Debugf("Called AddIssueLabelsFn")
 | 
				
			||||||
 | 
						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("issue index is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						labelsRaw, ok := req.GetArguments()["labels"].([]interface{})
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("labels (array of IDs) is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var labels []int64
 | 
				
			||||||
 | 
						for _, l := range labelsRaw {
 | 
				
			||||||
 | 
							if labelID, ok := l.(float64); ok {
 | 
				
			||||||
 | 
								labels = append(labels, int64(labelID))
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return to.ErrorResult(fmt.Errorf("invalid label ID in labels array"))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opt := gitea_sdk.IssueLabelsOption{
 | 
				
			||||||
 | 
							Labels: labels,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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 {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("add labels to %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return to.TextResult(issueLabels)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ReplaceIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
						log.Debugf("Called ReplaceIssueLabelsFn")
 | 
				
			||||||
 | 
						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("issue index is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						labelsRaw, ok := req.GetArguments()["labels"].([]interface{})
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("labels (array of IDs) is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var labels []int64
 | 
				
			||||||
 | 
						for _, l := range labelsRaw {
 | 
				
			||||||
 | 
							if labelID, ok := l.(float64); ok {
 | 
				
			||||||
 | 
								labels = append(labels, int64(labelID))
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return to.ErrorResult(fmt.Errorf("invalid label ID in labels array"))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opt := gitea_sdk.IssueLabelsOption{
 | 
				
			||||||
 | 
							Labels: labels,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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 {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("replace labels on %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return to.TextResult(issueLabels)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ClearIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
						log.Debugf("Called ClearIssueLabelsFn")
 | 
				
			||||||
 | 
						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("issue index is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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 {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("clear labels on %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return to.TextResult("Labels cleared successfully")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func RemoveIssueLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
						log.Debugf("Called RemoveIssueLabelFn")
 | 
				
			||||||
 | 
						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("issue index is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						labelID, ok := req.GetArguments()["label_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.DeleteIssueLabel(owner, repo, int64(index), int64(labelID))
 | 
				
			||||||
 | 
						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.TextResult("Label removed successfully")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,14 +1,20 @@
 | 
				
			|||||||
package operation
 | 
					package operation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gitea.com/gitea/gitea-mcp/operation/issue"
 | 
						"gitea.com/gitea/gitea-mcp/operation/issue"
 | 
				
			||||||
 | 
						"gitea.com/gitea/gitea-mcp/operation/label"
 | 
				
			||||||
	"gitea.com/gitea/gitea-mcp/operation/pull"
 | 
						"gitea.com/gitea/gitea-mcp/operation/pull"
 | 
				
			||||||
	"gitea.com/gitea/gitea-mcp/operation/repo"
 | 
						"gitea.com/gitea/gitea-mcp/operation/repo"
 | 
				
			||||||
	"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"
 | 
				
			||||||
 | 
						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"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -27,6 +33,9 @@ func RegisterTool(s *server.MCPServer) {
 | 
				
			|||||||
	// Issue Tool
 | 
						// Issue Tool
 | 
				
			||||||
	s.AddTools(issue.Tool.Tools()...)
 | 
						s.AddTools(issue.Tool.Tools()...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Label Tool
 | 
				
			||||||
 | 
						s.AddTools(label.Tool.Tools()...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Pull Tool
 | 
						// Pull Tool
 | 
				
			||||||
	s.AddTools(pull.Tool.Tools()...)
 | 
						s.AddTools(pull.Tool.Tools()...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -39,22 +48,46 @@ func RegisterTool(s *server.MCPServer) {
 | 
				
			|||||||
	s.DeleteTools("")
 | 
						s.DeleteTools("")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getContextWithToken(ctx context.Context, r *http.Request) context.Context {
 | 
				
			||||||
 | 
						authHeader := r.Header.Get("Authorization")
 | 
				
			||||||
 | 
						if authHeader == "" {
 | 
				
			||||||
 | 
							return ctx
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						parts := strings.Split(authHeader, " ")
 | 
				
			||||||
 | 
						if len(parts) != 2 || parts[0] != "Bearer" {
 | 
				
			||||||
 | 
							return ctx
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return context.WithValue(ctx, mcpContext.TokenContextKey, parts[1])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Run() error {
 | 
					func Run() error {
 | 
				
			||||||
	mcpServer = newMCPServer(flag.Version)
 | 
						mcpServer = newMCPServer(flag.Version)
 | 
				
			||||||
	RegisterTool(mcpServer)
 | 
						RegisterTool(mcpServer)
 | 
				
			||||||
	switch flag.Mode {
 | 
						switch flag.Mode {
 | 
				
			||||||
	case "stdio":
 | 
						case "stdio":
 | 
				
			||||||
		if err := server.ServeStdio(mcpServer); err != nil {
 | 
							if err := server.ServeStdio(
 | 
				
			||||||
 | 
								mcpServer,
 | 
				
			||||||
 | 
							); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	case "sse":
 | 
						case "sse":
 | 
				
			||||||
		sseServer := server.NewSSEServer(mcpServer)
 | 
							sseServer := server.NewSSEServer(
 | 
				
			||||||
 | 
								mcpServer,
 | 
				
			||||||
 | 
								server.WithSSEContextFunc(getContextWithToken),
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
		log.Infof("Gitea MCP SSE server listening on :%d", flag.Port)
 | 
							log.Infof("Gitea MCP SSE server listening on :%d", flag.Port)
 | 
				
			||||||
		if err := sseServer.Start(fmt.Sprintf(":%d", flag.Port)); err != nil {
 | 
							if err := sseServer.Start(fmt.Sprintf(":%d", flag.Port)); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	case "http":
 | 
						case "http":
 | 
				
			||||||
		httpServer := server.NewStreamableHTTPServer(mcpServer)
 | 
							httpServer := server.NewStreamableHTTPServer(
 | 
				
			||||||
 | 
								mcpServer,
 | 
				
			||||||
 | 
								server.WithLogger(log.New()),
 | 
				
			||||||
 | 
								server.WithHeartbeatInterval(30*time.Second),
 | 
				
			||||||
 | 
								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)
 | 
				
			||||||
		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
 | 
				
			||||||
@@ -71,5 +104,6 @@ func newMCPServer(version string) *server.MCPServer {
 | 
				
			|||||||
		version,
 | 
							version,
 | 
				
			||||||
		server.WithToolCapabilities(true),
 | 
							server.WithToolCapabilities(true),
 | 
				
			||||||
		server.WithLogging(),
 | 
							server.WithLogging(),
 | 
				
			||||||
 | 
							server.WithRecovery(),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -84,7 +84,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 +129,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 +167,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,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,11 @@
 | 
				
			|||||||
package repo
 | 
					package repo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"bufio"
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"encoding/base64"
 | 
						"encoding/base64"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gitea.com/gitea/gitea-mcp/pkg/gitea"
 | 
						"gitea.com/gitea/gitea-mcp/pkg/gitea"
 | 
				
			||||||
@@ -16,6 +19,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	GetFileToolName    = "get_file_content"
 | 
						GetFileToolName    = "get_file_content"
 | 
				
			||||||
 | 
						GetDirToolName     = "get_dir_content"
 | 
				
			||||||
	CreateFileToolName = "create_file"
 | 
						CreateFileToolName = "create_file"
 | 
				
			||||||
	UpdateFileToolName = "update_file"
 | 
						UpdateFileToolName = "update_file"
 | 
				
			||||||
	DeleteFileToolName = "delete_file"
 | 
						DeleteFileToolName = "delete_file"
 | 
				
			||||||
@@ -29,6 +33,16 @@ var (
 | 
				
			|||||||
		mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
 | 
							mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
 | 
				
			||||||
		mcp.WithString("ref", mcp.Required(), mcp.Description("ref can be branch/tag/commit")),
 | 
							mcp.WithString("ref", mcp.Required(), mcp.Description("ref can be branch/tag/commit")),
 | 
				
			||||||
		mcp.WithString("filePath", mcp.Required(), mcp.Description("file path")),
 | 
							mcp.WithString("filePath", mcp.Required(), mcp.Description("file path")),
 | 
				
			||||||
 | 
							mcp.WithBoolean("withLines", mcp.Description("whether to return file content with lines")),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						GetDirContentTool = mcp.NewTool(
 | 
				
			||||||
 | 
							GetDirToolName,
 | 
				
			||||||
 | 
							mcp.WithDescription("Get a list of entries in a directory"),
 | 
				
			||||||
 | 
							mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
 | 
				
			||||||
 | 
							mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
 | 
				
			||||||
 | 
							mcp.WithString("ref", mcp.Required(), mcp.Description("ref can be branch/tag/commit")),
 | 
				
			||||||
 | 
							mcp.WithString("filePath", mcp.Required(), mcp.Description("directory path")),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	CreateFileTool = mcp.NewTool(
 | 
						CreateFileTool = mcp.NewTool(
 | 
				
			||||||
@@ -50,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")),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
@@ -72,6 +86,10 @@ func init() {
 | 
				
			|||||||
		Tool:    GetFileContentTool,
 | 
							Tool:    GetFileContentTool,
 | 
				
			||||||
		Handler: GetFileContentFn,
 | 
							Handler: GetFileContentFn,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
						Tool.RegisterRead(server.ServerTool{
 | 
				
			||||||
 | 
							Tool:    GetDirContentTool,
 | 
				
			||||||
 | 
							Handler: GetDirContentFn,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
	Tool.RegisterWrite(server.ServerTool{
 | 
						Tool.RegisterWrite(server.ServerTool{
 | 
				
			||||||
		Tool:    CreateFileTool,
 | 
							Tool:    CreateFileTool,
 | 
				
			||||||
		Handler: CreateFileFn,
 | 
							Handler: CreateFileFn,
 | 
				
			||||||
@@ -86,6 +104,11 @@ func init() {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ContentLine struct {
 | 
				
			||||||
 | 
						LineNumber int    `json:"line"`
 | 
				
			||||||
 | 
						Content    string `json:"content"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GetFileContentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
					func GetFileContentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
	log.Debugf("Called GetFileFn")
 | 
						log.Debugf("Called GetFileFn")
 | 
				
			||||||
	owner, ok := req.GetArguments()["owner"].(string)
 | 
						owner, ok := req.GetArguments()["owner"].(string)
 | 
				
			||||||
@@ -101,10 +124,78 @@ 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))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						withLines, _ := req.GetArguments()["withLines"].(bool)
 | 
				
			||||||
 | 
						if withLines {
 | 
				
			||||||
 | 
							rawContent, err := base64.StdEncoding.DecodeString(*content.Content)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return to.ErrorResult(fmt.Errorf("decode base64 content err: %v", err))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							contentLines := make([]ContentLine, 0)
 | 
				
			||||||
 | 
							line := 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							scanner := bufio.NewScanner(bytes.NewReader(rawContent))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for scanner.Scan() {
 | 
				
			||||||
 | 
								line++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								contentLines = append(contentLines, ContentLine{
 | 
				
			||||||
 | 
									LineNumber: line,
 | 
				
			||||||
 | 
									Content:    scanner.Text(),
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err := scanner.Err(); err != nil {
 | 
				
			||||||
 | 
								return to.ErrorResult(fmt.Errorf("scan content err: %v", err))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// remove the last blank line if exists
 | 
				
			||||||
 | 
							// git does not consider the last line as a new line
 | 
				
			||||||
 | 
							if contentLines[len(contentLines)-1].Content == "" {
 | 
				
			||||||
 | 
								contentLines = contentLines[:len(contentLines)-1]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							contentBytes, err := json.MarshalIndent(contentLines, "", "  ")
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return to.ErrorResult(fmt.Errorf("marshal content lines err: %v", err))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							contentStr := string(contentBytes)
 | 
				
			||||||
 | 
							content.Content = &contentStr
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return to.TextResult(content)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetDirContentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
 | 
						log.Debugf("Called GetDirContentFn")
 | 
				
			||||||
 | 
						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"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ref, _ := req.GetArguments()["ref"].(string)
 | 
				
			||||||
 | 
						filePath, ok := req.GetArguments()["filePath"].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("filePath is required"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						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 {
 | 
				
			||||||
 | 
							return to.ErrorResult(fmt.Errorf("get dir content err: %v", err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return to.TextResult(content)
 | 
						return to.TextResult(content)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -133,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))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -170,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))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -204,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))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,6 +34,7 @@ var (
 | 
				
			|||||||
		mcp.WithString("title", mcp.Required(), mcp.Description("release title")),
 | 
							mcp.WithString("title", mcp.Required(), mcp.Description("release title")),
 | 
				
			||||||
		mcp.WithBoolean("is_draft", mcp.Description("Whether the release is draft"), mcp.DefaultBool(false)),
 | 
							mcp.WithBoolean("is_draft", mcp.Description("Whether the release is draft"), mcp.DefaultBool(false)),
 | 
				
			||||||
		mcp.WithBoolean("is_pre_release", mcp.Description("Whether the release is pre-release"), mcp.DefaultBool(false)),
 | 
							mcp.WithBoolean("is_pre_release", mcp.Description("Whether the release is pre-release"), mcp.DefaultBool(false)),
 | 
				
			||||||
 | 
							mcp.WithString("body", mcp.Description("release body")),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	DeleteReleaseTool = mcp.NewTool(
 | 
						DeleteReleaseTool = mcp.NewTool(
 | 
				
			||||||
@@ -131,11 +132,17 @@ func CreateReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	isDraft, _ := req.GetArguments()["is_draft"].(bool)
 | 
						isDraft, _ := req.GetArguments()["is_draft"].(bool)
 | 
				
			||||||
	isPreRelease, _ := req.GetArguments()["is_pre_release"].(bool)
 | 
						isPreRelease, _ := req.GetArguments()["is_pre_release"].(bool)
 | 
				
			||||||
 | 
						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,
 | 
				
			||||||
 | 
							Note:         body,
 | 
				
			||||||
		IsDraft:      isDraft,
 | 
							IsDraft:      isDraft,
 | 
				
			||||||
		IsPrerelease: isPreRelease,
 | 
							IsPrerelease: isPreRelease,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
@@ -161,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)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -184,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)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -203,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)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -234,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))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,68 +15,102 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
 | 
						// GetMyUserInfoToolName is the unique tool name used for MCP registration and lookup of the get_my_user_info command.
 | 
				
			||||||
	GetMyUserInfoToolName = "get_my_user_info"
 | 
						GetMyUserInfoToolName = "get_my_user_info"
 | 
				
			||||||
 | 
						// GetUserOrgsToolName is the unique tool name used for MCP registration and lookup of the get_user_orgs command.
 | 
				
			||||||
	GetUserOrgsToolName = "get_user_orgs"
 | 
						GetUserOrgsToolName = "get_user_orgs"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// defaultPage is the default starting page number used for paginated organization listings.
 | 
				
			||||||
 | 
						defaultPage = 1
 | 
				
			||||||
 | 
						// defaultPageSize is the default number of organizations per page for paginated queries.
 | 
				
			||||||
 | 
						defaultPageSize = 100
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Tool is the MCP tool manager instance for registering all MCP tools in this package.
 | 
				
			||||||
var Tool = tool.New()
 | 
					var Tool = tool.New()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
 | 
						// GetMyUserInfoTool is the MCP tool for retrieving the current user's info.
 | 
				
			||||||
 | 
						// It is registered with a specific name and a description string.
 | 
				
			||||||
	GetMyUserInfoTool = mcp.NewTool(
 | 
						GetMyUserInfoTool = mcp.NewTool(
 | 
				
			||||||
		GetMyUserInfoToolName,
 | 
							GetMyUserInfoToolName,
 | 
				
			||||||
		mcp.WithDescription("Get my user info"),
 | 
							mcp.WithDescription("Get my user info"),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GetUserOrgsTool is the MCP tool for listing organizations for the authenticated user.
 | 
				
			||||||
 | 
						// It supports pagination via "page" and "pageSize" arguments with default values specified above.
 | 
				
			||||||
	GetUserOrgsTool = mcp.NewTool(
 | 
						GetUserOrgsTool = mcp.NewTool(
 | 
				
			||||||
		GetUserOrgsToolName,
 | 
							GetUserOrgsToolName,
 | 
				
			||||||
		mcp.WithDescription("Get organizations associated with the authenticated user"),
 | 
							mcp.WithDescription("Get organizations associated with the authenticated user"),
 | 
				
			||||||
		mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1)),
 | 
							mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(defaultPage)),
 | 
				
			||||||
		mcp.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)),
 | 
							mcp.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(defaultPageSize)),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// init registers all MCP tools in Tool at package initialization.
 | 
				
			||||||
 | 
					// This function ensures the handler functions are registered before server usage.
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	Tool.RegisterRead(server.ServerTool{
 | 
						registerTools()
 | 
				
			||||||
		Tool:    GetMyUserInfoTool,
 | 
					 | 
				
			||||||
		Handler: GetUserInfoFn,
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Tool.RegisterRead(server.ServerTool{
 | 
					 | 
				
			||||||
		Tool:    GetUserOrgsTool,
 | 
					 | 
				
			||||||
		Handler: GetUserOrgsFn,
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// registerTools registers all local MCP tool definitions and their handler functions.
 | 
				
			||||||
 | 
					// To add new functionality, append your tool/handler pair to the tools slice below.
 | 
				
			||||||
 | 
					func registerTools() {
 | 
				
			||||||
 | 
						tools := []server.ServerTool{
 | 
				
			||||||
 | 
							{Tool: GetMyUserInfoTool, Handler: GetUserInfoFn},
 | 
				
			||||||
 | 
							{Tool: GetUserOrgsTool, Handler: GetUserOrgsFn},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, t := range tools {
 | 
				
			||||||
 | 
							Tool.RegisterRead(t)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// getIntArg parses an integer argument from the MCP request arguments map.
 | 
				
			||||||
 | 
					// Returns def if missing, not a number, or less than 1. Used for pagination arguments.
 | 
				
			||||||
 | 
					func getIntArg(req mcp.CallToolRequest, name string, def int) int {
 | 
				
			||||||
 | 
						val, ok := req.GetArguments()[name].(float64)
 | 
				
			||||||
 | 
						if !ok || val < 1 {
 | 
				
			||||||
 | 
							return def
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return int(val)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetUserInfoFn is the handler for "get_my_user_info" MCP tool requests.
 | 
				
			||||||
 | 
					// 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("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))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	return to.TextResult(user)
 | 
						return to.TextResult(user)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetUserOrgsFn is the handler for "get_user_orgs" MCP tool requests.
 | 
				
			||||||
 | 
					// Logs invocation, pulls validated pagination arguments from request,
 | 
				
			||||||
 | 
					// performs Gitea organization listing, and wraps the result for MCP.
 | 
				
			||||||
func GetUserOrgsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
					func GetUserOrgsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
				
			||||||
	log.Debugf("Called GetUserOrgsFn")
 | 
						log.Debugf("[User] Called GetUserOrgsFn")
 | 
				
			||||||
	page, ok := req.GetArguments()["page"].(float64)
 | 
						page := getIntArg(req, "page", defaultPage)
 | 
				
			||||||
	if !ok || page < 1 {
 | 
						pageSize := getIntArg(req, "pageSize", defaultPageSize)
 | 
				
			||||||
		page = 1
 | 
					
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	pageSize, ok := req.GetArguments()["pageSize"].(float64)
 | 
					 | 
				
			||||||
	if !ok || pageSize < 1 {
 | 
					 | 
				
			||||||
		pageSize = 100
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	opt := gitea_sdk.ListOrgsOptions{
 | 
						opt := gitea_sdk.ListOrgsOptions{
 | 
				
			||||||
		ListOptions: gitea_sdk.ListOptions{
 | 
							ListOptions: gitea_sdk.ListOptions{
 | 
				
			||||||
			Page:     int(page),
 | 
								Page:     page,
 | 
				
			||||||
			PageSize: int(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))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	return to.TextResult(orgs)
 | 
						return to.TextResult(orgs)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,12 +19,10 @@ const (
 | 
				
			|||||||
	GetGiteaMCPServerVersion = "get_gitea_mcp_server_version"
 | 
						GetGiteaMCPServerVersion = "get_gitea_mcp_server_version"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var GetGiteaMCPServerVersionTool = mcp.NewTool(
 | 
				
			||||||
	GetGiteaMCPServerVersionTool = mcp.NewTool(
 | 
					 | 
				
			||||||
	GetGiteaMCPServerVersion,
 | 
						GetGiteaMCPServerVersion,
 | 
				
			||||||
	mcp.WithDescription("Get Gitea MCP Server Version"),
 | 
						mcp.WithDescription("Get Gitea MCP Server Version"),
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	Tool.RegisterRead(server.ServerTool{
 | 
						Tool.RegisterRead(server.ServerTool{
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										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,47 +1,47 @@
 | 
				
			|||||||
package gitea
 | 
					package gitea
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
	"crypto/tls"
 | 
						"crypto/tls"
 | 
				
			||||||
 | 
						"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 {
 | 
					 | 
				
			||||||
	clientOnce.Do(func() {
 | 
					 | 
				
			||||||
		var err error
 | 
					 | 
				
			||||||
		if client != nil {
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	opts := []gitea.ClientOption{
 | 
						opts := []gitea.ClientOption{
 | 
				
			||||||
			gitea.SetToken(flag.Token),
 | 
							gitea.SetToken(token),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if flag.Insecure {
 | 
						if flag.Insecure {
 | 
				
			||||||
			httpClient := &http.Client{
 | 
							httpClient.Transport.(*http.Transport).TLSClientConfig = &tls.Config{
 | 
				
			||||||
				Transport: &http.Transport{
 | 
					 | 
				
			||||||
					TLSClientConfig: &tls.Config{
 | 
					 | 
				
			||||||
			InsecureSkipVerify: true,
 | 
								InsecureSkipVerify: true,
 | 
				
			||||||
					},
 | 
							}
 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	opts = append(opts, gitea.SetHTTPClient(httpClient))
 | 
						opts = append(opts, gitea.SetHTTPClient(httpClient))
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	if flag.Debug {
 | 
						if flag.Debug {
 | 
				
			||||||
		opts = append(opts, gitea.SetDebugMode())
 | 
							opts = append(opts, gitea.SetDebugMode())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
		client, err = gitea.NewClient(flag.Host, opts...)
 | 
						client, err := gitea.NewClient(flag.Host, opts...)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
			log.Fatalf("create gitea client err: %v", err)
 | 
							return nil, fmt.Errorf("create gitea client err: %w", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	})
 | 
					
 | 
				
			||||||
	return client
 | 
						// Set user agent for the client
 | 
				
			||||||
 | 
						client.SetUserAgent(fmt.Sprintf("gitea-mcp-server/%s", flag.Version))
 | 
				
			||||||
 | 
						return client, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ClientFromContext(ctx context.Context) (*gitea.Client, error) {
 | 
				
			||||||
 | 
						token, ok := ctx.Value(mcpContext.TokenContextKey).(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							token = flag.Token
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return NewClient(token)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,10 @@ var (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func Default() *zap.Logger {
 | 
					func Default() *zap.Logger {
 | 
				
			||||||
	defaultLoggerOnce.Do(func() {
 | 
						defaultLoggerOnce.Do(func() {
 | 
				
			||||||
		if defaultLogger == nil {
 | 
							if defaultLogger != nil {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ec := zap.NewProductionEncoderConfig()
 | 
							ec := zap.NewProductionEncoderConfig()
 | 
				
			||||||
		ec.EncodeTime = zapcore.TimeEncoderOfLayout(time.DateTime)
 | 
							ec.EncodeTime = zapcore.TimeEncoderOfLayout(time.DateTime)
 | 
				
			||||||
		ec.EncodeLevel = zapcore.CapitalLevelEncoder
 | 
							ec.EncodeLevel = zapcore.CapitalLevelEncoder
 | 
				
			||||||
@@ -32,8 +35,14 @@ func Default() *zap.Logger {
 | 
				
			|||||||
			home = os.TempDir()
 | 
								home = os.TempDir()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							logDir := fmt.Sprintf("%s/.gitea-mcp", home)
 | 
				
			||||||
 | 
							if err := os.MkdirAll(logDir, 0o700); err != nil {
 | 
				
			||||||
 | 
								// Fallback to temp directory if creation fails
 | 
				
			||||||
 | 
								logDir = os.TempDir()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		wss = append(wss, zapcore.AddSync(&lumberjack.Logger{
 | 
							wss = append(wss, zapcore.AddSync(&lumberjack.Logger{
 | 
				
			||||||
				Filename:   fmt.Sprintf("%s/.gitea-mcp/gitea-mcp.log", home),
 | 
								Filename:   fmt.Sprintf("%s/gitea-mcp.log", logDir),
 | 
				
			||||||
			MaxSize:    100,
 | 
								MaxSize:    100,
 | 
				
			||||||
			MaxBackups: 10,
 | 
								MaxBackups: 10,
 | 
				
			||||||
			MaxAge:     30,
 | 
								MaxAge:     30,
 | 
				
			||||||
@@ -59,7 +68,6 @@ func Default() *zap.Logger {
 | 
				
			|||||||
			zap.AddCallerSkip(1),
 | 
								zap.AddCallerSkip(1),
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		defaultLogger = zap.New(core, options...)
 | 
							defaultLogger = zap.New(core, options...)
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return defaultLogger
 | 
						return defaultLogger
 | 
				
			||||||
@@ -71,8 +79,22 @@ func SetDefault(logger *zap.Logger) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Logger() *zap.Logger {
 | 
					func New() *Logger {
 | 
				
			||||||
	return defaultLogger
 | 
						return &Logger{
 | 
				
			||||||
 | 
							defaultLogger: Default(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Logger struct {
 | 
				
			||||||
 | 
						defaultLogger *zap.Logger
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l *Logger) Infof(msg string, args ...any) {
 | 
				
			||||||
 | 
						l.defaultLogger.Sugar().Infof(msg, args...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l *Logger) Errorf(msg string, args ...any) {
 | 
				
			||||||
 | 
						l.defaultLogger.Sugar().Errorf(msg, args...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Debug(msg string, fields ...zap.Field) {
 | 
					func Debug(msg string, fields ...zap.Field) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,8 +12,8 @@ type Tool struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func New() *Tool {
 | 
					func New() *Tool {
 | 
				
			||||||
	return &Tool{
 | 
						return &Tool{
 | 
				
			||||||
		write: make([]server.ServerTool, 100),
 | 
							write: make([]server.ServerTool, 0, 100),
 | 
				
			||||||
		read:  make([]server.ServerTool, 100),
 | 
							read:  make([]server.ServerTool, 0, 100),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user