diff --git a/README.md b/README.md index ae7a1c5..1ee644c 100644 --- a/README.md +++ b/README.md @@ -1,287 +1,132 @@ # Gitea MCP Server -[繁體中文](README.zh-tw.md) | [简体中文](README.zh-cn.md) +שרת MCP (Model Context Protocol) לגיטאה — מאפשר ניהול מלא של ריפוזיטוריות, issues, pull requests, wikis, actions ועוד דרך Claude Code וכלי MCP אחרים. -**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 במקביל.** -[![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) - -## Table of Contents - -- [Gitea MCP Server](#gitea-mcp-server) - - [Table of Contents](#table-of-contents) - - [What is Gitea?](#what-is-gitea) - - [What is MCP?](#what-is-mcp) - - [🚧 Installation](#-installation) - - [Usage with Claude Code](#usage-with-claude-code) - - [Usage with VS Code](#usage-with-vs-code) - - [📥 Download the official binary release](#-download-the-official-binary-release) - - [🔧 Build from Source](#-build-from-source) - - [📁 Add to PATH](#-add-to-path) - - [🚀 Usage](#-usage) - - [✅ Available Tools](#-available-tools) - - [🐛 Debugging](#-debugging) - - [🛠 Troubleshooting](#-troubleshooting) - -## 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 - -### Usage with Claude Code - -This method uses `go run` and requires [Go](https://go.dev) to be installed. +## התקנה מהירה ```bash -claude mcp add --transport stdio --scope user gitea \ - --env GITEA_ACCESS_TOKEN=token \ - --env GITEA_HOST=https://gitea.com \ - -- go run gitea.com/gitea/gitea-mcp@latest -t stdio +curl -fsSL https://gitea.dev.marcus-law.co.il/Chaim/gitea-mcp/raw/branch/main/install.sh | bash ``` -### Usage with VS Code +הסקריפט: +- משכפל את הריפו ל-`~/.gitea-mcp` +- בונה בינארי ומתקין ל-`$GOPATH/bin` +- מגדיר MCP server גלובלי ב-`~/.claude/settings.json` +- מאפשר הוספת שרתים נוספים באופן אינטראקטיבי +- מוחק הגדרות כפולות מקבצי `.mcp.json` ברמת פרויקט -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)`. +- [Go](https://go.dev) 1.24+ +- git +- jq -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": { - "gitea-mcp": { - "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 [official Gitea MCP binary releases](https://gitea.com/gitea/gitea-mcp/releases). - -### 🔧 Build from Source - -You can download the source code by cloning the repository using Git: +אותה פקודה בדיוק — הסקריפט מזהה התקנה קיימת ומעדכן: ```bash -git clone https://gitea.com/gitea/gitea-mcp.git +curl -fsSL https://gitea.dev.marcus-law.co.il/Chaim/gitea-mcp/raw/branch/main/install.sh | bash ``` -Before building, make sure you have the following installed: +## הגדרה ידנית -- make -- Golang (Go 1.24 or later recommended) - -Then run: - -```bash -make install -``` - -### 📁 Add to PATH - -After installing, copy the binary gitea-mcp to a directory included in your system's PATH. For example: - -```bash -cp gitea-mcp /usr/local/bin/ -``` - -## 🚀 Usage - -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": { + "gitea-dev": { "command": "gitea-mcp", - "args": [ - "-t", - "stdio", - "--host", - "https://gitea.com" - // "--token", "" - ], + "args": ["-t", "stdio", "--host", "https://gitea.example.com"], "env": { - // "GITEA_HOST": "https://gitea.com", - // "GITEA_INSECURE": "true", - "GITEA_ACCESS_TOKEN": "" + "GITEA_ACCESS_TOKEN": "" } } } } ``` -- **http mode** +### כמה שרתים + +הוסף את `GITEA_SERVERS` כ-JSON object עם שם → `{host, token, insecure}`: ```json { "mcpServers": { - "gitea": { - "url": "http://localhost:8080/mcp", - "headers": { - "Authorization": "Bearer " + "gitea-dev": { + "command": "gitea-mcp", + "args": ["-t", "stdio", "--host", "https://gitea.example.com"], + "env": { + "GITEA_ACCESS_TOKEN": "", + "GITEA_SERVERS": "{\"prod\":{\"host\":\"https://gitea.prod.example.com\",\"token\":\"\"},\"staging\":{\"host\":\"https://staging.example.com\",\"token\":\"\",\"insecure\":true}}" } } } } ``` -**Default log path**: `$HOME/.gitea-mcp/gitea-mcp.log` +כל כלי מקבל פרמטר `server` אופציונלי: -> [!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 - -> [!NOTE] -> Many tools support `page` and `perPage` parameters for pagination. The maximum effective page size is determined by the Gitea server's `[api].MAX_RESPONSE_ITEMS` setting (default: **50**). Requesting a `perPage` value higher than this limit will be silently capped by the server. - -Once everything is set up, try typing the following in your MCP-compatible chatbox: - -```text -list all my repositories +``` +list_my_repos # ברירת מחדל +list_my_repos(server: "prod") # שרת ספציפי +list_servers # הצג שרתים מוגדרים ``` -## ✅ Available Tools +## משתני סביבה -The Gitea MCP Server supports the following tools: +| משתנה | תיאור | +|---|---| +| `GITEA_ACCESS_TOKEN` | טוקן גישה לשרת ברירת המחדל | +| `GITEA_HOST` | כתובת שרת ברירת המחדל | +| `GITEA_SERVERS` | JSON object של שרתים נוספים | +| `GITEA_READONLY` | `true` להצגת כלי קריאה בלבד | +| `GITEA_DEBUG` | `true` למצב debug | +| `GITEA_INSECURE` | `true` לדלג על בדיקת TLS | +| `MCP_MODE` | `stdio` (ברירת מחדל) או `http` | -| Tool | Scope | Description | -| :-------------------------------: | :----------: | :------------------------------------------------------: | -| get_my_user_info | User | Get the information of the authenticated user | -| get_user_orgs | User | Get organizations associated with 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 | -| create_release | Release | Create a new release in a repository | -| delete_release | Release | Delete a release from a repository | -| get_release | Release | Get a release | -| get_latest_release | Release | Get the latest release in a repository | -| list_releases | Release | List all releases in a repository | -| create_tag | Tag | Create a new tag | -| delete_tag | Tag | Delete a tag | -| get_tag | Tag | Get a tag | -| list_tags | Tag | List all tags in a repository | -| list_repo_commits | Commit | List all commits in a repository | -| get_file_content | File | Get the content and metadata of a file | -| get_dir_content | File | Get a list of entries in a directory | -| 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 | -| edit_issue | Issue | Edit a issue | -| edit_issue_comment | Issue | Edit a comment on an issue | -| get_issue_comments_by_index | Issue | Get comments of an issue by its index | -| get_pull_request_by_index | Pull Request | Get a pull request by its index | -| get_pull_request_diff | Pull Request | Get a pull request diff | -| 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_reviewer | Pull Request | Add reviewers to a pull request | -| delete_pull_request_reviewer | Pull Request | Remove reviewers from a pull request | -| list_pull_request_reviews | Pull Request | List all reviews for a pull request | -| get_pull_request_review | Pull Request | Get a specific review by ID | -| list_pull_request_review_comments | Pull Request | List inline comments for a review | -| create_pull_request_review | Pull Request | Create a review with optional inline comments | -| submit_pull_request_review | Pull Request | Submit a pending review | -| delete_pull_request_review | Pull Request | Delete a review | -| dismiss_pull_request_review | Pull Request | Dismiss a review with optional message | -| merge_pull_request | Pull Request | Merge a pull request | -| search_users | User | Search for users | -| search_org_teams | Organization | Search for teams in an organization | -| list_org_labels | Organization | List labels defined at organization level | -| create_org_label | Organization | Create a label in an organization | -| edit_org_label | Organization | Edit a label in an organization | -| delete_org_label | Organization | Delete a label in an organization | -| search_repos | Repository | Search for repositories | -| list_repo_action_secrets | Actions | List repository Actions secrets (metadata only) | -| upsert_repo_action_secret | Actions | Create/update (upsert) a repository Actions secret | -| delete_repo_action_secret | Actions | Delete a repository Actions secret | -| list_org_action_secrets | Actions | List organization Actions secrets (metadata only) | -| upsert_org_action_secret | Actions | Create/update (upsert) an organization Actions secret | -| delete_org_action_secret | Actions | Delete an organization Actions secret | -| list_repo_action_variables | Actions | List repository Actions variables | -| get_repo_action_variable | Actions | Get a repository Actions variable | -| create_repo_action_variable | Actions | Create a repository Actions variable | -| update_repo_action_variable | Actions | Update a repository Actions variable | -| delete_repo_action_variable | Actions | Delete a repository Actions variable | -| list_org_action_variables | Actions | List organization Actions variables | -| get_org_action_variable | Actions | Get an organization Actions variable | -| create_org_action_variable | Actions | Create an organization Actions variable | -| update_org_action_variable | Actions | Update an organization Actions variable | -| delete_org_action_variable | Actions | Delete an organization Actions variable | -| list_repo_action_workflows | Actions | List repository Actions workflows | -| get_repo_action_workflow | Actions | Get a repository Actions workflow | -| dispatch_repo_action_workflow | Actions | Trigger (dispatch) a repository Actions workflow | -| list_repo_action_runs | Actions | List repository Actions runs | -| get_repo_action_run | Actions | Get a repository Actions run | -| cancel_repo_action_run | Actions | Cancel a repository Actions run | -| rerun_repo_action_run | Actions | Rerun a repository Actions run | -| list_repo_action_jobs | Actions | List repository Actions jobs | -| list_repo_action_run_jobs | Actions | List Actions jobs for a run | -| get_repo_action_job_log_preview | Actions | Get a job log preview (tail/limited) | -| download_repo_action_job_log | Actions | Download a job log to a file | -| get_gitea_mcp_server_version | Server | Get the version of the Gitea MCP Server | -| list_wiki_pages | Wiki | List all wiki pages in a repository | -| get_wiki_page | Wiki | Get a wiki page content and metadata | -| get_wiki_revisions | Wiki | Get revisions history of a wiki page | -| create_wiki_page | Wiki | Create a new wiki page | -| update_wiki_page | Wiki | Update an existing wiki page | -| delete_wiki_page | Wiki | Delete a wiki page | +## כלים זמינים -## 🐛 Debugging +השרת מספק 60+ כלי MCP: -To enable debug mode, add the `-d` flag when running the Gitea MCP Server with http mode: +| תחום | כלים | +|---|---| +| משתמש | `get_me`, `get_user_orgs` | +| ריפוזיטוריות | `create_repo`, `fork_repo`, `list_my_repos`, `list_org_repos`, `get_repo`, `repo_write`, `list_forks` | +| ענפים | `list_branches`, `create_branch`, `delete_branch`, `branch_protection_read/write` | +| תגיות | `list_tags`, `get_tag`, `create_tag`, `delete_tag` | +| קבצים | `get_file_contents`, `get_dir_contents`, `create_or_update_file`, `delete_file`, `get_repository_tree` | +| קומיטים | `list_commits`, `get_commit`, `get_compare` | +| Issues | `list_issues`, `issue_read`, `issue_write`, `search_issues` | +| Pull Requests | `list_pull_requests`, `pull_request_read`, `pull_request_write`, `pull_request_review_write` | +| Labels | `label_read`, `label_write` | +| Milestones | `milestone_read`, `milestone_write` | +| Releases | `list_releases`, `get_release`, `get_latest_release`, `create_release`, `delete_release` | +| Wiki | `wiki_read`, `wiki_write` | +| מעקב זמן | `timetracking_read`, `timetracking_write` | +| Actions | `actions_run_read`, `actions_run_write`, `actions_config_read`, `actions_config_write` | +| התראות | `notification_read`, `notification_write` | +| ארגונים | `org_read`, `org_write` | +| Webhooks | `webhook_read`, `webhook_write` | +| Collaborators | `collaborator_read`, `collaborator_write` | +| Deploy Keys | `deploy_key_read`, `deploy_key_write` | +| Topics | `topic_read`, `topic_write` | +| חיפוש | `search_users`, `search_repos`, `search_org_teams` | +| שרת | `get_gitea_mcp_server_version`, `list_servers` | -```sh -./gitea-mcp -t http [--port 8080] --token -d +## פיתוח + +```bash +make build # בנייה +make install # בנייה + התקנה ל-GOPATH/bin +make dev # hot reload (דורש air) +make clean # ניקוי +go test ./... # טסטים ``` -## 🛠 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! +MIT diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..67c6e67 --- /dev/null +++ b/install.sh @@ -0,0 +1,376 @@ +#!/bin/bash +# install.sh - Install/upgrade gitea-mcp and configure global MCP server +# +# Usage: +# curl -fsSL https://gitea.dev.marcus-law.co.il/Chaim/gitea-mcp/raw/branch/main/install.sh | bash +# # or locally: +# bash install.sh +# +# Options (via env vars): +# GITEA_MCP_DIR=~/custom-dir # Custom install directory (default: ~/.gitea-mcp) +# GITEA_MCP_BRANCH=dev # Custom branch (default: main) +# GITEA_DEFAULT_HOST=https://... # Default Gitea host +# GITEA_DEFAULT_TOKEN=xxx # Default Gitea token +# AUTO_YES=1 # Skip all prompts + +set -euo pipefail + +REPO_URL="https://gitea.dev.marcus-law.co.il/Chaim/gitea-mcp.git" +INSTALL_DIR="${GITEA_MCP_DIR:-$HOME/.gitea-mcp}" +BRANCH="${GITEA_MCP_BRANCH:-main}" +AUTO_YES="${AUTO_YES:-0}" + +MCP_NAME="gitea-dev" +BINARY_NAME="gitea-mcp" +GOBIN="${GOPATH:-$HOME/go}/bin" +BINARY_PATH="$GOBIN/$BINARY_NAME" + +CLAUDE_JSON="$HOME/.claude.json" +CLAUDE_SETTINGS="$HOME/.claude/settings.json" + +# ── Helpers ───────────────────────────────────────────────────────────────── + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' +info() { echo -e "${CYAN}>>>${NC} $*"; } +ok() { echo -e "${GREEN} ✓${NC} $*"; } +warn() { echo -e "${YELLOW} !${NC} $*"; } +error() { echo -e "${RED} ✗${NC} $*" >&2; } + +ask_yes_no() { + local prompt="$1" + if [ "$AUTO_YES" = "1" ]; then return 0; fi + if [ -e /dev/tty ]; then + printf "%s [Y/n] " "$prompt" + local answer="" + read -r answer < /dev/tty 2>/dev/null || answer="y" + case "$answer" in [nN]*) return 1 ;; *) return 0 ;; esac + else + return 0 + fi +} + +ask_input() { + local prompt="$1" var_name="$2" default="${3:-}" + if [ -e /dev/tty ]; then + if [ -n "$default" ]; then + printf "%s [%s]: " "$prompt" "$default" + else + printf "%s: " "$prompt" + fi + local answer="" + read -r answer < /dev/tty 2>/dev/null || answer="" + if [ -z "$answer" ]; then answer="$default"; fi + eval "$var_name=\$answer" + else + eval "$var_name=\$default" + fi +} + +# ── Step 0: Prerequisites ────────────────────────────────────────────────── + +echo "" +echo -e "${CYAN}=== Gitea MCP Server Installer ===${NC}" +echo "" + +info "Checking prerequisites..." + +if ! command -v go &>/dev/null; then + error "Go is required but not found. Install from https://go.dev/dl/" + exit 1 +fi +GO_VERSION=$(go version | grep -oP 'go\d+\.\d+') +ok "go ($GO_VERSION)" + +if ! command -v git &>/dev/null; then + error "git is required but not found" + exit 1 +fi +ok "git ($(git --version | cut -d' ' -f3))" + +if ! command -v jq &>/dev/null; then + error "jq is required but not found. Install: sudo apt install jq" + exit 1 +fi +ok "jq ($(jq --version 2>/dev/null))" + +# ── Step 1: Clone or update ──────────────────────────────────────────────── + +echo "" +if [ -d "$INSTALL_DIR/.git" ]; then + info "Updating existing installation in $INSTALL_DIR ..." + cd "$INSTALL_DIR" + git fetch origin 2>&1 | tail -2 + git reset --hard "origin/$BRANCH" 2>&1 | tail -1 + ok "Updated to latest $BRANCH" +elif [ -d "$INSTALL_DIR" ]; then + # Directory exists but is not a git repo — remove and re-clone + warn "$INSTALL_DIR exists but is not a git repo, re-cloning..." + rm -rf "$INSTALL_DIR" + git clone --branch "$BRANCH" --single-branch "$REPO_URL" "$INSTALL_DIR" 2>&1 | tail -2 + cd "$INSTALL_DIR" + ok "Cloned" +else + info "Cloning to $INSTALL_DIR ..." + git clone --branch "$BRANCH" --single-branch "$REPO_URL" "$INSTALL_DIR" 2>&1 | tail -2 + cd "$INSTALL_DIR" + ok "Cloned" +fi + +# ── Step 2: Build ────────────────────────────────────────────────────────── + +echo "" +info "Building $BINARY_NAME ..." +mkdir -p "$GOBIN" +go build -o "$BINARY_PATH" . 2>&1 +VERSION=$("$BINARY_PATH" --version 2>/dev/null || echo "unknown") +ok "Built $BINARY_PATH ($VERSION)" + +# ── Step 3: Find existing MCP config ─────────────────────────────────────── + +echo "" +info "Checking existing MCP configuration..." + +# Determine where the MCP entry currently lives +MCP_CONFIG_FILE="" +EXISTING_ENTRY="" + +# Check global settings.json first (preferred location) +if [ -f "$CLAUDE_SETTINGS" ]; then + EXISTING_ENTRY=$(jq -r --arg name "$MCP_NAME" '.mcpServers[$name] // empty' "$CLAUDE_SETTINGS" 2>/dev/null || true) + if [ -n "$EXISTING_ENTRY" ]; then + MCP_CONFIG_FILE="$CLAUDE_SETTINGS" + fi +fi + +# Check ~/.claude.json +if [ -z "$EXISTING_ENTRY" ] && [ -f "$CLAUDE_JSON" ]; then + EXISTING_ENTRY=$(jq -r --arg name "$MCP_NAME" '.mcpServers[$name] // empty' "$CLAUDE_JSON" 2>/dev/null || true) + if [ -n "$EXISTING_ENTRY" ]; then + MCP_CONFIG_FILE="$CLAUDE_JSON" + fi +fi + +# Check for project-level .mcp.json files +FOUND_PROJECT_MCP=() +while IFS= read -r -d '' mcp_file; do + if jq -e --arg name "$MCP_NAME" '.mcpServers[$name] != null' "$mcp_file" &>/dev/null; then + FOUND_PROJECT_MCP+=("$mcp_file") + fi +done < <(find "$HOME" -maxdepth 3 -name ".mcp.json" \ + -not -path "*/.claude/*" \ + -not -path "*/.gitea-mcp/*" \ + -not -path "*/node_modules/*" \ + -print0 2>/dev/null || true) + +# ── Step 4: Migrate / deduplicate ────────────────────────────────────────── + +# If found in project-level files, migrate to global +if [ ${#FOUND_PROJECT_MCP[@]} -gt 0 ]; then + for mcp_file in "${FOUND_PROJECT_MCP[@]}"; do + warn "Found '$MCP_NAME' in project-level config: $mcp_file" + + if [ -z "$EXISTING_ENTRY" ]; then + # No global config yet - migrate from project file + if ask_yes_no " Migrate to global config ($CLAUDE_SETTINGS)?"; then + EXISTING_ENTRY=$(jq --arg name "$MCP_NAME" '.mcpServers[$name]' "$mcp_file") + MCP_CONFIG_FILE="$CLAUDE_SETTINGS" + fi + fi + + # Remove from project-level regardless + if ask_yes_no " Remove duplicate from $mcp_file?"; then + tmp=$(jq --arg name "$MCP_NAME" 'del(.mcpServers[$name])' "$mcp_file") + echo "$tmp" > "$mcp_file" + ok "Removed from $mcp_file" + fi + done +fi + +# ── Step 5: Configure default server ─────────────────────────────────────── + +echo "" + +# Extract existing values if we have a config +CURRENT_HOST="" +CURRENT_TOKEN="" +CURRENT_SERVERS="" + +if [ -n "$EXISTING_ENTRY" ]; then + CURRENT_HOST=$(echo "$EXISTING_ENTRY" | jq -r ' + (.args // []) | to_entries | + map(select(.value == "--host")) | first | + if . then (.key + 1) as $k | input | .[$k] else empty end + ' 2>/dev/null || true) + # Simpler extraction + CURRENT_HOST=$(echo "$EXISTING_ENTRY" | jq -r ' + .args as $a | ($a | index("--host")) as $i | + if $i then $a[$i + 1] else "" end + ' 2>/dev/null || true) + CURRENT_TOKEN=$(echo "$EXISTING_ENTRY" | jq -r '.env.GITEA_ACCESS_TOKEN // ""' 2>/dev/null || true) + CURRENT_SERVERS=$(echo "$EXISTING_ENTRY" | jq -r '.env.GITEA_SERVERS // ""' 2>/dev/null || true) + + ok "Existing config found" + echo " Default: ${CURRENT_HOST:-not set}" + if [ -n "$CURRENT_SERVERS" ] && [ "$CURRENT_SERVERS" != "" ]; then + echo "$CURRENT_SERVERS" | jq -r 'to_entries[] | " \(.key): \(.value.host)"' 2>/dev/null || true + fi +fi + +# Determine default host and token (env vars > existing > prompt) +DEFAULT_HOST="${GITEA_DEFAULT_HOST:-$CURRENT_HOST}" +DEFAULT_TOKEN="${GITEA_DEFAULT_TOKEN:-$CURRENT_TOKEN}" + +if [ -z "$DEFAULT_HOST" ]; then + info "No default Gitea server configured." + ask_input " Default Gitea host URL" DEFAULT_HOST "https://gitea.com" + ask_input " Access token" DEFAULT_TOKEN "" +fi + +if [ -z "$DEFAULT_TOKEN" ]; then + warn "No access token for $DEFAULT_HOST" + ask_input " Access token" DEFAULT_TOKEN "" +fi + +# ── Step 6: Add additional servers ───────────────────────────────────────── + +SERVERS_JSON="${CURRENT_SERVERS:-}" + +while true; do + echo "" + if ! ask_yes_no "Add an additional Gitea server?"; then + break + fi + + srv_name="" srv_host="" srv_token="" srv_insecure="false" + ask_input " Server name (e.g. prod, staging)" srv_name "" + if [ -z "$srv_name" ]; then break; fi + + # Check if already exists + if [ -n "$SERVERS_JSON" ] && echo "$SERVERS_JSON" | jq -e --arg n "$srv_name" '.[$n]' &>/dev/null; then + existing_host=$(echo "$SERVERS_JSON" | jq -r --arg n "$srv_name" '.[$n].host') + warn "Server '$srv_name' already configured: $existing_host" + if ! ask_yes_no " Overwrite?"; then continue; fi + fi + + ask_input " Host URL" srv_host "" + ask_input " Access token" srv_token "" + ask_input " Skip TLS verification? (y/n)" srv_insecure "n" + [[ "$srv_insecure" =~ ^[Yy] ]] && srv_insecure="true" || srv_insecure="false" + + if [ -z "$SERVERS_JSON" ] || [ "$SERVERS_JSON" = "" ]; then + SERVERS_JSON="{}" + fi + SERVERS_JSON=$(echo "$SERVERS_JSON" | jq \ + --arg n "$srv_name" --arg h "$srv_host" --arg t "$srv_token" --argjson i "$srv_insecure" \ + '.[$n] = {host: $h, token: $t, insecure: $i}') + + ok "Added server '$srv_name': $srv_host" +done + +# ── Step 7: Write global MCP config ─────────────────────────────────────── + +echo "" +info "Writing global MCP configuration..." + +# Build the MCP entry +if [ -n "$SERVERS_JSON" ] && [ "$SERVERS_JSON" != "{}" ] && [ "$SERVERS_JSON" != "" ]; then + MCP_ENTRY=$(jq -n \ + --arg cmd "$BINARY_PATH" \ + --arg host "$DEFAULT_HOST" \ + --arg token "$DEFAULT_TOKEN" \ + --arg servers "$SERVERS_JSON" \ + '{ + command: $cmd, + args: ["-t", "stdio", "--host", $host], + env: { + GITEA_ACCESS_TOKEN: $token, + GITEA_SERVERS: $servers + } + }') +else + MCP_ENTRY=$(jq -n \ + --arg cmd "$BINARY_PATH" \ + --arg host "$DEFAULT_HOST" \ + --arg token "$DEFAULT_TOKEN" \ + '{ + command: $cmd, + args: ["-t", "stdio", "--host", $host], + env: { + GITEA_ACCESS_TOKEN: $token + } + }') +fi + +# Write to settings.json (global/user level) +mkdir -p "$(dirname "$CLAUDE_SETTINGS")" +if [ ! -f "$CLAUDE_SETTINGS" ]; then + echo '{}' > "$CLAUDE_SETTINGS" +fi + +tmp=$(jq --arg name "$MCP_NAME" --argjson entry "$MCP_ENTRY" \ + '.mcpServers[$name] = $entry' "$CLAUDE_SETTINGS") +echo "$tmp" > "$CLAUDE_SETTINGS" +ok "Written to $CLAUDE_SETTINGS" + +# Also register via claude CLI if available (belt and suspenders) +if command -v claude &>/dev/null; then + # Check if claude mcp already has this entry to avoid duplicates + if claude mcp list 2>/dev/null | grep -q "$MCP_NAME"; then + claude mcp remove "$MCP_NAME" --scope user 2>/dev/null || true + fi +fi + +# ── Step 8: Clean up duplicates in other config files ────────────────────── + +echo "" +info "Checking for duplicate configs..." + +# Clean from project-level .mcp.json files +while IFS= read -r -d '' mcp_file; do + if jq -e --arg name "$MCP_NAME" '.mcpServers[$name] != null' "$mcp_file" &>/dev/null; then + warn "Duplicate found in $mcp_file" + if ask_yes_no " Remove '$MCP_NAME' from $mcp_file?"; then + tmp=$(jq --arg name "$MCP_NAME" 'del(.mcpServers[$name])' "$mcp_file") + echo "$tmp" > "$mcp_file" + ok "Removed" + fi + fi +done < <(find "$HOME" -maxdepth 3 -name ".mcp.json" \ + -not -path "*/.claude/*" \ + -not -path "*/.gitea-mcp/*" \ + -not -path "*/node_modules/*" \ + -print0 2>/dev/null || true) + +# Clean from ~/.claude.json if we wrote to settings.json +if [ -f "$CLAUDE_JSON" ]; then + if jq -e --arg name "$MCP_NAME" '.mcpServers[$name] != null' "$CLAUDE_JSON" &>/dev/null; then + warn "Duplicate found in $CLAUDE_JSON" + if ask_yes_no " Remove from $CLAUDE_JSON? (already in settings.json)"; then + tmp=$(jq --arg name "$MCP_NAME" 'del(.mcpServers[$name])' "$CLAUDE_JSON") + echo "$tmp" > "$CLAUDE_JSON" + ok "Removed" + fi + fi +fi + +# ── Done ─────────────────────────────────────────────────────────────────── + +echo "" +echo -e "${CYAN}=== Configured Servers ===${NC}" +echo " default: $DEFAULT_HOST" +if [ -n "$SERVERS_JSON" ] && [ "$SERVERS_JSON" != "{}" ] && [ "$SERVERS_JSON" != "" ]; then + echo "$SERVERS_JSON" | jq -r 'to_entries[] | " \(.key): \(.value.host)\(if .value.insecure then " (insecure)" else "" end)"' +fi + +echo "" +echo -e "${GREEN}=== Installation complete! ===${NC}" +echo "" +echo " Binary: $BINARY_PATH ($VERSION)" +echo " Config: $CLAUDE_SETTINGS" +echo " Source: $INSTALL_DIR" +echo "" +echo "Restart Claude Code to load the new MCP server." +echo "" +echo "To upgrade later:" +echo " curl -fsSL https://gitea.dev.marcus-law.co.il/Chaim/gitea-mcp/raw/branch/main/install.sh | bash" +echo ""