mirror of
https://gitea.com/gitea/gitea-mcp.git
synced 2025-08-24 14:53:06 +00:00
Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
df47a0c9eb | ||
|
e3307adbdf | ||
|
afada4435e | ||
|
6285bd2467 | ||
|
5bbf8e0afb | ||
|
0535f5bab7 | ||
|
d892b05048 | ||
|
592cf51c9b | ||
|
2a9504fc5d | ||
|
2f17f37053 | ||
|
5270d2eb08 | ||
|
97b98c3fc2 | ||
|
58328680a6 | ||
|
28947a030e | ||
|
d48e233fba | ||
|
0e225f21da | ||
|
b9e575ad64 | ||
|
8395957553 | ||
|
0aa33e0d62 | ||
|
7d2a0985a3 | ||
|
6f86512a7d |
51
.gitea/workflows/release-latest.yml
Normal file
51
.gitea/workflows/release-latest.yml
Normal file
@@ -0,0 +1,51 @@
|
||||
name: release-nightly
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
tags:
|
||||
- "*"
|
||||
|
||||
jobs:
|
||||
release-image:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOCKER_ORG: gitea
|
||||
DOCKER_LATEST: nightly
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # all history for all branches and tags
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker BuildX
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
|
||||
- name: Get Meta
|
||||
id: meta
|
||||
run: |
|
||||
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT
|
||||
echo REPO_VERSION=$(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//') >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: |
|
||||
linux/amd64
|
||||
linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}-server:${{ env.DOCKER_LATEST }}
|
||||
build-args: |
|
||||
VERSION=${{ steps.meta.outputs.REPO_VERSION }}
|
@@ -36,3 +36,44 @@ jobs:
|
||||
files: |-
|
||||
bin/**
|
||||
token: '${{secrets.RELEASE_TOKEN}}'
|
||||
release-image:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOCKER_ORG: gitea
|
||||
DOCKER_LATEST: latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # all history for all branches and tags
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker BuildX
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
|
||||
- name: Get Meta
|
||||
id: meta
|
||||
run: |
|
||||
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT
|
||||
echo REPO_VERSION=${GITHUB_REF_NAME#v} >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: |
|
||||
linux/amd64
|
||||
linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
${{ 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 }}
|
||||
|
18
Dockerfile
Normal file
18
Dockerfile
Normal file
@@ -0,0 +1,18 @@
|
||||
FROM golang:1.24-alpine AS builder
|
||||
|
||||
ARG VERSION
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
COPY . .
|
||||
RUN go mod download
|
||||
|
||||
RUN CGO_ENABLED=0 go build -ldflags="-s -w -X main.version=${VERSION}" -o gitea-mcp
|
||||
|
||||
FROM scratch
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=builder /build/gitea-mcp .
|
||||
|
||||
CMD ["./gitea-mcp", "-t", "stdio"]
|
130
README.md
130
README.md
@@ -2,9 +2,62 @@
|
||||
|
||||
**Gitea MCP Server** is an integration plugin designed to connect Gitea with Model Context Protocol (MCP) systems. This allows for seamless command execution and repository management through an MCP-compatible chat interface.
|
||||
|
||||
[](https://insiders.vscode.dev/redirect/mcp/install?name=gitea&inputs=[{%22id%22:%22gitea_token%22,%22type%22:%22promptString%22,%22description%22:%22Gitea%20Personal%20Access%20Token%22,%22password%22:true}]&config={%22command%22:%22docker%22,%22args%22:[%22run%22,%22-i%22,%22--rm%22,%22-e%22,%22GITEA_ACCESS_TOKEN%22,%22docker.gitea.com/gitea-mcp-server%22],%22env%22:{%22GITEA_ACCESS_TOKEN%22:%22${input:gitea_token}%22}}) [](https://insiders.vscode.dev/redirect/mcp/install?name=gitea&inputs=[{%22id%22:%22gitea_token%22,%22type%22:%22promptString%22,%22description%22:%22Gitea%20Personal%20Access%20Token%22,%22password%22:true}]&config={%22command%22:%22docker%22,%22args%22:[%22run%22,%22-i%22,%22--rm%22,%22-e%22,%22GITEA_ACCESS_TOKEN%22,%22docker.gitea.com/gitea-mcp-server%22],%22env%22:{%22GITEA_ACCESS_TOKEN%22:%22${input:gitea_token}%22}}&quality=insiders)
|
||||
|
||||
## What is Gitea?
|
||||
|
||||
Gitea is a community-managed lightweight code hosting solution written in Go. It is published under the MIT license. Gitea provides Git hosting including a repository viewer, issue tracking, pull requests, and more.
|
||||
|
||||
## What is MCP?
|
||||
|
||||
Model Context Protocol (MCP) is a protocol that allows for the integration of various tools and systems through a chat interface. It enables seamless command execution and management of repositories, users, and other resources.
|
||||
|
||||
## 🚧 Installation
|
||||
|
||||
There is currently no official release. You will need to build the Gitea MCP Server from source.
|
||||
### Usage with VS Code
|
||||
|
||||
For quick installation, use one of the one-click install buttons at the top of this README.
|
||||
|
||||
For manual installation, add the following JSON block to your User Settings (JSON) file in VS Code. You can do this by pressing `Ctrl + Shift + P` and typing `Preferences: Open User Settings (JSON)`.
|
||||
|
||||
Optionally, you can add it to a file called `.vscode/mcp.json` in your workspace. This will allow you to share the configuration with others.
|
||||
|
||||
> Note that the `mcp` key is not needed in the `.vscode/mcp.json` file.
|
||||
|
||||
```json
|
||||
{
|
||||
"mcp": {
|
||||
"inputs": [
|
||||
{
|
||||
"type": "promptString",
|
||||
"id": "gitea_token",
|
||||
"description": "Gitea Personal Access Token",
|
||||
"password": true
|
||||
}
|
||||
],
|
||||
"servers": {
|
||||
"github": {
|
||||
"command": "docker",
|
||||
"args": [
|
||||
"run",
|
||||
"-i",
|
||||
"--rm",
|
||||
"-e",
|
||||
"GITEA_ACCESS_TOKEN",
|
||||
"docker.gitea.com/gitea-mcp-server"
|
||||
],
|
||||
"env": {
|
||||
"GITEA_ACCESS_TOKEN": "${input:gitea_token}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 📥 Download the official binary release
|
||||
|
||||
You can download the official release from [here](https://gitea.com/gitea/gitea-mcp/releases).
|
||||
|
||||
### 🔧 Build from Source
|
||||
|
||||
@@ -25,7 +78,7 @@ Then run:
|
||||
make build
|
||||
```
|
||||
|
||||
### 🛠️ Add to PATH
|
||||
### 📁 Add to PATH
|
||||
|
||||
After building, copy the binary gitea-mcp to a directory included in your system's PATH. For example:
|
||||
|
||||
@@ -39,14 +92,17 @@ This example is for Cursor, you can also use plugins in VSCode.
|
||||
To configure the MCP server for Gitea, add the following to your MCP configuration file:
|
||||
|
||||
- **stdio mode**
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"gitea": {
|
||||
"command": "gitea-mcp",
|
||||
"args": [
|
||||
"-t", "stdio",
|
||||
"--host", "https://gitea.com"
|
||||
"-t",
|
||||
"stdio",
|
||||
"--host",
|
||||
"https://gitea.com"
|
||||
// "--token", "<your personal access token>"
|
||||
],
|
||||
"env": {
|
||||
@@ -59,6 +115,7 @@ To configure the MCP server for Gitea, add the following to your MCP configurati
|
||||
```
|
||||
|
||||
- **sse mode**
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
@@ -69,6 +126,8 @@ To configure the MCP server for Gitea, add the following to your MCP configurati
|
||||
}
|
||||
```
|
||||
|
||||
**Default log path**: `$HOME/.gitea-mcp/gitea-mcp.log`
|
||||
|
||||
> [!NOTE]
|
||||
> You can provide your Gitea host and access token either as command-line arguments or environment variables.
|
||||
> Command-line arguments have the highest priority
|
||||
@@ -79,42 +138,51 @@ Once everything is set up, try typing the following in your MCP-compatible chatb
|
||||
list all my repositories
|
||||
```
|
||||
|
||||
## ✅Available Tools
|
||||
## ✅ Available Tools
|
||||
|
||||
The Gitea MCP Server supports the following tools:
|
||||
|
||||
| Tool | Scope | Description |
|
||||
|:------:|:-------:|:------------:|
|
||||
|get_my_user_info|User|Get the information of the authenticated user|
|
||||
|create_repo|Repository|Create a new repository|
|
||||
|fork_repo|Repository|Fork a repository|
|
||||
|list_my_repos|Repository|List all repositories owned by the authenticated user|
|
||||
|create_branch|Branch|Create a new branch|
|
||||
|delete_branch|Branch|Delete a branch|
|
||||
|list_branches|Branch|List all branches in a repository|
|
||||
|list_repo_commits|Commit|List all commits in a repository|
|
||||
|get_file|File|Get the content of a file|
|
||||
|create_file|File|Create a new file|
|
||||
|update_file|File|Update an existing file|
|
||||
|delete_file|File|Delete a file|
|
||||
|get_issue_by_index|Issue|Get an issue by its index|
|
||||
|list_repo_issues|Issue|List all issues in a repository|
|
||||
|create_issue|Issue|Create a new issue|
|
||||
|create_issue_comment|Issue|Create a comment on an issue|
|
||||
|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|
|
||||
|create_pull_request|Pull Request|Create a new pull request|
|
||||
|search_users|User|Search for users|
|
||||
|search_org_teams|Organization|Search for teams in an organization|
|
||||
|search_repos|Repository|Search for repositories|
|
||||
|get_gitea_mcp_server_version|Server|Get the version of the Gitea MCP Server|
|
||||
|
||||
| :--------------------------: | :----------: | :---------------------------------------------------: |
|
||||
| get_my_user_info | User | Get the information of the authenticated user |
|
||||
| create_repo | Repository | Create a new repository |
|
||||
| fork_repo | Repository | Fork a repository |
|
||||
| list_my_repos | Repository | List all repositories owned by the authenticated user |
|
||||
| create_branch | Branch | Create a new branch |
|
||||
| delete_branch | Branch | Delete a branch |
|
||||
| list_branches | Branch | List all branches 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 |
|
||||
| create_file | File | Create a new file |
|
||||
| update_file | File | Update an existing file |
|
||||
| delete_file | File | Delete a file |
|
||||
| get_issue_by_index | Issue | Get an issue by its index |
|
||||
| list_repo_issues | Issue | List all issues in a repository |
|
||||
| create_issue | Issue | Create a new issue |
|
||||
| create_issue_comment | Issue | Create a comment on an issue |
|
||||
| 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 |
|
||||
| create_pull_request | Pull Request | Create a new pull request |
|
||||
| search_users | User | Search for users |
|
||||
| search_org_teams | Organization | Search for teams in an organization |
|
||||
| search_repos | Repository | Search for repositories |
|
||||
| get_gitea_mcp_server_version | Server | Get the version of the Gitea MCP Server |
|
||||
|
||||
## 🐛 Debugging
|
||||
|
||||
To enable debug mode, add the `-d` flag when running the Gitea MCP Server with sse mode:
|
||||
|
||||
```sh
|
||||
./gitea-mcp -t sse --token <your personal access token> -d
|
||||
./gitea-mcp -t sse [--port 8080] --token <your personal access token> -d
|
||||
```
|
||||
|
||||
## 🛠 Troubleshooting
|
||||
|
||||
If you encounter any issues, here are some common troubleshooting steps:
|
||||
|
||||
1. **Check your PATH**: Ensure that the `gitea-mcp` binary is in a directory included in your system's PATH.
|
||||
2. **Verify dependencies**: Make sure you have all the required dependencies installed, such as `make` and `Golang`.
|
||||
3. **Review configuration**: Double-check your MCP configuration file for any errors or missing information.
|
||||
4. **Consult logs**: Check the logs for any error messages or warnings that can provide more information about the issue.
|
||||
|
||||
Enjoy exploring and managing your Gitea repositories via chat!
|
||||
|
11
cmd/cmd.go
11
cmd/cmd.go
@@ -13,6 +13,7 @@ import (
|
||||
var (
|
||||
transport string
|
||||
host string
|
||||
port int
|
||||
token string
|
||||
|
||||
debug bool
|
||||
@@ -37,6 +38,12 @@ func init() {
|
||||
"https://gitea.com",
|
||||
"Gitea host",
|
||||
)
|
||||
flag.IntVar(
|
||||
&port,
|
||||
"port",
|
||||
8080,
|
||||
"sse port",
|
||||
)
|
||||
flag.StringVar(
|
||||
&token,
|
||||
"token",
|
||||
@@ -66,11 +73,15 @@ func init() {
|
||||
flagPkg.Host = "https://gitea.com"
|
||||
}
|
||||
|
||||
flagPkg.Port = port
|
||||
|
||||
flagPkg.Token = token
|
||||
if flagPkg.Token == "" {
|
||||
flagPkg.Token = os.Getenv("GITEA_ACCESS_TOKEN")
|
||||
}
|
||||
|
||||
flagPkg.Mode = transport
|
||||
|
||||
if debug {
|
||||
flagPkg.Debug = debug
|
||||
}
|
||||
|
7
go.mod
7
go.mod
@@ -3,9 +3,10 @@ module gitea.com/gitea/gitea-mcp
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
code.gitea.io/sdk/gitea v0.20.0
|
||||
github.com/mark3labs/mcp-go v0.15.0
|
||||
code.gitea.io/sdk/gitea v0.21.0
|
||||
github.com/mark3labs/mcp-go v0.18.0
|
||||
go.uber.org/zap v1.27.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -17,5 +18,5 @@ require (
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.36.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
)
|
||||
|
18
go.sum
18
go.sum
@@ -1,5 +1,5 @@
|
||||
code.gitea.io/sdk/gitea v0.20.0 h1:Zm/QDwwZK1awoM4AxdjeAQbxolzx2rIP8dDfmKu+KoU=
|
||||
code.gitea.io/sdk/gitea v0.20.0/go.mod h1:faouBHC/zyx5wLgjmRKR62ydyvMzwWf3QnU0bH7Cw6U=
|
||||
code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4=
|
||||
code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA=
|
||||
github.com/42wim/httpsig v1.2.2 h1:ofAYoHUNs/MJOLqQ8hIxeyz2QxOz8qdSVvp3PX/oPgA=
|
||||
github.com/42wim/httpsig v1.2.2/go.mod h1:P/UYo7ytNBFwc+dg35IubuAUIs8zj5zzFIgUCEl55WY=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
@@ -12,12 +12,12 @@ 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/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/mark3labs/mcp-go v0.15.0 h1:lViiC4dk6chJHZccezaTzZLMOQVUXJDGNQPtzExr5NQ=
|
||||
github.com/mark3labs/mcp-go v0.15.0/go.mod h1:xBB350hekQsJAK7gJAii8bcEoWemboLm2mRm5/+KBaU=
|
||||
github.com/mark3labs/mcp-go v0.18.0 h1:YuhgIVjNlTG2ZOwmrkORWyPTp0dz1opPEqvsPtySXao=
|
||||
github.com/mark3labs/mcp-go v0.18.0/go.mod h1:KmJndYv7GIgcPVwEKJjNcbhVQ+hJGJhrCCB/9xITzpE=
|
||||
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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
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/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=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
@@ -36,13 +36,15 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
@@ -68,19 +68,19 @@ func GetIssueByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
|
||||
log.Debugf("Called GetIssueByIndexFn")
|
||||
owner, ok := req.Params.Arguments["owner"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("owner is required")
|
||||
return to.ErrorResult(fmt.Errorf("owner is required"))
|
||||
}
|
||||
repo, ok := req.Params.Arguments["repo"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("repo is required")
|
||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||
}
|
||||
index, ok := req.Params.Arguments["index"].(float64)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("index is required")
|
||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
||||
}
|
||||
issue, _, err := gitea.Client().GetIssue(owner, repo, int64(index))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get %v/%v/issue/%v err", owner, repo, int64(index))
|
||||
return to.ErrorResult(fmt.Errorf("get %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
|
||||
}
|
||||
|
||||
return to.TextResult(issue)
|
||||
@@ -90,11 +90,11 @@ func ListRepoIssuesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
|
||||
log.Debugf("Called ListIssuesFn")
|
||||
owner, ok := req.Params.Arguments["owner"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("owner is required")
|
||||
return to.ErrorResult(fmt.Errorf("owner is required"))
|
||||
}
|
||||
repo, ok := req.Params.Arguments["repo"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("repo is required")
|
||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||
}
|
||||
state, ok := req.Params.Arguments["state"].(string)
|
||||
if !ok {
|
||||
@@ -117,7 +117,7 @@ func ListRepoIssuesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
|
||||
}
|
||||
issues, _, err := gitea.Client().ListRepoIssues(owner, repo, opt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get %v/%v/issues err", owner, repo)
|
||||
return to.ErrorResult(fmt.Errorf("get %v/%v/issues err: %v", owner, repo, err))
|
||||
}
|
||||
return to.TextResult(issues)
|
||||
}
|
||||
@@ -126,26 +126,26 @@ func CreateIssueFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
|
||||
log.Debugf("Called CreateIssueFn")
|
||||
owner, ok := req.Params.Arguments["owner"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("owner is required")
|
||||
return to.ErrorResult(fmt.Errorf("owner is required"))
|
||||
}
|
||||
repo, ok := req.Params.Arguments["repo"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("repo is required")
|
||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||
}
|
||||
title, ok := req.Params.Arguments["title"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("title is required")
|
||||
return to.ErrorResult(fmt.Errorf("title is required"))
|
||||
}
|
||||
body, ok := req.Params.Arguments["body"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("body is required")
|
||||
return to.ErrorResult(fmt.Errorf("body is required"))
|
||||
}
|
||||
issue, _, err := gitea.Client().CreateIssue(owner, repo, gitea_sdk.CreateIssueOption{
|
||||
Title: title,
|
||||
Body: body,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create %v/%v/issue err", owner, repo)
|
||||
return to.ErrorResult(fmt.Errorf("create %v/%v/issue err", owner, repo))
|
||||
}
|
||||
|
||||
return to.TextResult(issue)
|
||||
@@ -155,26 +155,26 @@ func CreateIssueCommentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
|
||||
log.Debugf("Called CreateIssueCommentFn")
|
||||
owner, ok := req.Params.Arguments["owner"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("owner is required")
|
||||
return to.ErrorResult(fmt.Errorf("owner is required"))
|
||||
}
|
||||
repo, ok := req.Params.Arguments["repo"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("repo is required")
|
||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||
}
|
||||
index, ok := req.Params.Arguments["index"].(float64)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("index is required")
|
||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
||||
}
|
||||
body, ok := req.Params.Arguments["body"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("body is required")
|
||||
return to.ErrorResult(fmt.Errorf("body is required"))
|
||||
}
|
||||
opt := gitea_sdk.CreateIssueCommentOption{
|
||||
Body: body,
|
||||
}
|
||||
issueComment, _, err := gitea.Client().CreateIssueComment(owner, repo, int64(index), opt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create %v/%v/issue/%v/comment err", owner, repo, int64(index))
|
||||
return to.ErrorResult(fmt.Errorf("create %v/%v/issue/%v/comment err", owner, repo, int64(index)))
|
||||
}
|
||||
|
||||
return to.TextResult(issueComment)
|
||||
|
@@ -50,8 +50,8 @@ func Run(transport, version string) error {
|
||||
}
|
||||
case "sse":
|
||||
sseServer := server.NewSSEServer(mcpServer)
|
||||
log.Infof("Gitea MCP SSE server listening on :8080")
|
||||
if err := sseServer.Start(":8080"); err != nil {
|
||||
log.Infof("Gitea MCP SSE server listening on :%d", flag.Port)
|
||||
if err := sseServer.Start(fmt.Sprintf(":%d", flag.Port)); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
|
@@ -33,9 +33,11 @@ var (
|
||||
mcp.WithDescription("List repository pull requests"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithString("state", mcp.Description("state")),
|
||||
mcp.WithString("sort", mcp.Description("sort")),
|
||||
mcp.WithString("state", mcp.Description("state"), mcp.Enum("open", "closed", "all"), mcp.DefaultString("all")),
|
||||
mcp.WithString("sort", mcp.Description("sort"), mcp.Enum("oldest", "recentupdate", "leastupdate", "mostcomment", "leastcomment", "priority"), mcp.DefaultString("recentupdate")),
|
||||
mcp.WithNumber("milestone", mcp.Description("milestone")),
|
||||
mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1)),
|
||||
mcp.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)),
|
||||
)
|
||||
|
||||
CreatePullRequestTool = mcp.NewTool(
|
||||
@@ -60,19 +62,19 @@ func GetPullRequestByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp
|
||||
log.Debugf("Called GetPullRequestByIndexFn")
|
||||
owner, ok := req.Params.Arguments["owner"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("owner is required")
|
||||
return to.ErrorResult(fmt.Errorf("owner is required"))
|
||||
}
|
||||
repo, ok := req.Params.Arguments["repo"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("repo is required")
|
||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||
}
|
||||
index, ok := req.Params.Arguments["index"].(float64)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("index is required")
|
||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
||||
}
|
||||
pr, _, err := gitea.Client().GetPullRequest(owner, repo, int64(index))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get %v/%v/pr/%v err", owner, repo, int64(index))
|
||||
return to.ErrorResult(fmt.Errorf("get %v/%v/pr/%v err: %v", owner, repo, int64(index), err))
|
||||
}
|
||||
|
||||
return to.TextResult(pr)
|
||||
@@ -82,27 +84,38 @@ func ListRepoPullRequestsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.
|
||||
log.Debugf("Called ListRepoPullRequests")
|
||||
owner, ok := req.Params.Arguments["owner"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("owner is required")
|
||||
return to.ErrorResult(fmt.Errorf("owner is required"))
|
||||
}
|
||||
repo, ok := req.Params.Arguments["repo"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("repo is required")
|
||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||
}
|
||||
state, _ := req.Params.Arguments["state"].(string)
|
||||
sort, _ := req.Params.Arguments["sort"].(string)
|
||||
sort, ok := req.Params.Arguments["sort"].(string)
|
||||
if !ok {
|
||||
sort = "recentupdate"
|
||||
}
|
||||
milestone, _ := req.Params.Arguments["milestone"].(float64)
|
||||
page, ok := req.Params.Arguments["page"].(float64)
|
||||
if !ok {
|
||||
page = 1
|
||||
}
|
||||
pageSize, ok := req.Params.Arguments["pageSize"].(float64)
|
||||
if !ok {
|
||||
pageSize = 100
|
||||
}
|
||||
opt := gitea_sdk.ListPullRequestsOptions{
|
||||
State: gitea_sdk.StateType(state),
|
||||
Sort: sort,
|
||||
Milestone: int64(milestone),
|
||||
ListOptions: gitea_sdk.ListOptions{
|
||||
Page: 1,
|
||||
PageSize: 1000,
|
||||
Page: int(page),
|
||||
PageSize: int(pageSize),
|
||||
},
|
||||
}
|
||||
pullRequests, _, err := gitea.Client().ListRepoPullRequests("", "", opt)
|
||||
pullRequests, _, err := gitea.Client().ListRepoPullRequests(owner, repo, opt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list %v/%v/pull_requests err", owner, repo)
|
||||
return to.ErrorResult(fmt.Errorf("list %v/%v/pull_requests err: %v", owner, repo, err))
|
||||
}
|
||||
|
||||
return to.TextResult(pullRequests)
|
||||
@@ -112,27 +125,27 @@ func CreatePullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Cal
|
||||
log.Debugf("Called CreatePullRequestFn")
|
||||
owner, ok := req.Params.Arguments["owner"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("owner is required")
|
||||
return to.ErrorResult(fmt.Errorf("owner is required"))
|
||||
}
|
||||
repo, ok := req.Params.Arguments["repo"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("repo is required")
|
||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||
}
|
||||
title, ok := req.Params.Arguments["title"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("title is required")
|
||||
return to.ErrorResult(fmt.Errorf("title is required"))
|
||||
}
|
||||
body, ok := req.Params.Arguments["body"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("body is required")
|
||||
return to.ErrorResult(fmt.Errorf("body is required"))
|
||||
}
|
||||
head, ok := req.Params.Arguments["head"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("head is required")
|
||||
return to.ErrorResult(fmt.Errorf("head is required"))
|
||||
}
|
||||
base, ok := req.Params.Arguments["base"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("base is required")
|
||||
return to.ErrorResult(fmt.Errorf("base is required"))
|
||||
}
|
||||
pr, _, err := gitea.Client().CreatePullRequest(owner, repo, gitea_sdk.CreatePullRequestOption{
|
||||
Title: title,
|
||||
@@ -141,7 +154,7 @@ func CreatePullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Cal
|
||||
Base: base,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create %v/%v/pull_request err", owner, repo)
|
||||
return to.ErrorResult(fmt.Errorf("create %v/%v/pull_request err: %v", owner, repo, err))
|
||||
}
|
||||
|
||||
return to.TextResult(pr)
|
||||
|
@@ -48,15 +48,15 @@ func CreateBranchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
|
||||
log.Debugf("Called CreateBranchFn")
|
||||
owner, ok := req.Params.Arguments["owner"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("owner is required")
|
||||
return to.ErrorResult(fmt.Errorf("owner is required"))
|
||||
}
|
||||
repo, ok := req.Params.Arguments["repo"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("repo is required")
|
||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||
}
|
||||
branch, ok := req.Params.Arguments["branch"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("branch is required")
|
||||
return to.ErrorResult(fmt.Errorf("branch is required"))
|
||||
}
|
||||
oldBranch, _ := req.Params.Arguments["old_branch"].(string)
|
||||
|
||||
@@ -65,7 +65,7 @@ func CreateBranchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
|
||||
OldBranchName: oldBranch,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Create Branch Error: %v", err)
|
||||
return to.ErrorResult(fmt.Errorf("create branch error: %v", err))
|
||||
}
|
||||
|
||||
return mcp.NewToolResultText("Branch Created"), nil
|
||||
@@ -75,19 +75,19 @@ func DeleteBranchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
|
||||
log.Debugf("Called DeleteBranchFn")
|
||||
owner, ok := req.Params.Arguments["owner"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("owner is required")
|
||||
return to.ErrorResult(fmt.Errorf("owner is required"))
|
||||
}
|
||||
repo, ok := req.Params.Arguments["repo"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("repo is required")
|
||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||
}
|
||||
branch, ok := req.Params.Arguments["branch"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("branch is required")
|
||||
return to.ErrorResult(fmt.Errorf("branch is required"))
|
||||
}
|
||||
_, _, err := gitea.Client().DeleteRepoBranch(owner, repo, branch)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Delete Branch Error: %v", err)
|
||||
return to.ErrorResult(fmt.Errorf("delete branch error: %v", err))
|
||||
}
|
||||
|
||||
return to.TextResult("Branch Deleted")
|
||||
@@ -97,11 +97,11 @@ func ListBranchesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
|
||||
log.Debugf("Called ListBranchesFn")
|
||||
owner, ok := req.Params.Arguments["owner"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("owner is required")
|
||||
return to.ErrorResult(fmt.Errorf("owner is required"))
|
||||
}
|
||||
repo, ok := req.Params.Arguments["repo"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("repo is required")
|
||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||
}
|
||||
opt := gitea_sdk.ListRepoBranchesOptions{
|
||||
ListOptions: gitea_sdk.ListOptions{
|
||||
@@ -111,7 +111,7 @@ func ListBranchesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
|
||||
}
|
||||
branches, _, err := gitea.Client().ListRepoBranches(owner, repo, opt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("List Branches Error: %v", err)
|
||||
return to.ErrorResult(fmt.Errorf("list branches error: %v", err))
|
||||
}
|
||||
|
||||
return to.TextResult(branches)
|
||||
|
@@ -22,8 +22,10 @@ var (
|
||||
mcp.WithDescription("List repository commits"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithString("sha", mcp.Description("sha")),
|
||||
mcp.WithString("path", mcp.Description("path")),
|
||||
mcp.WithString("sha", mcp.Description("SHA or branch to start listing commits from")),
|
||||
mcp.WithString("path", mcp.Description("path indicates that only commits that include the path's file/dir should be returned.")),
|
||||
mcp.WithNumber("page", mcp.Required(), mcp.Description("page number"), mcp.DefaultNumber(1), mcp.Min(1)),
|
||||
mcp.WithNumber("page_size", mcp.Required(), mcp.Description("page size"), mcp.DefaultNumber(50), mcp.Min(1)),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -31,25 +33,33 @@ func ListRepoCommitsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
|
||||
log.Debugf("Called ListRepoCommitsFn")
|
||||
owner, ok := req.Params.Arguments["owner"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("owner is required")
|
||||
return to.ErrorResult(fmt.Errorf("owner is required"))
|
||||
}
|
||||
repo, ok := req.Params.Arguments["repo"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("repo is required")
|
||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||
}
|
||||
page, ok := req.Params.Arguments["page"].(float64)
|
||||
if !ok {
|
||||
return to.ErrorResult(fmt.Errorf("page is required"))
|
||||
}
|
||||
pageSize, ok := req.Params.Arguments["page_size"].(float64)
|
||||
if !ok {
|
||||
return to.ErrorResult(fmt.Errorf("page_size is required"))
|
||||
}
|
||||
sha, _ := req.Params.Arguments["sha"].(string)
|
||||
path, _ := req.Params.Arguments["path"].(string)
|
||||
opt := gitea_sdk.ListCommitOptions{
|
||||
ListOptions: gitea_sdk.ListOptions{
|
||||
Page: 1,
|
||||
PageSize: 1000,
|
||||
Page: int(page),
|
||||
PageSize: int(pageSize),
|
||||
},
|
||||
SHA: sha,
|
||||
Path: path,
|
||||
}
|
||||
commits, _, err := gitea.Client().ListRepoCommits(owner, repo, opt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list repo commits err: %v", err)
|
||||
return to.ErrorResult(fmt.Errorf("list repo commits err: %v", err))
|
||||
}
|
||||
return to.TextResult(commits)
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
"gitea.com/gitea/gitea-mcp/pkg/gitea"
|
||||
@@ -13,19 +14,19 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
GetFileToolName = "get_file"
|
||||
GetFileToolName = "get_file_content"
|
||||
CreateFileToolName = "create_file"
|
||||
UpdateFileToolName = "update_file"
|
||||
DeleteFileToolName = "delete_file"
|
||||
)
|
||||
|
||||
var (
|
||||
GetFileTool = mcp.NewTool(
|
||||
GetFileContentTool = mcp.NewTool(
|
||||
GetFileToolName,
|
||||
mcp.WithDescription("Get file"),
|
||||
mcp.WithDescription("Get file Content and Metadata"),
|
||||
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")),
|
||||
mcp.WithString("ref", mcp.Required(), mcp.Description("ref can be branch/tag/commit")),
|
||||
mcp.WithString("filePath", mcp.Required(), mcp.Description("file path")),
|
||||
)
|
||||
|
||||
@@ -47,7 +48,8 @@ var (
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithString("filePath", mcp.Required(), mcp.Description("file path")),
|
||||
mcp.WithString("content", mcp.Required(), mcp.Description("file content")),
|
||||
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("message", mcp.Required(), mcp.Description("commit message")),
|
||||
mcp.WithString("branch_name", mcp.Required(), mcp.Description("branch name")),
|
||||
)
|
||||
@@ -64,47 +66,47 @@ var (
|
||||
)
|
||||
)
|
||||
|
||||
func GetFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
func GetFileContentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called GetFileFn")
|
||||
owner, ok := req.Params.Arguments["owner"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("owner is required")
|
||||
return to.ErrorResult(fmt.Errorf("owner is required"))
|
||||
}
|
||||
repo, ok := req.Params.Arguments["repo"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("repo is required")
|
||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||
}
|
||||
ref, _ := req.Params.Arguments["ref"].(string)
|
||||
filePath, ok := req.Params.Arguments["filePath"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("filePath is required")
|
||||
return to.ErrorResult(fmt.Errorf("filePath is required"))
|
||||
}
|
||||
file, _, err := gitea.Client().GetFile(owner, repo, ref, filePath)
|
||||
content, _, err := gitea.Client().GetContents(owner, repo, ref, filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get file err: %v", err)
|
||||
return to.ErrorResult(fmt.Errorf("get file err: %v", err))
|
||||
}
|
||||
return to.TextResult(file)
|
||||
return to.TextResult(content)
|
||||
}
|
||||
|
||||
func CreateFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called CreateFileFn")
|
||||
owner, ok := req.Params.Arguments["owner"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("owner is required")
|
||||
return to.ErrorResult(fmt.Errorf("owner is required"))
|
||||
}
|
||||
repo, ok := req.Params.Arguments["repo"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("repo is required")
|
||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||
}
|
||||
filePath, ok := req.Params.Arguments["filePath"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("filePath is required")
|
||||
return to.ErrorResult(fmt.Errorf("filePath is required"))
|
||||
}
|
||||
content, _ := req.Params.Arguments["content"].(string)
|
||||
message, _ := req.Params.Arguments["message"].(string)
|
||||
branchName, _ := req.Params.Arguments["branch_name"].(string)
|
||||
opt := gitea_sdk.CreateFileOptions{
|
||||
Content: content,
|
||||
Content: base64.StdEncoding.EncodeToString([]byte(content)),
|
||||
FileOptions: gitea_sdk.FileOptions{
|
||||
Message: message,
|
||||
BranchName: branchName,
|
||||
@@ -113,7 +115,7 @@ func CreateFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
|
||||
|
||||
_, _, err := gitea.Client().CreateFile(owner, repo, filePath, opt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create file err: %v", err)
|
||||
return to.ErrorResult(fmt.Errorf("create file err: %v", err))
|
||||
}
|
||||
return to.TextResult("Create file success")
|
||||
}
|
||||
@@ -122,20 +124,26 @@ func UpdateFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
|
||||
log.Debugf("Called UpdateFileFn")
|
||||
owner, ok := req.Params.Arguments["owner"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("owner is required")
|
||||
return to.ErrorResult(fmt.Errorf("owner is required"))
|
||||
}
|
||||
repo, ok := req.Params.Arguments["repo"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("repo is required")
|
||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||
}
|
||||
filePath, ok := req.Params.Arguments["filePath"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("filePath is required")
|
||||
return to.ErrorResult(fmt.Errorf("filePath is required"))
|
||||
}
|
||||
sha, ok := req.Params.Arguments["sha"].(string)
|
||||
if !ok {
|
||||
return to.ErrorResult(fmt.Errorf("sha is required"))
|
||||
}
|
||||
content, _ := req.Params.Arguments["content"].(string)
|
||||
message, _ := req.Params.Arguments["message"].(string)
|
||||
branchName, _ := req.Params.Arguments["branch_name"].(string)
|
||||
|
||||
opt := gitea_sdk.UpdateFileOptions{
|
||||
SHA: sha,
|
||||
Content: content,
|
||||
FileOptions: gitea_sdk.FileOptions{
|
||||
Message: message,
|
||||
@@ -144,7 +152,7 @@ func UpdateFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
|
||||
}
|
||||
_, _, err := gitea.Client().UpdateFile(owner, repo, filePath, opt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("update file err: %v", err)
|
||||
return to.ErrorResult(fmt.Errorf("update file err: %v", err))
|
||||
}
|
||||
return to.TextResult("Update file success")
|
||||
}
|
||||
@@ -153,27 +161,32 @@ func DeleteFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
|
||||
log.Debugf("Called DeleteFileFn")
|
||||
owner, ok := req.Params.Arguments["owner"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("owner is required")
|
||||
return to.ErrorResult(fmt.Errorf("owner is required"))
|
||||
}
|
||||
repo, ok := req.Params.Arguments["repo"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("repo is required")
|
||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||
}
|
||||
filePath, ok := req.Params.Arguments["filePath"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("filePath is required")
|
||||
return to.ErrorResult(fmt.Errorf("filePath is required"))
|
||||
}
|
||||
message, _ := req.Params.Arguments["message"].(string)
|
||||
branchName, _ := req.Params.Arguments["branch_name"].(string)
|
||||
sha, ok := req.Params.Arguments["sha"].(string)
|
||||
if !ok {
|
||||
return to.ErrorResult(fmt.Errorf("sha is required"))
|
||||
}
|
||||
opt := gitea_sdk.DeleteFileOptions{
|
||||
FileOptions: gitea_sdk.FileOptions{
|
||||
Message: message,
|
||||
BranchName: branchName,
|
||||
},
|
||||
SHA: sha,
|
||||
}
|
||||
_, err := gitea.Client().DeleteFile(owner, repo, filePath, opt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("delete file err: %v", err)
|
||||
return to.ErrorResult(fmt.Errorf("delete file err: %v", err))
|
||||
}
|
||||
return to.TextResult("Delete file success")
|
||||
}
|
||||
|
@@ -60,7 +60,7 @@ func RegisterTool(s *server.MCPServer) {
|
||||
s.AddTool(ListMyReposTool, ListMyReposFn)
|
||||
|
||||
// File
|
||||
s.AddTool(GetFileTool, GetFileFn)
|
||||
s.AddTool(GetFileContentTool, GetFileContentFn)
|
||||
s.AddTool(CreateFileTool, CreateFileFn)
|
||||
s.AddTool(UpdateFileTool, UpdateFileFn)
|
||||
s.AddTool(DeleteFileTool, DeleteFileFn)
|
||||
@@ -78,7 +78,7 @@ func CreateRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
|
||||
log.Debugf("Called CreateRepoFn")
|
||||
name, ok := req.Params.Arguments["name"].(string)
|
||||
if !ok {
|
||||
return nil, errors.New("repository name is required")
|
||||
return to.ErrorResult(errors.New("repository name is required"))
|
||||
}
|
||||
description, _ := req.Params.Arguments["description"].(string)
|
||||
private, _ := req.Params.Arguments["private"].(bool)
|
||||
@@ -104,7 +104,7 @@ func CreateRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
|
||||
}
|
||||
repo, _, err := gitea.Client().CreateRepo(opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return to.ErrorResult(fmt.Errorf("create repo err: %v", err))
|
||||
}
|
||||
return to.TextResult(repo)
|
||||
}
|
||||
@@ -113,21 +113,29 @@ func ForkRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResu
|
||||
log.Debugf("Called ForkRepoFn")
|
||||
user, ok := req.Params.Arguments["user"].(string)
|
||||
if !ok {
|
||||
return nil, errors.New("user name is required")
|
||||
return to.ErrorResult(errors.New("user name is required"))
|
||||
}
|
||||
repo, ok := req.Params.Arguments["repo"].(string)
|
||||
if !ok {
|
||||
return nil, errors.New("repository name is required")
|
||||
return to.ErrorResult(errors.New("repository name is required"))
|
||||
}
|
||||
organization, ok := req.Params.Arguments["organization"].(string)
|
||||
organizationPtr := ptr.To(organization)
|
||||
if !ok || organization == "" {
|
||||
organizationPtr = nil
|
||||
}
|
||||
name, ok := req.Params.Arguments["name"].(string)
|
||||
namePtr := ptr.To(name)
|
||||
if !ok || name == "" {
|
||||
namePtr = nil
|
||||
}
|
||||
organization, _ := req.Params.Arguments["organization"].(string)
|
||||
name, _ := req.Params.Arguments["name"].(string)
|
||||
opt := gitea_sdk.CreateForkOption{
|
||||
Organization: ptr.To(organization),
|
||||
Name: ptr.To(name),
|
||||
Organization: organizationPtr,
|
||||
Name: namePtr,
|
||||
}
|
||||
_, _, err := gitea.Client().CreateFork(user, repo, opt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fork repository error %v", err)
|
||||
return to.ErrorResult(fmt.Errorf("fork repository error %v", err))
|
||||
}
|
||||
return to.TextResult("Fork success")
|
||||
}
|
||||
@@ -136,21 +144,21 @@ func ListMyReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
|
||||
log.Debugf("Called ListMyReposFn")
|
||||
page, ok := req.Params.Arguments["page"].(float64)
|
||||
if !ok {
|
||||
return nil, errors.New("get page number error")
|
||||
page = 1
|
||||
}
|
||||
size, ok := req.Params.Arguments["pageSize"].(float64)
|
||||
pageSize, ok := req.Params.Arguments["pageSize"].(float64)
|
||||
if !ok {
|
||||
return nil, errors.New("get page size number error")
|
||||
pageSize = 100
|
||||
}
|
||||
opt := gitea_sdk.ListReposOptions{
|
||||
ListOptions: gitea_sdk.ListOptions{
|
||||
Page: int(page),
|
||||
PageSize: int(size),
|
||||
PageSize: int(pageSize),
|
||||
},
|
||||
}
|
||||
repos, _, err := gitea.Client().ListMyRepos(opt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list my repositories error %v", err)
|
||||
return to.ErrorResult(fmt.Errorf("list my repositories error: %v", err))
|
||||
}
|
||||
|
||||
return to.TextResult(repos)
|
||||
|
@@ -65,7 +65,7 @@ func SearchUsersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
|
||||
log.Debugf("Called SearchUsersFn")
|
||||
keyword, ok := req.Params.Arguments["keyword"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("keyword is required")
|
||||
return to.ErrorResult(fmt.Errorf("keyword is required"))
|
||||
}
|
||||
page, ok := req.Params.Arguments["page"].(float64)
|
||||
if !ok {
|
||||
@@ -84,7 +84,7 @@ func SearchUsersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
|
||||
}
|
||||
users, _, err := gitea.Client().SearchUsers(opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return to.ErrorResult(fmt.Errorf("search users err: %v", err))
|
||||
}
|
||||
return to.TextResult(users)
|
||||
}
|
||||
@@ -93,11 +93,11 @@ func SearchOrgTeamsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
|
||||
log.Debugf("Called SearchOrgTeamsFn")
|
||||
org, ok := req.Params.Arguments["org"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("organization is required")
|
||||
return to.ErrorResult(fmt.Errorf("organization is required"))
|
||||
}
|
||||
query, ok := req.Params.Arguments["query"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("query is required")
|
||||
return to.ErrorResult(fmt.Errorf("query is required"))
|
||||
}
|
||||
includeDescription, _ := req.Params.Arguments["includeDescription"].(bool)
|
||||
page, ok := req.Params.Arguments["page"].(float64)
|
||||
@@ -118,7 +118,7 @@ func SearchOrgTeamsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
|
||||
}
|
||||
teams, _, err := gitea.Client().SearchOrgTeams(org, &opt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("search organization teams error: %v", err)
|
||||
return to.ErrorResult(fmt.Errorf("search organization teams error: %v", err))
|
||||
}
|
||||
return to.TextResult(teams)
|
||||
}
|
||||
@@ -127,7 +127,7 @@ func SearchReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
|
||||
log.Debugf("Called SearchReposFn")
|
||||
keyword, ok := req.Params.Arguments["keyword"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("keyword is required")
|
||||
return to.ErrorResult(fmt.Errorf("keyword is required"))
|
||||
}
|
||||
keywordIsTopic, _ := req.Params.Arguments["keywordIsTopic"].(bool)
|
||||
keywordInDescription, _ := req.Params.Arguments["keywordInDescription"].(bool)
|
||||
@@ -160,7 +160,7 @@ func SearchReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
|
||||
}
|
||||
repos, _, err := gitea.Client().SearchRepos(opt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("search repos error: %v", err)
|
||||
return to.ErrorResult(fmt.Errorf("search repos error: %v", err))
|
||||
}
|
||||
return to.TextResult(repos)
|
||||
}
|
||||
|
@@ -31,7 +31,7 @@ func GetUserInfoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
|
||||
log.Debugf("Called GetUserInfoFn")
|
||||
user, _, err := gitea.Client().GetMyUserInfo()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get user info err: %v", err)
|
||||
return to.ErrorResult(fmt.Errorf("get user info err: %v", err))
|
||||
}
|
||||
|
||||
return to.TextResult(user)
|
||||
|
@@ -2,8 +2,10 @@ package flag
|
||||
|
||||
var (
|
||||
Host string
|
||||
Port int
|
||||
Token string
|
||||
Version string
|
||||
Mode string
|
||||
|
||||
Debug bool
|
||||
)
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -8,6 +9,7 @@ import (
|
||||
"gitea.com/gitea/gitea-mcp/pkg/flag"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -20,12 +22,27 @@ func Default() *zap.Logger {
|
||||
if defaultLogger == nil {
|
||||
ec := zap.NewProductionEncoderConfig()
|
||||
ec.EncodeTime = zapcore.TimeEncoderOfLayout(time.DateTime)
|
||||
ec.EncodeLevel = zapcore.CapitalColorLevelEncoder
|
||||
ec.EncodeLevel = zapcore.CapitalLevelEncoder
|
||||
|
||||
var ws zapcore.WriteSyncer
|
||||
var wss []zapcore.WriteSyncer
|
||||
|
||||
home, _ := os.UserHomeDir()
|
||||
if home == "" {
|
||||
home = os.TempDir()
|
||||
}
|
||||
|
||||
wss = append(wss, zapcore.AddSync(&lumberjack.Logger{
|
||||
Filename: fmt.Sprintf("%s/.gitea-mcp/gitea-mcp.log", home),
|
||||
MaxSize: 100,
|
||||
MaxBackups: 10,
|
||||
MaxAge: 30,
|
||||
}))
|
||||
|
||||
if flag.Mode == "sse" {
|
||||
wss = append(wss, zapcore.AddSync(os.Stdout))
|
||||
}
|
||||
|
||||
ws = zapcore.NewMultiWriteSyncer(wss...)
|
||||
|
||||
enc := zapcore.NewConsoleEncoder(ec)
|
||||
@@ -37,7 +54,7 @@ func Default() *zap.Logger {
|
||||
}
|
||||
core := zapcore.NewCore(enc, ws, level)
|
||||
options := []zap.Option{
|
||||
zap.AddStacktrace(zapcore.ErrorLevel),
|
||||
zap.AddStacktrace(zapcore.DPanicLevel),
|
||||
zap.AddCaller(),
|
||||
zap.AddCallerSkip(1),
|
||||
}
|
||||
|
@@ -21,3 +21,8 @@ func TextResult(v any) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Text Result: %s", string(resultBytes))
|
||||
return mcp.NewToolResultText(string(resultBytes)), nil
|
||||
}
|
||||
|
||||
func ErrorResult(err error) (*mcp.CallToolResult, error) {
|
||||
log.Errorf(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
Reference in New Issue
Block a user