Add "Architecture"
166
Architecture.md
Normal file
166
Architecture.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# 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:
|
||||
```typescript
|
||||
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:
|
||||
```typescript
|
||||
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.
|
||||
|
||||
---
|
||||
Reference in New Issue
Block a user