Agent-facing MCP query layer over najaeda: navigate elaborated SystemVerilog designs without loading source into context.
naja-scope demonstrates strong schema and parameter discipline across 18 EDA-domain tools. All tools have complete input schemas with typed, described parameters. Descriptions are domain-specific and actionable (80-300 chars average), explicitly stating prerequisites and side effects. Tool naming follows verb_object conventions (load_*, get_*, resolve, find, trace_*). The server properly uses ToolAnnotations (READ_ONLY, SESSION_MUTATION, SESSION_REPLACEMENT, FILESYSTEM_WRITE, SESSION_RESET, ARBITRARY_PYTHON) to communicate safety properties. However, there are gaps: (1) output schemas are not explicitly documented in the source (inferred from descriptions only), (2) error recovery guidance is sparse, tools return ScopeError but the response format and 'did-you-mean' behavior exist at runtime but are not visible in static definitions, (3) some parameter descriptions are domain-jargon-heavy without enough context for an LLM unfamiliar with EDA (e.g., 'lowered objects', 'SNL↔slang link', 'equipotential_size' appear without beginner explanation). The tool set is well-composed, each tool does one thing; tool outputs (paths with source refs) chain to downstream tool inputs (path parameters). Idempotency is good for read-only tools and snapshot/reset operations. The server properly avoids exposing secrets and uses path-based resource access rather than opaque IDs.
Find design objects matching a case-sensitive glob pattern and return their paths with source references. The final segment of the pattern may include `*` and `?` wildcards; include a dot to match full hierarchical paths. This read-only query requires a loaded design.
Return which sequential or combinational cell outputs drive a net or port. The object may be a port (e.g. `top.tx_o`), a net (e.g. `top.u_uart.tx_ff_q`), or a pin (e.g. `top.u_uart.u_flop.Q`). Returns the driving cell model and the source location of the module definition, or an error and did-you-mean suggestions on unknown path.
Describe the instance tree at and under a hierarchical path, with source location for each module definition. Omit path to start at the design root; depth=0 is tree leaves, depth=1 is children, etc. Stops traversing at blackbox instances. Use resolve to look up an unknown path.
Query the warm intent layer: the live SNL↔slang link that walks the in-engine AST to answer high-level questions about a SystemVerilog design. Requires that the design was loaded with intent=True. The layer is a thin client over najaeda's in-engine SNL↔slang link (naja.intent_*), which walks the live slang AST in C++ and returns plain data. There is no second elaboration, and the only requirement is a najaeda build that retains the link (keep_ast_link).
Output schemas not documented in source code. Return types are inferred from descriptions but not formally specified in schema documents. For tools like get_hierarchy, get_module_card, get_stats, the structured output format is not visible in the tool definitions, only in docstrings. This forces LLMs to reason about return structure from prose rather than machine-readable schemas.
Domain-specific jargon without beginner context. Terms like 'lowered objects', 'SNL↔slang link', 'equipotential_size', 'naja.intent_*', 'slang AST' are used in descriptions but not explained for agents unfamiliar with EDA. An LLM seeing 'Anonymous lowered objects are addressable by #<id>' has no guidance on what a 'lowered object' is or why it matters.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | B | 74 | 2026-07-28+ | v2 |
Return which combinational or sequential cell inputs a net or port feeds. The object may be a port, net, or cell pin. Includes an equipotential_size count of all loads on the same net (crossing lowered assign glue). Returns the loads in a paginated list, or an error and did-you-mean suggestions on unknown path.
Return a summary card for a module: ports (with types, widths, and source locations), instantiation count, direct child modules, instance model counts by type, and leaf cell counts by model.
Return the SystemVerilog or Verilog source text snippet associated with an instance, module definition, port, net, or cell, and its file and line range. Only works for objects elaborated from source (RTL or synthesis source). Gate-level netlists and blackboxes have no source.
Return global design statistics: all module definitions in the hierarchy, per-module flattened gate and register counts, and global leaf cell counts by model. Use this to understand design size and cell usage across the entire hierarchy.
Register standard-cell models from local Liberty `.lib` files in the active session. Use this before load_verilog when a gate netlist instantiates those cells; use load_primitives instead for the built-in Xilinx or Yosys model sets. This changes session state and returns `{"ok": true}`.
Register primitive models in the active session from either a built-in `name` (`xilinx` or `yosys`) or one local Python `file` defining `load(db)`. Provide one source; `name` takes precedence when both are set. A custom file executes unsandboxed Python in the server process, so only use trusted code. Use load_liberty instead for standard-cell `.lib` files. Returns `{"ok": true}`.
Load a compatible save_snapshot directory into the active session in seconds instead of re-elaborating. Use load_systemverilog/load_verilog when no compatible snapshot exists. The directory must match this najaeda version. intent=True also re-elaborates the warm intent layer from the flist saved in the snapshot (for get_intent).
Elaborate local SystemVerilog sources into the active design session. Use this for RTL; use load_verilog with load_liberty/load_primitives for a structural gate netlist. Requires at least `files` or `flist` and changes the in-memory design session. Anonymous lowered objects are addressable by #<id>. defines are preprocessor -D entries ("NAME" or "NAME=VALUE"). allow_unknown_designs=True blackboxes any module still undefined instead of failing (e.g. undelivered hard macros in a partly-open-source design). intent=True retains naja's in-engine SNL↔slang link for get_intent.
Load local gate-level or structural Verilog into the active session. First call load_liberty for Liberty cells or load_primitives for built-ins; use load_systemverilog instead for RTL elaboration. Unknown modules fail unless `allow_unknown_designs` is true. Gate netlists carry no source info, so get_source/get_intent cannot answer for them.
Discard the active design and all in-memory session state. Use before starting an unrelated design; do not use merely to inspect status. This is destructive to the current session but does not delete source or snapshots. Repeating it is safe and returns `{"ok": true}`.
Resolve a known hierarchical object path to instance, term, or net descriptors with source references. The final segment accepts a glob and bit selects (for example `top.u_uart.tx_o[0]`). Use find when the path is unknown; use get_hierarchy to browse children. This read-only query requires a loaded design and returns did-you-mean suggestions on failure.
Write the active design and source metadata to a local directory for fast reload. Use after loading a design; load_snapshot reads the result. Existing snapshot files in the directory may be overwritten. Snapshots are tied to their producing najaeda version, and returns include the saved path.
Inspect the current in-memory session without changing it. Use this before design queries to confirm a design is loaded and whether get_intent is live (`intent_loaded`) or can be reloaded (`intent_loadable`). Returns `loaded` and, when available, the top summary and loaded source files.
Trace a combinational or register-cutting logic cone from a net or port, returning all nodes, a frontier of sequential cells or blackboxes, and cross-hierarchy statistics. When direction is 'fanin', walk backward to find what drives the object; 'fanout' walks forward to find what it drives. Stops at sequential cells (flops) and blackboxes, reporting them in the frontier. Use this to answer "what logic is upstream?" or "what logic is downstream?"
Error recovery guidance not visible in static definitions. Tool descriptions mention 'did-you-mean suggestions' and 'ScopeError' responses, but the exact error format, recovery action, and what to do when a path is ambiguous is not documented in the tool schema, only in the implementation (errors.py not shown). LLMs cannot plan error recovery without seeing the error structure.
Parameter interdependencies not fully documented. load_systemverilog accepts both 'files' and 'flist' as optional, but requires at least one. The constraint 'at least one of files or flist' is stated in the tool description but not in parameter annotations. Similarly, load_primitives states 'name takes precedence when both are set' but the mutual relationship could be clearer.
Limit parameters lack explicit bounds in some tools. Tools like trace_cone accept max_frontier (defaults 1000, capped 10000) and resolve/find accept limit (defaults 20, capped 200), but these bounds are not documented as parameter constraints in the schema, only in descriptions. LLMs cannot easily parse 'capped at 200' from prose.