Tool descriptor — --describe
Every Vyges engine can describe itself: run vyges-<engine> --describe and it prints one
JSON object saying what it is, how to call it, what it produces, and what claim its result may
establish about your design.
vyges-drc --describe
One probe is enough to drive an engine you have never seen. That is what lets
vyges mcp hand an AI IDE a typed tool for each engine — real per-parameter
schemas rather than an opaque argument string — and it is equally usable from your own
orchestrator, CI job, or script.
The descriptor is emitted by the binary, so it cannot drift from the code that implements it. If an engine gains a flag, its descriptor gains it in the same build.
The schema field
Every descriptor names the format it speaks:
"schema": "vyges-tool-descriptor/1.1"
Check the major, ignore the minor. The format grows additively, so a reader that understands
1.x can consume a 1.7 payload by ignoring fields it does not recognise; a 2.x payload is the
one that needs attention. This is deliberately not the engine’s release version — engines
release from their own repos, and a newer engine driven by an older caller is fine as long as both
speak the same descriptor format.
A payload with no schema predates the convention. Treat it as an older build rather than a
broken one: read what you recognise and carry on. vyges modules reports this per engine in its
CONTRACT column, where pre means exactly that.
Who this page is for: anyone driving the engines programmatically — flow authors, orchestrators, CI, and agent tooling. If you just want to run an engine by hand,
--helpis the friendlier surface.
A complete descriptor
This is the real output of vyges-em-ir --describe:
{
"schema": "vyges-tool-descriptor/1.1", // the descriptor format this payload speaks
"name": "em-ir",
"summary": "EM / IR-drop power-integrity sign-off (PDN -> report)",
"maturity": "workflow-validated",
"provenance_limitations": [
"The job names the PDN or DEF/LEF and any power and decap maps; input_hash covers the job path and arguments, not their contents."
],
"invocation": {
"args_template": ["run", "{job}"],
"optional": [ { "arg": "out", "flag": "-o" } ],
"emits_json": true
},
"inputs": {
"type": "object",
"required": ["job"],
"properties": {
"job": { "type": "string", "description": "Path to the EM/IR-drop job file (PDN + limits)." },
"out": { "type": "string", "description": "Write output to FILE instead of stdout." }
}
},
"artifacts": [ { "role": "emir_report", "field": "report_path" } ],
"assertion": {
"id": "power-integrity-met",
"field": "pi_met",
"pass_when": { "is_true": true }
},
"consumes": ["pdn", "power_report", "spef", "emgeom"]
}
Given only that, a caller can build the command, validate arguments before spending a run, find the report afterwards, and read a pass/fail verdict — without knowing anything else about power integrity.
Top-level fields
| Field | Required | What it is |
|---|---|---|
name | yes | Stable tool id (drc, sta-si, …). The MCP tool takes this name. |
summary | yes | One line, shown as the tool’s description. |
invocation | yes | How to build the command — see below. |
inputs | no | JSON Schema for the callable parameters. Defaults to an empty object schema. |
artifacts | no | The files the run produces, and how to locate them. |
assertion | no | How to derive the engineering verdict. Omitted → the result is unknown. |
maturity | no | How far the evidence has been proven. Omitted → discovered, which suppresses the verdict. |
provenance_limitations | yes | What input_hash does not cover, in the engine’s own words. |
consumes | no | Input artifact roles the engine expects (e.g. ["netlist","liberty","spef"]). Declarative today — published for flow authors, not interpreted by the CLI. |
A payload without a usable invocation.args_template is not a descriptor; callers should fall
back to passing raw CLI arguments.
invocation — building the command
"invocation": {
"args_template": ["check", "{gds}", "--rules", "{deck}"],
"optional": [ { "arg": "top", "flag": "--top" } ],
"emits_json": true
}
args_template— the argument vector. A{name}token is substituted with the caller’s value forname. Tokens are required: a missing one is a caller error, not a default.optional— appended only when supplied. Withflag, the pair--top TOPis appended; without one, the bare value is appended.emits_json— when true (the default), callers append--jsonif it is not already present, so the engine’s machine-readable output comes back.
For the descriptor above, {job: "block.emir", out: "emir.rpt"} builds:
vyges-em-ir run block.emir -o emir.rpt --json
inputs — the parameter schema
A standard JSON Schema object describing the callable parameters. vyges mcp passes it through
as the MCP tool’s inputSchema, which is why an engine’s parameters appear individually named
and documented in an AI IDE. Use it to validate a call before running anything.
Keep name and inputs in mind as the compatibility surface: changing either changes how every
existing caller must invoke the tool.
artifacts — finding what the run produced
Each entry names a role and says where the path comes from:
"artifacts": [
{ "role": "timing_report", "field": "report_path" }, // path comes from the engine's --json
{ "role": "sdf", "from_arg": "sdf" } // path is the value of an input argument
]
An entry needs a source. role alone says a file exists but not where to find it, so the
registry drops it — the role then has no effect and the file never appears in the result. Pair
every role with a field or a from_arg, and expose the output argument in invocation.optional
so a caller can ask for it in the first place.
Prefer field for anything the engine writes. A sign-off engine given -o FILE reports the
path back as report_path in its --json, so the artifact is located from the result rather
than by echoing an input. That also means the payload still arrives on stdout when -o is used —
-o writes the report, it does not redirect the machine output — so one call yields both the
verdict and the artifact. Use from_arg where the path really is just an input the engine wrote
to and does not echo back (sta-si’s --sdf, for instance).
| Key | Meaning |
|---|---|
role | What the file is — drc_report, timing_report, lvs_report, netlist, svg, … |
field | Key in the engine’s --json output holding the path. |
from_arg | Input-argument name whose value is the path (for -o-style outputs). |
Roles are not just labels. vyges mcp derives a tool’s read-only vs mutating classification
from them — an engine that produces a netlist edits your design, one that produces a
drc_report does not — which is what the VYGES_MCP_PROFILE tiers gate on. See
read-only vs mutating tools.
assertion — the design verdict
An engine run has two independent outcomes: whether the process succeeded, and what the
evidence says about your design. assertion declares how to derive the second from the
engine’s own --json output.
"assertion": {
"id": "drc-clean", // stable name for the claim
"field": "clean", // key in the engine's --json output
"pass_when": { "is_true": true },
"summary_field": "summary" // optional: a human-readable line to surface
}
pass_when | Passes when the field is |
|---|---|
{ "is_true": true } | boolean true |
{ "eq": <value> } | equal to that value (numbers compare numerically) |
{ "lte": <number> } | a number less than or equal to it |
Alternatively { "id": "...", "not_applicable": true } declares that the operation makes no
engineering claim — a viewer or format converter — which is different from having one and not
reaching it.
The derived verdict lands in the loom-result envelope as engineering.status
(pass | fail | unknown | not_applicable), separate from the execution status — see
the result envelope. The rules
are deliberately conservative: no declared assertion, a run that did not complete, or a verdict
field that is missing, null, or the wrong type all yield unknown — never pass, and never
fail. Nothing is reported as passing on absent evidence, and a tooling defect is never
reported as a design defect. See the two status axes.
Declaring one well
The verdict field must cover the whole check. Two traps are worth naming, because both occurred in this suite and both would have overstated the result:
- A partial verdict. Timing has setup and hold; power integrity has IR drop and electromigration. Asserting on a field that covers only one half passes designs that fail the other. Where an engine reports several partial verdicts, assert on a combined field.
- A genuinely inconclusive state. Some checks can finish without reaching a conclusion — a
layout-vs-schematic MATCH the bounded search could not confirm, or a timing run with no
endpoints to analyze. Emit the verdict field as
nullin those cases rather than forcing a boolean;nullresolves tounknown, which is the honest answer.
maturity — how far the evidence has been proven
Separate from what a verdict says is how much it can be relied on. maturity is that second
axis, and it is deliberately about the engine, not the design.
| Level | Means |
|---|---|
discovered | The binary resolves and reports a version. Nothing about its output is guaranteed. |
structured | It publishes a versioned operation and a normalized result — a descriptor like this one. |
workflow-validated | Its repository additionally carries a pinned design or fixture that the test suite runs end to end and asserts against, so the operation is proven on a real input, not merely described. |
It gates the verdict, it is not decoration. At discovered there is no validated result shape
behind an assertion, so the claim is not reported — engineering.status stays unknown however
well-formed the assertion is. Absent or unrecognized means discovered: an engine has to say more
to claim more, the same principle as an absent assertion.
The level also rides along in the generated MCP tool description, so an agent weighing a result sees it without a second call.
Where the Loom engines stand
Published because publishing your own gaps is the point — a ladder nobody is ever on the bottom rung of would say nothing.
| Level | Engines |
|---|---|
workflow-validated | sta-si · em-ir · lvs · extract · thermal · power |
structured | drc · cdc · glitch · lec · char · gds-view · resize · vt-swap · buffer-insert · hold-fix |
The structured engines are not less correct — several carry substantial unit-test suites. They
simply do not yet ship a pinned end-to-end case in-repo, and the ladder reports what is proven
there, not what is believed.
provenance_limitations — the boundary of the hash
Required. Every descriptor must state what input_hash does not cover.
The reason it is required rather than encouraged: the boundary is easy to leave implicit and
expensive to discover later. input_hash is taken over the resolved binary identity, version,
declared environment and the argument vector — not the content of any file those arguments
name, and not anything those files in turn reference. A job file that points at a netlist, a
Liberty and a SPEF contributes only its own path to the hash. Edit the netlist in place and the
hash does not move.
"provenance_limitations": [
"The job names the netlist, Liberty and SPEF; input_hash covers the job path and arguments, not their contents, and Liberty `include` files are not enumerated."
]
The declared strings travel with the evidence they qualify — they appear as
provenance.limitations in every result envelope, beside the input_hash they
are about, so a consumer reads the caveat where it matters rather than having to go looking for it.
Stability
A descriptor carries no version of its own — it is whatever the binary you invoked emits, so
pin the engine version if you need a fixed contract (vyges-<engine> --version).
name and inputs form the compatibility surface: treat a change to either as a breaking change
for callers. New optional fields may appear over time — ignore what you do not recognize, and
never infer a passing verdict from a field you cannot interpret.