tools

package
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 Imports: 16 Imported by: 0

Documentation

Overview

Package tools is the tool surface this server exposes to a model.

Two rules shape everything in it.

A tool is a job, not an endpoint

The API has ~190 customer operations. Wrapping each one in a tool is the standard way to build an MCP server and it is the wrong way: every tool's schema is sent to the model on every request, so a hundred tools spend the context window before the conversation starts and leave the model choosing between near-identical names. There are sixteen tools here, named after what somebody wants done rather than after the endpoint that does it, and several of them make two or three API calls so the model does not have to.

An annotation is a promise, and it is derived rather than typed

`readOnlyHint` and `destructiveHint` are what a client uses to decide whether to ask the human first. A tool that deletes a server while claiming to be read-only defeats that check, and a mislabelled tool is a known vulnerability class rather than a cosmetic slip. So the annotations here are computed from the tool's SCOPE, the scope is the strongest scope of the API operations the tool actually calls, and `spec_test.go` reads the platform's own OpenAPI document to hold the declaration to it. Nothing in this file is a hand-written claim about danger.

Index

Constants

View Source
const EnvDocsURL = "FIRSTBOOT_DOCS_URL"

DocsBase is where the documentation lives. It is a knob rather than a constant because a self-hosted deployment serves its own docs, and a search tool pointed at somebody else's site would answer questions about the wrong platform.

Variables

This section is empty.

Functions

func Instructions

func Instructions() string

Instructions is the standing note a client puts in front of the model.

It lives here rather than in main because it describes THESE tools, and because a claim about how the platform behaves is worth a test: a wrong instruction is worse than a missing one, since the model believes it over its own priors.

Everything in it is a fact that is NOT true of cloud providers in general. A model with no such note answers from the generic case, and the generic case is hourly billing, stopping to save money, and adding a firewall rule rather than replacing the set.

func Register

func Register(s *mcp.Server, c *firstboot.Client, opts Options) error

Register adds every tool to the server.

It validates each Spec first and returns an error rather than registering a contradictory one: a server that starts while promising a delete is read-only is worse than a server that refuses to start.

Types

type Options

type Options struct {
	// DocsURL is the documentation site search_docs reads. A self-hosted
	// deployment serves its own, and a search tool pointed at the wrong site
	// answers questions about the wrong platform.
	DocsURL string
	// HTTP is used only for the documentation fetch. The API client has its own,
	// carrying the retry and idempotency transports, and reusing it here would
	// put a documentation request through a retry policy built for creates.
	HTTP *http.Client
}

Options carries what the tools need beyond the API client.

type Scope

type Scope string

Scope mirrors the API's own token scopes. The values are the platform's, not this package's invention: they are what `x-firstboot-scope` carries on every operation in the published spec.

const (
	// ScopeRead is in every token. Reading changes nothing.
	ScopeRead Scope = "read"
	// ScopeDeploy changes what is RUNNING and commits no new capacity: a
	// build, a rollback, a reboot.
	ScopeDeploy Scope = "deploy"
	// ScopeWrite creates and reconfigures. It is the scope that spends the
	// wallet.
	ScopeWrite Scope = "write"
	// ScopeDestroy covers what pressing the button again cannot fix.
	ScopeDestroy Scope = "destroy"
)

func Max

func Max(a, b Scope) Scope

Max returns the stronger of two scopes.

type Spec

type Spec struct {
	// Name is what the model sees and what it calls.
	Name string
	// Title is the human-readable label a client may show instead.
	Title string
	// Description is the sentence the model chooses on. It is part of the
	// prompt, so it says what the tool is FOR and what it costs, not how it is
	// implemented.
	Description string
	// Scope is the strongest token scope this tool needs. It is declared here
	// and verified against the spec, rather than derived at runtime, so that a
	// tool whose operations get more dangerous fails a test instead of shipping
	// with a comfortable annotation.
	Scope Scope
	// Operations lists the API operationIds this tool calls. It is the join key
	// to the platform's published scope table, and it is also documentation:
	// "what does this tool actually do" has one answer per tool.
	Operations []string
	// Destructive marks a tool whose effect is worse than its scope implies.
	// Tightening is always allowed; the test refuses the opposite. A resize
	// restarts a machine and a rollback replaces what is serving traffic, and
	// neither is something to do behind a human's back just because the scope
	// that permits it is not `destroy`.
	Destructive bool
	// Idempotent means calling twice with the SAME arguments has no additional
	// effect. It is a claim about this tool, not about the endpoint: the
	// creating tools earn it by deriving their idempotency key from their
	// arguments (see idem.go), which is what turns the API's optional header
	// into a property a model can rely on.
	Idempotent bool
	// External marks a tool that reaches a host outside this platform's API.
	// Only the documentation search does; it is the one place `openWorldHint`
	// is true, and the one tool with no operations to check against the spec.
	External bool
}

Spec is everything about a tool that is checkable without calling it.

func All

func All() []Spec

All is every tool this server exposes, in the order a session tends to need them: find out what exists, find out what is allowed, then change something.

It is also the list the tests walk. A tool that is registered but absent here would ship without its scope checked against the API, which is the one thing this package promises, so `TestEveryRegisteredToolIsDeclared` refuses it.

func (Spec) Tool

func (s Spec) Tool() *mcp.Tool

Tool builds the MCP tool definition, annotations included.

The annotations are computed here and nowhere else. A caller cannot pass one in, which is the point: the only way to change what this server promises about a tool is to change what the tool DOES, and then the test notices.

func (Spec) Validate

func (s Spec) Validate() error

Validate catches the contradictions a Spec can express before they reach a client. It runs at registration rather than only in a test, because a server that starts with a nonsense annotation is worse than one that refuses to.

Jump to

Keyboard shortcuts

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