d8be2209ad
Build & Release / Build & Release Pipeline (push) Has been cancelled
Changelog Validation / Validate CHANGELOG.md (push) Has been cancelled
Deploy to Demo Server (SFTP) / Verify Deployment Permission (push) Has been cancelled
MCP Build & Validate / build (20) (push) Has been cancelled
MCP Build & Validate / build (22) (push) Has been cancelled
MCP Tool Inventory / inventory (push) Has been cancelled
Standards Compliance / Secret Scanning (push) Has been cancelled
Standards Compliance / License Header Validation (push) Has been cancelled
Standards Compliance / Repository Structure Validation (push) Has been cancelled
Standards Compliance / Coding Standards Check (push) Has been cancelled
MCP Release / Build, Validate & Release (push) Has been cancelled
Standards Compliance / Workflow Configuration Check (push) Has been cancelled
Standards Compliance / Documentation Quality Check (push) Has been cancelled
Standards Compliance / README Completeness Check (push) Has been cancelled
Standards Compliance / Git Repository Hygiene (push) Has been cancelled
Standards Compliance / Script Integrity Validation (push) Has been cancelled
Standards Compliance / Line Length Check (push) Has been cancelled
Standards Compliance / File Naming Standards (push) Has been cancelled
Standards Compliance / Insecure Code Pattern Detection (push) Has been cancelled
Standards Compliance / Version Consistency Check (push) Has been cancelled
CodeQL Security Scanning / Analyze (actions) (push) Has been cancelled
CodeQL Security Scanning / Analyze (javascript) (push) Has been cancelled
Standards Compliance / Dead Code Detection (push) Has been cancelled
Standards Compliance / File Size Limits (push) Has been cancelled
Standards Compliance / Binary File Detection (push) Has been cancelled
Standards Compliance / TODO/FIXME Tracking (push) Has been cancelled
Standards Compliance / Code Complexity Analysis (push) Has been cancelled
Standards Compliance / Broken Link Detection (push) Has been cancelled
Standards Compliance / Code Duplication Detection (push) Has been cancelled
Standards Compliance / API Documentation Coverage (push) Has been cancelled
Standards Compliance / Accessibility Check (push) Has been cancelled
Standards Compliance / Performance Metrics (push) Has been cancelled
Standards Compliance / Dependency Vulnerability Scanning (push) Has been cancelled
Standards Compliance / Unused Dependencies Check (push) Has been cancelled
Standards Compliance / Terraform Configuration Validation (push) Has been cancelled
Deploy to Demo Server (SFTP) / SFTP Deploy → Demo (push) Has been cancelled
CodeQL Security Scanning / Security Scan Summary (push) Has been cancelled
Standards Compliance / Enterprise Readiness Check (push) Has been cancelled
Standards Compliance / Repository Health Check (push) Has been cancelled
Standards Compliance / Compliance Summary (push) Has been cancelled
Sync Version from README / Propagate README version (push) Has been cancelled
TypeScript MCP server exposing Gitea REST API v1 as AI assistant tools. Covers repos, files, branches, commits, issues, labels, milestones, pull requests (create/merge/review), releases, tags, actions, orgs, users, webhooks, wiki, notifications, and raw API passthrough. Multi-connection support with interactive setup wizard. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
121 lines
3.4 KiB
TypeScript
121 lines
3.4 KiB
TypeScript
/* 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.Client
|
|
* INGROUP: gitea-api-mcp
|
|
* REPO: https://git.mokoconsulting.tech/MokoConsulting/gitea-api-mcp
|
|
* PATH: /src/client.ts
|
|
* VERSION: 01.00.00
|
|
* BRIEF: HTTP client for Gitea REST API v1
|
|
*/
|
|
|
|
import * as https from 'node:https';
|
|
import * as http from 'node:http';
|
|
import type { GiteaConnection, ApiResponse } from './types.js';
|
|
|
|
const API_PREFIX = '/api/v1';
|
|
const TIMEOUT_MS = 30_000;
|
|
|
|
export class GiteaClient {
|
|
private readonly base_url: string;
|
|
private readonly headers: Record<string, string>;
|
|
private readonly insecure: boolean;
|
|
|
|
constructor(conn: GiteaConnection) {
|
|
this.base_url = conn.baseUrl.replace(/\/+$/, '') + API_PREFIX;
|
|
this.headers = {
|
|
'Authorization': `token ${conn.token}`,
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json',
|
|
};
|
|
this.insecure = conn.insecure ?? false;
|
|
}
|
|
|
|
async get(endpoint: string, params?: Record<string, string>): Promise<ApiResponse> {
|
|
return this.request(this.buildUrl(endpoint, params), 'GET');
|
|
}
|
|
|
|
async post(endpoint: string, body?: unknown): Promise<ApiResponse> {
|
|
return this.request(this.buildUrl(endpoint), 'POST', body);
|
|
}
|
|
|
|
async patch(endpoint: string, body: unknown): Promise<ApiResponse> {
|
|
return this.request(this.buildUrl(endpoint), 'PATCH', body);
|
|
}
|
|
|
|
async put(endpoint: string, body: unknown): Promise<ApiResponse> {
|
|
return this.request(this.buildUrl(endpoint), 'PUT', body);
|
|
}
|
|
|
|
async delete(endpoint: string): Promise<ApiResponse> {
|
|
return this.request(this.buildUrl(endpoint), 'DELETE');
|
|
}
|
|
|
|
private buildUrl(endpoint: string, params?: Record<string, string>): string {
|
|
const path = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
|
|
const url = new URL(`${this.base_url}${path}`);
|
|
if (params) {
|
|
for (const [key, value] of Object.entries(params)) {
|
|
url.searchParams.set(key, value);
|
|
}
|
|
}
|
|
return url.toString();
|
|
}
|
|
|
|
private request(url: string, method: string, body?: unknown): Promise<ApiResponse> {
|
|
return new Promise((resolve, reject) => {
|
|
const parsed = new URL(url);
|
|
const is_https = parsed.protocol === 'https:';
|
|
const transport = is_https ? https : http;
|
|
|
|
const options: https.RequestOptions = {
|
|
hostname: parsed.hostname,
|
|
port: parsed.port || (is_https ? 443 : 80),
|
|
path: parsed.pathname + parsed.search,
|
|
method,
|
|
headers: { ...this.headers },
|
|
timeout: TIMEOUT_MS,
|
|
};
|
|
|
|
if (this.insecure && is_https) {
|
|
options.rejectUnauthorized = false;
|
|
}
|
|
|
|
const payload = body !== undefined ? JSON.stringify(body) : undefined;
|
|
if (payload) {
|
|
(options.headers as Record<string, string>)['Content-Length'] = Buffer.byteLength(payload).toString();
|
|
}
|
|
|
|
const req = transport.request(options, (res) => {
|
|
const chunks: Buffer[] = [];
|
|
res.on('data', (chunk: Buffer) => chunks.push(chunk));
|
|
res.on('end', () => {
|
|
const raw = Buffer.concat(chunks).toString('utf-8');
|
|
let data: unknown;
|
|
try {
|
|
data = JSON.parse(raw);
|
|
} catch {
|
|
data = raw;
|
|
}
|
|
resolve({ status: res.statusCode ?? 0, data });
|
|
});
|
|
});
|
|
|
|
req.on('error', (err) => reject(err));
|
|
req.on('timeout', () => {
|
|
req.destroy();
|
|
reject(new Error('Request timed out'));
|
|
});
|
|
|
|
if (payload) {
|
|
req.write(payload);
|
|
}
|
|
req.end();
|
|
});
|
|
}
|
|
}
|