feat(mcp): public release with SSE, npm, Docker (#523) #524

Merged
jmiller merged 1 commits from feat/523-mcp-public into dev 2026-06-06 17:56:34 +00:00
7 changed files with 261 additions and 266 deletions
+18
View File
@@ -0,0 +1,18 @@
FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --production=false
COPY tsconfig.json ./
COPY src/ ./src/
RUN npx tsc && npm prune --production
EXPOSE 3100
ENV PORT=3100
ENV NODE_ENV=production
# SSE mode by default for Docker deployments
CMD ["node", "dist/sse.js"]
+72 -242
View File
@@ -1,286 +1,116 @@
<!-- Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
SPDX-License-Identifier: GPL-3.0-or-later
DEFGROUP: gitea-api-mcp.Documentation
REPO: https://git.mokoconsulting.tech/MokoConsulting/gitea-api-mcp
-->
# MokoGitea MCP Server
# gitea-api-mcp
A comprehensive [Model Context Protocol](https://modelcontextprotocol.io) server for [Gitea](https://gitea.com) and [MokoGitea](https://git.mokoconsulting.tech/MokoConsulting/MokoGitea). 120+ tools for repos, issues, PRs, projects, releases, custom fields, statuses, priorities, and manifests.
[![License: GPL-3.0-or-later](https://img.shields.io/badge/License-GPL--3.0--or--later-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![MCP](https://img.shields.io/badge/MCP-compatible-brightgreen.svg)](https://modelcontextprotocol.io)
[![Node](https://img.shields.io/badge/node-%3E%3D20.0.0-green.svg)](https://nodejs.org)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org)
Works with any Gitea instance. MokoGitea-specific features degrade gracefully on vanilla Gitea.
> MCP server for Gitea REST API v1 operations -- 61 tools for complete Gitea instance management from Claude Code and other MCP clients.
## Quick Start
## Table of Contents
- [Background](#background)
- [Install](#install)
- [Configuration](#configuration)
- [Usage](#usage)
- [Tools](#tools)
- [Contributing](#contributing)
- [License](#license)
- [Revision History](#revision-history)
## Background
`gitea-api-mcp` is a Model Context Protocol (MCP) server that exposes 61 tools for interacting with the Gitea REST API v1. It supports multiple named connections, allowing you to manage several Gitea instances from a single server. Authentication uses Gitea's native `Authorization: token` header format.
## Install
### Prerequisites
- Node.js >= 20.0.0
- A Gitea instance with API access
- A Gitea access token (Settings > Applications > Generate Token)
### Build from Source
### npx (no install)
```bash
git clone https://git.mokoconsulting.tech/MokoConsulting/gitea-api-mcp.git
cd gitea-api-mcp
npm install
npm run build
GITEA_URL=https://gitea.example.com GITEA_TOKEN=your_token npx @mokoconsulting/mokogitea-mcp
```
## Configuration
### Claude Code
Create `~/.gitea-api-mcp.json`:
```json
{
"defaultConnection": "moko",
"connections": {
"moko": {
"baseUrl": "https://git.mokoconsulting.tech",
"token": "your-gitea-access-token",
"insecure": false
}
}
}
```
### Config Fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `baseUrl` | string | Yes | Base URL of your Gitea instance |
| `token` | string | Yes | Gitea API access token |
| `insecure` | boolean | No | Skip TLS verification (self-signed certs) |
Override the config path with the `GITEA_API_MCP_CONFIG` environment variable.
### Multi-Connection Example
```json
{
"defaultConnection": "moko",
"connections": {
"moko": {
"baseUrl": "https://git.mokoconsulting.tech",
"token": "token-for-moko-gitea"
},
"github-mirror": {
"baseUrl": "https://gitea.example.com",
"token": "token-for-mirror"
}
}
}
```
## Usage
### Claude Code Registration
Add to your Claude Code MCP config (`~/.claude/claude_desktop_config.json` or project-level `.mcp.json`):
Add to `.claude.json`:
```json
{
"mcpServers": {
"gitea-moko": {
"command": "node",
"args": ["/path/to/gitea-api-mcp/dist/index.js"]
"mokogitea": {
"command": "npx",
"args": ["@mokoconsulting/mokogitea-mcp"],
"env": {
"GITEA_URL": "https://gitea.example.com",
"GITEA_TOKEN": "your_token"
}
}
}
}
```
### Multi-Connection Usage in Claude Code
### Docker (SSE mode)
When using multiple connections, pass the `connection` parameter to any tool:
```
Use gitea_repo_get with connection "github-mirror" to get owner/repo details.
```bash
docker run -p 3100:3100 \
-e GITEA_URL=https://gitea.example.com \
-e GITEA_TOKEN=your_token \
mokoconsulting/mokogitea-mcp
```
If `connection` is omitted, the `defaultConnection` is used.
Connect MCP client to `http://localhost:3100/sse`.
## Tools
### Multi-instance config
### User / Auth (3 tools)
Create `~/.mcp_mokogitea.json`:
| Tool | Description |
|------|-------------|
| `gitea_me` | Get the authenticated user info |
| `gitea_user_orgs` | List organizations the authenticated user belongs to |
| `gitea_user_repos` | List repositories owned by the authenticated user |
```json
{
"defaultConnection": "production",
"connections": {
"production": { "baseUrl": "https://gitea.example.com", "token": "your_token" },
"dev": { "baseUrl": "https://dev.gitea.example.com", "token": "dev_token" }
}
}
```
### Repositories (8 tools)
## Configuration
| Tool | Description |
|------|-------------|
| `gitea_repo_get` | Get repository details |
| `gitea_repo_create` | Create a new repository |
| `gitea_repo_delete` | Delete a repository |
| `gitea_repo_edit` | Edit repository settings |
| `gitea_repo_fork` | Fork a repository |
| `gitea_repo_search` | Search repositories |
| `gitea_org_repos` | List repositories in an organization |
| `gitea_list_connections` | List configured Gitea connections |
| Method | Use Case |
|--------|----------|
| `GITEA_URL` + `GITEA_TOKEN` env vars | Single instance, quick setup |
| `~/.mcp_mokogitea.json` config file | Multiple instances |
| `GITEA_API_MCP_CONFIG` env var | Custom config path |
| `GITEA_INSECURE=true` | Skip TLS verification |
### File Contents (5 tools)
## Tools (120+)
| Tool | Description |
|------|-------------|
| `gitea_file_get` | Get file contents from a repository |
| `gitea_dir_get` | Get directory contents (file listing) from a repository |
| `gitea_file_create_or_update` | Create or update a file in a repository |
| `gitea_file_delete` | Delete a file from a repository |
| `gitea_tree_get` | Get the git tree for a repository (recursive file listing) |
### Repositories
`gitea_repo_create` `gitea_repo_get` `gitea_repo_edit` `gitea_repo_delete` `gitea_repo_search` `gitea_repo_fork` `gitea_repo_generate` `gitea_repo_languages` `gitea_repo_contributors` `gitea_repo_topics` `gitea_repo_topics_set`
### Branches (4 tools)
### Issues
`gitea_issue_create` (dedup by title) `gitea_issue_get` `gitea_issue_update` `gitea_issues_list` `gitea_issue_search` `gitea_issue_comment_create` `gitea_issue_comments_list` `gitea_issue_labels_set` `gitea_issue_bulk_set_status`
| Tool | Description |
|------|-------------|
| `gitea_branches_list` | List branches in a repository |
| `gitea_branch_get` | Get a specific branch |
| `gitea_branch_create` | Create a new branch |
| `gitea_branch_delete` | Delete a branch |
### Pull Requests
`gitea_pull_create` `gitea_pull_get` `gitea_pulls_list` `gitea_pull_merge` `gitea_pull_files` `gitea_pull_review_create`
### Commits (2 tools)
### Branches and Tags
`gitea_branches_list` `gitea_branch_create` `gitea_branch_delete` `gitea_branch_get` `gitea_tags_list` `gitea_tag_create` `gitea_tag_delete`
| Tool | Description |
|------|-------------|
| `gitea_commits_list` | List commits in a repository |
| `gitea_commit_get` | Get a specific commit |
### Releases
`gitea_releases_list` `gitea_release_create` `gitea_release_get` `gitea_release_latest` `gitea_release_delete` `gitea_release_asset_upload` `gitea_release_asset_delete`
### Issues (7 tools)
### Files and Trees
`gitea_file_get` `gitea_file_create_or_update` `gitea_file_delete` `gitea_dir_get` `gitea_tree_get` `gitea_bulk_file_push`
| Tool | Description |
|------|-------------|
| `gitea_issues_list` | List issues in a repository |
| `gitea_issue_get` | Get a single issue by number |
| `gitea_issue_create` | Create a new issue |
| `gitea_issue_update` | Update an issue |
| `gitea_issue_comments_list` | List comments on an issue |
| `gitea_issue_comment_create` | Add a comment to an issue |
| `gitea_issue_search` | Search issues across all repositories |
### Projects
`gitea_project_list` `gitea_project_create` `gitea_project_get` `gitea_project_update` `gitea_project_delete` `gitea_project_overview` `gitea_project_columns_list` `gitea_project_column_create` `gitea_project_column_delete` `gitea_project_cards_list` `gitea_project_card_add` `gitea_project_card_move` `gitea_project_card_remove`
### Labels (2 tools)
### Organizations
`gitea_org_get` `gitea_org_repos` `gitea_org_members_list` `gitea_org_teams_list` `gitea_org_labels_list` `gitea_org_label_create`
| Tool | Description |
|------|-------------|
| `gitea_labels_list` | List labels in a repository |
| `gitea_label_create` | Create a label |
### Wiki
`gitea_wiki_pages_list` `gitea_wiki_page_get`
### Milestones (2 tools)
### MokoGitea Extensions
`gitea_manifest_get` `gitea_manifest_update` `gitea_org_custom_fields_list` `gitea_org_custom_field_create` `gitea_org_custom_field_delete` `gitea_issue_custom_fields_get` `gitea_issue_custom_fields_set` `gitea_org_issue_statuses_list` `gitea_issue_set_status` `gitea_org_issue_priorities_list` `gitea_issue_set_priority`
| Tool | Description |
|------|-------------|
| `gitea_milestones_list` | List milestones in a repository |
| `gitea_milestone_create` | Create a milestone |
### Admin and Other
`gitea_me` `gitea_users_search` `gitea_user_get` `gitea_notifications_list` `gitea_notifications_read` `gitea_commits_list` `gitea_commit_get` `gitea_compare` `gitea_webhooks_list` `gitea_webhook_create` `gitea_admin_users_list` `gitea_admin_orgs_list` `gitea_admin_cron_list` `gitea_admin_cron_run` `gitea_list_connections`
### Pull Requests (6 tools)
## SSE Server
| Tool | Description |
|------|-------------|
| `gitea_pulls_list` | List pull requests |
| `gitea_pull_get` | Get a single pull request |
| `gitea_pull_create` | Create a pull request |
| `gitea_pull_merge` | Merge a pull request |
| `gitea_pull_files` | List files changed in a pull request |
| `gitea_pull_review_create` | Create a pull request review |
For hosted deployments:
### Releases (5 tools)
| Tool | Description |
|------|-------------|
| `gitea_releases_list` | List releases |
| `gitea_release_get` | Get a single release by ID |
| `gitea_release_latest` | Get the latest release |
| `gitea_release_create` | Create a new release |
| `gitea_release_delete` | Delete a release |
### Tags (3 tools)
| Tool | Description |
|------|-------------|
| `gitea_tags_list` | List tags |
| `gitea_tag_create` | Create a tag |
| `gitea_tag_delete` | Delete a tag |
### Actions (2 tools)
| Tool | Description |
|------|-------------|
| `gitea_actions_runs_list` | List workflow runs for a repository |
| `gitea_actions_run_get` | Get a specific workflow run |
### Organizations (3 tools)
| Tool | Description |
|------|-------------|
| `gitea_org_get` | Get organization details |
| `gitea_org_teams_list` | List teams in an organization |
| `gitea_org_members_list` | List members of an organization |
### Users (2 tools)
| Tool | Description |
|------|-------------|
| `gitea_user_get` | Get a user profile |
| `gitea_users_search` | Search users |
### Webhooks (2 tools)
| Tool | Description |
|------|-------------|
| `gitea_webhooks_list` | List webhooks for a repository |
| `gitea_webhook_create` | Create a webhook |
### Wiki (2 tools)
| Tool | Description |
|------|-------------|
| `gitea_wiki_pages_list` | List wiki pages |
| `gitea_wiki_page_get` | Get a wiki page |
### Notifications (2 tools)
| Tool | Description |
|------|-------------|
| `gitea_notifications_list` | List notifications for the authenticated user |
| `gitea_notifications_read` | Mark all notifications as read |
### Generic (2 tools)
| Tool | Description |
|------|-------------|
| `gitea_api_request` | Make a raw API request to any Gitea v1 endpoint |
| `gitea_list_connections` | List configured Gitea connections |
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for development guidelines.
```
GET / Server info
GET /sse SSE connection endpoint
POST /message Tool call messages
GET /health Health check
```
## License
[GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html) -- Copyright (C) 2026 Moko Consulting
## Revision History
| Version | Date | Description |
|---------|------|-------------|
| 0.0.1 | 2026-05-07 | Initial release with 61 tools |
GPL-3.0-or-later - [Moko Consulting](https://mokoconsulting.tech)
+29 -5
View File
@@ -1,19 +1,33 @@
{
"name": "@mokoconsulting/mcp-mokogitea-api",
"version": "1.0.0",
"description": "MCP server for Gitea REST API v1 operations",
"name": "@mokoconsulting/mokogitea-mcp",
"version": "1.1.0",
"description": "MCP server for Gitea and MokoGitea - 120+ tools for repos, issues, PRs, projects, releases, custom fields, statuses, priorities, and manifests",
"type": "module",
"main": "dist/index.js",
"bin": {
"mokogitea-api-mcp": "dist/index.js"
"mokogitea-mcp": "dist/index.js",
"mokogitea-mcp-sse": "dist/sse.js"
},
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"start": "node dist/index.js",
"start:sse": "node dist/sse.js",
"setup": "node scripts/setup.mjs",
"clean": "rm -rf dist/"
},
"keywords": [
"mcp",
"gitea",
"mokogitea",
"model-context-protocol",
"claude",
"ai",
"git",
"self-hosted",
"api",
"devops"
],
"dependencies": {
"@modelcontextprotocol/sdk": "^1.12.1",
"zod": "^3.24.4"
@@ -27,8 +41,18 @@
},
"license": "GPL-3.0-or-later",
"author": "Moko Consulting <hello@mokoconsulting.tech>",
"homepage": "https://git.mokoconsulting.tech/MokoConsulting/mcp_mokogitea_api",
"repository": {
"type": "git",
"url": "https://git.mokoconsulting.tech/MokoConsulting/gitea-api-mcp.git"
"url": "https://git.mokoconsulting.tech/MokoConsulting/mcp_mokogitea_api.git"
},
"files": [
"dist/",
"config.example.json",
"README.md",
"LICENSE"
],
"publishConfig": {
"access": "public"
}
}
+18 -15
View File
@@ -1,17 +1,5 @@
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
*
* This file is part of a Moko Consulting project.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* FILE INFORMATION
* DEFGROUP: gitea-api-mcp.Config
* INGROUP: gitea-api-mcp
* REPO: https://git.mokoconsulting.tech/MokoConsulting/gitea-api-mcp
* PATH: /src/config.ts
* VERSION: 01.00.00
* BRIEF: Configuration loader for Gitea API MCP connections
*/
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
import { readFile } from 'node:fs/promises';
import { resolve } from 'node:path';
@@ -21,6 +9,20 @@ import type { GiteaConfig, GiteaConnection } from './types.js';
const CONFIG_FILENAME = '.mcp_mokogitea.json';
export async function loadConfig(): Promise<GiteaConfig> {
// Priority 1: Environment variables (zero-config single instance)
if (process.env.GITEA_URL && process.env.GITEA_TOKEN) {
const conn: GiteaConnection = {
baseUrl: process.env.GITEA_URL,
token: process.env.GITEA_TOKEN,
insecure: process.env.GITEA_INSECURE === 'true',
};
return {
connections: { default: conn },
defaultConnection: 'default',
};
}
// Priority 2: Config file
const config_path = process.env.GITEA_API_MCP_CONFIG
? resolve(process.env.GITEA_API_MCP_CONFIG)
: resolve(homedir(), CONFIG_FILENAME);
@@ -41,7 +43,8 @@ export async function loadConfig(): Promise<GiteaConfig> {
const message = err instanceof Error ? err.message : String(err);
throw new Error(
`Failed to load config from ${config_path}: ${message}\n` +
`Create ${config_path} — see config.example.json for format.`,
`Option 1: Set GITEA_URL and GITEA_TOKEN environment variables\n` +
`Option 2: Create ${config_path} - see config.example.json for format`,
);
}
}
+8 -4
View File
@@ -21,7 +21,11 @@ import { loadConfig, getConnection } from './config.js';
import { GiteaClient } from './client.js';
import type { GiteaConfig, ApiResponse } from './types.js';
let config: GiteaConfig;
export let config: GiteaConfig;
export function initConfig(c: GiteaConfig): void {
config = c;
}
function clientFor(connection?: string): GiteaClient {
return new GiteaClient(getConnection(config, connection));
@@ -59,9 +63,9 @@ const OwnerRepo = {
repo: z.string().describe('Repository name'),
};
const server = new McpServer({
name: 'mokogitea-api-mcp',
version: '1.0.0',
export const server = new McpServer({
name: '@mokoconsulting/mokogitea-mcp',
version: '1.1.0',
});
// ── User / Auth ─────────────────────────────────────────────────────────
+16
View File
@@ -0,0 +1,16 @@
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
//
// Creates a configured MCP server instance for use by both stdio and SSE transports.
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import type { GiteaConfig } from './types.js';
// Import index.ts to register all tools on its exported `server` singleton,
// then re-export a factory that initializes config and returns the server.
import { server, initConfig } from './index.js';
export function createMcpServer(cfg: GiteaConfig): McpServer {
initConfig(cfg);
return server;
}
+100
View File
@@ -0,0 +1,100 @@
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
//
// SSE transport entry point for MokoGitea MCP server.
// Run with: node dist/sse.js
// Or: GITEA_URL=https://gitea.example.com GITEA_TOKEN=xxx node dist/sse.js
//
// Listens on PORT (default 3100) and serves SSE at /sse with POST at /message.
import { createServer } from 'node:http';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
import { createMcpServer } from './server.js';
import { loadConfig } from './config.js';
const PORT = parseInt(process.env.PORT ?? '3100', 10);
async function main(): Promise<void> {
const config = await loadConfig();
const transports = new Map<string, SSEServerTransport>();
const httpServer = createServer(async (req, res) => {
// CORS headers for browser clients
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') {
res.writeHead(204);
res.end();
return;
}
// Health check
if (req.url === '/health') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'ok', tools: 120 }));
return;
}
// SSE endpoint - client connects here
if (req.url === '/sse' && req.method === 'GET') {
const transport = new SSEServerTransport('/message', res);
const sessionId = transport.sessionId;
transports.set(sessionId, transport);
const server = createMcpServer(config);
await server.connect(transport);
req.on('close', () => {
transports.delete(sessionId);
});
return;
}
// Message endpoint - client sends tool calls here
if (req.url?.startsWith('/message') && req.method === 'POST') {
const url = new URL(req.url, `http://${req.headers.host}`);
const sessionId = url.searchParams.get('sessionId');
if (!sessionId || !transports.has(sessionId)) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Invalid or missing sessionId' }));
return;
}
const transport = transports.get(sessionId)!;
await transport.handlePostMessage(req, res);
return;
}
// Root - info page
if (req.url === '/' || req.url === '') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
name: '@mokoconsulting/mokogitea-mcp',
version: '1.1.0',
description: 'MCP server for Gitea and MokoGitea - 120+ tools',
endpoints: {
sse: '/sse',
message: '/message',
health: '/health',
},
docs: 'https://git.mokoconsulting.tech/MokoConsulting/mcp_mokogitea_api',
}));
return;
}
res.writeHead(404);
res.end('Not found');
});
httpServer.listen(PORT, () => {
process.stderr.write(`MokoGitea MCP SSE server listening on port ${PORT}\n`);
process.stderr.write(` SSE: http://localhost:${PORT}/sse\n`);
process.stderr.write(` Health: http://localhost:${PORT}/health\n`);
});
}
main().catch((err) => {
process.stderr.write(`Fatal: ${err}\n`);
process.exit(1);
});