mcp

module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 27, 2026 License: Apache-2.0

README

firstboot-mcp

The Model Context Protocol server for the Firstboot cloud platform. It gives a model sixteen tools over one account.

{
  "mcpServers": {
    "firstboot": {
      "command": "firstboot-mcp",
      "env": {
        "FIRSTBOOT_API_URL": "https://api.example.com",
        "FIRSTBOOT_TOKEN": "pat_..."
      }
    }
  }
}

It speaks over stdin and stdout, which is what an MCP client launches it for.

go install github.com/firstboot-io/mcp/cmd/firstboot-mcp@latest

The command lives under cmd/firstboot-mcp so that line produces a binary by that name. A main at the repository root would install as mcp, which is both wrong on a PATH and not what the configuration above launches.

The token is the security decision

The server authenticates with one API token and can do nothing that token cannot. The boundary is enforced by the API, not by this process, so a bug here cannot widen it.

Create the token in the panel under Account settings → API tokens and give it the narrowest scopes the work needs:

Give it To let the model
read only answer questions, read logs, check balance and quotas
+ deploy deploy apps, roll back, restart servers
+ write create servers, resize, edit DNS and firewall rules
+ destroy delete servers

Without destroy, delete_server simply fails. For a server whose job is answering questions about an account, that is the right outcome rather than a limitation.

Three classes of thing are closed to every token whatever its scopes, and they are the three that matter most with a model in the loop:

  • Credentials on the wire. An app's environment variables and a database's connection details. Neither can reach a chat transcript through this server, because neither can reach this server.
  • Interactive root. A container shell and a VM console. A live byte stream cannot be bounded by a scope the way a request can.
  • Personal data. A domain's registrant profile is a real person's home address and phone number.

The tools

Sixteen, named after jobs rather than endpoints. That is a budget: every tool's schema is sent to the model on every request, so a server that wrapped the API's ~190 operations one-to-one would spend the context window before the conversation started, and leave the model choosing between near-identical names.

Tool Scope Notes
list_resources read one tool, a kind argument: servers, apps, databases, volumes, networks, load balancers, floating IPs, DNS zones, domains. A tags argument narrows to what carries every tag listed
get_resource read the same kinds, one resource in full
get_catalog read plans, regions and images in one answer, showing which plans each region actually sells
account_limits read quotas with what is left, and which products are switched on
wallet_status read balance, hourly burn, runway
app_status read state, replicas, releases and recent builds in one answer
app_logs read asks the node and waits for the answer, which is two API calls
search_docs read the platform's own documentation
create_server write waits until it is running, returns the address; takes tags so the machine is labelled at birth
power_server deploy start, stop, reboot, force_stop; waits for the action
resize_server write waits for the machine to come back
delete_server destroy refuses unless the server's name is repeated back
deploy_app deploy builds AND waits, then says whether it worked
rollback_app deploy puts an earlier release back
set_dns_record write upsert: creates or updates, so "point www here" is one call
set_firewall_rules write replaces the whole set, which is what the API offers

Tags arrived on 2026-08-27 and cost no tool, which is the rule working: a list_tagged_resources would have been a seventeenth schema in every request's context to say something the model can already say as an argument. The count is held by a test.

Annotations are derived, not typed

readOnlyHint and destructiveHint are what a client uses to decide whether to stop and ask the human. A tool that deletes a server while claiming to be read-only walks straight past that check, which is why a mislabelled tool is a vulnerability class rather than a cosmetic slip.

So nothing here is a hand-written claim about danger. Each tool declares the API operations it calls; the platform publishes what scope each operation costs (x-firstboot-scope, stamped from the same table its middleware consults); the tool's scope is the strongest of those; and the annotations are computed from the scope. The chain is:

what the tool calls → what the API says that costs → what this server promises

internal/tools/spec_test.go reads the platform's own openapi.json and breaks the build if any link fails, naming the operation. A tool may be more conservative than its scope implies and never less: resize_server is write-scoped and marked destructive because it restarts the machine.

A tool that calls an operation closed to API tokens fails the same test. That is how "the model cannot read your app's secrets" stays true as the tools grow.

Retrying a create cannot buy a second server

The SDK mints an Idempotency-Key per request and reuses it across its own retries, which protects the request. It does not protect the conversation, and the conversation is what this server sits in: a model that calls create_server, loses the answer to a timeout and calls it again has made two logical requests, and the account gets two machines and two months of billing.

So the key is derived from the arguments. Two calls asking for exactly the same thing carry the same key and the API answers the second with the first one's resource. That is what lets create_server honestly advertise idempotentHint.

The cost is that two genuinely separate requests for an identical resource collapse into one for 24 hours. The arguments include the name, so "another server like that one" differs in the field that has to differ anyway, and two calls with the same name are exactly the case that should collapse.

What it deliberately does not do

  • No credential reads, no shells, no consoles, no registrant contacts. Not because this server declines: because a token cannot reach them.
  • No DNS record deletion. Removing DNS is how a site goes dark, and it is not something to do from a chat turn. The panel has it.
  • No password reset. serverPasswordReset returns the new root password in its response body, which for an MCP client means the password lands in a chat transcript. It is not exposed as a tool.
  • No topping up, no promo codes, no billing changes. Those are closed to tokens anyway.

Configuration

Variable Meaning
FIRSTBOOT_API_URL the API's base URL
FIRSTBOOT_TOKEN the API token (pat_…)
FIRSTBOOT_DOCS_URL the documentation site search_docs reads; a self-hosted deployment serves its own

--docs-url overrides the last one. --version prints the version and exits.

Development

go build ./...
go test ./...

The SDK is consumed as a released module (github.com/firstboot-io/go-sdk), not as a sibling checkout: the replace directive is gone. While that repository is private the module proxy cannot serve it, so a build needs

go env -w GOPRIVATE='github.com/firstboot-io/*'
git config --global url."git@github.com:firstboot-io/".insteadOf "https://github.com/firstboot-io/"

once, and nothing after the repositories go public.

The scope tests read ../platform/api/openapi/openapi.json. Without the platform checked out beside this repository those tests SKIP, which is why CI checks it out and then fails if the skip happened: a passing suite that checked nothing is the outcome this package exists to avoid.

Requirements

Go 1.25 or newer.

License

Apache License 2.0. See LICENSE.

Directories

Path Synopsis
cmd
firstboot-mcp command
Command firstboot-mcp is the Model Context Protocol server for the Firstboot cloud platform.
Command firstboot-mcp is the Model Context Protocol server for the Firstboot cloud platform.
internal
tools
Package tools is the tool surface this server exposes to a model.
Package tools is the tool surface this server exposes to a model.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL