Skip to main content
Glama

Hirð

The king's hirð wading a river, drawn by Erik Werenskiold

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  # optional

With 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

hird check <file-or-dir>

type- and effect-check; coded diagnostics

hird build <file-or-dir>

emit readable Erlang, compile it to .beam

hird run <file-or-dir>

build, then execute fn main on BEAM

hird demo

record one run of the built-in demo, replay it against variants of the program

hird emit-ast <file> --json

the typed IR of every definition

hird emit-effect-graph <file-or-dir> --json

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_fleet

Record 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       # replay

The 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 on Glama

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 by bd.

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-features

BEAM-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

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 tools
emit_actor_effect_graphEmit actor effect graphA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileYesPath 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_nameYesThe 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

ParametersJSON Schema
NameRequiredDescription
rootYes
toolsYes
actorsYes
moduleYes
supervisorsYes
schema_versionYes

TDQS

A4.6/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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 protocolA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileYesPath 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_nameYesThe 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

ParametersJSON Schema
NameRequiredDescription
fileYes
actorYes
moduleYes

TDQS

A4.3/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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 rowA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileYesPath 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_nameYesThe 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

ParametersJSON Schema
NameRequiredDescription
fileYes
nameYes
openYes
pureYes
typeYes
effectsYes
effect_rowYes

TDQS

A4.8/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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 budgetA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileYesPath 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

ParametersJSON Schema
NameRequiredDescription
fileYes
noteYes
moduleYes
approx_tokensYes

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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 contextA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileYesPath 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.
nameYesThe 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`.
budgetYesApproximate 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

ParametersJSON Schema
NameRequiredDescription
fileYes
kindYes
budgetYes
symbolYes
omittedYes
summaryYes
approx_tokensYes

TDQS

A4.7/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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 typeA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileYesPath 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.
lineYes1-based source line of the expression.
columnYes1-based character (not byte) column. Any position inside the expression's token works; when several tokens touch it an identifier is preferred.

Output Schema

ParametersJSON Schema
NameRequiredDescription
fileYes
lineYes
typeYes
tokenYes
columnYes
effect_rowYes

TDQS

A4.7/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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 definitionA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileYesPath 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.
nameYesThe 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

ParametersJSON Schema
NameRequiredDescription
docNo
fileYes
kindYes
lineYes
nameYes
typeNo

TDQS

A4.6/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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 fragmentA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileYesPath 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.
nameYesThe 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

ParametersJSON Schema
NameRequiredDescription
irYesThe declaration, as the compiler serializes its IR.
fileYes
nameYes
moduleYes

TDQS

A4.6/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

  1. 8 tool updates
    • Changedemit_actor_effect_graph4 fields changed
      • changedInput schema / properties / actor_name / description
        Previous 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`."
      • changedInput schema / properties / file / description
        Previous 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."
      • changedInput schema / required
        Previous value: -[
        -  "file",
        -  "actor_name"
        -]New value: +[
        +  "actor_name",
        +  "file"
        +]
      • changedOutput 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"
        +}
    • Changedexplain_actor_protocol4 fields changed
      • changedInput schema / properties / actor_name / description
        Previous 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`."
      • changedInput schema / properties / file / description
        Previous 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."
      • changedInput schema / required
        Previous value: -[
        -  "file",
        -  "actor_name"
        -]New value: +[
        +  "actor_name",
        +  "file"
        +]
      • changedOutput 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"
        +}
    • Changedexplain_effect_row2 fields changed
      • changedInput schema / properties / file / description
        Previous 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."
      • changedOutput 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"
        +}
    • Changedget_context_budget2 fields changed
      • changedInput schema / properties / file / description
        Previous 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."
      • changedOutput 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"
        +}
    • Changedget_context_for_symbol4 fields changed
      • changedInput schema / properties / budget / description
        Previous 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."
      • changedInput schema / properties / file / description
        Previous 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."
      • changedInput schema / required
        Previous value: -[
        -  "file",
        -  "name"
        -]New value: +[
        +  "budget",
        +  "file",
        +  "name"
        +]
      • changedOutput 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"
        +}
    • Changedinfer_type5 fields changed
      • changedInput schema / properties / column / description
        Previous 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."
      • changedInput schema / properties / file / description
        Previous 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."
      • changedInput schema / properties / line / description
        Previous value: -"1-based source line."New value: +"1-based source line of the expression."
      • changedInput schema / required
        Previous value: -[
        -  "file",
        -  "line",
        -  "column"
        -]New value: +[
        +  "column",
        +  "file",
        +  "line"
        +]
      • changedOutput 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"
        +}
    • Changedlookup_definition2 fields changed
      • changedInput schema / properties / file / description
        Previous 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."
      • changedOutput 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"
        +}
    • Changedrender_ir_fragment2 fields changed
      • changedInput schema / properties / file / description
        Previous 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."
      • changedOutput 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"
        +}
  2. 8 tool updatesv0.1.1
    • Changedemit_actor_effect_graph1 field changed
      • changedInput schema / properties / file / description
        Previous 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."
    • Changedexplain_actor_protocol1 field changed
      • changedInput schema / properties / file / description
        Previous 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."
    • Changedexplain_effect_row2 fields changed
      • changedInput schema / properties / file / description
        Previous 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."
      • changedInput schema / properties / fn_name / description
        Previous 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`."
    • Changedget_context_budget1 field changed
      • changedInput schema / properties / file / description
        Previous 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."
    • Changedget_context_for_symbol2 fields changed
      • changedInput schema / properties / file / description
        Previous 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."
      • changedInput schema / properties / name / description
        Previous 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`."
    • Changedinfer_type1 field changed
      • changedInput schema / properties / file / description
        Previous 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."
    • Changedlookup_definition2 fields changed
      • changedInput schema / properties / file / description
        Previous 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."
      • changedInput schema / properties / name / description
        Previous 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`."
    • Changedrender_ir_fragment2 fields changed
      • changedInput schema / properties / file / description
        Previous 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."
      • changedInput schema / properties / name / description
        Previous 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`."
  3. 8 tool updatesv0.1.0
    • First observedemit_actor_effect_graph
    • First observedexplain_actor_protocol
    • First observedexplain_effect_row
    • First observedget_context_budget
    • First observedget_context_for_symbol
    • First observedinfer_type
    • First observedlookup_definition
    • First observedrender_ir_fragment

TDQS

A4.7/5.0
Disambiguation5/5

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.

Naming Consistency5/5

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.

Tool Count5/5

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.

Completeness5/5

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

ActivityActive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables 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.
    -
  • F
    license
    A
    quality
    A
    maintenance
    Deterministic code intelligence engine — indexes 27 languages into a queryable symbol graph for real-time blast-radius analysis, no embeddings or LLM calls.
    5
    24
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables querying a TypeScript codebase's graph for call flows, type relationships, and symbol locations without reading file bodies.
    -
  • A
    license
    A
    quality
    A
    maintenance
    A 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 l
    6
    MIT

Latest Blog Posts

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