mcptools

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jul 21, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Index

Constants

View Source
const WorkspaceParamName = "workspaceId"

WorkspaceParamName is the required workspace selector every desktop tool carries. It is not a spec path arg — it routes the call to the (client, workspace) grant that signs the request, and is substituted into any `{workspaceId}` path placeholder when present.

Variables

This section is empty.

Functions

func CoerceBodyDates

func CoerceBodyDates(payload any) error

CoerceBodyDates is a thin alias over qparam.CoerceBodyDates so the MCP path reads symmetrically alongside coerceBodyDates calls in the CLI's generated write handlers — same coercion, single source of truth.

func ErrorResult

func ErrorResult(err error) *mcp.CallToolResult

ErrorResult wraps a Bron API error (or any error) into an MCP tool-error payload — the structured envelope (status, code, message, requestId) survives for the agent to branch on without parsing strings. All string fields go through output.SanitizeForTerminal because backend error messages can echo user-controlled input (e.g. "external id 'foo<script>' already taken") which a naive renderer might interpret.

func ExtractBodyBaseline

func ExtractBodyBaseline(in map[string]any) (any, error)

ExtractBodyBaseline pulls the optional `body` field out of the input map and returns it as the JSON baseline. Maps pass through (interface{} == any in Go 1.18+, no copy needed); anything else gets re-marshalled through a json.Decoder with UseNumber so big-int amount fields don't lose precision (`15000000000` would otherwise round-trip as `1.5e+10` and fail the backend's decimal parser).

func IsReadOnlyEndpoint

func IsReadOnlyEndpoint(resource, verb string, e catalog.HelpEntry) bool

IsReadOnlyEndpoint flags endpoints that are safe to expose under `bron mcp --read-only`. The source of truth is the OpenAPI spec's per-endpoint API-key permissions list (mined into `e.Permissions` by cligen): a tool is read-only iff its permissions include "View only". This avoids the "GET that mutates" footgun where a future endpoint like `GET /transactions/{id}/retry-broadcast` would slip through a method-only heuristic, and inverts the safety polarity to fail-closed (no permissions metadata → not read-only).

One explicit allow-list entry: `tx.dry-run` is a POST per spec but pure validation (no DB writes, no audit-log entries, no rate-counter advance — confirmed against backend). It's the only POST surfaced in read-only mode.

func RegisterHelp

func RegisterHelp(server *mcp.Server)

RegisterHelp adds the read-only `bron_help` tool: static data-model help with no network access. It is not a spec endpoint, so it registers directly rather than through RegisterSpecDriven.

func RegisterSpecDriven

func RegisterSpecDriven(server *mcp.Server, doer Doer, opts Options)

RegisterSpecDriven auto-registers one MCP tool per CLI endpoint that passes opts, all driven by the generated metadata. Untouched when new endpoints land — regen is the only step.

func SanitizeName

func SanitizeName(s string) string

func SortedKeys

func SortedKeys[V any](m map[string]V) []string

func StringValue

func StringValue(v any) string

StringValue stringifies one input value the way the CLI does — strings pass through, numbers/booleans become their JSON repr (so body.Compose's json.Unmarshal recovers the typed scalar), nested objects/arrays go through json.Marshal. Empty / nil → empty string so callers can `if s == ""` skip.

func ToolName

func ToolName(resource, verb string) string

ToolName converts (resource, verb) into a stable MCP tool name.

bron_<resource>_<verb> with dashes turned into underscores.

The MCP spec restricts tool names to [a-zA-Z0-9_-]; our resources and verbs already comply, but address-book/create-signing-request style verbs need the dash → underscore swap so the JSON-Schema name pattern ($_a-z0-9) is satisfied uniformly.

func WrapUntrustedFields

func WrapUntrustedFields(v any) any

WrapUntrustedFields walks a JSON-shaped value tree and wraps known user-controlled string fields (`description`, `memo`, `note`, `comment`, `reason`) in `<untrusted source="<key>">…</untrusted>` markers. Pairs with the server-instructions directive that tells the agent to treat envelope content as inert data.

Field-name match is intentionally narrow — wrapping every `name` field would also catch server-controlled labels (asset names, network labels, account names that are technically operator-set but high-trust within the workspace). Better to under-wrap than to flood the agent with markers.

In-place mutation; safe to call on `any` (returns input on non-map roots).

func WrapUntrustedFieldsWithKeys

func WrapUntrustedFieldsWithKeys(v any, keys map[string]bool) any

WrapUntrustedFieldsWithKeys is the spec-driven variant: it wraps the global always-wrap set plus every string under a key in `keys` (the endpoint's `format:"external-text"` set from the datamodel), anywhere in the tree. The structural DTO-shape heuristics are dropped — the spec set supersedes them.

Types

type APIError

type APIError struct {
	Status    int
	Code      string
	Message   string
	RequestID string
}

APIError is the toolkit's own structured Bron API error. Consumers adapt their transport error into it at the boundary (bron-cli maps sdk/http's APIError; desktop builds it from its parsed response) so the lib never has to import sdk/http — which would pull sdk/auth + JWT into every consumer.

func (*APIError) Error

func (e *APIError) Error() string

type Doer

type Doer interface {
	Do(ctx context.Context, method, path string, pathParams map[string]string, body, query, result any) error
}

Doer executes one Bron API request. Both the CLI's HTTP client (process-wide workspace + API key) and Desktop's per-grant signer satisfy it, so the spec-driven registration below is shared: the caller supplies how a request is authenticated and dispatched, the registrar owns which tools exist and how their arguments map onto a request.

type EmbedAugmentor

type EmbedAugmentor struct {
	Description string
	Apply       func(ctx context.Context, doer Doer, result any, tokens []string) error
}

EmbedAugmentor encapsulates a client-side join: when the agent passes an `embed` token on a registered (resource, verb), Apply mutates the result in place to attach the resolved/calculated extras under `_embedded`.

type Options

type Options struct {
	ReadOnly          bool
	Allow             func(toolName string) bool
	WorkspaceParam    bool
	EmbedAugmentors   map[string]*EmbedAugmentor
	PreCallValidators map[string]func(in map[string]any) error
}

Options tunes spec-driven registration for one consumer.

  • ReadOnly skips state-changing endpoints (keeps GET + `tx.dry-run`).
  • Allow gates each candidate tool by name; nil means "register everything".
  • WorkspaceParam adds a required `workspaceId` selector to every tool and routes it into the request path — Desktop resolves the signing grant from it. The CLI leaves it off (its client injects a process-wide workspace).
  • EmbedAugmentors / PreCallValidators attach per-tool client-side behaviour.

type SpecTool

type SpecTool struct {
	Name        string
	Resource    string
	Verb        string
	Entry       catalog.HelpEntry
	Description string
	InputSchema *jsonschema.Schema
}

SpecTool is one spec-driven MCP tool that passed the active Options filters.

func SpecTools

func SpecTools(opts Options) []SpecTool

SpecTools returns the endpoint tools that survive the ReadOnly and Allow filters, with input schemas already built for opts. RegisterSpecDriven wires these onto a server; tests inspect them directly.

Jump to

Keyboard shortcuts

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