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>
This commit is contained in:
76
src/client.ts
Normal file
76
src/client.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user