protocol

package
v1.3.1 Latest Latest
Warning

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

Go to latest
Published: Jun 11, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

Package protocol implements the seven `tools.*` Protocol methods the Console Tools page (Phase 73f / D-116) consumes:

  • tools.list — paginated, faceted catalog projection.
  • tools.get — single catalog-row projection.
  • tools.describe — full manifest projection.
  • tools.metrics — per-tool error-rate gauges + status pill.
  • tools.content_stats — per-tool result-size histogram + DisplayMode.
  • tools.set_approval_policy — ADMIN: mutate a tool's approval policy.
  • tools.revoke_oauth — ADMIN: revoke a tool's OAuth bindings.

The seam (CLAUDE.md §4.4)

The Service depends on the `Projector` interface, not on a concrete tool catalog. The V1 production implementation is `CatalogProjector` (catalog_projector.go) — a thin read-only projection over a `tools.ToolCatalog` plus optional metric / admin backends. A future remote-catalog projector slots in behind the same interface without reshaping the Service.

Identity is mandatory (CLAUDE.md §6 rule 9)

Every method takes the wire request's `IdentityScope`. An incomplete triple fails closed with `ErrIdentityRequired` — there is no identity-downgrading knob. The Service NEVER reads identity from a package-level global; the triple flows in via the request.

Admin gating (D-079)

`tools.set_approval_policy` and `tools.revoke_oauth` MUTATE runtime tool state and require the verified `auth.ScopeAdmin` claim. The Service receives an `adminScoped bool` the wire handler computes from the verified JWT scope set; a false value on an admin method fails closed with `ErrAdminScopeRequired`. There is NO `tools.admin` scope — the closed two-scope set (`admin` + `console:fleet`) is the only admit surface, and the Tools admin methods gate on `ScopeAdmin`.

Concurrent reuse (D-025)

A constructed *Service is immutable after NewService and safe to share across N concurrent goroutines: it holds only the Projector reference + an optional bus + logger; every method's per-call state lives in the call's arguments and locals, never on the Service.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrIdentityRequired — the request carried an incomplete identity
	// triple. RFC §5.5 / CLAUDE.md §6 rule 9 — fails closed.
	ErrIdentityRequired = errors.New("tools/protocol: identity scope incomplete")
	// ErrAdminScopeRequired — an admin method (`tools.set_approval_policy`
	// / `tools.revoke_oauth`) was called without the verified
	// `auth.ScopeAdmin` claim (D-079).
	ErrAdminScopeRequired = errors.New("tools/protocol: admin scope claim required")
	// ErrToolNotFound — the requested tool ID is not registered in the
	// catalog visible to the caller's identity scope.
	ErrToolNotFound = errors.New("tools/protocol: tool not found")
	// ErrInvalidRequest — the request was structurally invalid (an
	// empty tool ID, an out-of-range page size, an unknown enum value).
	ErrInvalidRequest = errors.New("tools/protocol: invalid request")
	// ErrMisconfigured — NewService was called with a nil Projector.
	ErrMisconfigured = errors.New("tools/protocol: NewService missing a mandatory dependency")
	// ErrAdminUnsupported — an admin method was called but the
	// Projector does not implement the corresponding admin backend.
	// Fails loud rather than silently no-op'ing (CLAUDE.md §13).
	ErrAdminUnsupported = errors.New("tools/protocol: admin backend not configured")
)

Sentinel errors the Service returns. The wire handler maps each onto a canonical Protocol Code + HTTP status; in-process callers compare with errors.Is.

Functions

This section is empty.

Types

type Annotator

type Annotator interface {
	// OAuthStatus returns the tool's OAuth binding status.
	OAuthStatus(ctx context.Context, id identity.Identity, toolID string) prototypes.ToolOAuthStatus
	// ApprovalPolicy returns the tool's configured approval policy.
	ApprovalPolicy(ctx context.Context, id identity.Identity, toolID string) prototypes.ToolApprovalPolicy
	// LastUsedAt returns the timestamp of the tool's most recent
	// invocation in the caller's scope; the zero value means "never".
	LastUsedAt(ctx context.Context, id identity.Identity, toolID string) time.Time
	// Metrics returns per-tool error-rate gauges over the window.
	Metrics(ctx context.Context, id identity.Identity, toolID string, window prototypes.ToolMetricsWindow) prototypes.ToolMetrics
	// ContentStats returns the per-tool result-size histogram.
	ContentStats(ctx context.Context, id identity.Identity, toolID string) prototypes.ToolContentStats
	// DisplayModes returns the negotiated MCP-Apps DisplayMode map for
	// the tool (empty for non-MCP tools).
	DisplayModes(ctx context.Context, id identity.Identity, toolID string) map[string]string
}

Annotator is the optional per-tool annotation backend CatalogProjector reads OAuth / approval / metrics / content-stats data through. Production wiring supplies an implementation backed by tools/auth + tools/approval + the events stream; tests and partial-builds run without one.

type ApprovalPolicySetter

type ApprovalPolicySetter interface {
	// SetApprovalPolicy mutates toolID's approval policy. Returns
	// ErrToolNotFound when toolID is unknown.
	SetApprovalPolicy(ctx context.Context, id identity.Identity, toolID string, policy prototypes.ToolApprovalPolicy) error
}

ApprovalPolicySetter is the optional admin backend the `tools.set_approval_policy` method requires. A Projector that does not implement it makes the method fail loud with ErrAdminUnsupported.

type CatalogProjector

type CatalogProjector struct {
	// contains filtered or unexported fields
}

CatalogProjector is the V1 production Projector — a read-only projection over a tools.ToolCatalog plus optional metric / annotation backends. It maps the runtime-internal tools.Tool descriptor onto the flat Protocol wire shapes the Console Tools page renders.

Annotation seam

The catalog's tools.Tool descriptor carries transport / side-effect / scopes / examples / schemas, but NOT the per-tool OAuth-binding status, approval policy, last-used timestamp, or invocation metrics — those live in sibling subsystems (tools/auth, tools/approval, the events stream). CatalogProjector reads them through the optional Annotator interface. When no Annotator is wired, the projector returns conservative defaults (OAuth `n/a`, approval `auto`, zero metrics) so a partial-build Console still renders the catalog rather than failing — the defaults are honest ("we don't have this data"), not silent degradation of a known value.

Concurrent reuse (D-025)

CatalogProjector is immutable after NewCatalogProjector: it holds the catalog + annotator references. The catalog is itself D-025-safe; the projector adds no mutable state. The optional admin backends mutate runtime state, but that mutation lives behind the catalog / annotator implementations, internally synchronised there.

func NewCatalogProjector

func NewCatalogProjector(catalog tools.ToolCatalog, opts ...CatalogProjectorOption) (*CatalogProjector, error)

NewCatalogProjector builds the V1 production Projector over a tools.ToolCatalog. The catalog is mandatory — a nil fails loud with ErrMisconfigured. The returned *CatalogProjector is D-025-safe.

func (*CatalogProjector) DescribeTool

func (p *CatalogProjector) DescribeTool(ctx context.Context, id identity.Identity, toolID string) (prototypes.ToolManifest, error)

DescribeTool implements Projector.DescribeTool.

func (*CatalogProjector) GetTool

func (p *CatalogProjector) GetTool(ctx context.Context, id identity.Identity, toolID string) (prototypes.Tool, error)

GetTool implements Projector.GetTool.

func (*CatalogProjector) ListTools

ListTools implements Projector.ListTools.

func (*CatalogProjector) RevokeOAuth

func (p *CatalogProjector) RevokeOAuth(ctx context.Context, id identity.Identity, toolID string) (int64, error)

RevokeOAuth implements OAuthRevoker. When the wired Annotator implements OAuthRevoker the call is delegated; otherwise the projector fails loud with ErrAdminUnsupported — there is no meaningful in-memory fallback for OAuth-binding revocation.

func (*CatalogProjector) SetApprovalPolicy

func (p *CatalogProjector) SetApprovalPolicy(ctx context.Context, id identity.Identity, toolID string, policy prototypes.ToolApprovalPolicy) error

SetApprovalPolicy implements ApprovalPolicySetter. When the wired Annotator implements ApprovalPolicySetter the call is delegated (the persisting path); otherwise the projector records an in-memory override so the change is observable on the next list — never a silent no-op.

func (*CatalogProjector) ToolContentStats

func (p *CatalogProjector) ToolContentStats(ctx context.Context, id identity.Identity, toolID string) (prototypes.ToolContentStats, error)

ToolContentStats implements Projector.ToolContentStats.

func (*CatalogProjector) ToolMetrics

ToolMetrics implements Projector.ToolMetrics.

type CatalogProjectorOption

type CatalogProjectorOption func(*CatalogProjector)

CatalogProjectorOption configures NewCatalogProjector.

func WithAnnotator

func WithAnnotator(a Annotator) CatalogProjectorOption

WithAnnotator wires the per-tool annotation backend. A nil annotator is treated as "WithAnnotator not supplied" — the projector returns conservative defaults.

type OAuthRevoker

type OAuthRevoker interface {
	// RevokeOAuth revokes every OAuth binding for toolID and returns the
	// count revoked. Returns ErrToolNotFound when toolID is unknown.
	RevokeOAuth(ctx context.Context, id identity.Identity, toolID string) (int64, error)
}

OAuthRevoker is the optional admin backend the `tools.revoke_oauth` method requires. A Projector that does not implement it makes the method fail loud with ErrAdminUnsupported.

type Option

type Option func(*Service)

Option configures NewService.

func WithBus

func WithBus(b events.EventBus) Option

WithBus wires the canonical events.EventBus the Service publishes the `audit.admin_scope_used` event onto when an admin method succeeds. A nil bus is treated as "WithBus not supplied" — the admin path still works, but the audit observation is logged at Info instead of published. Production wiring SHOULD supply both WithBus and WithRedactor so the admin audit trail reaches the bus.

func WithLogger

func WithLogger(l *slog.Logger) Option

WithLogger sets the slog.Logger the Service logs admin actions and audit-emit failures to. A nil logger routes to slog.Default().

func WithRedactor

func WithRedactor(r audit.Redactor) Option

WithRedactor wires the audit.Redactor the Service runs the `audit.admin_scope_used` payload through before publishing. A nil redactor is treated as "WithRedactor not supplied". The AdminScopeUsedPayload is a SafePayload by construction; the redactor is wired for defence-in-depth + parity with the Phase 72b emit site.

type Projector

type Projector interface {
	// ListTools returns every catalog row visible to id, sorted by Name.
	// The Service applies the facet filter + pagination on top; the
	// Projector returns the full identity-scoped set.
	ListTools(ctx context.Context, id identity.Identity) ([]prototypes.Tool, error)
	// GetTool returns the catalog row for toolID, or ErrToolNotFound.
	GetTool(ctx context.Context, id identity.Identity, toolID string) (prototypes.Tool, error)
	// DescribeTool returns the full manifest for toolID, or
	// ErrToolNotFound.
	DescribeTool(ctx context.Context, id identity.Identity, toolID string) (prototypes.ToolManifest, error)
	// ToolMetrics returns per-tool error-rate gauges for toolID over the
	// resolved window, or ErrToolNotFound.
	ToolMetrics(ctx context.Context, id identity.Identity, toolID string, window prototypes.ToolMetricsWindow) (prototypes.ToolMetrics, error)
	// ToolContentStats returns the per-tool result-size histogram for
	// toolID, or ErrToolNotFound.
	ToolContentStats(ctx context.Context, id identity.Identity, toolID string) (prototypes.ToolContentStats, error)
}

Projector is the read + mutate seam the Service depends on. The V1 production implementation is CatalogProjector. Every method takes the verified identity triple so the implementation scopes its reads — the Service never trusts a Projector to apply identity itself for the list path, but passes the triple so the implementation CAN scope a per-tenant view.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service implements the seven `tools.*` Protocol methods. It is a D-025-safe compiled artifact — immutable after NewService.

func NewService

func NewService(projector Projector, opts ...Option) (*Service, error)

NewService builds the Tools Protocol service over a Projector. The projector is mandatory — a nil fails loud with ErrMisconfigured rather than building a Service that would nil-panic on the first request (CLAUDE.md §5). The returned *Service is immutable after construction (D-025) and safe for concurrent use by N goroutines.

func (*Service) ContentStats

ContentStats implements the `tools.content_stats` method — the per-tool result-size histogram + negotiated DisplayMode snapshot.

func (*Service) Describe

Describe implements the `tools.describe` method — the full manifest projection.

func (*Service) Get

Get implements the `tools.get` method — a single catalog-row projection.

func (*Service) List

List implements the `tools.list` method. It validates identity, resolves the identity-scoped catalog from the Projector, applies the facet filter + free-text search + pagination, and computes the filtered-view aggregates.

func (*Service) Metrics

Metrics implements the `tools.metrics` method — per-tool error-rate gauges + status pill over the resolved window.

func (*Service) RevokeOAuth

RevokeOAuth implements the `tools.revoke_oauth` ADMIN method. adminScoped is the verified-JWT scope decision; a false value fails closed with ErrAdminScopeRequired (D-079). On success, emits an `audit.admin_scope_used` event.

func (*Service) SetApprovalPolicy

SetApprovalPolicy implements the `tools.set_approval_policy` ADMIN method. adminScoped is the verified-JWT scope decision the wire handler computes; a false value fails closed with ErrAdminScopeRequired (D-079). On success, emits an `audit.admin_scope_used` event.

type ToolsAdminActionPayload

type ToolsAdminActionPayload struct {
	events.SafeSealed
	// Actor is the verified admin identity at the Protocol edge — the
	// (tenant, user, session) triple the JWT carried.
	Actor identity.Identity
	// Method is the Protocol method that carried the admin action
	// (`tools.set_approval_policy` or `tools.revoke_oauth`).
	Method string
	// ToolID is the catalog key of the tool the action mutated.
	ToolID string
}

ToolsAdminActionPayload is the typed SafePayload published on the canonical `audit.admin_scope_used` event when an operator invokes one of the two Tools-page admin methods (`tools.set_approval_policy` / `tools.revoke_oauth`). Phase 73f / D-116.

SafePayload by construction: every field is a bounded identity component, a Protocol method name, or a tool ID — no caller-supplied bytes reach the bus. The Tools wire surface rejects malformed requests at the Protocol edge before the emit.

The payload is distinct from `auth.AdminScopeUsedPayload` (the Phase 72b impersonation shape) and `events.AdminScopeUsedPayload` (the Phase 05 Subscribe shape): all three ride the same canonical `audit.admin_scope_used` event type, but each emit source declares its own typed payload (events.go §"Other emit sites ... MAY add new payload types"). A subscriber type-switches on the payload.

Jump to

Keyboard shortcuts

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