1
Architecture
Prophet731 edited this page 2026-03-16 00:33:56 -04:00

Architecture

This page describes the project structure, key patterns, and the request flow from MCP tool invocation through to the VirtFusion API.

Project Structure

virtfusion-mcp/
  src/
    index.ts              Entry point: env validation, McpServer + StdioServerTransport setup
    client.ts             VirtFusionClient: fetch wrapper with Bearer auth, query param filtering
    errors.ts             VirtFusionApiError class + formatErrorResponse() for MCP error results
    types.ts              Shared interfaces (PaginatedResponse, QueryParams)
    tools/
      index.ts            Barrel: registerAllTools() wires all 17 modules to McpServer
      general.ts          1 tool  (test connection)
      hypervisors.ts      2 tools
      hypervisor-groups.ts 3 tools
      servers.ts          18 tools (largest module)
      servers-power.ts    4 tools
      servers-network.ts  5 tools
      servers-firewall.ts 4 tools
      servers-traffic.ts  4 tools
      ip-blocks.ts        3 tools
      backups.ts          1 tool
      dns.ts              1 tool
      media.ts            2 tools
      packages.ts         2 tools
      queue.ts            1 tool
      ssh-keys.ts         4 tools
      users.ts            7 tools
      self-service.ts     19 tools
  scripts/
    extract-endpoints.ts  Parse openapi.yaml into JSON endpoint manifest
    check-endpoint-drift.ts Compare spec vs manifest for drift detection
  openapi.yaml            VirtFusion Admin API OpenAPI specification
  endpoint-manifest.json  Snapshot of all known endpoints (84 total)
  .gitea/workflows/       CI/CD workflow definitions

Core Modules

Entry Point (src/index.ts)

The entry point performs three tasks:

  1. Environment validation -- Checks for VIRTFUSION_API_URL and VIRTFUSION_API_TOKEN, exiting with a descriptive error if either is missing.
  2. Initialization -- Creates a VirtFusionClient instance and an McpServer instance, then calls registerAllTools() to wire up all 84 tools.
  3. Transport -- Connects the MCP server to a StdioServerTransport for communication with the MCP client.

HTTP Client (src/client.ts)

VirtFusionClient is a lightweight fetch wrapper that handles:

  • Bearer authentication -- Automatically adds Authorization: Bearer <token> to every request.
  • URL construction -- Combines the base URL with the path and query parameters. Trailing slashes on the base URL are stripped.
  • Query parameter filtering -- Any query parameter with an undefined value is silently omitted.
  • Content-Type handling -- Only sets Content-Type: application/json when a body is present.
  • 204 responses -- Returns { success: true } for No Content responses.
  • Error propagation -- Non-OK responses throw a VirtFusionApiError with the status code, status text, and parsed response body.

The client exposes four public methods: get(), post(), put(), and delete(). All share the signature (path, body?, query?).

Error Handling (src/errors.ts)

Two exports:

  • VirtFusionApiError -- An Error subclass carrying statusCode, statusText, and errorBody. Thrown by the client on non-OK responses.
  • formatErrorResponse() -- Converts any caught error into an MCP-compatible CallToolResult with isError: true. For VirtFusionApiError instances, it includes structured status and error detail information. For other errors, it includes the error message.

Shared Types (src/types.ts)

  • PaginatedResponse<T> -- Interface matching VirtFusion's paginated response format (current_page, data, links, total, etc.).
  • QueryParams -- Type alias for Record<string, string | number | boolean | undefined>.

Tool Module Pattern

Every tool module in src/tools/ follows the same pattern:

  1. Export a single registration function with the signature:

    export function registerXxxTools(server: McpServer, client: VirtFusionClient): void
    
  2. Register tools by calling server.tool() with four arguments:

    • Tool name (snake_case)
    • Human-readable description
    • Zod schema object for parameter validation
    • Async handler function
  3. Handler structure -- Every handler follows this pattern:

    async (params) => {
      try {
        // Build body/query from params
        const result = await client.get|post|put|delete(path, body?, query?);
        return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
    
  4. Optional parameters -- When a tool has optional body parameters, the handler conditionally adds them to the body object only when defined.

Tool Registration

The barrel module src/tools/index.ts imports all 17 registration functions and exposes a single registerAllTools() function. The entry point calls this once during startup to wire everything up.

Request Flow

MCP Client (Claude, VS Code, etc.)
  |
  | MCP protocol (stdio)
  v
McpServer (src/index.ts)
  |
  | Zod validates parameters
  v
Tool Handler (src/tools/*.ts)
  |
  | Builds request body/query from validated params
  v
VirtFusionClient (src/client.ts)
  |
  | fetch() with Bearer auth headers
  v
VirtFusion REST API
  |
  | JSON response
  v
VirtFusionClient
  |
  | Parses response; throws VirtFusionApiError on non-OK
  v
Tool Handler
  |
  | Wraps result in MCP content format, or catches error
  v
McpServer
  |
  | MCP protocol (stdio)
  v
MCP Client

CI/CD Workflows

The project uses Gitea Actions with four workflows:

Workflow File Trigger Purpose
CI ci.yaml Push, PR Build and type-check
Endpoint Sync endpoint-sync.yaml Weekly, openapi.yaml changes Detect API drift, create issue
Release release.yaml Semver tag push Create Gitea release with changelog
Version Check version-check.yaml PRs changing src/ Warn if package.json version not bumped

Key Design Decisions

  • No runtime dependencies beyond MCP SDK and Zod -- The client uses Node.js built-in fetch (available in Node 22+).
  • Flat tool namespace -- All 84 tools share a single namespace with snake_case naming convention {category}_{action}_{noun}.
  • Server-side safety -- The servers_delete tool enforces a minimum 300-second delay to prevent accidental destruction.
  • All responses as JSON text -- Every tool returns its API response as pretty-printed JSON in a text content block, keeping the interface uniform.