21 Commits

Author SHA1 Message Date
hiifong
df47a0c9eb Add Dockerfile 2025-04-06 13:09:14 +08:00
hiifong
e3307adbdf Add Dockerfile (#16)
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/16
Co-authored-by: hiifong <f@ilo.nz>
Co-committed-by: hiifong <f@ilo.nz>
2025-04-06 04:54:56 +00:00
hiifong
afada4435e Add Dockerfile (#15)
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/15
Co-authored-by: hiifong <f@ilo.nz>
Co-committed-by: hiifong <f@ilo.nz>
2025-04-06 04:46:27 +00:00
hiifong
6285bd2467 test (#14)
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/14
Co-authored-by: hiifong <f@ilo.nz>
Co-committed-by: hiifong <f@ilo.nz>
2025-04-06 04:12:06 +00:00
hiifong
5bbf8e0afb Add Dockerfile (#13)
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/13
Co-authored-by: hiifong <f@ilo.nz>
Co-committed-by: hiifong <f@ilo.nz>
2025-04-06 04:06:34 +00:00
appleboy
0535f5bab7 docs: improve documentation with new sections and better readability (#12)
- Add a "What is Gitea?" section explaining Gitea
- Add a "What is MCP?" section describing Model Context Protocol
- Reformat the MCP server configuration instructions for better readability
- Correct the markdown table for tool support
- Add a Troubleshooting section with common steps to resolve issues

Signed-off-by: appleboy <appleboy.tw@gmail.com>

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/12
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: appleboy <appleboy.tw@gmail.com>
Co-committed-by: appleboy <appleboy.tw@gmail.com>
2025-04-05 05:06:34 +00:00
hiifong
d892b05048 Support custom sse port (#11)
fix: #10
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/11
2025-04-01 08:07:47 +00:00
hiifong
592cf51c9b fix bug (#9)
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/9
2025-03-28 03:10:29 +00:00
hiifong
2a9504fc5d update (#8)
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/8
2025-03-27 07:31:39 +00:00
hiifong
2f17f37053 fix bug (#7)
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/7
2025-03-27 07:18:51 +00:00
hiifong
5270d2eb08 Adding more logs (#6)
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/6
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: hiifong <i@hiif.ong>
Co-committed-by: hiifong <i@hiif.ong>
2025-03-25 17:22:39 +00:00
Lunny Xiao
97b98c3fc2 Merge pull request 'Add SHA to update file, convert get_file into get_file_content' (#5) from yp05327/gitea-mcp:add-sha-to-update-file into main
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/5
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-03-25 04:30:18 +00:00
hiifong
58328680a6 Merge branch 'main' into add-sha-to-update-file 2025-03-25 04:29:07 +00:00
Lunny Xiao
28947a030e Merge pull request 'Update README' (#4) from yp05327/gitea-mcp:update-readme into main
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/4
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-03-25 04:28:34 +00:00
yp05327
d48e233fba update readme 2025-03-25 04:26:47 +00:00
yp05327
0e225f21da fix 2025-03-25 04:25:28 +00:00
yp05327
b9e575ad64 fix 2025-03-25 03:42:37 +00:00
yp05327
8395957553 update 2025-03-25 03:11:33 +00:00
yp05327
0aa33e0d62 change the emoji 2025-03-25 03:10:06 +00:00
yp05327
7d2a0985a3 change the emoji 2025-03-25 03:09:12 +00:00
yp05327
6f86512a7d update 2025-03-25 02:56:56 +00:00
19 changed files with 411 additions and 151 deletions

View 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 }}

View File

@@ -36,3 +36,44 @@ jobs:
files: |- files: |-
bin/** bin/**
token: '${{secrets.RELEASE_TOKEN}}' 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
View 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
View File

@@ -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. **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.
[![Install with Docker in VS Code](https://img.shields.io/badge/VS_Code-Install_Server-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](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}}) [![Install with Docker in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install_Server-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](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 ## 🚧 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 ### 🔧 Build from Source
@@ -25,7 +78,7 @@ Then run:
make build 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: 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: To configure the MCP server for Gitea, add the following to your MCP configuration file:
- **stdio mode** - **stdio mode**
```json ```json
{ {
"mcpServers": { "mcpServers": {
"gitea": { "gitea": {
"command": "gitea-mcp", "command": "gitea-mcp",
"args": [ "args": [
"-t", "stdio", "-t",
"--host", "https://gitea.com" "stdio",
"--host",
"https://gitea.com"
// "--token", "<your personal access token>" // "--token", "<your personal access token>"
], ],
"env": { "env": {
@@ -59,6 +115,7 @@ To configure the MCP server for Gitea, add the following to your MCP configurati
``` ```
- **sse mode** - **sse mode**
```json ```json
{ {
"mcpServers": { "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] > [!NOTE]
> You can provide your Gitea host and access token either as command-line arguments or environment variables. > You can provide your Gitea host and access token either as command-line arguments or environment variables.
> Command-line arguments have the highest priority > 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 list all my repositories
``` ```
## ✅Available Tools ## ✅ Available Tools
The Gitea MCP Server supports the following tools: The Gitea MCP Server supports the following tools:
| Tool | Scope | Description | | Tool | Scope | Description |
|:------:|:-------:|:------------:| | :--------------------------: | :----------: | :---------------------------------------------------: |
|get_my_user_info|User|Get the information of the authenticated user| | get_my_user_info | User | Get the information of the authenticated user |
|create_repo|Repository|Create a new repository| | create_repo | Repository | Create a new repository |
|fork_repo|Repository|Fork a repository| | fork_repo | Repository | Fork a repository |
|list_my_repos|Repository|List all repositories owned by the authenticated user| | list_my_repos | Repository | List all repositories owned by the authenticated user |
|create_branch|Branch|Create a new branch| | create_branch | Branch | Create a new branch |
|delete_branch|Branch|Delete a branch| | delete_branch | Branch | Delete a branch |
|list_branches|Branch|List all branches in a repository| | list_branches | Branch | List all branches 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|File|Get the content of a file| | get_file_content | File | Get the content and metadata of a file |
|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 |
|get_issue_by_index|Issue|Get an issue by its index| | get_issue_by_index | Issue | Get an issue by its index |
|list_repo_issues|Issue|List all issues in a repository| | list_repo_issues | Issue | List all issues in a repository |
|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 |
|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 |
|search_users|User|Search for users| | search_users | User | Search for users |
|search_org_teams|Organization|Search for teams in an organization| | search_org_teams | Organization | Search for teams in an organization |
|search_repos|Repository|Search for repositories| | search_repos | Repository | Search for repositories |
|get_gitea_mcp_server_version|Server|Get the version of the Gitea MCP Server| | get_gitea_mcp_server_version | Server | Get the version of the Gitea MCP Server |
## 🐛 Debugging ## 🐛 Debugging
To enable debug mode, add the `-d` flag when running the Gitea MCP Server with sse mode: To enable debug mode, add the `-d` flag when running the Gitea MCP Server with sse mode:
```sh ```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! Enjoy exploring and managing your Gitea repositories via chat!

View File

@@ -13,6 +13,7 @@ import (
var ( var (
transport string transport string
host string host string
port int
token string token string
debug bool debug bool
@@ -37,6 +38,12 @@ func init() {
"https://gitea.com", "https://gitea.com",
"Gitea host", "Gitea host",
) )
flag.IntVar(
&port,
"port",
8080,
"sse port",
)
flag.StringVar( flag.StringVar(
&token, &token,
"token", "token",
@@ -66,11 +73,15 @@ func init() {
flagPkg.Host = "https://gitea.com" flagPkg.Host = "https://gitea.com"
} }
flagPkg.Port = port
flagPkg.Token = token flagPkg.Token = token
if flagPkg.Token == "" { if flagPkg.Token == "" {
flagPkg.Token = os.Getenv("GITEA_ACCESS_TOKEN") flagPkg.Token = os.Getenv("GITEA_ACCESS_TOKEN")
} }
flagPkg.Mode = transport
if debug { if debug {
flagPkg.Debug = debug flagPkg.Debug = debug
} }

7
go.mod
View File

@@ -3,9 +3,10 @@ module gitea.com/gitea/gitea-mcp
go 1.24.0 go 1.24.0
require ( require (
code.gitea.io/sdk/gitea v0.20.0 code.gitea.io/sdk/gitea v0.21.0
github.com/mark3labs/mcp-go v0.15.0 github.com/mark3labs/mcp-go v0.18.0
go.uber.org/zap v1.27.0 go.uber.org/zap v1.27.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
) )
require ( require (
@@ -17,5 +18,5 @@ require (
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.36.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
View File

@@ -1,5 +1,5 @@
code.gitea.io/sdk/gitea v0.20.0 h1:Zm/QDwwZK1awoM4AxdjeAQbxolzx2rIP8dDfmKu+KoU= code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4=
code.gitea.io/sdk/gitea v0.20.0/go.mod h1:faouBHC/zyx5wLgjmRKR62ydyvMzwWf3QnU0bH7Cw6U= 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 h1:ofAYoHUNs/MJOLqQ8hIxeyz2QxOz8qdSVvp3PX/oPgA=
github.com/42wim/httpsig v1.2.2/go.mod h1:P/UYo7ytNBFwc+dg35IubuAUIs8zj5zzFIgUCEl55WY= 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= 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/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/mark3labs/mcp-go v0.15.0 h1:lViiC4dk6chJHZccezaTzZLMOQVUXJDGNQPtzExr5NQ= github.com/mark3labs/mcp-go v0.18.0 h1:YuhgIVjNlTG2ZOwmrkORWyPTp0dz1opPEqvsPtySXao=
github.com/mark3labs/mcp-go v0.15.0/go.mod h1:xBB350hekQsJAK7gJAii8bcEoWemboLm2mRm5/+KBaU= 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 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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 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 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=
@@ -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-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.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 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.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 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= 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.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/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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -68,19 +68,19 @@ func GetIssueByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
log.Debugf("Called GetIssueByIndexFn") log.Debugf("Called GetIssueByIndexFn")
owner, ok := req.Params.Arguments["owner"].(string) owner, ok := req.Params.Arguments["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return to.ErrorResult(fmt.Errorf("owner is required"))
} }
repo, ok := req.Params.Arguments["repo"].(string) repo, ok := req.Params.Arguments["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return to.ErrorResult(fmt.Errorf("repo is required"))
} }
index, ok := req.Params.Arguments["index"].(float64) index, ok := req.Params.Arguments["index"].(float64)
if !ok { 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)) issue, _, err := gitea.Client().GetIssue(owner, repo, int64(index))
if err != nil { 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) return to.TextResult(issue)
@@ -90,11 +90,11 @@ func ListRepoIssuesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
log.Debugf("Called ListIssuesFn") log.Debugf("Called ListIssuesFn")
owner, ok := req.Params.Arguments["owner"].(string) owner, ok := req.Params.Arguments["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return to.ErrorResult(fmt.Errorf("owner is required"))
} }
repo, ok := req.Params.Arguments["repo"].(string) repo, ok := req.Params.Arguments["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return to.ErrorResult(fmt.Errorf("repo is required"))
} }
state, ok := req.Params.Arguments["state"].(string) state, ok := req.Params.Arguments["state"].(string)
if !ok { if !ok {
@@ -117,7 +117,7 @@ func ListRepoIssuesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
} }
issues, _, err := gitea.Client().ListRepoIssues(owner, repo, opt) issues, _, err := gitea.Client().ListRepoIssues(owner, repo, opt)
if err != nil { 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) return to.TextResult(issues)
} }
@@ -126,26 +126,26 @@ func CreateIssueFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
log.Debugf("Called CreateIssueFn") log.Debugf("Called CreateIssueFn")
owner, ok := req.Params.Arguments["owner"].(string) owner, ok := req.Params.Arguments["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return to.ErrorResult(fmt.Errorf("owner is required"))
} }
repo, ok := req.Params.Arguments["repo"].(string) repo, ok := req.Params.Arguments["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return to.ErrorResult(fmt.Errorf("repo is required"))
} }
title, ok := req.Params.Arguments["title"].(string) title, ok := req.Params.Arguments["title"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("title is required") return to.ErrorResult(fmt.Errorf("title is required"))
} }
body, ok := req.Params.Arguments["body"].(string) body, ok := req.Params.Arguments["body"].(string)
if !ok { 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{ issue, _, err := gitea.Client().CreateIssue(owner, repo, gitea_sdk.CreateIssueOption{
Title: title, Title: title,
Body: body, Body: body,
}) })
if err != nil { 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) return to.TextResult(issue)
@@ -155,26 +155,26 @@ func CreateIssueCommentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
log.Debugf("Called CreateIssueCommentFn") log.Debugf("Called CreateIssueCommentFn")
owner, ok := req.Params.Arguments["owner"].(string) owner, ok := req.Params.Arguments["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return to.ErrorResult(fmt.Errorf("owner is required"))
} }
repo, ok := req.Params.Arguments["repo"].(string) repo, ok := req.Params.Arguments["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return to.ErrorResult(fmt.Errorf("repo is required"))
} }
index, ok := req.Params.Arguments["index"].(float64) index, ok := req.Params.Arguments["index"].(float64)
if !ok { if !ok {
return nil, fmt.Errorf("index is required") return to.ErrorResult(fmt.Errorf("index is required"))
} }
body, ok := req.Params.Arguments["body"].(string) body, ok := req.Params.Arguments["body"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("body is required") return to.ErrorResult(fmt.Errorf("body is required"))
} }
opt := gitea_sdk.CreateIssueCommentOption{ opt := gitea_sdk.CreateIssueCommentOption{
Body: body, Body: body,
} }
issueComment, _, err := gitea.Client().CreateIssueComment(owner, repo, int64(index), opt) issueComment, _, err := gitea.Client().CreateIssueComment(owner, repo, int64(index), opt)
if err != nil { 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) return to.TextResult(issueComment)

View File

@@ -50,8 +50,8 @@ func Run(transport, version string) error {
} }
case "sse": case "sse":
sseServer := server.NewSSEServer(mcpServer) sseServer := server.NewSSEServer(mcpServer)
log.Infof("Gitea MCP SSE server listening on :8080") log.Infof("Gitea MCP SSE server listening on :%d", flag.Port)
if err := sseServer.Start(":8080"); err != nil { if err := sseServer.Start(fmt.Sprintf(":%d", flag.Port)); err != nil {
return err return err
} }
default: default:

View File

@@ -33,9 +33,11 @@ var (
mcp.WithDescription("List repository pull requests"), mcp.WithDescription("List repository pull requests"),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")), mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithString("state", mcp.Description("state")), mcp.WithString("state", mcp.Description("state"), mcp.Enum("open", "closed", "all"), mcp.DefaultString("all")),
mcp.WithString("sort", mcp.Description("sort")), 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("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( CreatePullRequestTool = mcp.NewTool(
@@ -60,19 +62,19 @@ func GetPullRequestByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp
log.Debugf("Called GetPullRequestByIndexFn") log.Debugf("Called GetPullRequestByIndexFn")
owner, ok := req.Params.Arguments["owner"].(string) owner, ok := req.Params.Arguments["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return to.ErrorResult(fmt.Errorf("owner is required"))
} }
repo, ok := req.Params.Arguments["repo"].(string) repo, ok := req.Params.Arguments["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return to.ErrorResult(fmt.Errorf("repo is required"))
} }
index, ok := req.Params.Arguments["index"].(float64) index, ok := req.Params.Arguments["index"].(float64)
if !ok { 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)) pr, _, err := gitea.Client().GetPullRequest(owner, repo, int64(index))
if err != nil { 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) return to.TextResult(pr)
@@ -82,27 +84,38 @@ func ListRepoPullRequestsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.
log.Debugf("Called ListRepoPullRequests") log.Debugf("Called ListRepoPullRequests")
owner, ok := req.Params.Arguments["owner"].(string) owner, ok := req.Params.Arguments["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return to.ErrorResult(fmt.Errorf("owner is required"))
} }
repo, ok := req.Params.Arguments["repo"].(string) repo, ok := req.Params.Arguments["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return to.ErrorResult(fmt.Errorf("repo is required"))
} }
state, _ := req.Params.Arguments["state"].(string) 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) 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{ opt := gitea_sdk.ListPullRequestsOptions{
State: gitea_sdk.StateType(state), State: gitea_sdk.StateType(state),
Sort: sort, Sort: sort,
Milestone: int64(milestone), Milestone: int64(milestone),
ListOptions: gitea_sdk.ListOptions{ ListOptions: gitea_sdk.ListOptions{
Page: 1, Page: int(page),
PageSize: 1000, PageSize: int(pageSize),
}, },
} }
pullRequests, _, err := gitea.Client().ListRepoPullRequests("", "", opt) pullRequests, _, err := gitea.Client().ListRepoPullRequests(owner, repo, opt)
if err != nil { 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) return to.TextResult(pullRequests)
@@ -112,27 +125,27 @@ func CreatePullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Cal
log.Debugf("Called CreatePullRequestFn") log.Debugf("Called CreatePullRequestFn")
owner, ok := req.Params.Arguments["owner"].(string) owner, ok := req.Params.Arguments["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return to.ErrorResult(fmt.Errorf("owner is required"))
} }
repo, ok := req.Params.Arguments["repo"].(string) repo, ok := req.Params.Arguments["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return to.ErrorResult(fmt.Errorf("repo is required"))
} }
title, ok := req.Params.Arguments["title"].(string) title, ok := req.Params.Arguments["title"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("title is required") return to.ErrorResult(fmt.Errorf("title is required"))
} }
body, ok := req.Params.Arguments["body"].(string) body, ok := req.Params.Arguments["body"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("body is required") return to.ErrorResult(fmt.Errorf("body is required"))
} }
head, ok := req.Params.Arguments["head"].(string) head, ok := req.Params.Arguments["head"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("head is required") return to.ErrorResult(fmt.Errorf("head is required"))
} }
base, ok := req.Params.Arguments["base"].(string) base, ok := req.Params.Arguments["base"].(string)
if !ok { 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{ pr, _, err := gitea.Client().CreatePullRequest(owner, repo, gitea_sdk.CreatePullRequestOption{
Title: title, Title: title,
@@ -141,7 +154,7 @@ func CreatePullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Cal
Base: base, Base: base,
}) })
if err != nil { 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) return to.TextResult(pr)

View File

@@ -48,15 +48,15 @@ func CreateBranchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
log.Debugf("Called CreateBranchFn") log.Debugf("Called CreateBranchFn")
owner, ok := req.Params.Arguments["owner"].(string) owner, ok := req.Params.Arguments["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return to.ErrorResult(fmt.Errorf("owner is required"))
} }
repo, ok := req.Params.Arguments["repo"].(string) repo, ok := req.Params.Arguments["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return to.ErrorResult(fmt.Errorf("repo is required"))
} }
branch, ok := req.Params.Arguments["branch"].(string) branch, ok := req.Params.Arguments["branch"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("branch is required") return to.ErrorResult(fmt.Errorf("branch is required"))
} }
oldBranch, _ := req.Params.Arguments["old_branch"].(string) oldBranch, _ := req.Params.Arguments["old_branch"].(string)
@@ -65,7 +65,7 @@ func CreateBranchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
OldBranchName: oldBranch, OldBranchName: oldBranch,
}) })
if err != nil { 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 return mcp.NewToolResultText("Branch Created"), nil
@@ -75,19 +75,19 @@ func DeleteBranchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
log.Debugf("Called DeleteBranchFn") log.Debugf("Called DeleteBranchFn")
owner, ok := req.Params.Arguments["owner"].(string) owner, ok := req.Params.Arguments["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return to.ErrorResult(fmt.Errorf("owner is required"))
} }
repo, ok := req.Params.Arguments["repo"].(string) repo, ok := req.Params.Arguments["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return to.ErrorResult(fmt.Errorf("repo is required"))
} }
branch, ok := req.Params.Arguments["branch"].(string) branch, ok := req.Params.Arguments["branch"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("branch is required") return to.ErrorResult(fmt.Errorf("branch is required"))
} }
_, _, err := gitea.Client().DeleteRepoBranch(owner, repo, branch) _, _, err := gitea.Client().DeleteRepoBranch(owner, repo, branch)
if err != nil { 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") return to.TextResult("Branch Deleted")
@@ -97,11 +97,11 @@ func ListBranchesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
log.Debugf("Called ListBranchesFn") log.Debugf("Called ListBranchesFn")
owner, ok := req.Params.Arguments["owner"].(string) owner, ok := req.Params.Arguments["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return to.ErrorResult(fmt.Errorf("owner is required"))
} }
repo, ok := req.Params.Arguments["repo"].(string) repo, ok := req.Params.Arguments["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return to.ErrorResult(fmt.Errorf("repo is required"))
} }
opt := gitea_sdk.ListRepoBranchesOptions{ opt := gitea_sdk.ListRepoBranchesOptions{
ListOptions: gitea_sdk.ListOptions{ 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) branches, _, err := gitea.Client().ListRepoBranches(owner, repo, opt)
if err != nil { 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) return to.TextResult(branches)

View File

@@ -22,8 +22,10 @@ var (
mcp.WithDescription("List repository commits"), mcp.WithDescription("List repository commits"),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")), mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithString("sha", mcp.Description("sha")), mcp.WithString("sha", mcp.Description("SHA or branch to start listing commits from")),
mcp.WithString("path", mcp.Description("path")), 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") log.Debugf("Called ListRepoCommitsFn")
owner, ok := req.Params.Arguments["owner"].(string) owner, ok := req.Params.Arguments["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return to.ErrorResult(fmt.Errorf("owner is required"))
} }
repo, ok := req.Params.Arguments["repo"].(string) repo, ok := req.Params.Arguments["repo"].(string)
if !ok { 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) sha, _ := req.Params.Arguments["sha"].(string)
path, _ := req.Params.Arguments["path"].(string) path, _ := req.Params.Arguments["path"].(string)
opt := gitea_sdk.ListCommitOptions{ opt := gitea_sdk.ListCommitOptions{
ListOptions: gitea_sdk.ListOptions{ ListOptions: gitea_sdk.ListOptions{
Page: 1, Page: int(page),
PageSize: 1000, PageSize: int(pageSize),
}, },
SHA: sha, SHA: sha,
Path: path, Path: path,
} }
commits, _, err := gitea.Client().ListRepoCommits(owner, repo, opt) commits, _, err := gitea.Client().ListRepoCommits(owner, repo, opt)
if err != nil { 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) return to.TextResult(commits)
} }

View File

@@ -2,6 +2,7 @@ package repo
import ( import (
"context" "context"
"encoding/base64"
"fmt" "fmt"
"gitea.com/gitea/gitea-mcp/pkg/gitea" "gitea.com/gitea/gitea-mcp/pkg/gitea"
@@ -13,19 +14,19 @@ import (
) )
const ( const (
GetFileToolName = "get_file" GetFileToolName = "get_file_content"
CreateFileToolName = "create_file" CreateFileToolName = "create_file"
UpdateFileToolName = "update_file" UpdateFileToolName = "update_file"
DeleteFileToolName = "delete_file" DeleteFileToolName = "delete_file"
) )
var ( var (
GetFileTool = mcp.NewTool( GetFileContentTool = mcp.NewTool(
GetFileToolName, GetFileToolName,
mcp.WithDescription("Get file"), mcp.WithDescription("Get file Content and Metadata"),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
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")), 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")),
) )
@@ -47,7 +48,8 @@ var (
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
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("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("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")),
) )
@@ -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") log.Debugf("Called GetFileFn")
owner, ok := req.Params.Arguments["owner"].(string) owner, ok := req.Params.Arguments["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return to.ErrorResult(fmt.Errorf("owner is required"))
} }
repo, ok := req.Params.Arguments["repo"].(string) repo, ok := req.Params.Arguments["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return to.ErrorResult(fmt.Errorf("repo is required"))
} }
ref, _ := req.Params.Arguments["ref"].(string) ref, _ := req.Params.Arguments["ref"].(string)
filePath, ok := req.Params.Arguments["filePath"].(string) filePath, ok := req.Params.Arguments["filePath"].(string)
if !ok { 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 { 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) { func CreateFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called CreateFileFn") log.Debugf("Called CreateFileFn")
owner, ok := req.Params.Arguments["owner"].(string) owner, ok := req.Params.Arguments["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return to.ErrorResult(fmt.Errorf("owner is required"))
} }
repo, ok := req.Params.Arguments["repo"].(string) repo, ok := req.Params.Arguments["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return to.ErrorResult(fmt.Errorf("repo is required"))
} }
filePath, ok := req.Params.Arguments["filePath"].(string) filePath, ok := req.Params.Arguments["filePath"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("filePath is required") return to.ErrorResult(fmt.Errorf("filePath is required"))
} }
content, _ := req.Params.Arguments["content"].(string) content, _ := req.Params.Arguments["content"].(string)
message, _ := req.Params.Arguments["message"].(string) message, _ := req.Params.Arguments["message"].(string)
branchName, _ := req.Params.Arguments["branch_name"].(string) branchName, _ := req.Params.Arguments["branch_name"].(string)
opt := gitea_sdk.CreateFileOptions{ opt := gitea_sdk.CreateFileOptions{
Content: content, Content: base64.StdEncoding.EncodeToString([]byte(content)),
FileOptions: gitea_sdk.FileOptions{ FileOptions: gitea_sdk.FileOptions{
Message: message, Message: message,
BranchName: branchName, BranchName: branchName,
@@ -113,7 +115,7 @@ func CreateFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
_, _, err := gitea.Client().CreateFile(owner, repo, filePath, opt) _, _, err := gitea.Client().CreateFile(owner, repo, filePath, opt)
if err != nil { 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") return to.TextResult("Create file success")
} }
@@ -122,20 +124,26 @@ func UpdateFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
log.Debugf("Called UpdateFileFn") log.Debugf("Called UpdateFileFn")
owner, ok := req.Params.Arguments["owner"].(string) owner, ok := req.Params.Arguments["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return to.ErrorResult(fmt.Errorf("owner is required"))
} }
repo, ok := req.Params.Arguments["repo"].(string) repo, ok := req.Params.Arguments["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return to.ErrorResult(fmt.Errorf("repo is required"))
} }
filePath, ok := req.Params.Arguments["filePath"].(string) filePath, ok := req.Params.Arguments["filePath"].(string)
if !ok { 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) content, _ := req.Params.Arguments["content"].(string)
message, _ := req.Params.Arguments["message"].(string) message, _ := req.Params.Arguments["message"].(string)
branchName, _ := req.Params.Arguments["branch_name"].(string) branchName, _ := req.Params.Arguments["branch_name"].(string)
opt := gitea_sdk.UpdateFileOptions{ opt := gitea_sdk.UpdateFileOptions{
SHA: sha,
Content: content, Content: content,
FileOptions: gitea_sdk.FileOptions{ FileOptions: gitea_sdk.FileOptions{
Message: message, Message: message,
@@ -144,7 +152,7 @@ func UpdateFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
} }
_, _, err := gitea.Client().UpdateFile(owner, repo, filePath, opt) _, _, err := gitea.Client().UpdateFile(owner, repo, filePath, opt)
if err != nil { 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") return to.TextResult("Update file success")
} }
@@ -153,27 +161,32 @@ func DeleteFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
log.Debugf("Called DeleteFileFn") log.Debugf("Called DeleteFileFn")
owner, ok := req.Params.Arguments["owner"].(string) owner, ok := req.Params.Arguments["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return to.ErrorResult(fmt.Errorf("owner is required"))
} }
repo, ok := req.Params.Arguments["repo"].(string) repo, ok := req.Params.Arguments["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return to.ErrorResult(fmt.Errorf("repo is required"))
} }
filePath, ok := req.Params.Arguments["filePath"].(string) filePath, ok := req.Params.Arguments["filePath"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("filePath is required") return to.ErrorResult(fmt.Errorf("filePath is required"))
} }
message, _ := req.Params.Arguments["message"].(string) message, _ := req.Params.Arguments["message"].(string)
branchName, _ := req.Params.Arguments["branch_name"].(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{ opt := gitea_sdk.DeleteFileOptions{
FileOptions: gitea_sdk.FileOptions{ FileOptions: gitea_sdk.FileOptions{
Message: message, Message: message,
BranchName: branchName, BranchName: branchName,
}, },
SHA: sha,
} }
_, err := gitea.Client().DeleteFile(owner, repo, filePath, opt) _, err := gitea.Client().DeleteFile(owner, repo, filePath, opt)
if err != nil { 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") return to.TextResult("Delete file success")
} }

View File

@@ -60,7 +60,7 @@ func RegisterTool(s *server.MCPServer) {
s.AddTool(ListMyReposTool, ListMyReposFn) s.AddTool(ListMyReposTool, ListMyReposFn)
// File // File
s.AddTool(GetFileTool, GetFileFn) s.AddTool(GetFileContentTool, GetFileContentFn)
s.AddTool(CreateFileTool, CreateFileFn) s.AddTool(CreateFileTool, CreateFileFn)
s.AddTool(UpdateFileTool, UpdateFileFn) s.AddTool(UpdateFileTool, UpdateFileFn)
s.AddTool(DeleteFileTool, DeleteFileFn) s.AddTool(DeleteFileTool, DeleteFileFn)
@@ -78,7 +78,7 @@ func CreateRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
log.Debugf("Called CreateRepoFn") log.Debugf("Called CreateRepoFn")
name, ok := req.Params.Arguments["name"].(string) name, ok := req.Params.Arguments["name"].(string)
if !ok { 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) description, _ := req.Params.Arguments["description"].(string)
private, _ := req.Params.Arguments["private"].(bool) 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) repo, _, err := gitea.Client().CreateRepo(opt)
if err != nil { if err != nil {
return nil, err return to.ErrorResult(fmt.Errorf("create repo err: %v", err))
} }
return to.TextResult(repo) return to.TextResult(repo)
} }
@@ -113,21 +113,29 @@ func ForkRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResu
log.Debugf("Called ForkRepoFn") log.Debugf("Called ForkRepoFn")
user, ok := req.Params.Arguments["user"].(string) user, ok := req.Params.Arguments["user"].(string)
if !ok { 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) repo, ok := req.Params.Arguments["repo"].(string)
if !ok { 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{ opt := gitea_sdk.CreateForkOption{
Organization: ptr.To(organization), Organization: organizationPtr,
Name: ptr.To(name), Name: namePtr,
} }
_, _, err := gitea.Client().CreateFork(user, repo, opt) _, _, err := gitea.Client().CreateFork(user, repo, opt)
if err != nil { 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") return to.TextResult("Fork success")
} }
@@ -136,21 +144,21 @@ func ListMyReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
log.Debugf("Called ListMyReposFn") log.Debugf("Called ListMyReposFn")
page, ok := req.Params.Arguments["page"].(float64) page, ok := req.Params.Arguments["page"].(float64)
if !ok { 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 { if !ok {
return nil, errors.New("get page size number error") pageSize = 100
} }
opt := gitea_sdk.ListReposOptions{ opt := gitea_sdk.ListReposOptions{
ListOptions: gitea_sdk.ListOptions{ ListOptions: gitea_sdk.ListOptions{
Page: int(page), Page: int(page),
PageSize: int(size), PageSize: int(pageSize),
}, },
} }
repos, _, err := gitea.Client().ListMyRepos(opt) repos, _, err := gitea.Client().ListMyRepos(opt)
if err != nil { 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) return to.TextResult(repos)

View File

@@ -65,7 +65,7 @@ func SearchUsersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
log.Debugf("Called SearchUsersFn") log.Debugf("Called SearchUsersFn")
keyword, ok := req.Params.Arguments["keyword"].(string) keyword, ok := req.Params.Arguments["keyword"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("keyword is required") return to.ErrorResult(fmt.Errorf("keyword is required"))
} }
page, ok := req.Params.Arguments["page"].(float64) page, ok := req.Params.Arguments["page"].(float64)
if !ok { if !ok {
@@ -84,7 +84,7 @@ func SearchUsersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
} }
users, _, err := gitea.Client().SearchUsers(opt) users, _, err := gitea.Client().SearchUsers(opt)
if err != nil { if err != nil {
return nil, err return to.ErrorResult(fmt.Errorf("search users err: %v", err))
} }
return to.TextResult(users) return to.TextResult(users)
} }
@@ -93,11 +93,11 @@ func SearchOrgTeamsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
log.Debugf("Called SearchOrgTeamsFn") log.Debugf("Called SearchOrgTeamsFn")
org, ok := req.Params.Arguments["org"].(string) org, ok := req.Params.Arguments["org"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("organization is required") return to.ErrorResult(fmt.Errorf("organization is required"))
} }
query, ok := req.Params.Arguments["query"].(string) query, ok := req.Params.Arguments["query"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("query is required") return to.ErrorResult(fmt.Errorf("query is required"))
} }
includeDescription, _ := req.Params.Arguments["includeDescription"].(bool) includeDescription, _ := req.Params.Arguments["includeDescription"].(bool)
page, ok := req.Params.Arguments["page"].(float64) 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) teams, _, err := gitea.Client().SearchOrgTeams(org, &opt)
if err != nil { 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) return to.TextResult(teams)
} }
@@ -127,7 +127,7 @@ func SearchReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
log.Debugf("Called SearchReposFn") log.Debugf("Called SearchReposFn")
keyword, ok := req.Params.Arguments["keyword"].(string) keyword, ok := req.Params.Arguments["keyword"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("keyword is required") return to.ErrorResult(fmt.Errorf("keyword is required"))
} }
keywordIsTopic, _ := req.Params.Arguments["keywordIsTopic"].(bool) keywordIsTopic, _ := req.Params.Arguments["keywordIsTopic"].(bool)
keywordInDescription, _ := req.Params.Arguments["keywordInDescription"].(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) repos, _, err := gitea.Client().SearchRepos(opt)
if err != nil { 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) return to.TextResult(repos)
} }

View File

@@ -31,7 +31,7 @@ func GetUserInfoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
log.Debugf("Called GetUserInfoFn") log.Debugf("Called GetUserInfoFn")
user, _, err := gitea.Client().GetMyUserInfo() user, _, err := gitea.Client().GetMyUserInfo()
if err != nil { 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) return to.TextResult(user)

View File

@@ -2,8 +2,10 @@ package flag
var ( var (
Host string Host string
Port int
Token string Token string
Version string Version string
Mode string
Debug bool Debug bool
) )

View File

@@ -1,6 +1,7 @@
package log package log
import ( import (
"fmt"
"os" "os"
"sync" "sync"
"time" "time"
@@ -8,6 +9,7 @@ import (
"gitea.com/gitea/gitea-mcp/pkg/flag" "gitea.com/gitea/gitea-mcp/pkg/flag"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore" "go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
) )
var ( var (
@@ -20,12 +22,27 @@ func Default() *zap.Logger {
if defaultLogger == nil { if defaultLogger == nil {
ec := zap.NewProductionEncoderConfig() ec := zap.NewProductionEncoderConfig()
ec.EncodeTime = zapcore.TimeEncoderOfLayout(time.DateTime) ec.EncodeTime = zapcore.TimeEncoderOfLayout(time.DateTime)
ec.EncodeLevel = zapcore.CapitalColorLevelEncoder ec.EncodeLevel = zapcore.CapitalLevelEncoder
var ws zapcore.WriteSyncer var ws zapcore.WriteSyncer
var wss []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)) wss = append(wss, zapcore.AddSync(os.Stdout))
}
ws = zapcore.NewMultiWriteSyncer(wss...) ws = zapcore.NewMultiWriteSyncer(wss...)
enc := zapcore.NewConsoleEncoder(ec) enc := zapcore.NewConsoleEncoder(ec)
@@ -37,7 +54,7 @@ func Default() *zap.Logger {
} }
core := zapcore.NewCore(enc, ws, level) core := zapcore.NewCore(enc, ws, level)
options := []zap.Option{ options := []zap.Option{
zap.AddStacktrace(zapcore.ErrorLevel), zap.AddStacktrace(zapcore.DPanicLevel),
zap.AddCaller(), zap.AddCaller(),
zap.AddCallerSkip(1), zap.AddCallerSkip(1),
} }

View File

@@ -21,3 +21,8 @@ func TextResult(v any) (*mcp.CallToolResult, error) {
log.Debugf("Text Result: %s", string(resultBytes)) log.Debugf("Text Result: %s", string(resultBytes))
return mcp.NewToolResultText(string(resultBytes)), nil return mcp.NewToolResultText(string(resultBytes)), nil
} }
func ErrorResult(err error) (*mcp.CallToolResult, error) {
log.Errorf(err.Error())
return nil, err
}