Documentation
¶
Overview ¶
Package compositetools provides a MultiSession decorator that adds composite tool (workflow) capabilities to a session.
Index ¶
- func CheckAnnotationContradiction(explicit, floor *vmcp.ToolAnnotations) error
- func CompositeToolNames(defs map[string]*composer.WorkflowDefinition) []string
- func ConvertWorkflowDefsToTools(defs map[string]*composer.WorkflowDefinition, ...) []vmcp.Tool
- func DeriveCompositeAnnotations(stepAnn []*vmcp.ToolAnnotations) *vmcp.ToolAnnotations
- func FilterWorkflowDefsByAnnotations(defs map[string]*composer.WorkflowDefinition, ...) map[string]*composer.WorkflowDefinition
- func FilterWorkflowDefsForSession(defs map[string]*composer.WorkflowDefinition, rt *vmcp.RoutingTable) map[string]*composer.WorkflowDefinition
- func MergeAnnotations(floor, explicit *vmcp.ToolAnnotations) *vmcp.ToolAnnotations
- func NewDecorator(sess sessiontypes.MultiSession, compositeTools []vmcp.Tool, ...) sessiontypes.MultiSession
- func ValidateNoToolConflicts(backendTools []vmcp.Tool, compositeNames []string) error
- type StepAnnotationResolver
- type WorkflowExecutor
- type WorkflowResult
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CheckAnnotationContradiction ¶ added in v0.42.1
func CheckAnnotationContradiction(explicit, floor *vmcp.ToolAnnotations) error
CheckAnnotationContradiction reports whether explicit annotations would make a composite tool look SAFER than its derived safety floor allows:
- readOnlyHint=true while the floor is not true
- destructiveHint=false while the floor is true
- openWorldHint=false while the floor is true
idempotentHint never contradicts, and an explicit hint MORE conservative than the floor (e.g. readOnlyHint=false when the floor is true) is allowed.
DeriveCompositeAnnotations always populates ReadOnlyHint, DestructiveHint, and OpenWorldHint when it returns a non-nil floor, so the nil-hint branches below are defensive (they would only matter for a hand-built floor). A nil floor (no tool steps) or nil explicit means there is nothing to guard and the check returns nil.
func CompositeToolNames ¶ added in v0.42.1
func CompositeToolNames(defs map[string]*composer.WorkflowDefinition) []string
CompositeToolNames returns the names of the composite tools in defs. Order is undefined (map iteration). Used for name-conflict detection so annotation policy cannot drop a colliding name from the conflict check.
func ConvertWorkflowDefsToTools ¶
func ConvertWorkflowDefsToTools( defs map[string]*composer.WorkflowDefinition, stepResolver StepAnnotationResolver, ) []vmcp.Tool
ConvertWorkflowDefsToTools converts workflow definitions to vmcp.Tool format.
This creates the tool metadata (name, description, schema) that gets exposed via the MCP tools/list endpoint. The actual workflow execution logic is handled by the workflow executor adapters created separately.
Each workflow definition becomes a tool with:
- Name: workflow.Name
- Description: workflow.Description
- InputSchema: workflow.Parameters (JSON Schema format)
- OutputSchema: workflow.Output (JSON Schema format, if defined)
- Annotations: the safety floor derived from the step tools' annotations, merged with the workflow's explicit annotations, if any. When a workflow has at least one tool step the floor is always non-nil (fail-closed): backends that declare no annotations produce a conservative floor rather than no floor.
stepResolver may be nil (or return nil for every step), in which case each tool step's annotations are treated as unknown and taint the floor conservatively. When a workflow's explicit annotations contradict the derived safety floor, the composite tool is DROPPED (not advertised) with a warning that names the offending step tool(s) — an explicit declaration must never make a tool look safer than its steps allow.
Returns a slice of vmcp.Tool ready for aggregation and exposure to clients.
func DeriveCompositeAnnotations ¶ added in v0.42.1
func DeriveCompositeAnnotations(stepAnn []*vmcp.ToolAnnotations) *vmcp.ToolAnnotations
DeriveCompositeAnnotations computes the safety-floor annotations for a composite tool from the annotations of its step tools.
Fail-closed contract (issue #6192):
- A workflow with NO tool steps (e.g. only elicitation steps) yields an empty/nil stepAnn slice, and Derive returns nil — there is nothing to derive, so no floor is advertised.
- A workflow with one or more tool steps yields a non-nil stepAnn slice, and Derive ALWAYS returns a conservative floor, even when every step tool's annotations are nil/unknown. An unknown step never makes the tool look safer — it taints destructive/openWorld to true and readOnly to false. This means an explicit readOnlyHint:true over backends that declare no annotations (the common case) CONTRADICTS the floor and is dropped by CheckAnnotationContradiction.
Derivation rules per hint:
- ReadOnlyHint: AND across steps — true only when every step declares readOnlyHint=true; any step that is nil or false makes it non-read-only.
- DestructiveHint: OR across steps — true when any step is nil or declares destructiveHint=true (an unknown step is tainted conservatively).
- OpenWorldHint: OR across steps — true when any step is nil or declares openWorldHint=true.
- IdempotentHint: never derived (always nil) — idempotency does not compose across multi-step workflows.
func FilterWorkflowDefsByAnnotations ¶ added in v0.42.1
func FilterWorkflowDefsByAnnotations( defs map[string]*composer.WorkflowDefinition, stepResolver StepAnnotationResolver, ) map[string]*composer.WorkflowDefinition
FilterWorkflowDefsByAnnotations returns only the workflow definitions whose explicit annotations do not contradict the derived safety floor. Definitions that contradict are omitted (with the same warning resolveCompositeAnnotations already emits). This is the CallTool/ListTools shared gate — callers that execute composites must use this filtered set so a dropped tool is never callable.
func FilterWorkflowDefsForSession ¶
func FilterWorkflowDefsForSession( defs map[string]*composer.WorkflowDefinition, rt *vmcp.RoutingTable, ) map[string]*composer.WorkflowDefinition
FilterWorkflowDefsForSession returns only the workflow definitions whose every tool step references a backend tool that is present in the session routing table.
If a session does not have access to a backend tool (e.g. due to identity-based filtering), any composite tool that depends on that backend tool is also excluded. This prevents a session from invoking a composite tool that would fail at runtime because one or more of its underlying tools are not routable for that session.
func MergeAnnotations ¶ added in v0.42.1
func MergeAnnotations(floor, explicit *vmcp.ToolAnnotations) *vmcp.ToolAnnotations
MergeAnnotations merges explicit annotations over the derived floor. Per hint, an explicit non-nil value wins over the floor; Title behaves the same (a non-empty explicit Title wins). Returns nil when both inputs are nil.
The returned value is a deep copy: *bool pointer fields and the Title string are copied so the advertised tool does not alias the author's config pointers. The inputs are not modified.
func NewDecorator ¶
func NewDecorator( sess sessiontypes.MultiSession, compositeTools []vmcp.Tool, executors map[string]WorkflowExecutor, ) sessiontypes.MultiSession
NewDecorator wraps sess with composite tool support. compositeTools is the metadata list appended to session.Tools(). executors maps each composite tool name to its workflow executor. Both may be nil/empty.
func ValidateNoToolConflicts ¶
ValidateNoToolConflicts validates that composite tool names don't conflict with backend tool names.
Tool name conflicts would cause ambiguity in routing/execution:
- Which tool should be invoked when a client calls the name?
- Should it route to the backend or execute the workflow?
This validation ensures clear separation and prevents runtime confusion. Returns an error listing all conflicting tool names if any conflicts are found.
Prefer CompositeToolNames(defs) for the compositeNames argument so conflict detection never depends on annotation conversion (which can drop tools).
Types ¶
type StepAnnotationResolver ¶ added in v0.42.1
type StepAnnotationResolver func(stepTool string) *vmcp.ToolAnnotations
StepAnnotationResolver resolves a composite-tool step's tool reference ("{workloadID}.{toolName}") to the backend tool's annotations. It returns nil when the step tool is unknown or the backend declares no annotations.
type WorkflowExecutor ¶
type WorkflowExecutor interface {
ExecuteWorkflow(ctx context.Context, params map[string]any) (*WorkflowResult, error)
}
WorkflowExecutor executes a named composite tool workflow.
type WorkflowResult ¶
WorkflowResult holds the output of a workflow execution.