Files
virtfusion-mcp/src/client.ts
Andrew 40d5e8161a
All checks were successful
Endpoint Sync Check / check-drift (push) Successful in 20s
CI / build (push) Successful in 26s
feat: initial implementation of VirtFusion MCP server
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>
2026-03-15 23:34:48 -04:00

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);
}
}