Complete MCP server wrapping all 84 VirtFusion Admin API endpoints: - Core HTTP client with Bearer auth and error handling - 17 tool modules organized by API category - Endpoint drift detection scripts and Gitea Actions CI/CD - Comprehensive README with configuration examples - CLAUDE.md for AI assistant onboarding Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
77 lines
2.1 KiB
TypeScript
77 lines
2.1 KiB
TypeScript
import { VirtFusionApiError } from './errors.js';
|
|
import { QueryParams } from './types.js';
|
|
|
|
export class VirtFusionClient {
|
|
private readonly baseUrl: string;
|
|
private readonly token: string;
|
|
|
|
constructor(baseUrl: string, token: string) {
|
|
this.baseUrl = baseUrl.replace(/\/+$/, '');
|
|
this.token = token;
|
|
}
|
|
|
|
private buildUrl(path: string, query?: QueryParams): string {
|
|
const url = new URL(`${this.baseUrl}${path}`);
|
|
if (query) {
|
|
for (const [key, value] of Object.entries(query)) {
|
|
if (value !== undefined) {
|
|
url.searchParams.set(key, String(value));
|
|
}
|
|
}
|
|
}
|
|
return url.toString();
|
|
}
|
|
|
|
private async request(method: string, path: string, body?: unknown, query?: QueryParams): Promise<unknown> {
|
|
const url = this.buildUrl(path, query);
|
|
const headers: Record<string, string> = {
|
|
Authorization: `Bearer ${this.token}`,
|
|
Accept: 'application/json',
|
|
};
|
|
|
|
if (body !== undefined) {
|
|
headers['Content-Type'] = 'application/json';
|
|
}
|
|
|
|
const response = await fetch(url, {
|
|
method,
|
|
headers,
|
|
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
});
|
|
|
|
if (response.status === 204) {
|
|
return { success: true };
|
|
}
|
|
|
|
let responseBody: unknown;
|
|
const text = await response.text();
|
|
try {
|
|
responseBody = JSON.parse(text);
|
|
} catch {
|
|
responseBody = text;
|
|
}
|
|
|
|
if (!response.ok) {
|
|
throw new VirtFusionApiError(response.status, response.statusText, responseBody);
|
|
}
|
|
|
|
return responseBody;
|
|
}
|
|
|
|
async get(path: string, query?: QueryParams): Promise<unknown> {
|
|
return this.request('GET', path, undefined, query);
|
|
}
|
|
|
|
async post(path: string, body?: unknown, query?: QueryParams): Promise<unknown> {
|
|
return this.request('POST', path, body, query);
|
|
}
|
|
|
|
async put(path: string, body?: unknown, query?: QueryParams): Promise<unknown> {
|
|
return this.request('PUT', path, body, query);
|
|
}
|
|
|
|
async delete(path: string, body?: unknown, query?: QueryParams): Promise<unknown> {
|
|
return this.request('DELETE', path, body, query);
|
|
}
|
|
}
|