mcp

package
v1.4.0 Latest Latest
Warning

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

Go to latest
Published: Oct 20, 2026 License: Apache-2.0 Imports: 26 Imported by: 0

Documentation

Overview

Brokered system tools of the Agent Runtime MCP endpoint: the sandboxed Agent reaches the Model execution layer and the Memory store through the same MCP surface it uses for tenant tools, with the fenced attempt identity and capability grants injected by the runtime worker. Nothing here trusts agent-supplied identity: the AttemptContext (and its capability grants) come from the IdentityResolver the worker controls.

Package mcp implements the Model Context Protocol edge adapter: the JSON-RPC 2.0 message model and the Streamable HTTP transport boundary the Tool Gateway is exposed through (architecture §15.1: Agent ↔ Tool = MCP). Every input is treated as untrusted: strict JSON, size caps, bounded handling and no session state beyond the request (the pinned 2026-07-28 core has no session).

Per-attempt identity binding for shared Agent endpoints: a runtime worker opens one execution window per assignment and the brokered MCP tools resolve the fenced identity from the execution id the agent echoes in the X-Fenced-Execution header. Concurrent attempts through one endpoint stay correctly fenced, and calls outside any open window deny closed.

Index

Constants

View Source
const (
	SystemModelInvoke  = "fenced.model.invoke"
	SystemMemoryPut    = "fenced.memory.put"
	SystemMemorySearch = "fenced.memory.search"
	SystemTaskSpawn    = "fenced.task.spawn"
	SystemIPCSend      = "fenced.ipc.send"
	SystemIPCReceive   = "fenced.ipc.receive"
	SystemIPCAck       = "fenced.ipc.ack"
)

System tool names exposed alongside the tenant tool registry.

View Source
const ExecutionHeader = "X-Fenced-Execution"

ExecutionHeader carries the agent's execution id (the Runtime Interface executionId, i.e. the attempt id) so a shared endpoint can scope brokered calls to one open execution window.

View Source
const ProtocolVersion = "2026-07-28"

ProtocolVersion is the pinned MCP protocol version this adapter implements (tech baseline §6.3). Negotiation always answers with this version.

Variables

View Source
var ErrNoActiveWindow = errors.New("no active execution window")

ErrNoActiveWindow reports a header-less call with no open window.

View Source
var ErrNoExecutionContext = errors.New("no open execution window for the reported execution id")

ErrNoExecutionContext reports an MCP call whose execution id has no open window (default deny).

Functions

func ExecutionID

func ExecutionID(ctx context.Context) string

ExecutionID returns the execution id of the inbound call, empty when the agent did not report one.

func ParseRequest

func ParseRequest(body []byte) (*Request, *Error)

ParseRequest decodes exactly one strict JSON-RPC message. Batches, trailing documents, unknown fields, duplicate keys and non-2.0 versions are rejected; notifications carry no id.

Types

type AttemptContext

type AttemptContext struct {
	TenantID        string
	TaskID          uuid.UUID
	RunID           uuid.UUID
	AttemptID       uuid.UUID
	FencingToken    int64
	AgentVersionRef string
	// WorkflowID names the workflow run this attempt's task belongs to
	// (empty for standalone tasks); the brokered spawn tool extends that
	// run with dynamic steps.
	WorkflowID uuid.UUID
	// WorkflowVersion is the observed resource version of the workflow run
	// at assignment time.
	WorkflowVersion int64
	// ParentStepName names the workflow step this attempt's task executes
	// (empty for standalone tasks); spawned steps carry it as lineage.
	ParentStepName string
	// AllowedModels is the AgentVersion's declared model capability grant;
	// the brokered model tool denies everything outside it (default deny).
	AllowedModels []string
	// AllowedMemoryNamespaces is the declared memory capability grant; the
	// brokered memory tools deny writes and reads outside it.
	AllowedMemoryNamespaces []string
	// AllowedMemorySensitivities is independent of namespace access; absent
	// grants default to internal only.
	AllowedMemorySensitivities []string
	// CanSpawnTasks is the explicit AgentVersion capability for dynamic
	// workflow expansion. It defaults to false.
	CanSpawnTasks bool
	// AllowedChildAgents is the immutable allowlist of AgentVersion refs the
	// calling version may spawn. An empty list denies every child.
	AllowedChildAgents []string
}

AttemptContext is the fenced execution identity an MCP call is bound to. The adapter never trusts MCP-supplied identity: it comes from the resolver, which in production is injected by the runtime that owns the Attempt.

type Broker

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

Broker extends a ToolAdapter with the system tools: tools/list merges the tenant registry with the brokered declarations, tools/call dispatches the system names and delegates everything else to the tenant adapter.

func NewBroker

func NewBroker(tools *ToolAdapter, models ModelBroker, memories MemoryBroker, spawner WorkflowSpawner, mailbox MailboxBroker, identity IdentityResolver) *Broker

NewBroker builds the brokered MCP surface. models, memory, spawner and mailbox may be nil: the corresponding tools are then not listed and their calls deny closed.

func (*Broker) CallTool

func (b *Broker) CallTool(ctx context.Context, params json.RawMessage) (any, *Error)

CallTool dispatches the system tools and delegates tenant tools.

func (*Broker) ListTools

func (b *Broker) ListTools(ctx context.Context, params json.RawMessage) (any, *Error)

ListTools merges the tenant registry with the brokered system tools.

type Error

type Error struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Data    any    `json:"data,omitempty"`
}

Error is a JSON-RPC 2.0 error object.

func (*Error) Error

func (e *Error) Error() string

type ExecutionIdentityResolver

type ExecutionIdentityResolver interface {
	ResolveExecution(context.Context, string) (AttemptContext, error)
}

ExecutionIdentityResolver resolves the identity of one open execution window from the execution id the agent reports; shared Agent endpoints use it to keep concurrent attempts correctly fenced.

type ExecutionRegistry

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

ExecutionRegistry binds attempt identities to open execution windows. The most recently opened window also serves header-less calls, preserving the single-assignment runtime behavior.

func NewDevelopmentExecutionRegistry

func NewDevelopmentExecutionRegistry() *ExecutionRegistry

NewDevelopmentExecutionRegistry explicitly enables the single-execution convenience fallback. It must only be used by loopback development setups.

func NewExecutionRegistry

func NewExecutionRegistry() *ExecutionRegistry

NewExecutionRegistry builds a production registry. Header-less execution lookup is disabled: every shared-endpoint request must report its execution id and resolve through ResolveExecution.

func (*ExecutionRegistry) Open

func (r *ExecutionRegistry) Open(identity AttemptContext) func()

Open registers the fenced identity for one assignment and returns the closer that must run on every exit path of that assignment.

func (*ExecutionRegistry) Resolve

Resolve implements IdentityResolver for header-less callers: the most recently opened window, denying when none is open.

func (*ExecutionRegistry) ResolveExecution

func (r *ExecutionRegistry) ResolveExecution(_ context.Context, executionID string) (AttemptContext, error)

ResolveExecution implements the header-scoped lookup.

type Handler

type Handler interface {
	// ListTools returns the MCP tool declarations (result object).
	ListTools(context.Context, json.RawMessage) (any, *Error)
	// CallTool executes one tool invocation (result object).
	CallTool(context.Context, json.RawMessage) (any, *Error)
}

Handler implements the MCP methods beyond the core lifecycle. Params are raw JSON for the method; implementations parse them strictly.

type IdentityResolver

type IdentityResolver interface {
	Resolve(context.Context) (AttemptContext, error)
}

IdentityResolver supplies the fenced identity for inbound MCP calls.

type KernelMemoryBroker

type KernelMemoryBroker struct {
	Gateway *memory.Gateway
}

KernelMemoryBroker adapts the kernel memory.Gateway to the broker surface, filling tenant and source provenance from the fenced identity.

func (KernelMemoryBroker) Put

Put writes with tenant and provenance derived from the fenced identity.

func (KernelMemoryBroker) Search

Search reads with the tenant scope of the fenced identity.

type MailboxAckInput

type MailboxAckInput struct {
	MessageIDs []string
}

MailboxAckInput acknowledges a batch the caller has drained.

type MailboxAckOutcome

type MailboxAckOutcome struct {
	Acknowledged        []string
	AlreadyAcknowledged []string
}

MailboxAckOutcome partitions the batch into receipts this call created and receipts an earlier call had already created, so a retry is distinguishable from a first attempt.

type MailboxBroker

MailboxBroker sends, drains and acknowledges durable agent mailboxes with the fenced identity of the calling attempt. As with MemoryBroker the identity is explicit: the tenant, the sender's version reference and the mailbox a drain reads are all derived from the fence, so no agent argument can widen them.

Delivery is pull-only. The platform cannot notify a running agent that mail has arrived — fenced.runtime.v1 exposes pull RPCs and its HeartbeatResponse carries no payload — so an agent discovers mail by draining its own mailbox. A drain may wait for a bounded time, which is a poll, not a push.

type MailboxMessage

type MailboxMessage struct {
	MessageID           string
	FromAgentVersionRef string
	ToAgentVersionRef   string
	ToRunID             string
	Kind                string
	PayloadJSON         json.RawMessage
	AttachmentsJSON     json.RawMessage
	CorrelationID       string
	ReplyToMessageID    string
	SentAt              time.Time
	Deadline            time.Time
}

MailboxMessage is one message as drained. Attachments travel as references: a body above the mailbox payload limit has to be referenced, not inlined.

type MailboxReceiveInput

type MailboxReceiveInput struct {
	// MaxMessages is the batch bound; zero means the server default.
	MaxMessages int32
	// WaitMillis is the bounded wait for an empty mailbox; zero reads once.
	WaitMillis int32
}

type MailboxSendInput

type MailboxSendInput struct {
	// ToAgentVersionRef and ToRunID name the receiving mailbox. The run id,
	// not an attempt id: a mailbox follows the run so a message outlives the
	// attempt that was draining it.
	ToAgentVersionRef string
	ToRunID           string
	Kind              string
	// PayloadJSON is the message body as a JSON document; empty means {}.
	PayloadJSON json.RawMessage
	// IdempotencyKey is chosen by the caller on purpose: retrying a send must
	// resolve to the message the first attempt stored, while two deliberate
	// sends of the same body must remain two messages.
	IdempotencyKey   string
	CorrelationID    string
	ReplyToMessageID string
	// Deadline is optional; the zero time means the message carries none.
	Deadline time.Time
}

MailboxSendInput is one send with the sender taken from the fenced identity.

type MailboxSendOutcome

type MailboxSendOutcome struct {
	MessageID string
	// Replayed reports that the idempotency key resolved to a message an
	// earlier attempt had already stored.
	Replayed   bool
	AcceptedAt time.Time
}

type MemoryBroker

MemoryBroker reads and writes the tenant memory store with fenced provenance. The identity of the calling attempt is explicit: brokers never trust tenant or provenance fields from the agent-supplied arguments.

type ModelBroker

type ModelBroker interface {
	InvokeStream(context.Context, kernelmodel.InvokeInput, func(string)) (kernelmodel.InvokeOutput, error)
}

ModelBroker executes fenced model invocations. The kernel model.Invoker and the runtime-adapter gRPC client both satisfy it.

type Request

type Request struct {
	JSONRPC string          `json:"jsonrpc"`
	ID      json.RawMessage `json:"id,omitempty"`
	Method  string          `json:"method"`
	Params  json.RawMessage `json:"params,omitempty"`
}

Request is the inbound JSON-RPC message. ID is absent for notifications.

func (*Request) IsNotification

func (r *Request) IsNotification() bool

IsNotification reports whether the request carries no id.

type Response

type Response struct {
	JSONRPC string          `json:"jsonrpc"`
	ID      json.RawMessage `json:"id,omitempty"`
	Result  any             `json:"result,omitempty"`
	Error   *Error          `json:"error,omitempty"`
}

Response is the outbound JSON-RPC message.

type Server

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

Server exposes a Handler over the MCP Streamable HTTP transport: POST / with a strict JSON-RPC 2.0 body, answered as application/json or text/event-stream depending on the client's Accept header. Notifications are acknowledged with 204. There is no session state.

func NewServer

func NewServer(name, version string, handler Handler) *Server

NewServer constructs the MCP boundary. name/version identify the server in the initialize handshake. Concurrent in-flight requests are capped (O8); NewServerWithConcurrencyLimit overrides the default.

func NewServerWithConcurrencyLimit

func NewServerWithConcurrencyLimit(name, version string, handler Handler, maxConcurrent int) *Server

NewServerWithConcurrencyLimit constructs the MCP boundary with an explicit cap on concurrent in-flight requests; excess requests answer 503.

func (*Server) ServeHTTP

func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request)

type SpawnOutcome

type SpawnOutcome struct {
	Code       string // "created" | "replayed" | guard denial code
	Message    string
	StepName   string
	SpawnDepth int
}

SpawnOutcome is the authoritative result of one spawn call.

type SpawnRequest

type SpawnRequest struct {
	TenantID        string
	AttemptID       uuid.UUID
	FencingToken    int64
	WorkflowID      uuid.UUID
	ParentStepName  string
	Name            string
	Goal            string
	AgentVersionRef string
	Spec            json.RawMessage
	MaxAttempts     int
	IdempotencyKey  string
	Arguments       json.RawMessage
}

SpawnRequest is one dynamic-step spawn derived from the fenced identity: the workflow lineage (workflow id, version, parent step) comes from the worker-injected execution window, never from agent arguments.

type StaticIdentity

type StaticIdentity struct {
	Context AttemptContext
}

StaticIdentity is a fixed identity for deployments where the attempt context is known at construction (tests and explicitly wired dev setups).

func (StaticIdentity) Resolve

type ToolAdapter

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

ToolAdapter exposes a Tool Gateway as an MCP tools server. Mapping rules (documented, v0.1):

  • MCP tool name is the descriptor name; version and risk ride in the description, and calls resolve the latest granted version. tools/list shows exactly one entry per name (P1-02): the resolved default version, so the model never sees two same-named tools, while an explicit "name@version" reference still pins any granted older version.
  • action defaults to the descriptor's first declared action ("invoke" when none), resource to the first resource pattern ("tool:<name>" when none).
  • the idempotency key is derived deterministically from the attempt, the resolved tool version and the canonical arguments, so identical calls replay instead of repeating an external side effect while different versions of one tool never share replay semantics (at-least-once + idempotent consumer).

func NewToolAdapter

func NewToolAdapter(invoker ToolInvoker, identity IdentityResolver) *ToolAdapter

func (*ToolAdapter) CallTool

func (a *ToolAdapter) CallTool(ctx context.Context, params json.RawMessage) (any, *Error)

CallTool implements tools/call.

func (*ToolAdapter) ListTools

func (a *ToolAdapter) ListTools(ctx context.Context, params json.RawMessage) (any, *Error)

ListTools implements tools/list.

type ToolInvoker

type ToolInvoker interface {
	InvokeTool(context.Context, tool.InvokeInput) (tool.InvokeResult, error)
	ListTools(context.Context, string) ([]store.ToolDescriptor, error)
}

ToolInvoker is the fenced decision chain MCP calls flow through. The tool.Gateway satisfies it.

type WorkflowSpawner

type WorkflowSpawner interface {
	Spawn(context.Context, SpawnRequest) (SpawnOutcome, error)
}

WorkflowSpawner executes dynamic-step spawns through the kernel's guarded transaction. The runtime-adapter gRPC client satisfies it.

Jump to

Keyboard shortcuts

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