Hirð
This server is an MCP interface over the Hirð compiler, letting LLM agents query .hird source files for types, effects, actor protocols, and token-budget-aware context.
infer_type: get the type and effect row of an expression at a specific line/column in a .hird file.
lookup_definition: find a top-level definition's source location, type, doc comment, and kind.
explain_effect_row: see a function's canonical effect row plus a human-readable explanation of each effect.
render_ir_fragment: get the typed IR of a top-level definition as JSON.
explain_actor_protocol: inspect an actor's message constructors, state type, init, handler signatures, and effect summary.
emit_actor_effect_graph: see reachable actors, supervisor relationships, and transitive tool effects from a root actor.
get_context_for_symbol: get a token-budget-aware summary of a symbol, including kind, signature, effect row, doc, callers, and callees.
get_context_budget: estimate token costs of including a file's types, effects, actors, supervisors, tools, and function signatures in an LLM context.
It returns structured errors (e.g., undefined names list available ones), so agents can self-correct from tool output alone.
Hirð

A hirð is a Norse king's household guard: sworn retainers, each with a named duty, answerable to one lord. Illustration by Erik Werenskiold for Magnús Erlingsson's saga in Snorri's Heimskringla (public domain, via Wikimedia Commons).
A typed language for long-running agent systems on BEAM: effect-row tracking, auditable tool effects, typed actors, and OTP supervision. Python agent frameworks hide side effects in coroutine soup; Hirð makes every tool call, every actor message, and every supervisor boundary visible in the types and queryable by tooling.
What that buys: deterministic replay of real agent traffic. Every
tool call is recorded unconditionally in a canonical wire format, so a
recorded run is a file you can replay — the same calls, in the same
order, each served the result the recorded run got back, with no service
contacted. That is a regression test with no oracle to maintain, a bug
report that reproduces, and a fixed environment to evaluate a change in.
It is also not something you can retrofit onto a framework that hides
its side effects: it needs the effects in the types and a single
dispatch path underneath them. hird demo is that claim in one command:
it records a run of the demo planner, replays that one recording against
three variants of the program, and prints where each parted from it.
And systems that stand. A Hirð program is not a script that exits:
fn main can start a supervision tree and stand, leaving typed actors
serving after its own work is done — driving their own periodic rounds
off a clock capability, crashing and restarting under a declared budget,
every round on the audit stream. hird run demo/agent_fleet is that
claim running: a hirð of three retainers that keeps working through a
deliberate crash.
Status: pre-1.0 and experimental. The v0.1 compiler pipeline works
end to end (the demos below type-check, compile to Erlang, and run on
BEAM), but the language surface is unstable, nothing is published to
crates.io, and breaking changes land without deprecation cycles. The
roadmap lives in the in-repo issue tracker (see .beads/README.md).
Install
Prebuilt binaries for Linux, macOS, and Windows are attached to every
release: extract the
archive for your platform and put hird (the compiler), hird-lsp, and
hird-mcp on your PATH.
From source, with Rust 1.97 or newer:
cargo install --git https://github.com/no-materials/hird hird-cli
cargo install --git https://github.com/no-materials/hird hird-lsp # optional
cargo install --git https://github.com/no-materials/hird hird-mcp # optionalWith Nix, the same three binaries are flake outputs
(nix run github:no-materials/hird#hird-mcp).
Compiling and running programs needs Erlang/OTP on PATH
(apt install erlang, brew install erlang, …); hird check works
without it.
Related MCP server: N3MO
Quick start
Hirð has no ambient print. Anything a program tells the outside world
goes through a tool — a declared, typed, audited external operation —
so the smallest observable program is a tool call. Save this as
hello.hird:
module Hello
tool Say : { message: String } → ()
fn quiet_say(args: { message: String }) → () = ()
fn main() → () ! {} =
handle {
Tool<Say> → quiet_say,
} in say({ message: "hello, world" })hird run hello.hird{"schema_version":1,"tool":"Say","args":{"message":"hello, world"},"result":{"ok":null},"timestamp":"…","caller":"Hello.main"}Three things happened. Declaring tool Say created the effect
Tool<Say> and a callable say. The handle block supplied an
implementation and discharged that effect, so main is honestly ! {}.
And the call was recorded on the audit stream — unconditionally, because
mocked and real tool calls audit identically. ASCII operator spellings
(->) normalise to their Unicode forms (→) at lex time, so either is
legal input.
Command | What it does |
| type- and effect-check; coded diagnostics |
| emit readable Erlang, compile it to |
| build, then execute |
| record one run of the built-in demo, replay it against variants of the program |
| the typed IR of every definition |
| actors, mailboxes, handler rows, supervisors, tools |
docs/writing-hird-human.md is the guided
tour, and phrasebook.md the dense syntax reference.
The flagship demo: a standing hirð of agents
A hirð is retainers with named duties; demo/agent_fleet/ is the
metaphor made literal. Three supervised actors serve for as long as the
program stands: a Planner ticks itself on a clock and forges each
round's order (pure planning imported from a second module — the source
spans a real use boundary), an Executor carries the order out
through Tool<RunErrand> and reports onward, an Auditor chronicles
every outcome through Tool<Chronicle>. Round 3 crashes the executor
on purpose: FleetSup restarts rest_for_one, so the auditor —
downstream of the crash — restarts with it, the planner keeps its round
counter, and the rounds keep coming. Actor state dies with its process;
the audit stream is the durable record.
hird run demo/agent_fleet{"schema_version":1,"tool":"RunErrand","args":{"errand":"mend the palisade","round":2},"result":{"ok":"done"},"timestamp":"…","caller":"Executor.handle_msg/Carry"}
{"schema_version":1,"tool":"Chronicle","args":{"note":"done","round":2},"result":{"ok":null},"timestamp":"…","caller":"Auditor.handle_msg/Record"}
{"schema_version":1,"tool":"Log","args":{"level":"info","message":"executor takes its post"},"result":{"ok":null},"timestamp":"…","caller":"Executor.init"}
{"schema_version":1,"tool":"Log","args":{"level":"info","message":"auditor takes its post"},"result":{"ok":null},"timestamp":"…","caller":"Auditor.init"}
{"schema_version":1,"tool":"RunErrand","args":{"errand":"scout the border","round":4},"result":{"ok":"done"},"timestamp":"…","caller":"Executor.handle_msg/Carry"}Round 3 never beats — the crash consumed its order — and the two re-posted inits are the supervisor's work, visible in the same stream as everything else. The tree itself is queryable; its effect graph is the system's live org chart, every retainer with its duty and its effects:
hird emit-effect-graph demo/agent_fleetRecord and replay a run
demo/agent_planner.hird drives one planning round against a supervised
Planner: repository state in through Tool<ReadRepo>, pure analysis,
tickets out through Tool<CreateTicket>, progress through Tool<Log>.
Every tool invocation — mocked or real — lands on the audit stream, one
canonical JSON line per call:
{"schema_version":1,"tool":"CreateTicket","args":{"body":"The parser has no fuzz harness.","title":"Fuzz the parser"},"result":{"ok":{"ctor":"TicketId","args":["Fuzz the parser"]}},"timestamp":"2026-07-28T06:44:42.893Z","caller":"AgentPlanner.file_tickets"}Because the stream is complete — every call, full arguments, tagged result — a recorded run is a replayable environment:
hird run demo/agent_planner.hird --audit-file run.jsonl # record
hird run demo/agent_planner.hird --replay run.jsonl # replayThe replay cursor outranks every handle and install block, so no
tool runs and no service is contacted; each call receives its logged
result, failures included. Matching is strict: the call at each position
must be the one the log recorded there, or the run crashes with a
replay_divergence naming the position, the recorded call and the
offered one — and a log the run did not read to the end fails too.
So a checked-in recording is a regression test with no oracle to
maintain: demo/agent_planner.golden.jsonl is one run of the planner,
replayed by the demo suite in CI, and the build fails the moment the
program's decisions drift from it. And because the log serves every
result, one recording is a fixed environment to compare variants of a
program in — every arm meets a byte-identical world, so what differs is
attributable to the programs:
baseline agreed with all 7 calls
announce-first parted at call 2 (tool_mismatch)
eager parted at call 4 (args_mismatch)That evaluation is hird demo: no arguments, nothing to install beyond
Erlang, and nothing checked in that it has to be trusted about — it
writes the planner and the two edited variants into _build/hird-demo,
records the episode itself, and replays it against all three.
docs/audit-evidence.md states what the
stream guarantees and what it does not;
docs/tool-effects.md is the normative format
and replay specification.
LLM tooling (MCP)
hird-mcp is a Model Context Protocol server over the same compiler
pipeline, speaking stdio. It gives LLM agents structured compiler
queries instead of source-reading guesswork: infer_type,
lookup_definition, explain_effect_row, render_ir_fragment,
explain_actor_protocol, emit_actor_effect_graph,
get_context_for_symbol (token-budget-aware symbol summaries), and
get_context_budget. Errors come back structured — undefined names
list the available ones, parse and type errors carry coded
diagnostics — so agents can self-correct from tool output alone.
The repository ships a project-scoped .mcp.json, so Claude Code
sessions started here pick the server up automatically (it launches
nix run .#hird-mcp; run nix build .#hird-mcp once so the first
session start doesn't wait on a cold build). Any other MCP client can
launch the hird-mcp binary directly, with no arguments.
Things worth asking an agent wired to it:
"What does the Planner actor in demo/agent_planner.hird do? Ask the compiler instead of reading the source."
"If the Executor in demo/agent_fleet crashes mid-round, who restarts it, who restarts with it, and what's the restart budget?"
"Give me a 50-token summary of the Planner actor. Now 400 tokens. What got dropped?"
"Write a new Hirð module with a supervised actor, and iterate with the hird tools until they confirm it's clean."
demo/counter_demo.hird is that last prompt's output: a supervised
counter written by an LLM agent that verified itself against the MCP
tools alone — it type-checks and runs on BEAM unmodified. And
demo/heartbeat.hird is the smallest standing program: one actor, one
clock, one beat a second until Ctrl-C. docs/writing-hird-llm.md is
the agent-facing guide.
Editor support
hird-lsp is a Language Server Protocol server over the compiler front
end, speaking stdio: diagnostics on open and save, hover with inferred
types and effect rows, go-to-definition for top-level declarations.
Point any LSP client at the binary, with no arguments.
tree-sitter-hird/ is a tree-sitter grammar for the v0.1 surface, with
highlight, indent, and fold queries, built by the flake as a package
output. docs/editor-setup.md has client
configuration (including Neovim, with and without nix), the grammar
development loop, and the v0.1 limitations.
Repository layout
crates/— the Rust compiler workspace (lexer, parser, checker, IR, codegen, CLI, LSP and MCP servers).tree-sitter-hird/— the tree-sitter grammar and editor queries.runtime/— the hand-written Erlang runtime support library (tool dispatch, audit sink, handler registry).demo/— the v0.1 demo programs.conformance/— golden files for the audit-log wire format.docs/— normative specifications (grammar, error model, tool effects wire format), the audit stream's guarantees, and editor setup.phrasebook.md— dense surface-syntax reference.DECISIONS.md— architecture decision records..beads/README.md— the issue tracker and roadmap, driven bybd.
Development
MSRV is Rust 1.97 (edition 2024). Before sending changes:
cargo fmt --all
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-featuresBEAM-dependent tests skip themselves when erlc is not on PATH.
CONTRIBUTING.md has the rest: the dev shell, the
checks CI runs beyond those three, what "done" means, and how to report
a bug or file an issue from outside the repository.
License
Licensed under either of
Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Available Tools
8 toolsemit_actor_effect_graphEmit actor effect graphARead-onlyIdempotent
Emit the actor/effect graph rooted at one actor: every actor reachable through Send, Await, Spawn, and Schedule effects (matched by message type), every supervisor of an included actor together with its whole child set, and every tool an included actor's effect summary names. Use it for 'what does this actor transitively do or depend on'; effect rows are per-process, so no single signature shows this. Use explain_actor_protocol for one actor's own interface and get_context_budget to gauge the size before requesting it. Returns schema_version (1), module, root, and the included actors, supervisors, and tools, whose nodes share the shape of hird emit-effect-graph --json; declarations the root does not reach are omitted. Read-only: compiles the file's directory in memory (cached until a sibling changes) and writes or executes nothing. Failures are isError results with a stable error.code: file_not_found, read_error, invalid_params, parse_error, or check_error, the last two carrying coded diagnostics in error.data.diagnostics.
| Name | Required | Description | Default |
|---|---|---|---|
| file | Yes | Path to a .hird source file, absolute or relative to the server's working directory. Every .hird file in its directory is compiled with it as one program, so imported names resolve; answers may name a sibling file. | |
| actor_name | Yes | The root actor's name, as declared in this file. Actors are not resolved through imports; an unknown name fails with `not_found` and `error.data.available_actors`. |
Output Schema
| Name | Required | Description |
|---|---|---|
| root | Yes | |
| tools | Yes | |
| actors | Yes | |
| module | Yes | |
| supervisors | Yes | |
| schema_version | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already provide readOnlyHint=true, idempotentHint=true, destructiveHint=false, and the description goes well beyond them: it discloses in-memory directory compilation with caching until a sibling changes, states it 'writes or executes nothing', and enumerates the stable error.code values plus the diagnostics structure on failures. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long (roughly 150 words) but densely packed — scope, usage rationale, alternatives, return shape, read-only behavior, and error codes all in distinct clauses with no redundancy. Purpose is front-loaded. Slightly long, but every sentence earns its place given the tool's complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a graph-returning tool with compilation, caching, and multi-mode failures, the description covers scope definition, usage context, sibling routing, return shape (schema_version, module, root, actors/supervisors/tools), and the full error contract. An output schema exists, so return values need not be re-explained, and nothing an agent needs to call this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% — both file and actor_name are fully documented in the schema (path resolution, import behavior, not_found error with available_actors). The description adds little parameter-specific meaning beyond the schema, so the baseline of 3 applies. The only mild addition is the note that declarations the root does not reach are omitted, which concerns output rather than parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Emit the actor/effect graph rooted at one actor') and precisely enumerates the graph's contents (reachable actors via Send/Await/Spawn/Schedule, supervisors with child sets, named tools). Clearly differentiates itself from siblings by naming explain_actor_protocol and get_context_budget as the alternatives for narrower/sizing queries.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states the intended use case ('what does this actor transitively do or depend on'), explains why it is needed ('effect rows are per-process, so no single signature shows this'), and names two concrete alternatives with their selection conditions. This is exactly the when/when-not guidance the dimension calls for.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
explain_actor_protocolExplain actor protocolARead-onlyIdempotent
Describe one actor's protocol: its message type and constructors, state type, init parameters and effects, per-handler effect rows, and the declared effect summary. Use it to learn how to talk to an actor or what each handler may do; use emit_actor_effect_graph for the actors, supervisors, and tools it reaches transitively, and lookup_definition if you only need its location. Returns actor with name, line, state, message (name, constructors), init (params, effects), handlers (message, effects), and effects; types and rows carry a display string. Read-only: compiles the file's directory in memory (cached until a sibling changes) and writes or executes nothing. Failures are isError results with a stable error.code: file_not_found, read_error, invalid_params, parse_error, or check_error, the last two carrying coded diagnostics in error.data.diagnostics.
| Name | Required | Description | Default |
|---|---|---|---|
| file | Yes | Path to a .hird source file, absolute or relative to the server's working directory. Every .hird file in its directory is compiled with it as one program, so imported names resolve; answers may name a sibling file. | |
| actor_name | Yes | The actor's name, as declared in this file. Actors are not resolved through imports; an unknown name fails with `not_found` and `error.data.available_actors`. |
Output Schema
| Name | Required | Description |
|---|---|---|
| file | Yes | |
| actor | Yes | |
| module | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover readOnly/idempotent/destructive hints, so the bar is lower; the description nonetheless adds genuine context: in-memory compilation of the whole directory, caching until a sibling changes, writes/executes nothing, and a stable error.code set with diagnostics in error.data. One defect: the error-code list omits `not_found`, which the input schema cites for unknown actor names, creating a small internal inconsistency. No contradiction with the annotations themselves.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads purpose, then usage routing, then return shape, then error behavior — a logical order with every sentence earning its place. It is dense rather than padded, though the return-shape enumeration is somewhat long; acceptable given the tool's complexity and the presence of an output schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given an output schema exists (so return values are structurally defined), the description goes beyond by summarizing the returned `actor` shape and error codes. Both required parameters are fully documented, alternatives are routed, and the read-only/caching behavior is disclosed. The only gap is the `not_found` inconsistency, slightly muddying the error-handling story.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% — both file and actor_name are thoroughly documented, including the path-resolution rules and the not_found failure with error.data.available_actors. The description adds no parameter-level meaning beyond this, so the high-coverage baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Describe one actor's protocol') tied to a precise resource, and enumerates exactly what the explanation covers: message type, constructors, state type, init params/effects, per-handler effects, and the declared effect summary. It also names sibling tools (emit_actor_effect_graph, lookup_definition) it is not, so an agent can disambiguate without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use ('learn how to talk to an actor or what each handler may do') and then names the two alternatives with the conditions that select them — emit_actor_effect_graph for transitive reachability, lookup_definition for location-only lookups. No inference required.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
explain_effect_rowExplain effect rowARead-onlyIdempotent
Explain a function's effect row: the canonical row plus a one-sentence explanation of each effect. Use it for 'what may this function do' or to interpret an unfamiliar effect. It covers one named function; use infer_type for an arbitrary expression's row and emit_actor_effect_graph for what an actor transitively does. Returns type, effect_row, open (the row ends in a row variable, so it may carry more effects than listed), pure (closed and empty), and effects, each with effect and explanation. A name of non-function type is not_a_function; an unknown name is not_found with the available names in error.data.available. Read-only: compiles the file's directory in memory (cached until a sibling changes) and writes or executes nothing. Failures are isError results with a stable error.code: file_not_found, read_error, invalid_params, parse_error, or check_error, the last two carrying coded diagnostics in error.data.diagnostics.
| Name | Required | Description | Default |
|---|---|---|---|
| file | Yes | Path to a .hird source file, absolute or relative to the server's working directory. Every .hird file in its directory is compiled with it as one program, so imported names resolve; answers may name a sibling file. | |
| fn_name | Yes | The function's name, as the file's source would write it: a local definition, a member imported with `use Mod.{name}`, or `Qualifier.name` through `use Mod`. |
Output Schema
| Name | Required | Description |
|---|---|---|
| file | Yes | |
| name | Yes | |
| open | Yes | |
| pure | Yes | |
| type | Yes | |
| effects | Yes | |
| effect_row | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover readOnlyHint=true, idempotentHint=true, and destructiveHint=false. The description adds value beyond those: it discloses in-memory directory compilation with caching until a sibling changes, states it writes/executes nothing, explains the open/pure return semantics, and enumerates stable error codes with the diagnostic payload location. No contradiction with annotations; substantial additive context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense (~150 words) but every sentence carries distinct information: purpose, usage, return shape, error handling, read-only behavior, and error codes. It is front-loaded with purpose and usage before details. Slightly long but justified given the tool's error-handling and return complexity; no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite an output schema being present, the description still explains the semantic meaning of return fields (open, pure, effects with explanations) and error contract. It covers purpose, when-to-use, alternatives, behavior, and failure modes exhaustively. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and both parameters (file path, fn_name) are well documented in the schema, so baseline is 3. The description adds error-handling semantics for fn_name beyond the schema: a non-function name yields not_a_function, an unknown name yields not_found with available names in error.data.available. This enriches parameter behavior meaningfully, earning a 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Explain a function's effect row: the canonical row plus a one-sentence explanation of each effect.' It distinguishes itself from siblings by explicitly naming infer_type (arbitrary expression's row) and emit_actor_effect_graph (transitive actor effects), so an agent can tell them apart without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use guidance: 'Use it for "what may this function do" or to interpret an unfamiliar effect.' It also states what it covers ('one named function') and names the alternatives with their selection criteria (infer_type for arbitrary expressions, emit_actor_effect_graph for transitive actor effects), leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_context_budgetEstimate context budgetARead-onlyIdempotent
Estimate the token cost of loading a file's declarations into an LLM context window, per category: types, effects, actors, supervisors, tools, and function signatures. Use it before pulling a module in wholesale, to choose between get_context_for_symbol calls and a full read, or to pick a budget; it names no individual symbols. Returns approx_tokens with types, effects, actors, supervisors, tools, functions, and total, estimated at ~4 characters per token from one-line signatures, and a note restating that. Fails only when the file is unreadable or has parse or type errors. Read-only: compiles the file's directory in memory (cached until a sibling changes) and writes or executes nothing. Failures are isError results with a stable error.code: file_not_found, read_error, invalid_params, parse_error, or check_error, the last two carrying coded diagnostics in error.data.diagnostics.
| Name | Required | Description | Default |
|---|---|---|---|
| file | Yes | Path to a .hird source file, absolute or relative to the server's working directory. Every .hird file in its directory is compiled with it as one program, so imported names resolve; answers may name a sibling file. |
Output Schema
| Name | Required | Description |
|---|---|---|
| file | Yes | |
| note | Yes | |
| module | Yes | |
| approx_tokens | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and destructiveHint, but the description adds substantial context: it compiles the directory in memory (cached until a sibling changes), writes or executes nothing, and details failure modes with stable error codes and diagnostics. This goes well beyond the annotation profile.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but well-structured, starting with the core purpose, then usage guidance, then return shape, then failure modes. Each sentence adds essential information with no filler. The use of lists for categories and error codes keeps it scannable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter read-only tool with a rich output schema (implied) and thorough annotations, the description covers everything an agent needs: purpose, usage, estimation method, caching behavior, failure modes, and error codes. There is no missing piece that would prevent correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema covers the 'file' parameter at 100% with a clear description. The tool description adds extra meaning by explaining that every .hird file in the directory is compiled as one program, which affects how imported names resolve and can make answers reference sibling files. This is value beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb ('Estimate the token cost') and a precise resource ('loading a file's declarations into an LLM context window'), then enumerates the exact categories returned. It explicitly distinguishes itself from the sibling get_context_for_symbol by stating it names no individual symbols, so an agent can immediately tell them apart.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit usage scenarios: before pulling a module wholesale, to choose between get_context_for_symbol calls and a full read, or to pick a budget. It also names the alternative tool and implies the condition for choosing it, providing clear routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_context_for_symbolSummarize symbol for contextARead-onlyIdempotent
Summarize one symbol for an LLM prompt within an approximate token budget: signature, effect row, doc comment, callers, and callees, added in that order while they fit. Use it as the default way to bring a symbol into context; use lookup_definition for just the location, explain_effect_row for effect explanations, and render_ir_fragment for the full body. Returns kind, the budget applied, summary (prompt-ready text), approx_tokens (its estimated cost at ~4 characters per token), and omitted (the sections that did not fit). The signature is always present, truncated when the budget is smaller than it. An unknown name is not_found with the available names in error.data.available. Read-only: compiles the file's directory in memory (cached until a sibling changes) and writes or executes nothing. Failures are isError results with a stable error.code: file_not_found, read_error, invalid_params, parse_error, or check_error, the last two carrying coded diagnostics in error.data.diagnostics.
| Name | Required | Description | Default |
|---|---|---|---|
| file | Yes | Path to a .hird source file, absolute or relative to the server's working directory. Every .hird file in its directory is compiled with it as one program, so imported names resolve; answers may name a sibling file. | |
| name | Yes | The symbol's name, as the file's source would write it: a local definition, a member imported with `use Mod.{name}`, or `Qualifier.name` through `use Mod`. | |
| budget | Yes | Approximate token budget for the summary, at ~4 characters per token (default 400). A section that does not fit is dropped whole and named in `omitted`; only the signature is truncated. |
Output Schema
| Name | Required | Description |
|---|---|---|
| file | Yes | |
| kind | Yes | |
| budget | Yes | |
| symbol | Yes | |
| omitted | Yes | |
| summary | Yes | |
| approx_tokens | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations (readOnlyHint, idempotentHint, destructiveHint), the description adds substantial behavioral context: the in-memory compile with caching 'until a sibling changes', the claim that it 'writes or executes nothing', the exact failure taxonomy with stable error.code values (file_not_found, read_error, invalid_params, parse_error, check_error), the not_found behavior with available names, and the truncation/dropping semantics. This is far beyond what annotations alone convey, and it is consistent with them.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long (~180 words) but every sentence earns its place: purpose, sibling routing, return shape, truncation edge case, unknown-name behavior, safety/caching, and error codes. It is front-loaded with the core function and progressively covers edge cases, with no redundant or filler phrasing.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 3 required params, 7 siblings, an output schema, and multiple failure modes, the description covers every dimension an agent needs: what it does, when to choose it, what it returns, how budget behaves at the boundary, unknown-name handling, and all error codes with their payloads. Nothing an agent needs to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema documents all three parameters thoroughly; the description adds only marginal value on top, mostly reinforcing budget semantics (~4 chars/token, signature always present) and the not_found name resolution behavior. That is a small bonus over the baseline of 3, but the schema carries the heavy lifting for parameter meaning.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource: 'Summarize one symbol for an LLM prompt within an approximate token budget', then enumerates exactly what is included and in what order (signature, effect row, doc comment, callers, callees). It explicitly differentiates from siblings by naming lookup_definition, explain_effect_row, and render_ir_fragment and what each is for, so an agent can distinguish this tool without opening the sibling schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage guidance is explicit and prescriptive: 'Use it as the default way to bring a symbol into context', followed by three concrete alternatives with the condition that selects each (just location, effect explanations, full body). This leaves nothing to inference and is the strongest possible usage guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
infer_typeInfer expression typeARead-onlyIdempotent
Infer the type and effect row of the expression at a source location (1-based line and character column) in a .hird file. Use it for 'what is the type here', including local bindings, sub-expressions, and names inside use lists; for a named top-level definition prefer lookup_definition, and for a function's effects with explanations prefer explain_effect_row. Returns token (the source token found at the location), type (the normalized type as Hirð prints it), and effect_row (the row of a function-typed expression, {} otherwise). A location outside the file is invalid_params; one with no typed expression is not_found. Read-only: compiles the file's directory in memory (cached until a sibling changes) and writes or executes nothing. Failures are isError results with a stable error.code: file_not_found, read_error, invalid_params, parse_error, or check_error, the last two carrying coded diagnostics in error.data.diagnostics.
| Name | Required | Description | Default |
|---|---|---|---|
| file | Yes | Path to a .hird source file, absolute or relative to the server's working directory. Every .hird file in its directory is compiled with it as one program, so imported names resolve; answers may name a sibling file. | |
| line | Yes | 1-based source line of the expression. | |
| column | Yes | 1-based character (not byte) column. Any position inside the expression's token works; when several tokens touch it an identifier is preferred. |
Output Schema
| Name | Required | Description |
|---|---|---|
| file | Yes | |
| line | Yes | |
| type | Yes | |
| token | Yes | |
| column | Yes | |
| effect_row | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and idempotentHint, but the description goes further by disclosing that it compiles the file's directory in memory (cached until a sibling changes) and writes/executes nothing. It also details failure modes as isError results with stable error.code values and lists the codes, providing rich behavioral context beyond annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but efficiently organized: purpose first, then usage, then returns, then read-only note, then error handling. Every sentence carries operational value; there is no fluff or repetition. For a tool with this complexity, the length is justified and the structure aids scanning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, the description correctly focuses on behavior rather than return structure. It covers purpose, usage, return fields (token, type, effect_row), error codes, and the compilation/caching model. An agent has everything needed to invoke the tool correctly and interpret results.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has 100% coverage, describing file, line, and column with details like coordinate bases, character vs byte columns, and token preference. The description adds no additional parameter meaning beyond what the schema already states—it only references return values and error handling. Baseline 3 is appropriate given full schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (infer) and resource (type and effect row of an expression at a source location in a .hird file), and explicitly differentiates from siblings by naming lookup_definition and explain_effect_row as alternatives for different cases. The return values are also listed, making the tool's function unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use guidance ('what is the type here' for local bindings, sub-expressions, use-list names) and when-not-to-use with named alternatives (lookup_definition for top-level defs, explain_effect_row for explained effects). This is direct routing with no inference required.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
lookup_definitionLook up definitionARead-onlyIdempotent
Look up a top-level definition by name: defining file and line, kind, type, and doc comment. Use it first to locate or identify a symbol; use get_context_for_symbol when you also want effects, callers, and callees, explain_effect_row to interpret a function's effects, and render_ir_fragment for its body. Returns kind (function, type, constructor, effect, tool, tool_function, actor, message_type, message_constructor, supervisor, or extern), line, and nullable type and doc; file is the sibling module when the name is imported. An unknown name is not_found, with every name in the file's scope in error.data.available. Read-only: compiles the file's directory in memory (cached until a sibling changes) and writes or executes nothing. Failures are isError results with a stable error.code: file_not_found, read_error, invalid_params, parse_error, or check_error, the last two carrying coded diagnostics in error.data.diagnostics.
| Name | Required | Description | Default |
|---|---|---|---|
| file | Yes | Path to a .hird source file, absolute or relative to the server's working directory. Every .hird file in its directory is compiled with it as one program, so imported names resolve; answers may name a sibling file. | |
| name | Yes | The definition's name, as the file's source would write it: a local definition, a member imported with `use Mod.{name}`, or `Qualifier.name` through `use Mod`. |
Output Schema
| Name | Required | Description |
|---|---|---|
| doc | No | |
| file | Yes | |
| kind | Yes | |
| line | Yes | |
| name | Yes | |
| type | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false. The description adds genuine value beyond that: the in-memory directory compilation with caching behavior ("cached until a sibling changes"), the explicit "writes or executes nothing" confirmation, the not_found contract with error.data.available, and the stable error.code taxonomy with coded diagnostics. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded in the first sentence, alternatives follow, then return format, then error behavior, then read-only confirmation — a logical progression where every sentence earns its place. It is long, but the length is justified by the error taxonomy and routing guidance that an agent genuinely needs. Only minor trimming would be possible.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a lookup tool with an output schema, nothing needed is missing: purpose, alternatives, return kinds, nullable fields, import behavior, not_found handling, error codes, and safety profile are all covered. An agent has everything required to call it correctly and interpret failures.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% — both file and name are fully documented in the input schema, so baseline 3 applies. The description adds minimal param-level detail; the only extra is the return-side note that file is the sibling module when imported, which touches the parameter but is primarily return semantics. Adequate, not additive.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise verb+resource — "Look up a top-level definition by name: defining file and line, kind, type, and doc comment." It enumerates the returned fields and explicitly names three siblings with the conditions that select them (get_context_for_symbol, explain_effect_row, render_ir_fragment), so an agent can distinguish it from every sibling without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit routing: "Use it first to locate or identify a symbol" and then names alternatives with their differentiators — get_context_for_symbol for effects/callers/callees, explain_effect_row for interpreting effects, render_ir_fragment for the body. This is textbook when-to-use-vs-alternatives guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
render_ir_fragmentRender IR fragmentARead-onlyIdempotent
Render the typed intermediate representation (IR) of one top-level definition as JSON. Use it when the exact lowered structure matters: desugared bodies, the resolved type on every node, a tool's generated function. The IR is verbose and follows the compiler's declaration serialization, so for a human-oriented view prefer get_context_for_symbol or lookup_definition. Returns module, name, and ir (the serialized declaration). An unknown name is not_found with the available names in error.data.available. Read-only: compiles the file's directory in memory (cached until a sibling changes) and writes or executes nothing. Failures are isError results with a stable error.code: file_not_found, read_error, invalid_params, parse_error, or check_error, the last two carrying coded diagnostics in error.data.diagnostics.
| Name | Required | Description | Default |
|---|---|---|---|
| file | Yes | Path to a .hird source file, absolute or relative to the server's working directory. Every .hird file in its directory is compiled with it as one program, so imported names resolve; answers may name a sibling file. | |
| name | Yes | The definition's name, as the file's source would write it: a local definition, a member imported with `use Mod.{name}`, or `Qualifier.name` through `use Mod`. |
Output Schema
| Name | Required | Description |
|---|---|---|
| ir | Yes | The declaration, as the compiler serializes its IR. |
| file | Yes | |
| name | Yes | |
| module | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, destructiveHint=false, and idempotentHint=true. The description goes further by explaining the in-memory compilation and caching ('compiles the file's directory in memory (cached until a sibling changes) and writes or executes nothing') and by specifying error codes and diagnostics. No contradiction; it enriches beyond annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and well-structured, with purpose, usage, return format, and error handling presented in logical order. It is longer than minimal but each sentence earns its place; the only slight inefficiency is bundling multiple error codes into one run-on sentence.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (compiling a file directory, multiple error codes, return structure), the description covers everything an agent needs: purpose, when to use, return fields, error semantics, and read-only guarantees. The output schema exists but the description does not need to explain return values; it is complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and both parameter descriptions in the schema are already detailed. The tool description adds no additional parameter-level information beyond what the schema provides, so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb-resource pair ('Render the typed intermediate representation (IR) of one top-level definition as JSON') and explicitly contrasts with sibling tools (get_context_for_symbol, lookup_definition) by naming when the IR view is preferable. It distinguishes itself without ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use criteria ('when the exact lowered structure matters: desugared bodies, the resolved type on every node, a tool's generated function') and directs the agent to alternatives for human-oriented views. This is textbook usage guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
8 tool updates
- Changed
emit_actor_effect_graph4 fields changed- changed
Input schema / properties / actor_name / descriptionPrevious value: -"The root actor's name."New value: +"The root actor's name, as declared in this file. Actors are not resolved through imports; an unknown name fails with `not_found` and `error.data.available_actors`." - changed
Input schema / properties / file / descriptionPrevious value: -"Path to a .hird source file. Its directory's .hird files are compiled together as one program, so imported names resolve."New value: +"Path to a .hird source file, absolute or relative to the server's working directory. Every .hird file in its directory is compiled with it as one program, so imported names resolve; answers may name a sibling file." - changed
Input schema / requiredPrevious value: -[ - "file", - "actor_name" -]New value: +[ + "actor_name", + "file" +] - changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "actors": { + "items": { + "type": "object" + }, + "type": "array" + }, + "module": { + "type": "string" + }, + "root": { + "type": "string" + }, + "schema_version": { + "type": "integer" + }, + "supervisors": { + "items": { + "type": "object" + }, + "type": "array" + }, + "tools": { + "items": { + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "actors", + "module", + "root", + "schema_version", + "supervisors", + "tools" + ], + "type": "object" +}
- Changed
explain_actor_protocol4 fields changed- changed
Input schema / properties / actor_name / descriptionPrevious value: -"The actor's name."New value: +"The actor's name, as declared in this file. Actors are not resolved through imports; an unknown name fails with `not_found` and `error.data.available_actors`." - changed
Input schema / properties / file / descriptionPrevious value: -"Path to a .hird source file. Its directory's .hird files are compiled together as one program, so imported names resolve."New value: +"Path to a .hird source file, absolute or relative to the server's working directory. Every .hird file in its directory is compiled with it as one program, so imported names resolve; answers may name a sibling file." - changed
Input schema / requiredPrevious value: -[ - "file", - "actor_name" -]New value: +[ + "actor_name", + "file" +] - changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "actor": { + "properties": { + "effects": { + "description": "An effect row: `effects` (each with `head`, `args`, `display`), an optional `tail` row variable, and its `display` string.", + "type": "object" + }, + "handlers": { + "type": "array" + }, + "init": { + "type": "object" + }, + "line": { + "type": "integer" + }, + "message": { + "type": "object" + }, + "name": { + "type": "string" + }, + "state": { + "description": "A type, with its `display` string as Hirð prints it.", + "type": "object" + } + }, + "required": [ + "name", + "line", + "state", + "message", + "init", + "handlers", + "effects" + ], + "type": "object" + }, + "file": { + "type": "string" + }, + "module": { + "type": "string" + } + }, + "required": [ + "actor", + "file", + "module" + ], + "type": "object" +}
- Changed
explain_effect_row2 fields changed- changed
Input schema / properties / file / descriptionPrevious value: -"Path to a .hird source file. Its directory's .hird files are compiled together as one program, so imported names resolve."New value: +"Path to a .hird source file, absolute or relative to the server's working directory. Every .hird file in its directory is compiled with it as one program, so imported names resolve; answers may name a sibling file." - changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "effect_row": { + "type": "string" + }, + "effects": { + "items": { + "properties": { + "effect": { + "type": "string" + }, + "explanation": { + "type": "string" + } + }, + "required": [ + "effect", + "explanation" + ], + "type": "object" + }, + "type": "array" + }, + "file": { + "type": "string" + }, + "name": { + "type": "string" + }, + "open": { + "type": "boolean" + }, + "pure": { + "type": "boolean" + }, + "type": { + "type": "string" + } + }, + "required": [ + "effect_row", + "effects", + "file", + "name", + "open", + "pure", + "type" + ], + "type": "object" +}
- Changed
get_context_budget2 fields changed- changed
Input schema / properties / file / descriptionPrevious value: -"Path to a .hird source file. Its directory's .hird files are compiled together as one program, so imported names resolve."New value: +"Path to a .hird source file, absolute or relative to the server's working directory. Every .hird file in its directory is compiled with it as one program, so imported names resolve; answers may name a sibling file." - changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "approx_tokens": { + "properties": { + "actors": { + "type": "integer" + }, + "effects": { + "type": "integer" + }, + "functions": { + "type": "integer" + }, + "supervisors": { + "type": "integer" + }, + "tools": { + "type": "integer" + }, + "total": { + "type": "integer" + }, + "types": { + "type": "integer" + } + }, + "required": [ + "types", + "effects", + "actors", + "supervisors", + "tools", + "functions", + "total" + ], + "type": "object" + }, + "file": { + "type": "string" + }, + "module": { + "type": "string" + }, + "note": { + "type": "string" + } + }, + "required": [ + "approx_tokens", + "file", + "module", + "note" + ], + "type": "object" +}
- Changed
get_context_for_symbol4 fields changed- changed
Input schema / properties / budget / descriptionPrevious value: -"Approximate token budget for the summary (default 400)."New value: +"Approximate token budget for the summary, at ~4 characters per token (default 400). A section that does not fit is dropped whole and named in `omitted`; only the signature is truncated." - changed
Input schema / properties / file / descriptionPrevious value: -"Path to a .hird source file. Its directory's .hird files are compiled together as one program, so imported names resolve."New value: +"Path to a .hird source file, absolute or relative to the server's working directory. Every .hird file in its directory is compiled with it as one program, so imported names resolve; answers may name a sibling file." - changed
Input schema / requiredPrevious value: -[ - "file", - "name" -]New value: +[ + "budget", + "file", + "name" +] - changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "approx_tokens": { + "type": "integer" + }, + "budget": { + "type": "integer" + }, + "file": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "omitted": { + "items": { + "type": "string" + }, + "type": "array" + }, + "summary": { + "type": "string" + }, + "symbol": { + "type": "string" + } + }, + "required": [ + "approx_tokens", + "budget", + "file", + "kind", + "omitted", + "summary", + "symbol" + ], + "type": "object" +}
- Changed
infer_type5 fields changed- changed
Input schema / properties / column / descriptionPrevious value: -"1-based character column."New value: +"1-based character (not byte) column. Any position inside the expression's token works; when several tokens touch it an identifier is preferred." - changed
Input schema / properties / file / descriptionPrevious value: -"Path to a .hird source file. Its directory's .hird files are compiled together as one program, so imported names resolve."New value: +"Path to a .hird source file, absolute or relative to the server's working directory. Every .hird file in its directory is compiled with it as one program, so imported names resolve; answers may name a sibling file." - changed
Input schema / properties / line / descriptionPrevious value: -"1-based source line."New value: +"1-based source line of the expression." - changed
Input schema / requiredPrevious value: -[ - "file", - "line", - "column" -]New value: +[ + "column", + "file", + "line" +] - changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "column": { + "type": "integer" + }, + "effect_row": { + "type": "string" + }, + "file": { + "type": "string" + }, + "line": { + "type": "integer" + }, + "token": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "column", + "effect_row", + "file", + "line", + "token", + "type" + ], + "type": "object" +}
- Changed
lookup_definition2 fields changed- changed
Input schema / properties / file / descriptionPrevious value: -"Path to a .hird source file. Its directory's .hird files are compiled together as one program, so imported names resolve."New value: +"Path to a .hird source file, absolute or relative to the server's working directory. Every .hird file in its directory is compiled with it as one program, so imported names resolve; answers may name a sibling file." - changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "doc": { + "type": [ + "string", + "null" + ] + }, + "file": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "line": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "type": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "file", + "kind", + "line", + "name" + ], + "type": "object" +}
- Changed
render_ir_fragment2 fields changed- changed
Input schema / properties / file / descriptionPrevious value: -"Path to a .hird source file. Its directory's .hird files are compiled together as one program, so imported names resolve."New value: +"Path to a .hird source file, absolute or relative to the server's working directory. Every .hird file in its directory is compiled with it as one program, so imported names resolve; answers may name a sibling file." - changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "file": { + "type": "string" + }, + "ir": { + "description": "The declaration, as the compiler serializes its IR.", + "type": "object" + }, + "module": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "file", + "ir", + "module", + "name" + ], + "type": "object" +}
8 tool updates
v0.1.1- Changed
emit_actor_effect_graph1 field changed- changed
Input schema / properties / file / descriptionPrevious value: -"Path to a .hird source file."New value: +"Path to a .hird source file. Its directory's .hird files are compiled together as one program, so imported names resolve."
- Changed
explain_actor_protocol1 field changed- changed
Input schema / properties / file / descriptionPrevious value: -"Path to a .hird source file."New value: +"Path to a .hird source file. Its directory's .hird files are compiled together as one program, so imported names resolve."
- Changed
explain_effect_row2 fields changed- changed
Input schema / properties / file / descriptionPrevious value: -"Path to a .hird source file."New value: +"Path to a .hird source file. Its directory's .hird files are compiled together as one program, so imported names resolve." - changed
Input schema / properties / fn_name / descriptionPrevious value: -"The function's name."New value: +"The function's name, as the file's source would write it: a local definition, a member imported with `use Mod.{name}`, or `Qualifier.name` through `use Mod`."
- Changed
get_context_budget1 field changed- changed
Input schema / properties / file / descriptionPrevious value: -"Path to a .hird source file."New value: +"Path to a .hird source file. Its directory's .hird files are compiled together as one program, so imported names resolve."
- Changed
get_context_for_symbol2 fields changed- changed
Input schema / properties / file / descriptionPrevious value: -"Path to a .hird source file."New value: +"Path to a .hird source file. Its directory's .hird files are compiled together as one program, so imported names resolve." - changed
Input schema / properties / name / descriptionPrevious value: -"The symbol's name."New value: +"The symbol's name, as the file's source would write it: a local definition, a member imported with `use Mod.{name}`, or `Qualifier.name` through `use Mod`."
- Changed
infer_type1 field changed- changed
Input schema / properties / file / descriptionPrevious value: -"Path to a .hird source file."New value: +"Path to a .hird source file. Its directory's .hird files are compiled together as one program, so imported names resolve."
- Changed
lookup_definition2 fields changed- changed
Input schema / properties / file / descriptionPrevious value: -"Path to a .hird source file."New value: +"Path to a .hird source file. Its directory's .hird files are compiled together as one program, so imported names resolve." - changed
Input schema / properties / name / descriptionPrevious value: -"The definition's name."New value: +"The definition's name, as the file's source would write it: a local definition, a member imported with `use Mod.{name}`, or `Qualifier.name` through `use Mod`."
- Changed
render_ir_fragment2 fields changed- changed
Input schema / properties / file / descriptionPrevious value: -"Path to a .hird source file."New value: +"Path to a .hird source file. Its directory's .hird files are compiled together as one program, so imported names resolve." - changed
Input schema / properties / name / descriptionPrevious value: -"The definition's name."New value: +"The definition's name, as the file's source would write it: a local definition, a member imported with `use Mod.{name}`, or `Qualifier.name` through `use Mod`."
8 tool updates
v0.1.0- First observed
emit_actor_effect_graph - First observed
explain_actor_protocol - First observed
explain_effect_row - First observed
get_context_budget - First observed
get_context_for_symbol - First observed
infer_type - First observed
lookup_definition - First observed
render_ir_fragment
TDQS
Each tool targets a distinct concern: type inference at a location, top-level definitions, effect explanations, IR rendering, actor protocol, transitive effect graph, context summarization, and token budgeting. Overlaps are explicitly cross-referenced (e.g., infer_type vs explain_effect_row) and boundaries are clear.
All tool names follow a consistent verb_noun pattern with descriptive verbs (infer, lookup, explain, render, emit, get). Naming is uniform and predictable, making it easy to infer tool behavior from the name.
8 tools is well-scoped for a language analysis server. Each tool serves a distinct purpose and none are redundant or superfluous, covering the core workflows of exploring types, definitions, effects, actors, and context.
The tool set provides comprehensive read-only coverage for understanding a Hirð program: locating definitions, inferring types, explaining effects, inspecting IR, analyzing actor protocols and transitive dependencies, and estimating context usage. No obvious gaps for the intended domain.
Maintenance
Related MCP Connectors
Ground-truth code graph for your codebase: exact callers, callees, symbols & dependencies.
Ask a codebase what calls what: search, blast radius, paths between symbols, and diffs.
Deterministic context layer for your codebase: change impact, blast radius, answers with receipts.
Code intelligence platform for AI agents. 20 tools for architecture, security & impact analysis.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables querying and analyzing code relationships by building a lightweight graph of TypeScript and Python symbols. Supports symbol lookup, reference tracking, impact analysis from diffs, and code snippet retrieval through natural language.-
- FlicenseAqualityAmaintenanceDeterministic code intelligence engine — indexes 27 languages into a queryable symbol graph for real-time blast-radius analysis, no embeddings or LLM calls.524-
- AlicenseNot gradedqualityCmaintenanceEnables querying a TypeScript codebase's graph for call flows, type relationships, and symbol locations without reading file bodies.-
- AlicenseAqualityAmaintenanceA deterministic symbol oracle over a locally built index of the repository: whether a symbol exists, what a file declares, and what changed structurally since the last snapshot. It also ships a PreToolUse guard that stops an Edit/Write referencing a function, constant or type the repo does not declare, before the write lands rather than after the build fails. No LLM, no API keys, no network, no l6MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/no-materials/hird'
If you have feedback or need assistance with the MCP directory API, please join our Discord server