@prvtech/mcp
v0.1.0
Published
Model Context Protocol server for the PRV Technologies cloud API — read + safe-management tools over a scoped prv_ API key.
Maintainers
Readme
PRV Technologies MCP server
A Model Context Protocol server that lets AI
assistants (Claude, Codex, Gemini, …) manage PRV Technologies cloud resources —
instances, projects, SSH keys, firewalls, VPCs, IPs, billing and support —
through natural language, by calling our public REST API with a personal
prv_ API key.
It is hand-written on the official, free
@modelcontextprotocol/sdk
— no code-generation service, no account, no cost. The whole server is a single
generic proxy (~3 small source files) that reads the curated OpenAPI spec at
startup and exposes one MCP tool per operation. It is fully spec-driven, so
the same regeneration story as a generated SDK applies: when the API changes,
refresh the spec and the tools follow — without re-running any external tool.
| Prerequisite | Where |
| --- | --- |
| OpenAPI 3.1 spec of the public API | openapi.source.json (vendored from the frontend's public/openapi.json, itself generated by scripts/generate-openapi.mjs) |
| Scoped Bearer API keys (read / write / all) | issued at Settings → Security → API Keys; validated server-side at request time |
How it works
openapi.source.json ──(scripts/build-mcp-spec.mjs)──▶ spec/openapi.mcp.json
curate: drop unsafe ops │
▼
src/openapi.ts loadOps()
one ToolOp per operation
│
tools/list ◀── src/server.ts ──▶ tools/call
│
src/http.ts callApi()
Bearer prv_… → REST → JSON backscripts/build-mcp-spec.mjs— curation filter. Producesspec/openapi.mcp.json.src/openapi.ts— loads the curated spec, inlines$refs, and builds each tool's JSON Schema (path/query params + flattened request body).src/server.ts— low-level MCPServer:tools/listreturns every operation;tools/callrebuilds the HTTP request and proxies it.src/http.ts— the thin authenticatedfetchwrapper.src/index.ts— CLI / stdio entrypoint.
Surface modes — what the AI can do
By default the full API surface is exposed (like latitudesh-mcp): every
operation becomes a tool — read, create, update, delete, deploy, resize,
rebuild, reset-password. Safety then rests on two gates:
- Key scope (server-enforced, per request): a
readkey can only call GETs — every write tool returns a scope error; awritekey allows create/update but notDELETE;allallows everything. - Host approval: your MCP client asks before running each tool call.
npm run build:spec # FULL surface (default) → 73 tools
MCP_SAFE=1 npm run build:spec # SAFE surface → 58 tools (see below)
# or: node scripts/build-mcp-spec.mjs --safeSafe mode (--safe / MCP_SAFE=1) adds build-time defence-in-depth — it
strips the dangerous operations so they aren't registered as tools at all, and
even an all key cannot reach them:
- every
DELETE(destroy instance / project / firewall / VPC / SSH key / IP …) - billable POSTs —
deploy_instance,resize_instance(create WHMCS orders) - data-destroying —
rebuild_instance(reinstalls the OS) - security-sensitive —
reset_instance_password
| Mode | Tools | Surface |
| --- | --- | --- |
| full (default) | 73 (37 GET · 18 POST · 7 PATCH · 11 DELETE) | everything; gated by key scope + host approval |
| safe (--safe) | 58 (37 GET · 14 POST · 7 PATCH) | read + safe-management; dangerous ops not even registered |
Key-scope guidance. Give the AI the least privilege that fits the task. In full mode the key scope is your main guard rail: a
readkey is inspection-only; reservewrite/allfor trusted automation. Build with--safewhen you also want the destructive tools physically absent.
Build
Prerequisites: Node 18+ (uses the global fetch). No other tooling.
npm install # also runs the build via the "prepare" script
# or explicitly:
npm run build # build:spec (curate) + build:ts (compile to build/)npm run build:spec regenerates spec/openapi.mcp.json from
openapi.source.json; npm run build:ts compiles src/ to build/.
Package for Claude Desktop (.mcpb)
npm run mcpb:build # → dist/prvtech-mcp.mcpb (self-contained, prod deps only)Produces a MCPB bundle that Claude Desktop
installs in one click — no Node, clone, or npm install on the user's side. The
manifest (name, every tool, and the prv_ key prompt) is generated from the live
tool surface, so it never drifts from the server.
Run / install
The server speaks stdio and authenticates with the prv_ key passed as
--bearer (or the PRVTECH_API_KEY env var):
# Local smoke test — handshake + tools/list, no key or network needed
npm run smoke
# Run directly
node build/index.js start --bearer prv_live_xxxxxxxx
# Register with Claude Code
claude mcp add prvtech -- node /abs/path/to/prvtech_mcp/build/index.js start --bearer prv_live_xxxxxxxx
# Once published to npm (@prvtech/mcp):
claude mcp add prvtech -- npx -y @prvtech/mcp start --bearer prv_live_xxxxxxxxClaude Desktop config
{
"mcpServers": {
"prvtech": {
"command": "node",
"args": ["/abs/path/to/prvtech_mcp/build/index.js", "start"],
"env": { "PRVTECH_API_KEY": "prv_live_xxxxxxxx" }
}
}
}One-click install (.mcpb)
After npm run mcpb:build, in Claude Desktop → Settings → Extensions install
dist/prvtech-mcp.mcpb (or drag it into the window) and paste your prv_ key when
prompted. Nothing else to set up.
Remote / shared (SSE)
For a hosted server that many users connect to over HTTP, each with their own key:
node build/index.js start --transport sse --port 3000
# clients connect to http://<host>:3000/sse with `Authorization: Bearer prv_…`In SSE mode the per-connection Authorization header overrides --bearer, so one
running server can serve many users with their own keys. stdio remains the default
(and the only mode Claude Desktop uses).
Flags & env
| Flag | Env | Default |
| --- | --- | --- |
| --bearer <key> | PRVTECH_API_KEY | — (tools list without it; calls error) |
| --base-url <url> | PRVTECH_BASE_URL | https://prvtechnologies.com |
| --spec <path> | PRVTECH_SPEC | ../spec/openapi.mcp.json |
| --transport <stdio\|sse> | — | stdio |
| --port <n> (sse only) | PORT | 3000 |
Keeping it current
When the public API changes:
- Refresh
openapi.source.jsonfrom the frontend'spublic/openapi.json. npm run build— re-curates and recompiles. New endpoints become new tools automatically; check the script's printed kept/dropped list to confirm any new write/destructive operation lands on the right side of the filter.
Releasing
CI (.github/workflows/ci.yml) builds and smoke-tests every push/PR. To publish:
npm version patch # bumps version + creates a git tag
git push --follow-tags # triggers .github/workflows/release.ymlThe release workflow publishes @prvtech/mcp to npm (needs an NPM_TOKEN repo
secret with publish rights to the @prvtech scope) and attaches the .mcpb
bundle to the GitHub release. To publish manually instead:
npm publish --access public # runs prepublishOnly → build firstNotes & follow-ups
- Tool names are mapped to clean
verb_resourcenames (list_invoices,power_instance,create_firewall) insrc/tool-names.ts, keyed by the spec'soperationId. An operation with no entry falls back to its raw operationId, so add a line there when the spec gains an operation. - Transports: stdio (default) and SSE (
--transport sse). streamable-HTTP is a possible follow-up; the generic dispatcher is transport-agnostic. - Scopes are verb-level, not per-endpoint, today — so in full mode the key
scope (read/write/all) is the primary guard rail. Build with
--safewhen you want destructive operations physically absent regardless of key. - 73 tools (full mode) is still small enough to expose directly. If the surface grows much larger, consider a discovery/meta-tool layer to keep the LLM's tool list manageable.
