npx flameox@latest setup is the supported interactive path for connecting local MCP
clients. The npm package and Python distribution share an exact release
version. npm supplies a thin launcher and the upstream jsonc-parser editor;
the Python application service owns discovery, planning, runtime installation,
verification, locking, activation, rollback, and structured results. The same
setup service is available from flameox setup for standard JSON and TOML
clients.
Keep @latest in this command. An unqualified npx flameox invocation may reuse
an older cached bootstrap and offer stale runtime choices.
npx flameox@latest upgrade is the non-interactive npm shorthand for updating the
detected clients to the bootstrap package's matching Python runtime. It keeps
the same managed-runtime verification and atomic launcher activation as setup.
The first run requires an explicit multi-select. Detection preselects detected
clients for connection, while the user can adjust the selection before applying
it. Every mutation has a complete preview and a confirmation whose default is
no. Non-interactive application requires both an explicit target (--codex,
another client flag, --all, --refresh, --rollback, or --verify) and
--yes; --dry-run --json returns the plan
without mutation.
Interactive setup is also the lifecycle entry point for connecting or disconnecting clients, updating the active runtime, rolling back to an installed version, and verification. Verification checks the managed runtime version and real MCP handshake, then confirms that every configured client still contains the exact active executable and fixed launcher arguments. It is read-only and fails on configuration drift instead of repairing it implicitly.
Client launchers contain the absolute path of a verified managed runtime and
the fixed arguments mcp serve --project-root .. Setup never passes --init or
an external workspace; hosts that need one can add --workspace PATH to an
explicit server invocation.
Each MCP client therefore binds flameox's project root to the client's launch
directory. Client setup is complete when the launcher is verified; each checkout
still requires deliberate project initialization through flameox init,
flameox mcp serve --init, or the MCP initialize_workspace tool after the
client's launch directory has been verified. A fresh server reports
WORKSPACE_NOT_FOUND until that project step is complete.
Setup serializes mutations with a user-local lock. Before applying, it compares every config with the exact bytes used for the preview. It writes a recovery journal before the first config change, uses atomic replacement, and restores all original files after a partial failure or on the next invocation after a crash. Existing comments, unrelated servers, permissions, and formatting are preserved where the native format supports it. Malformed or non-UTF-8 configs are refused rather than replaced.
The CLI and MCP server expose the same application services to different trust contexts. The CLI may offer explicit local administration and destructive recovery operations. MCP exposes bounded, task-shaped operations suitable for an agent and does not expose arbitrary commands, raw SQL, deletion, or sensitive artifact bytes.
The transport does not own domain behavior. Results, validation, cancellation, provenance, and error semantics belong to the shared application layer.
The official MCP Registry name is io.github.morluto/flameox. Its checked-in
server.json points to the version-matched flameox package on PyPI with the
uvx runtime hint, stdio transport, and fixed package arguments
mcp serve --project-root .. PyPI is the canonical registry package because it
contains the maintained MCP implementation. The npm package is a setup
bootstrap and must not be advertised as the stdio server.
The package README carries the registry ownership marker. Release planning
updates the Python, npm, and registry metadata versions together. A release
build validates server.json; after the exact PyPI artifact is visible, the
tag workflow authenticates mcp-publisher with GitHub OIDC and publishes the
same metadata. Registry publication must succeed before the GitHub release is
created.
Registry launch does not imply workspace initialization. The client supplies
its working directory as ., and the server retains the normal explicit
initialize_workspace boundary.
The executable is flameox. Commands produce readable terminal output by
default and structured JSON with --json. CLI JSON is the domain result model;
MCP wraps that same model in its transport envelope.
Common options:
--workspace PATH
--project-root PATH
--json
--quiet
--log-level LEVEL
--timeout SECONDS
Exit codes:
0: successful operation;1: operation completed with a negative result such as failed validation;2: invalid arguments or configuration;3: required capability unavailable;4: capture or external process failed;5: artifact or corpus integrity failure;6: comparison invalid;7: lock or concurrency failure;8: operation cancelled or timed out;9: safety or policy refusal.
flameox init
flameox status
flameox capabilities [--active] [--refresh]
flameox config show
flameox validate
init creates only local files and never installs collectors. capabilities
reports installed tools, versions, supported modes, permissions, and exact
managed setup actions. MCP agents use start_capability_setup with an explicit
idempotency key for FlameOx-managed providers, then poll
get_capability_setup or cancel with cancel_capability_setup. The older
prepare_capabilities entry point remains a compatibility wrapper. The typed
prepare_adapter action handles an installed third-party entry point; none of
these actions runs a workload.
flameox capture plan <adapter> --workload <name> [--parameters '<json>'] [--adapter-options '<json>']
flameox capture run <adapter> --workload <name> [--parameters '<json>'] [--adapter-options '<json>']
flameox import <path> [--kind KIND]
capture plan is side-effect free. capture run prints the plan before
execution unless --json is used, in which case the plan is part of the result.
Both commands require a named workload and reject argv, cwd, and trailing
-- <argv...> arguments. Every import creates a new import run.
flameox workload list
flameox workload show <name>
flameox workload run <name> [parameter overrides]
flameox investigations create <structured-input>
flameox investigations list [filters]
flameox investigations show <investigation-id>
flameox hypotheses record <structured-input>
flameox hypotheses show <hypothesis-id>
flameox experiment plan <name> [parameter overrides]
flameox experiment run <name> [parameter overrides]
flameox experiment show <experiment-id>
flameox experiment trial <trial-id> [--experiment-id <experiment-id>]
flameox.toml is the project-owned workload declaration. Its strict schema is
[workloads.<name>] with an argument array argv, project-relative cwd,
positive timeout_seconds, bounded parameters choices, an environment map,
optional oracle, requirements, writable_paths, and identity. Templates
may reference only plain scalar parameter names such as {size}; shell strings,
shell expansion, and trailing -- <argv...> forms are unsupported.
MCP's configure_workload is the direct agent path for this schema. It accepts
the same typed fields, supports explicit create and replace operations, and
uses the current configuration digest for replacement. It validates the whole
project before atomically updating flameox.toml. The resulting workload is
immediately active and reports configuration_source = "agent"; configuration
never executes the workload. A manually authored, valid workload is also
immediately active. There is no separate workload approval or human-check step.
flameox analyze hotspots <run-or-artifact>
flameox analyze scaling <experiment-or-run-set>
flameox analyze compare <baseline-run-set> <candidate-run-set>
flameox analyze pytorch <run-or-artifact>
flameox analyze accelerator-launches <run-or-artifact> [--compare-to RUN] [--phase PHASE]
flameox analyze memory <run-or-artifact>
flameox analyze execution <run-or-artifact> [--compare RUN]
flameox analyze failures [filters]
flameox analyze record <structured-input>
flameox analyze record-comparison <structured-input>
flameox runs list [filters]
flameox runs show <run-id>
flameox artifacts list [filters]
flameox artifacts show <artifact-id>
flameox findings list [filters]
flameox findings show <finding-id>
flameox findings record <structured-input>
flameox evidence get <typed-reference>
flameox evidence summarize [bounded evidence selections] [--format json|markdown]
flameox measurements query [curated filters]
flameox stacks callers <run-or-artifact> <frame-id>
flameox stacks callees <run-or-artifact> <frame-id>
flameox stacks examples <run-or-artifact> <frame-id>
flameox trace window <artifact-id> --start NS --end NS
flameox open <artifact-id>
Drill-down commands are bounded and use reviewed query families. They report total and returned counts, truncation, coverage, and stable keyset cursors. There is no arbitrary SQL or free-form PerfettoSQL command.
open prints the appropriate installed viewer command by default.
flameox open --launch executes it explicitly. It never launches a browser when
--json is active.
flameox catalog validate
flameox catalog rebuild
flameox recover [--quarantine QUARANTINE_ID]
flameox repair [PLAN.json]
flameox gc [--dry-run | --apply | --purge TRASH_MANIFEST | --restore TRASH_MANIFEST]
Garbage collection is always dry-run by default. Apply may move only eligible staging, generations, caches, and unreferenced artifacts to recoverable trash. It reports exact paths, retention roots, and recoverability before mutation. Purge is a separate destructive action against a specific expired trash manifest.
flameox mcp serve [--init] [--workspace PATH]
flameox mcp inspect [--workspace PATH]
serve uses stdio exclusively. --init performs the additive workspace
initialization before protocol startup and does not configure project workloads.
A network transport is outside the supported product contract.
The server uses the official Python SDK pinned to stable mcp==2.0.0 with the
matching mcp-types==2.0.0 protocol models. Both packages remain exact-pinned
because their wire types are released and consumed as a matched pair.
The supported transport is stdio. The server:
- writes protocol messages only to stdout;
- writes diagnostics to stderr or MCP logging;
- initializes workspace and read services through SDK lifespan state;
- returns an explicit MCP result envelope with concise text and one structured payload;
- reports progress for long captures and analyses;
- propagates cancellation through the execution broker and containment backend;
- closes DuckDB and Perfetto resources on shutdown.
Handlers translate SDK inputs and outputs but contain no diagnostic logic:
from typing import Annotated
from mcp.server import MCPServer
from mcp.server.mcpserver import Context
from mcp_types import CallToolResult, TextContent, ToolAnnotations
server = MCPServer("flameox", lifespan=flameox_lifespan)
@server.tool(
annotations=ToolAnnotations(
read_only_hint=True,
destructive_hint=False,
idempotent_hint=True,
open_world_hint=False,
)
)
async def compare_run_sets(
request: CompareRunSetsRequest,
ctx: Context[AppContext],
) -> Annotated[CallToolResult, ToolPayload[ComparisonResult]]:
services = ctx.request_context.lifespan_context.services
try:
result = await services.comparisons.compare(request)
return _success(result, result.short_summary())
except DomainError as error:
return _failure(error)ToolPayload[T] is an object-root schema with schema_version, ok,
result | null, and error | null. Domain errors and malformed arguments use
isError=true with that structured payload; malformed arguments use the
INVALID_ARGUMENTS code and include the affected fields. Missing tools and
invalid JSON-RPC requests remain protocol errors. The shared helpers return a
JSON-compatible CallToolResult; the v2 SDK validates its structured content
against the annotated ToolPayload[T] once at the tool boundary. Uncaught
domain exceptions are forbidden because their structured detail is lost.
The concrete v2 SDK context, annotations, content blocks, and lifespan types are
confined to flameox.mcp. The server calls synchronous server.run() for stdio.
It does not depend on experimental MCP background-task APIs.
Tools are task-shaped and versioned through their result schemas. Every
registration supplies explicit ToolAnnotations.
- Query, status, and analysis tools set
read_only_hint=True,destructive_hint=False,idempotent_hint=True, andopen_world_hint=False. - Read-only analysis results are deterministic for their pinned inputs and do
not publish evidence.
record_analysisandrecord_comparisonare explicit additive operations that materialize typed provenance. plan_captureandplan_experimentare read-only, idempotent, and closed-world with the same annotation values.- Imports and finding revisions are mutating, additive where applicable,
non-idempotent, and closed-world:
read_only_hint=False,destructive_hint=False,idempotent_hint=False,open_world_hint=False. configure_workloadis mutating, non-executing, idempotent for an identical definition, and closed-world:read_only_hint=False,destructive_hint=False,idempotent_hint=True,open_world_hint=False.- Capture and experiment execution are mutating, non-idempotent, potentially
destructive, and open-world:
read_only_hint=False,destructive_hint=True,idempotent_hint=False,open_world_hint=True, because an uncontained workload may modify files or use the network.
flameox mcp inspect --json returns the exact installed schemas, annotations,
and the server instructions string returned by MCP initialize. The CLI
inspection is additive at schema version 1; in-process and real stdio clients
must observe the same instructions.
The supported tools are grouped as follows:
| Family | Tools |
|---|---|
| Workspace | initialize_workspace, workspace_status, workload_configuration_status, configure_workload, list_capabilities, start_capability_setup, get_capability_setup, cancel_capability_setup, prepare_capabilities, prepare_adapter, prepare_workload_dependencies, validate_workspace |
| Capture and import | plan_capture, execute_capture_plan, import_artifact, extract_benchmark_samples, extract_pyperf, extract_python_startup, extract_pytest, extract_coverage, extract_memray, extract_perfetto, extract_nsight_systems, extract_observations |
| Detached capture | start_detached_capture, get_detached_capture, cancel_detached_capture |
| Discovery | list_declared_workflows, get_declared_workflow, list_runs, list_findings |
| Investigations | create_investigation, list_investigations, get_investigation, record_hypothesis, get_hypothesis |
| Experiments | plan_experiment, run_experiment, get_experiment, freeze_run_set |
| Runs and artifacts | list_runs, get_run, list_artifacts, get_artifact, get_native_viewer_plan |
| Evidence products | summarize_evidence, register_artifact_pipeline, compare_artifact_pipelines |
| Reductions | plan_reduction, execute_reduction, get_reduction |
| Analysis | analyze_hotspots, analyze_scaling, analyze_pytorch, analyze_accelerator_launches, analyze_memory, analyze_execution, analyze_failures, compare_run_sets, record_analysis, record_comparison |
| Drill-down | get_evidence, query_measurements, get_frame_callers, get_frame_callees, get_stack_examples, get_trace_window |
| Findings | record_finding, list_findings, get_finding |
The normal first-run sequence is:
initialize_workspace
→ workload_configuration_status
→ configure_workload (when missing)
→ list_declared_workflows (no arguments lists workloads; use kind='experiment' for experiments)
→ get_declared_workflow
→ list_capabilities
→ start_capability_setup (when setup_adapters is non-empty)
→ prepare_adapter (when setup_third_party_adapters is non-empty)
→ prepare_workload_dependencies (when the workload declares missing Python distributions)
→ list_capabilities
→ plan_capture (preflight_mode='auto')
→ execute_capture_plan
Configuration and execution are separate operations. A configuration result
never starts the declared command, and a capture plan never accepts replacement
argv or cwd values.
plan_capture accepts preflight_mode='auto' and capture_mode='auto' by default. It runs the declared
workload directly and records that no enforced descendant containment was used.
Use capture_mode='managed' only when the project policy requires that stronger
guarantee; use capture_mode='trusted_local' to request the same direct local
execution explicitly. Planning still does not execute the workload;
execute_capture_plan is the explicit execution step.
Additive and idempotent. Initializes only the MCP server's fixed project root,
or the explicit workspace root selected when the server started.
If the server already owns an initialized workspace, the call returns its current
status without replacing the detached-capture manager. It cannot switch to a
different path or configure workloads. Hosts may instead start the server using
flameox mcp serve --init --workspace PATH.
Read-only and bounded. Reports whether the fixed project's flameox.toml is
missing, valid, or invalid, returns the relative path, valid configuration
digest, existing workload names, bounded diagnostics, and a machine-readable
next_tool. Missing configuration points to configure_workload. Invalid
configuration also points to configure_workload: a create operation may
repair it when the complete resulting project configuration parses and validates.
The candidate is checked before the atomic replacement; if it is still invalid,
the original file remains untouched and the error carries typed manual-recovery
context plus workload_configuration_status as its verification tool.
Mutating but non-executing. Accepts a typed name, argv, cwd,
timeout_seconds, parameters, environment, requirements,
writable_paths, identity, and oracle definition. operation="create"
adds a workload or is idempotent for the same definition. operation="replace"
requires the current configuration_id from
workload_configuration_status; a stale digest returns a revision conflict.
The complete resulting ProjectConfig is validated, unrelated workloads,
experiments, and comments are preserved, and flameox.toml is atomically
updated under the workspace lock. The canonical workload is immediately active.
This tool never runs
the command; call list_declared_workflows next, then
get_declared_workflow, list_capabilities, start_capability_setup when
needed, get_capability_setup, list_capabilities again, plan_capture, and finally
execute_capture_plan.
Read-only. Returns workspace identity, validation state, catalog state, storage usage, active captures, and warnings.
Read-only. Returns adapter capabilities, installed versions, required
permissions, unavailable features, remediation, and typed managed setup
actions. setup_adapters is the bounded list of missing providers FlameOx can
install; setup_third_party_adapters identifies installed entry points that
need exact identity approval through prepare_adapter. Call the relevant setup
tool and then call list_capabilities again. The default call performs only
passive inspection.
Active executable probes are separately requested, bounded, and executed
through the subprocess broker; merely listing capabilities does not run a
project-controlled binary found on PATH. The active_cached mode uses a
previously completed probe, while active_refresh explicitly reruns it.
plan_capture uses preflight_mode='auto' by default and performs the bounded
active probes needed by the selected plan.
Each capability also reports provisioning (bundled, managed_runtime,
host, third_party_approval, or unsupported) and setup_verification.
The latter distinguishes setup that is not applicable, pending, passively
observed, or actively verified. Setup results include a bounded verification
receipt naming the adapters checked and those still unavailable, so an agent can
decide whether to refresh discovery before planning.
Mutating but non-executing. This compatibility entry point starts a durable
operation and returns its operation ID instead of holding an MCP request open.
Use start_capability_setup with an explicit idempotency key for new callers,
get_capability_setup to reconnect and poll, and cancel_capability_setup to
request cleanup. The operation accepts only adapter names reported by
list_capabilities as managed setup actions: coverage, memray, perfetto,
py-spy, pytest, and torch.profiler. It installs the published FlameOx
extra into the active managed Python runtime and stages the pinned user-space
Trace Processor under the active workspace's tools/ directory for perfetto.
Setup progress uses validating_request, installing_packages,
staging_trace_processor, verifying, and completed; a staging failure keeps
the staging phase and bounded cause in its durable status. Its receipt includes
the workspace identity, exact request digest, named phase, bounded progress,
item outcomes, cancellation/cleanup state, and next recovery action. It never
runs the declared workload, mutates source, installs arbitrary packages,
changes host permissions, or provisions privileged collectors.
prepare_workload_dependencies follows the same side-effect rule and remains a
short compatibility entry point; its installer runs behind a worker boundary,
and its preflight result names the exact missing distributions and next action.
Mutating but non-executing. Accepts the exact adapter and installed
distribution pair from list_capabilities, hashes the installed distribution
identity, and writes an agent-created approval under the workspace lock. A
version or package-content change invalidates that approval. It does not install
the package, import plugin code, or run a workload; call list_capabilities
again before planning.
Mutating but non-executing. Accepts a declared workload name and installs only
its requirements.python_distributions into the active managed Python runtime.
Requirements may use package-index version specifiers but not direct URLs or
local paths. The result includes an active preflight, identifies installed and
remaining distributions, and points to plan_capture when ready. Missing
executables, permissions, and privileged host capabilities remain explicit
limitations with their bounded fallback adapters.
Workflow discovery is the authoritative source for current workload names,
declared parameters, requirements, and adapter choices. A workflow detail
returns each requirement's kind, required or optional status, and passive or
active probe requirement. It also returns at most 64 deterministic adapter
options with capability status, planning disposition, required preflight mode,
permission status, supported modes and formats, features, limitations, and
remediation. Options that need a permission check are reported as
active_probe_required; discovery remains passive until the caller requests
list_capabilities(mode="active_refresh").
list_declared_workflows accepts an argument-free call. It defaults to
kind="workload"; pass kind="experiment" when the experiment namespace is
needed.
Read-only. Default inputs are a current workload_name, declared scalar
parameter overrides, adapter, and requested features. Returns the exact
resolved plan, expected artifacts, overhead, permissions, limits, containment
state, warnings, request digest, and a short-lived plan_id. The ordinary
schema does not advertise argv or cwd. MCP does not expose ad-hoc capture
planning.
Plan IDs are 256-bit opaque random values held only in MCP-process memory. They are bound to workspace ID, workload definition hash, resolved executable and identity, arguments, working directory, child-environment policy and overrides, adapter/version/capabilities, requested features, validator, source state, limits, containment decision, and policy generation. Expiry uses monotonic time. Plans are atomically single-use, consumed before process creation, bounded in count, invalid after restart, and rejected if any bound input changes. A request digest is audit evidence, not an authorization boundary.
Mutating and command-executing. Executes an unexpired plan_id, streams
progress, observes cancellation, and returns a completed or failed run record.
Every bound identity, capability, workload definition, and policy input is rechecked
immediately before execution. It never accepts a shell string or replacement
arguments.
Python profiler adapters keep the declared workload interpreter and working
directory, including for python -m workloads, while invoking FlameOx's
standalone launcher directly. They do not require FlameOx to be importable from
the workload virtualenv and do not enable PYTHONPATH overrides.
Planning is read-only over a current named workload and experiment definition. Execution runs the predeclared variants and blocks, registers every attempted trial, validates outputs, and returns the experiment plus initial analysis references. The execution receipt links to the immutable experiment protocol, its bounded trial collection, and the first failing trial when one exists; it does not duplicate every trial or oracle receipt.
Trial collections are paged at 1,000 entries. The list_experiment_trials
read-only tool accepts a smaller page size and the returned cursor; the trial
collection resource exposes the first bounded page and its continuation cursor.
When a trial identifier is reused by multiple historical experiments, callers
must provide the experiment ID to resolve it; an unscoped lookup reports the
ambiguity instead of selecting a newer row.
Mutating, additive structured operations. Hypothesis revision requires
expected_revision, an explicit prediction, and a discriminating condition.
Corresponding bounded list/get tools allow agents to resume an investigation
without relying on prior conversation context.
Mutating. Imports a file under allowed roots, computes its identity, validates
the selected or detected kind, creates a new import run, and optionally extracts
evidence. Use kind='execution_trace' for Chrome and Torch profiler traces.
The default producer='auto' recognizes common Torch profiler markers and
preserves producer='torch.profiler' for analysis routing. If a trace is
ambiguous, pass that producer explicitly. An analysis error that requires Torch
evidence points back to this import step with the required kind and producer.
source_root='project' accepts project-local paths, including absolute paths
inside the fixed checkout. source_root='temp' accepts absolute or relative
paths beneath the system temporary directory, which covers profiler output
written outside the checkout without allowing arbitrary filesystem reads.
Read-only, filtered, sorted, and paginated. It cannot return unbounded manifests.
Read-only. Returns one run manifest summary, artifact references, validation,
string limitations, typed limitation_details, and runtime-resource
availability. Resource observations remain bounded and are available through
analyze_memory; host storage paths are not exposed.
Read-only metadata. Returns artifact identity, kind, size, sensitivity,
integrity, supported analyses, and paginated registrations. An optional
run_id selects one contextual registration. It does not return binary
content.
Read-only. Runs the appropriate profile or trace query and returns bounded source-linked hotspots.
Strictly read-only over an existing experiment or frozen run set. It never
executes missing trials. Work requiring collection uses plan_experiment and
run_experiment.
Read-only. Returns compatibility, metric differences, frame/operator changes, validation, attempted/failure counts, estimand, and limitations. Pairwise run IDs are accepted as one-element run-set shorthand.
Mutating and additive. These execute the same curated deterministic recipes as
the read-only tools, then persist an AnalysisRecord, result digest, exact
input corpus commit, typed evidence references, coverage, and limitations.
They do not accept arbitrary SQL or caller-supplied result bodies.
Read-only over a Perfetto-extracted trace. Returns operator and accelerator summaries.
For imported Torch Chrome traces, run extract_perfetto first. If the trace has not been
normalized, the tool returns the exact run_id and recovery action instead of an empty report.
Read-only over normalized trace.event evidence from Perfetto or the maintained Nsight
Systems extractor. It reports direct and graph runtime launches, accelerator kernels,
bounded per-stream idle-gap summaries with device/context/stream identity, and matched
host-to-device correlation coverage. Missing runtime or accelerator tracks produce a
typed partial result. Comparisons are descriptive and do not claim equivalent computation.
Read-only over an existing memory artifact or run.
Read-only over coverage, trace annotations, configuration observations, and SDK observations. Returns bounded source-path evidence and, optionally, differences between two runs.
Mutating. Stores a structured claim with typed evidence references. Updating
requires expected_revision. It validates reference existence, assessment
requirements, and evidence-level consistency.
Read-only, filtered, bounded, and paginated finding retrieval.
Read-only operations retrieve a typed evidence reference, curated measurement query, hotspot callers/callees/representative stacks, or a bounded trace time-window/event neighborhood. They return stable cursors, total/returned counts, coverage, and limitations. They do not accept raw SQL.
Read-only by default. Checks manifests, artifact hashes on request, Parquet schemas, references, and catalog freshness. Repairs require the explicit CLI repair command; MCP never repairs.
Resources provide stable, bounded representations:
flameox://runs/{run_id}
flameox://artifacts/{artifact_id}
flameox://findings/{finding_id}
flameox://investigations/{investigation_id}
flameox://hypotheses/{hypothesis_id}
flameox://experiments/{experiment_id}
flameox://experiments/{experiment_id}/trials
flameox://experiments/{experiment_id}/trials/{trial_id}
flameox://run-sets/{run_set_id}
Resources return JSON or text summaries. Large native artifacts are represented
by metadata and opaque resource references, never host storage paths and never
injected into model context. Template resources declare
mime_type="application/json", percent-encode identifiers, and resolve services
through a server-local lifespan closure. Stable SDK 2.0.0 still passes template
handlers through Pydantic call validation, which reconstructs Context without
its private request state; resource handlers therefore omit Context. Tool
handlers use injected ctx.request_context.lifespan_context. Contract tests
cover both paths so SDK lifecycle changes cannot silently detach resources from
the active workspace.
Mutable workspace and capability views remain tools rather than static
resources because they require current state and explicit recovery semantics.
Tool results should include MCP ResourceLink blocks for
addressable runs, artifacts, findings, analyses, and comparisons when useful.
Investigation recipes belong in executable domain logic and tool descriptions, not MCP prompt templates. Prompts can be added only when they express a stable human-facing workflow that cannot be represented by structured tools.
Long operations report named phases:
planning
warming_up
capturing
validating_artifact
extracting
publishing
analyzing
completed
Progress is one monotonic stream for the entire request; it never resets at a
phase boundary. A default operation uses fixed phase work units, for example
0/8 planning through 8/8 completed. Measurable sub-work may occupy a fixed
interval. Unknown-duration work reports phase transitions and uses MCP logging
for elapsed time rather than inventing percentages. Reporting is best-effort:
ctx.report_progress() is a no-op when the client supplied no progress token.
Detached operations persist the same phase transitions and retain only a bounded
progress history. Percentage values are monotonic when work units are known;
unknown-duration phases report a phase with completed and total omitted.
Operations using the shared lifecycle record their exact request digest,
workspace identity, idempotency digest, item-level outcomes, cancellation and
cleanup state, and terminal receipt before they are reported complete.
The original idempotency key always reconnects to the same operation, including
after a restart. A retryable failure or cancellation therefore advertises a
retry_new_operation recovery with a fresh key; reusing the original key never
starts duplicate side effects.
| Operation family | Side effect | Expected duration | Recovery |
|---|---|---|---|
| Workspace/configuration reads | none | bounded | reread the pinned workspace on stale state |
| Capability setup | package install or tool staging | long | poll status; reconnect with the same key, or use the receipt's fresh key for a new retry |
| Workload dependency setup | declared package install | bounded to long | retry the exact workload name and inspect preflight |
| Import and extraction | copy, external parser, or publication | bounded to long | identify the affected run and required extractor |
| Capture and experiments | external process and/or multiple trials | long | use the durable operation/run identity and poll |
| Analysis and measurement queries | read-only catalog access | bounded | inspect the evidence status and next tool |
| Full validation and reductions | large reads or generated evidence | long | poll the terminal receipt for the declared scope |
An empty array is not evidence of a complete negative result. Analysis and
query results carry an evidence object with status available, empty,
unavailable, partial, or unknown, a stable reason, and exact next-tool
arguments when recovery is possible.
An incoming MCP cancellation cancels the handler's AnyIO scope. Cleanup must therefore be explicitly shielded and bounded:
try:
return await execution.execute_capture_plan(plan)
except anyio.get_cancelled_exc_class():
with anyio.CancelScope(shield=True):
with anyio.fail_after(cleanup_timeout):
await execution.cancel_containment(plan.run_id)
await runs.publish_cancelled(plan.run_id)
raiseCleanup sends a graceful signal, waits a bounded interval, terminates the entire containment unit or process tree, retains complete validated artifacts, quarantines incomplete artifacts, publishes terminal lifecycle revisions, and releases staging resources and locks. It then re-raises cancellation. Startup recovery handles process death before shielded cleanup completes. Cancellation must not leave an active lease indefinitely.
Every tool returns the same object-root transport envelope. For an expected
domain failure, ok=false, result=null, error contains:
{
"code": "CAPABILITY_UNAVAILABLE",
"message": "Native stack capture is unavailable on this platform.",
"retryable": false,
"details": {},
"remediation": [
"Install py-spy or select Python-only stack capture."
],
"run_id": null
}Structured error codes include:
WORKSPACE_NOT_FOUND;WORKSPACE_INVALID;CAPABILITY_UNAVAILABLE;INVALID_CAPTURE_PLAN;EXECUTION_REFUSED;PROCESS_FAILED;PROCESS_TIMEOUT;PROCESS_CANCELLED;ARTIFACT_TOO_LARGE;ARTIFACT_INTEGRITY_FAILED;ARTIFACT_PARSE_FAILED;EVIDENCE_SCHEMA_MISMATCH;COMPARISON_INVALID;QUERY_BUDGET_EXCEEDED;STORAGE_QUOTA_EXCEEDED;WRITE_LOCK_TIMEOUT;SENSITIVE_ARTIFACT_REFUSED;REVISION_CONFLICT;STALE_CURSOR;INTERNAL_ERROR.
Tracebacks are logged locally but not returned by default.