Skip to content

Latest commit

 

History

History
1924 lines (1445 loc) · 102 KB

File metadata and controls

1924 lines (1445 loc) · 102 KB

Tools

Most tools work over the Atelier REST API and connect to any IRIS instance — no Docker required unless noted. Tools marked ✦ require IRIS_CONTAINER. Tools marked 🔒 are write-gated (suppressed on Live instances unless IRIS_ALLOW_PROD=1). Tools marked ☠ are destructive-gated — they require destructive_tools_enabled = true in addition to write_tools_enabled = true.

namespace defaults to "USER" on every tool. It is omitted from parameter tables below unless there is something non-obvious to say about it.


Quick reference

Tool Section
iris_servers Server Management
iris_add_server Server Management
iris_remove_server Server Management
iris_reload_pool Server Management
iris_test_server Server Management
iris_import_servers Server Management
iris_doc Code
iris_compile Code
iris_execute Code
iris_execute_method Code
iris_query Code
iris_test Code
iris_coverage Code
iris_global 🔒 Code
iris_source_control Code
iris_symbols Search and introspection
iris_symbols_local Search and introspection
docs_introspect Search and introspection
iris_search Search and introspection
iris_info Search and introspection
iris_macro Search and introspection
iris_table_info Search and introspection
resolve_dynamic_dispatch Search and introspection
extract_message_map_routing Search and introspection
find_subclass_implementations Search and introspection
iris_debug Debugging
iris_get_log Debugging
check_config Debugging
iris_generate Generation
iris_generate_class Generation
iris_generate_test Generation
iris_production Interoperability
iris_interop_query Interoperability
iris_production_item 🔒 Interoperability
iris_production_diff Interoperability
iris_message_body Interoperability
iris_business_rule_info Interoperability
iris_credential_list Interoperability
iris_credential_manage 🔒 ☠ Interoperability
iris_lookup_manage Interoperability
iris_lookup_transfer Interoperability
iris_ws_open WebSocket sessions
iris_ws_exec WebSocket sessions
iris_ws_close WebSocket sessions
global_preview Administration
global_kill 🔒 ☠ Administration
iris_namespace_list Administration
iris_namespace_create 🔒 ☠ Administration
iris_database_list Administration
iris_mirror_status Administration
iris_system_performance Administration
iris_database_stats Administration
journal_search Administration
query_audit_log Administration
stream_inspect Administration
my_access Administration
capability_matrix Administration
hl7_schema_list Administration
hl7_schema_inspect Administration
mermaid_class Administration
mermaid_production Administration
resolve_storage Administration
compare_document Administration
compare_namespace Administration
iris_admin Administration
iris_containers Administration
skill Skills and knowledge base
skill_community Skills and knowledge base
kb / kb_index / kb_recall Skills and knowledge base
agent_history / agent_stats Skills and knowledge base
telemetry_query Skills and knowledge base
telemetry_export_trace Skills and knowledge base

Parameters are declared, and the set is closed

Every tool advertises its parameters in its inputSchema: names, JSON types, and — where the handler branches on a fixed set of values — an enum. 28 parameters across 24 tools carry one, iris_admin.action (25 values) being the largest. The parameter tables in this file describe the same contract in prose; the schema is what a client can read without parsing English.

Every declared enum is the set the handler branches on, checked against the source rather than against these tables — a test extracts the string literals each dispatcher matches on and compares them with the schema. Two parameters have no branch literal behind them and say so in the test with a reason: iris_add_server.scheme, which is interpolated into a URL, and iris_admin.type, which filters the webapp list case-insensitively against a name derived from IRIS. Declaring an enum changes nothing at runtime: a value outside the set still reaches the handler and still comes back with the handler's own message naming the values it accepts.

Every tool also sets additionalProperties: false. A parameter the tool does not declare is rejected with UNKNOWN_PARAMETER and the error lists the names it does accept. Through 1.3.2, 31 tools advertised an open object with no properties at all, so a misspelled or invented parameter was dropped in silence — stream_inspect was documented with a character cap that no code read, and a caller asking for 10,000 characters got the whole stream and no warning. The name of that parameter is deliberately absent from this file: a guard in the test suite fails if it comes back, because the docs row is what created the bug in the first place.

Six tools take no parameters at all and declare an empty property set: agent_stats, check_config, iris_import_servers, iris_reload_pool, skill_community_list, skill_list.


Tool catalog size

This server exposes 80 to 84 tools depending on toolset (IRIS_TOOLSET=baseline|nostub|merged), with full schemas and descriptions: 94 KB to 107 KB of JSON, roughly 26K to 30K tokens, if a client loads the whole catalog on every connection. Two independent ways to avoid paying that cost, and you don't have to pick just one:

  • Client-side, zero server changes needed: Anthropic's Tool Search Tool (tool_search_tool_bm25_20251119/_regex_20251119) lets a model discover tools on demand instead of front-loading the full catalog. It already works against this server's MCP surface unmodified — enabling it on the client is the whole change.
  • Server-side: list_tools supports real cursor-based pagination. A plain, unconfigured tools/list call still returns the entire effective toolset in one response (the default page size is set above every toolset's real tool count, so nothing changes for existing clients). Set IRIS_LIST_TOOLS_PAGE_SIZE to a smaller value to page the catalog across multiple tools/list calls instead — each response includes a nextCursor when more tools remain; omit cursor on the first call and pass back whatever nextCursor you last received to get the next page, until a response with no nextCursor signals the end.

Discovery from a shell

tool --list prints one line per tool — name and a one-sentence summary. tool <name> --schema prints one tool's full description and inputSchema. Both read the same router tools/list reads, so the schema you get from the CLI is byte-identical to the schema an MCP client receives, and a test compares all 81 for every build.

Neither path connects to IRIS. Discovery works with no container running, no credentials, and a closed port:

iris-agentic-dev tool --list                      # 81 names + summaries
iris-agentic-dev tool --list --json               # same, as {"count": 81, "tools": [...]}
iris-agentic-dev tool iris_query --schema         # one tool's contract
iris-agentic-dev tool iris_query --schema --json  # same, as one JSON document

--json works on both paths. --list with a tool name is an error rather than a silent preference for one or the other.

Why it exists — measured against this tree:

Reading the surface via Bytes
MCP tools/list (81 tools, compact) 106,658
tool --list 7,648
tool <name> --schema, smallest 231
tool <name> --schema, median 1,357
tool <name> --schema, largest 10,134

An agent with only a shell reads the whole surface for 7.6 KB and then pays for the one or two schemas it actually needs, instead of 107 KB up front. A wrong name is refused with the nearest accepted name, the same suggestion UNKNOWN_PARAMETER uses for misspelled parameters.

IRIS_TOOLSET applies to --list, so the CLI lists the tier the server would serve.


Server Management

Tools for registering, testing, and managing IRIS server connections. All other tools accept an optional server parameter that routes to a named instance — see iris_servers to list what's registered.

iris_servers

List all registered IRIS instances from all configuration sources (iad-native config, VS Code Server Manager settings, workspace fleet config, environment variables). Shows name, host, port, namespace, source, and reachability status.

Default (probe omitted or false): fast path — reachable is null per entry, no HTTP requests made.

Pass probe: true to fan out a parallel Atelier REST probe to every server in the pool (5-second timeout each). Each entry then includes reachable, auth, latency_ms, iris_version, atelier_version, and error.

iris_add_server

Register a new IRIS instance. Writes server details to ~/.config/iris-agentic-dev/servers.json and stores the password in the OS keychain — the password never appears in any config file. Uses the same keychain format as VS Code Server Manager, so credentials are shared automatically if both tools are installed.

Parameters: name, host, port, namespace, username, password, description (optional), scheme (optional, default "http").

After adding a server, restart iad for the new connection to appear in the pool.

iris_remove_server

Remove a server from the iad-native config and its keychain entry. Requires destructive_tools_enabled = true. Cannot remove servers sourced from VS Code settings — edit settings.json directly for those.

iris_reload_pool

Hot-reload the connection pool from disk without restarting iad. Re-reads the workspace TOML config and the iad-native servers list, then atomically replaces the in-memory pool. Returns {success, servers_loaded, servers, note}. Parse errors preserve the existing pool and return {success: false, error_code: "TOML_PARSE_ERROR", ...}.

Background reload: iad also reloads the pool automatically when the workspace TOML changes on disk. The change is detected via file mtime on each call to iris_servers, iris_compile, iris_execute, and similar tools. A parse error during background reload is silently swallowed — the existing pool is kept.

iris_test_server

Probe an IRIS server for reachability. Returns reachable, auth, iris_version, atelier_version, and latency_ms.

Two modes:

  • Named server (name): looks up the server in the connection pool and probes it.
  • Ad-hoc (host, optional web_port, username, password): probes a server that is not in the pool. Useful for discovery before calling iris_add_server. Default port is 52773; default credentials are _SYSTEM/"". Returns reachable: true, auth: false when the server responds with HTTP 401 (reachable but credentials wrong).

iris_import_servers

One-time import of IRIS server definitions from VS Code or Cursor settings into the iad-native config. Reads passwords from the existing OS keychain — no re-entry required. Reports servers imported, skipped (already present), and those with no keychain entry.


Code

iris_doc

Read, write, delete, insert lines, or list IRIS documents.

Parameter Type Default Notes
mode (alias: action) string "get" See modes below
name (alias: document) string Document name, e.g. "MyApp.MyClass.cls"
names string[] [] Batch get/delete
content string Document content for put/insert
compile bool false Compile after put
start int Start line for get (fragment) or delete_lines
end int End line for get (fragment) or delete_lines
line int Insert-before line for insert
expected string CAS guard: current content at startend; fails with STALE_CONTENT if mismatch
pattern string Glob filter for list, e.g. "MyApp.*.cls"
category string "CLS" | "MAC" | "INT" | "INC" | "ALL"
max_results int 200 Max 1000; for list
compiled_type string "INT" "INT" is the only value; for compiled mode. OBJ returns INVALID_PARAMS
allow_storage_regeneration bool false Required to proceed when IRIS strips Storage blocks on PUT
elicitation_id string SCM checkout dialog resume ID
elicitation_answer string SCM checkout dialog answer
namespace string "USER"

Modes:

Mode What it does
get Read document content
put Write document content (SCM-gated, strips Storage blocks on IRIS 2025.1+)
delete Delete document
head Return metadata only (size, timestamp) without content
fragment Read lines startend
compiled Read compiled INT or OBJ source
list List documents matching pattern/category
insert Insert content before line (use expected for a CAS check)
delete_lines Delete lines startend (use expected for a CAS check)

Examples:

# Read a class
iris_doc(mode="get", name="MyApp.MyClass.cls")

# Write and compile
iris_doc(mode="put", name="MyApp.MyClass.cls", content="...", compile=true)

# Patch line 42 — fail if content changed since last read
iris_doc(mode="insert", name="MyApp.MyClass.cls", line=42,
         content="  Set x = 1\n", expected="  // placeholder\n")

# List all classes in a package
iris_doc(mode="list", pattern="MyApp.*.cls")

Storage block guard. IRIS 2025.1+ rejects Storage XML in a PUT request (upstream bug). iris_doc strips it before writing and refuses by default with STORAGE_RESET_REQUIRES_CONFIRMATION. Pass allow_storage_regeneration: true to proceed — but understand that recompiling without Storage forces IRIS to regenerate global layout, which can change the extent for %Persistent classes. Use mode=insert or mode=delete_lines when the edit does not touch the Storage block.

SCM checkout. On source-controlled instances, iris_doc runs the SCM pre-write check before writing. If checkout is required, the tool returns an elicitation dialog rather than writing. Resume it with elicitation_id + elicitation_answer.


iris_compile

Compile a class, routine, or wildcard pattern.

Parameter Type Default Notes
target string Required. Class name, routine, or glob like "MyApp.*.cls"
flags string "cuk" Compile flags
namespace string "USER"
force_writable bool false Override read-only check
inline bool false Return all errors inline (bypass log store)

Returns errors with line numbers.

iris_compile(target="MyApp.MyClass.cls")
iris_compile(target="MyApp.*.cls", flags="cukd")

iris_execute

Run arbitrary ObjectScript and return the output.

Parameter Type Default Notes
code string Required. ObjectScript to execute
namespace string "USER"
timeout int 120 Seconds; overridden by OBJECTSCRIPT_TEST_TIMEOUT env var
translate_sql bool true Rewrite &sql(...) macros to %SQL.Statement
use_session bool false Enable %ctx session carrier (see below)
session_state string Token from a prior call; restores %ctx
iris_execute(code="Write $ZVersion")
iris_execute(code="Set sc = ##class(MyApp.Util).Run() Write sc", namespace="MYAPP")

Code-edit guard — see Code-edit guard below.

Destructive-tier gateiris_execute requires write_tools_enabled = true (it is write-gated). Additionally, if the literal code string contains a Kill ^<global> pattern (any case variant), destructive_tools_enabled = true is also required. This check catches direct global kills written literally in the code. Indirect operations — Kill @variable, Xecute-dispatched code, or class method calls that internally kill a global — are not detected; IRIS-side credentials and the mcpTemplate env gate are the appropriate controls for those.

Session state

Set use_session: true to get a %ctx variable (%DynamicObject) injected before your code and serialized into a session_state token in the response. Pass that token back as session_state on the next call to restore %ctx. Nothing is written to IRIS — the token lives entirely in the client.

# Call 1 — compute something and stash it
iris_execute(
  use_session=true,
  code="Set %ctx.count = 1247  Set %ctx.label = \"patients\""
)
# → response includes session_state: "eyJjb3VudCI6MTI0N..."

# Call 2 — pick up where you left off
iris_execute(
  use_session=true,
  session_state="eyJjb3VudCI6MTI0N...",
  code="Write %ctx.count * 0.05"
)
# → output: 62.35

%Persistent objects are automatically stubbed on save ({"_cls": "...", "_id": "..."}) and re-opened on restore. %DynamicObject and scalar values survive round-trips unchanged. Values that cannot serialize (open file handles, result sets, device references) must be removed from %ctx before the epilogue runs.

Session error codes:

Code Meaning
SESSION_INVALID Token is malformed or %FromJSON failed
SESSION_RESTORE_FAILED A stubbed %Persistent object could not be re-opened (class missing or bad ID)
SESSION_SERIALIZE_FAILED %ctx could not be serialized at end of call

server (optional): route this call to a named registered IRIS instance. If omitted, uses the default connection. Use iris_servers to list available instances.

Execution paths and terminal-mode constraint

iris_execute has two execution paths:

  1. HTTP (primary) — wraps code in a temporary class method body, compiles it via Atelier REST, runs it, deletes the class. Block syntax ({}) works here because class method bodies support it.

  2. docker exec (fallback) — used when IRIS_CONTAINER is set and HTTP fails, or when docker_only = true in config. This path pipes code into iris session stdin, which is a line-by-line terminal interpreter. Block syntax is not supported in terminal mode. If cond { Write x } causes <SYNTAX> with no explanation.

Use classic terminal-compatible form for the docker exec path:

// Terminal-compatible: classic form
If cond  Write x

// Terminal-compatible: dotted-DO
If cond  Do
.  Write x

// NOT terminal-compatible: block syntax
If cond {
    Write x        // causes <SYNTAX> on docker exec path
}

Escape hatch for complex scripts on the docker exec path: write a .mac routine with iris_doc, compile it with iris_compile, then call iris_execute Do entry^RoutineName. Block syntax works inside compiled routines.

// Step 1: write the routine
iris_doc(mode="put", name="MyScript.mac", content="ROUTINE MyScript\nentry()\n    If x=1 {\n        Write \"yes\"\n    }\n    Quit")

// Step 2: compile it
iris_compile(target="MyScript.mac")

// Step 3: run it
iris_execute(code="Do entry^MyScript")

Error code TERMINAL_SYNTAX_UNSUPPORTED is returned when block syntax is detected before the docker exec round-trip, with an actionable message pointing to this pattern.


iris_execute_method

Invoke a ClassMethod directly by class, method name, and arguments.

Parameter Type Default Notes
class string Required. e.g. "%Library.Integer"
method string Required. e.g. "IsValid"
args string[] [] Positional string arguments
namespace string "USER"

String-returning methods only (v1).

iris_execute_method(class="MyApp.Util", method="GetVersion")
iris_execute_method(class="%Library.Integer", method="IsValid", args=["42"])

iris_query

Execute SQL and return rows as JSON.

Parameter Type Default Notes
query string "" SQL statement; required for read/explain/write
parameters string[] [] Bind parameters (positional ?)
mode string "read" "read" | "explain" | "count" | "write"
table string For mode=count without a query
max_rows_affected int 1000 Write mode only; clamped to [1, 10000]
namespace string "USER"
force bool false Bypass SQL safety validation
# Read
iris_query(query="SELECT ID, Name FROM MyApp.Patient WHERE Status = ?", parameters=["Active"])

# Query plan
iris_query(query="SELECT * FROM MyApp.Patient", mode="explain")

# Row count without fetching data
iris_query(table="MyApp.Patient", mode="count")

# DML (gated)
iris_query(query="UPDATE MyApp.Patient SET Status = 'Archived' WHERE ID = ?",
           parameters=["123"], mode="write")

server (optional): route this call to a named registered IRIS instance. If omitted, uses the default connection. Use iris_servers to list available instances.


iris_test

Run %UnitTest tests and return structured pass/fail results.

Parameter Type Default Notes
pattern string Required. Package name, e.g. "App.Tests"
namespace string "USER"
timeout int 60 Seconds
coverage bool Also measure line coverage inline
coverage_classes string[] Explicit class list for coverage; defaults to all classes in pattern
coverage_target_pct float Fail if coverage falls below this threshold
iris_test(pattern="MyApp.Tests")
iris_test(pattern="MyApp.Tests", coverage=true, coverage_target_pct=80)

iris_coverage

Standalone line coverage via %Monitor.System.LineByLine.

Parameter Type Default Notes
mode string Required. See modes below
classes string[] Classes to monitor; used with start/run
package string Package prefix — alternative to explicit classes
test_path string %UnitTest package to run; required for run
target_pct float Fail threshold %
cobertura_path string Write Cobertura XML here (requires TestCoverage IPM)
namespace string "USER"
Mode What it does
check Pre-flight: verify gmheap ≥ 256 MB; returns testcoverage_available
run All-in-one: start → RunTest → stop → report
start Start monitoring the given classes
stop Stop monitoring
report Collect results from a previously stopped run
iris_coverage(mode="check")
iris_coverage(mode="run", package="MyApp", test_path="MyApp.Tests", target_pct=80)
iris_coverage(mode="run", classes=["MyApp.Service", "MyApp.Util"], test_path="MyApp.Tests")

Requires gmheap ≥ 256 MB. Run mode=check first. If BBSIZ_NOT_CONFIGURED is returned, increase gmheap in Management Portal → System Administration → Configuration → Additional Settings → Advanced Memory, then restart IRIS.


iris_global 🔒

Read, write, kill, or list IRIS global nodes. Gated — see Data safety gates.

Parameter Type Default Notes
action string Required. "get" | "set" | "kill" | "list"
global_name string Required. With or without leading ^
subscripts string[] Subscript path; values validated [a-zA-Z0-9 _.:\-]+
value string Required for action=set
subtree bool get only: include all descendants
max_nodes int 100 get + subtree; max 1000
max_subscripts int 50 list action; max 500
acknowledgePhi bool Required when global name matches a PHI pattern
namespace string
iris_global(action="get", global_name="^MyApp.Config", subscripts=["timeout"])
iris_global(action="list", global_name="^MyApp.Config")
iris_global(action="set", global_name="^MyApp.Config", subscripts=["timeout"], value="30")

iris_source_control

Check lock status, checkout, or execute SCM actions.

Parameter Type Default Notes
action string Required. "status" | "menu" | "checkout" | "execute"
document string Document name
action_id string For action=execute: the menu action ID
elicitation_id string Elicitation dialog resume ID
answer string Elicitation dialog answer
namespace string "USER"

CheckIn is opt-in via IRIS_SCM_ALLOW_CHECKIN=1.

iris_source_control(action="status", document="MyApp.MyClass.cls")
iris_source_control(action="checkout", document="MyApp.MyClass.cls")
iris_source_control(action="menu")   # list available actions

Code-edit guard

iris_execute rejects any code matching class- or routine-editing patterns. The check runs before execution — a compound line mixing innocent data work with one blocked token is rejected entirely; nothing executes.

Blocked patterns: %Dictionary.*Definition, $system.OBJ (Load, Compile, Delete, and variants), %RoutineMgr, and direct writes to code-storage globals (^rOBJ, ^rINDEX, ^%occRoutine, etc.).

The error response includes a matched field naming the specific token and a remediation field pointing to the correct tools:

  • To write or delete a class/routine: iris_doc with mode=put or mode=delete.
  • To compile: iris_compile.

The guard is non-configurable and applies to all connections. Error code: CODE_EDIT_BLOCKED.


Search and introspection

iris_symbols

Search classes and methods via %Dictionary.

Parameter Type Default Notes
query string Required. Search term
limit int 20
namespace string "USER"
iris_symbols(query="IRISDemo.*")
iris_symbols(query="Patient", limit=50)

iris_symbols_local

Search .cls/.mac/.inc files on disk by glob pattern. No IRIS connection required.

Parameter Type Default Notes
query string Required. Search string
workspace_path string Root path to search; defaults to current workspace
limit int 50
kinds string[] Filter by symbol kind
iris_symbols_local(query="Patient")
iris_symbols_local(query="GetStatus", workspace_path="/home/user/myapp")

docs_introspect

Deep class inspection: methods, properties, parameters, XData blocks, superclasses. Returns xdata_flow for BPL and DTL classes showing the step tree.

Parameter Type Default Notes
class_name string Required.
namespace string "USER"
docs_introspect(class_name="MyApp.BP.PatientProcess")
docs_introspect(class_name="Ens.BusinessProcess")

iris_search

Full-text search across the namespace. Supports regex and category filters.

Parameter Type Default Notes
query string Required. Search string
documents string[] Required. Scope, e.g. ["MyApp.*.cls"]; empty triggers SCOPE_REQUIRED
regex bool false
case_sensitive bool false
category string "CLS" | "MAC" | "INT" | "INC" | "ALL"
namespace string "USER"
inline bool false Bypass log store

Namespace-wide grep times out — always provide a documents scope.

iris_search(query="GetPatient", documents=["MyApp.*.cls"])
iris_search(query="##class\(.*Patient", documents=["MyApp.*.cls"], regex=true)

iris_info

Namespace discovery: documents, jobs, CSP apps, metadata.

Parameter Type Default Notes
what string Required. "documents" | "modified" | "namespace" | "metadata" | "jobs" | "csp_apps" | "csp_debug" | "sa_schema"
doc_type string "CLS" | "MAC" | "INT" | "INC" | "CSP" | "ALL"
name string For what=sa_schema
namespace string "USER"
inline bool false
iris_info(what="namespace")
iris_info(what="documents", doc_type="CLS")
iris_info(what="jobs")

iris_macro

Inspect $$$ macros: list, signature, location, definition, expand.

Parameter Type Default Notes
action string Required. "list" | "signature" | "location" | "definition" | "expand"
name string Macro name
args string[] [] Arguments for expand
namespace string "USER"
iris_macro(action="list")
iris_macro(action="signature", name="ThrowOnError")
iris_macro(action="expand", name="ThrowOnError", args=["sc"])

iris_table_info

Inspect a SQL table: storage type, backing globals, optional row count.

Parameter Type Default Notes
table string Required. "Schema.Table" format
include_row_count bool false
namespace string "USER"
iris_table_info(table="MyApp.Patient")
iris_table_info(table="MyApp.Patient", include_row_count=true)

resolve_dynamic_dispatch

Resolve $classmethod/##class({var}) polymorphic dispatch to concrete candidate classes, with confidence scores.

Parameter Type Default Notes
method_name string Required.
package_prefix string Restrict candidates to a package, e.g. "MyApp"
limit int 50
namespace string "USER"
resolve_dynamic_dispatch(method_name="ProcessRequest", package_prefix="MyApp")

extract_message_map_routing

Extract a compiled Ensemble MessageMap routing table from a BusinessProcess or Router.

Parameter Type Default Notes
class_name string Required.
namespace string "USER"
extract_message_map_routing(class_name="MyApp.BP.PatientProcess")

find_subclass_implementations

Find all concrete subclass implementations of a method across the inheritance hierarchy.

Parameter Type Default Notes
method_name string Required.
base_classes string[] Required. Non-empty list
limit int 100
namespace string "USER"
find_subclass_implementations(method_name="OnProcessInput",
                              base_classes=["Ens.BusinessProcess"])

Debugging

iris_debug

Map INT offsets to source lines, fetch error logs, capture error state.

Parameter Type Default Notes
action string Required. "map_int" | "error_logs" | "capture" | "source_map"
error_string string For action=map_int, e.g. "^MyApp.Patient.1+42"
class_name string For action=source_map
limit int 20
namespace string "USER"

ObjectScript errors report INT line numbers. map_int resolves them to source locations.

iris_debug(action="map_int", error_string="^MyApp.Patient.1+42^MyApp.Patient")
iris_debug(action="error_logs", limit=50)
iris_debug(action="capture")

iris_get_log

Retrieve a full result when a tool returns truncated: true.

Parameter Type Default Notes
id string Log entry UUID; omit to list all stored entries
limit int Max entries; must be > 0 if provided
offset int 0 Start index into stored results
iris_get_log(id="a1b2c3d4-...")
iris_get_log()   # list all stored entries

check_config

Show active connection state. No parameters.

Returns host, port, namespace, discovery source, container name, config file path, and write tool status. Run this first if anything seems misconfigured.


Generation

iris_generate

Build a context-rich prompt for generating ObjectScript. Pulls relevant class definitions and coding conventions into a structured prompt. No API key required.

Parameter Type Default Notes
description string Required. What to generate
gen_type string "class" "class" | "test"
class_name string Source class for context
namespace string "USER"
iris_generate(description="A REST API handler that validates patient demographics",
              class_name="MyApp.Patient")

iris_generate_class

Generate and compile a class from a description. Requires an LLM API key in the connection config.

Parameter Type Default Notes
description string Required.
overwrite bool false Overwrite if class already exists
namespace string "USER"
iris_generate_class(description="A %Persistent class storing patient visit records with indexes on PatientID and VisitDate")

iris_generate_test

Generate %UnitTest scaffolding for an existing class.

Parameter Type Default Notes
class_name string Required.
namespace string "USER"
iris_generate_test(class_name="MyApp.Service")

Interoperability

iris_production

Start, stop, update, check, or recover a production.

Parameter Type Default Notes
action string Required. "status" | "start" | "stop" | "update" | "check" | "recover" | "get_autostart" | "set_autostart"
production_name string Production class name for start and stop; defaults to the currently running production
production string set_autostart only — the class name to autostart. No other action reads this key
timeout int 30 Seconds; stop only
force bool false stop only
full bool false status only: include per-item state
enabled bool set_autostart only
namespace string "USER"

production_name and production are two distinct keys, and each action reads exactly one of them. Passing production to start is silently ignored — the production name comes out empty and the call starts nothing.

Actions that modify production state require IRIS_CONTAINER.

iris_production(action="status")
iris_production(action="stop", timeout=60, force=true)
iris_production(action="start", production_name="MyApp.Production")
iris_production(action="set_autostart", production="MyApp.Production", enabled=true)

iris_interop_query

Query production logs, queue depths, or message archive.

Parameter Type Default Notes
what string "logs" Enum: "logs" | "queues" | "messages"
component string logs: filter by business host name
log_type string "error,warning" logs only; comma-separated, see below
limit int 50 Applies to logs and messages
source string messages: filter by source
target string messages: filter by target
message_class string messages: filter by message class
session_id int | string messages: one session's messages; decimal string accepted
since_id int | string messages: tail after this header ID
body_class string messages: body class to join, e.g. Ens.StringContainer
body_where string messages: SQL predicate on the joined body table
body_select array<string> messages: body-table columns to add to each row
search_table object messages: indexed Search Table search — see below
namespace string "USER"
server string Named server; omit for default

log_type takes any comma-separated subset of assert, error, warning, info, trace, alert — the six values of Ens.Util.Log.Type, matched case-insensitively. A name that is not one of those six contributes no filter, and the response reports it under unknown_log_types rather than quietly widening the query to every severity.

search_table takes prop (required), one of value or value_like, and optional class and extent (default EnsLib.HL7.SearchTable).

iris_interop_query(what="logs", log_type="error", limit=50)
iris_interop_query(what="queues")
iris_interop_query(what="messages", source="MyApp.BS.HL7Listener", limit=20)

iris_production_item 🔒

Enable, disable, or get/set settings on an individual production config item.

Parameter Type Default Notes
action string Required. "enable" | "disable" | "get_settings" | "set_settings"
item string Required. Production item name
settings map<string,string> {} For set_settings
namespace string "USER"
server string Named server; omit for default

Works via Atelier HTTP — no Docker required.

iris_production_item(action="get_settings", item="MyApp.BO.LabResults")
iris_production_item(action="set_settings", item="MyApp.BO.LabResults",
                     settings={"ReplyCodeActions": "E=R,D=C,~=C"})
iris_production_item(action="disable", item="MyApp.BS.HL7Listener")

iris_production_diff

Diff the running production config against the last source-controlled version.

Parameter Type Default Notes
production string Defaults to currently running production
namespace string "USER"
server string Named server; omit for default
iris_production_diff()
iris_production_diff(production="MyApp.Production")

iris_message_body

Read a message body by ID. Gated — see Data safety gates.

Parameter Type Default Notes
message_id string Required.
max_bytes int 65536 Max 1 MB (1048576)
acknowledgePhi bool false Required when dataPolicy = "allow"
dataPolicy string block block | allow | redact
namespace string "USER"
server string Named server; omit for default

The connection's dataPolicy is applied before the call's, so passing allow here cannot widen what [policy.<server>] restricted.

iris_message_body(message_id="123456")
iris_message_body(message_id="123456", acknowledgePhi=true)

iris_business_rule_info

List or inspect Ensemble business rules.

Parameter Type Default Notes
action string Required. "list" | "get"
rule_name string
namespace string "USER"
server string Named server; omit for default
iris_business_rule_info(action="list")
iris_business_rule_info(action="get", rule_name="MyApp.RoutingRule")

iris_credential_list

List Ensemble credentials. Passwords are never returned.

Parameter Type Default
namespace string "USER"

No other parameters.


iris_credential_manage 🔒 ☠

Create, update, or delete an Ensemble credential.

Parameter Type Default Notes
action string Required. "create" | "update" | "delete"
id string Required. Credential ID
username string
password string
namespace string "USER"

iris_lookup_manage

Read, write, delete, or list Ensemble lookup table entries. Write and delete actions are 🔒 ☠ gated. Read actions (get, list_keys, list_tables) are unrestricted.

Parameter Type Default Notes
action string Required. "get" | "set" | "delete" | "list_keys" | "list_tables"
table string Table name
key string
value string For action=set
namespace string "USER"
iris_lookup_manage(action="list_tables")
iris_lookup_manage(action="list_keys", table="FacilityMap")
iris_lookup_manage(action="get", table="FacilityMap", key="MGH")

iris_lookup_transfer

Export or import an Ensemble lookup table as XML. Import is 🔒 gated.

Parameter Type Default Notes
action string Required. "export" | "import"
table string Required.
xml string For action=import
namespace string "USER"

Administration

Most tools in this section are ported from Pierre Abdelsayed's Server Manager MCP work. The global confirmation pattern, namespace/database admin, observability tools, HL7 schema tools, Mermaid diagrams, and resolve_storage all originate from his design.

global_preview

Preview the top N subscripts of an IRIS global and mint a confirmation token for a subsequent global_kill. Returns up to 100 entries, the total subscript count, and a confirm_token that expires in 5 minutes.

Parameter Type Default Notes
global string Required. Global name, with or without ^
count number 20 Max entries to preview (1–100)
server string Named server; omit for default

global_kill 🔒 ☠

Kill an IRIS global after confirming with a token from global_preview. The token validates the global name and server — a token issued for ^Foo cannot be used to kill ^Bar. Tokens expire after 5 minutes. Write-gated.

Parameter Type Default Notes
global string Required. Must match the global in the token
confirm_token string Required. Token from global_preview
server string Named server; omit for default

iris_namespace_list

List all namespaces on the connected IRIS instance.

Parameter Type Default Notes
server string Named server; omit for default

iris_namespace_create 🔒 ☠

Create a new namespace and its backing database. Write-gated and destructive-gated.

Parameter Type Default Notes
name string Required. Namespace name (A–Z, 0–9, -)
db_path string Database directory path; defaults to name
server string Named server; omit for default

iris_database_list

List databases, their directory paths, and free space. Each entry includes size_mb, free_space_mb, free_pct, and max_size_mb (null when unlimited).

Parameter Type Default Notes
server string Named server; omit for default

iris_mirror_status

Report mirror membership and role for the connected IRIS instance. Non-mirror instances return {is_member: false}. Useful as a pre-flight check before operations that require a primary.

Parameter Type Default Notes
server string Named server; omit for default

Response fields: is_member (bool), mirror_name (string or null), member_type (string or null — primary/backup/async), is_primary (bool).

iris_system_performance

Manage IRIS SystemPerformance (pbuttons) profiles, start and poll runs, and locate the report a finished run wrote.

Parameter Type Default Notes
mode string Required. start / status / last_runid / list_profiles / add_profile / delete_profile / list_runs / report
profile string test mode=start: which profile to run. mode=add_profile / delete_profile: which profile to create or remove
run_id string Required for mode=status. Optional for mode=report — omit for the newest completed run
description string Required for mode=add_profile
interval_seconds integer Required for mode=add_profile. Seconds between samples; the shipped profiles use 1 to 60
sample_count integer Required for mode=add_profile. interval_seconds × sample_count is the run length
server string Named server; omit for default

Response fields: success (bool), mode (string).

  • mode=start returns profile and run_id. The run ID comes straight from $$run^SystemPerformance(profile).
  • mode=last_runid returns run_id and in_progress (bool). It reads ^IRIS.SystemPerformance("run") before ("history"), so a run that is still collecting is reported with in_progress: true — a run gets no history node until it finishes.
  • mode=status returns wait_time (string from $$waittime^SystemPerformance, e.g. "7 minutes").
  • mode=list_profiles returns profiles and count. Each profile has name, interval_seconds, sample_count, duration_minutes, and description.
  • mode=add_profile returns the stored profile, interval_seconds, sample_count, and duration_minutes. A duplicate name comes back as success: false with IRIS's own profile name exists already.
  • mode=delete_profile returns the deleted profile. A deleted profile is one add_profile away from being back, so this is a write, not a destructive operation.
  • mode=list_runs returns runs (newest first, capped at 100) and count. Each run has run_id, completed_at, output_dir, and profile.
  • mode=report returns run_id, completed_at, output_dir, report_path, size_bytes, and exists. A run still collecting has no report yet and comes back with exists: false and a note saying so.

Profile names accept letters, digits and underscore only. That is stricter than IRIS itself: addprofile accepts "bad name", returns success, and stores the profile as badname — so the name you asked for does not exist and nothing tells you. Names outside the safe set are rejected before the call.

The completed report lands in the instance's mgr directory as <host>_<instance>_<run_id>.html; mode=report resolves that path and falls back to scanning the run's output directory if the host name has changed since. There is no cancel entry point — a started profile runs to completion, so prefer test unless you need a longer window.

iris_database_stats

Show size, free space, and block stats for a specific database directory.

Parameter Type Default Notes
db string Required. Database directory path
server string Named server; omit for default

journal_search

Search the IRIS journal for global set/kill records in a time range. Bulk-PHI gated — requires dataPolicy = "allow" on the connection.

Parameter Type Default Notes
start string ISO 8601 start timestamp (inclusive)
end string ISO 8601 end timestamp (inclusive)
global_pattern string Substring to filter global names
max_entries number 100 Cap results (1–500)
server string Named server; omit for default

query_audit_log

Query the %SYS.Audit table for recent events.

Both filters are equality comparisons (EventType = ?, Username = ?), not substring matches — event_type="Login" will not find LoginFailure.

Parameter Type Default Notes
event_type string Exact match on the EventType column
user string Exact match on the Username column
start string Lower bound on UTCTimeStamp, inclusive; ISO 8601
end string Upper bound on UTCTimeStamp, inclusive; ISO 8601
limit number 100 Max rows; clamped to 1–500
server string Named server; omit for default

stream_inspect

Inspect a %Stream.GlobalBinary or %Stream.GlobalCharacter object by OID. Returns the whole stream and its size — there is no length cap, so a large stream produces a large response.

Parameter Type Default Notes
oid string Required. Stream OID
namespace string "USER" Namespace that contains the stream
server string Named server; omit for default

my_access

Show current user, roles, and privileges for the connected session.

Parameter Type Default Notes
server string Named server; omit for default

capability_matrix

Show the roles assigned to one user. Returns {user, full_name, roles} from Security.Users — one user, not a namespace-by-privilege matrix.

Parameter Type Default Notes
user string $USERNAME Username to look up
server string Named server; omit for default

hl7_schema_list

List available HL7 2.x schema versions. Returns HL7_NOT_AVAILABLE if EnsLib.HL7.Schema is absent. Requires HealthShare or IRIS for Health — not present on plain IRIS regardless of edition.

Parameter Type Default Notes
namespace string Namespace to list from
server string Named server; omit for default

hl7_schema_inspect

Show segment definitions, field names, and data types for a specific HL7 schema version and optional segment filter.

Parameter Type Default Notes
schema string Required. e.g. "2.6"
segment string Segment name filter, e.g. "PID" (optional)
namespace string Namespace to read the schema from
server string Named server; omit for default

mermaid_class

Generate a Mermaid class diagram showing inheritance for one class. Walks the Super hierarchy and strips %-prefixed system class names.

Parameter Type Default Notes
class string Required. Starting class
depth number 3 Levels of Super to walk
namespace string "USER" Namespace to query
server string Named server; omit for default

mermaid_production

Generate a Mermaid flowchart of an Ensemble/IRIS Interoperability production — hosts, connections, and enabled/disabled state.

Parameter Type Default Notes
production string Required. Production class name
namespace string "USER" Namespace that hosts the production
server string Named server; omit for default

resolve_storage

Show the storage definition (^oddDEF structure) for a persistent class. Helps diagnose global layout, extents, and index locations.

Parameter Type Default Notes
class string Required. Class name
namespace string "USER" Namespace to query
server string Named server; omit for default

compare_document

Compare a single document (class, routine, or include file) between two IRIS servers. Returns same: true/false and a unified diff when different.

Parameter Type Default Notes
document string Required. Document name, e.g. MyApp.cls
server_a string Required. First server name
server_b string Required. Second server name
namespace string "USER" Namespace on both servers

compare_namespace

Compare all classes in a namespace between two IRIS servers. Lists classes only in A, only in B, and classes present in both that differ. Caps comparison at 200 classes to avoid overload — unchecked_count reports how many were skipped.

Parameter Type Default Notes
namespace string "USER" Namespace to compare
server_a string Required. First server name
server_b string Required. Second server name

System observability

Five read-only iris_admin actions give a real-time view into IRIS internals. All run against %SYS directly — no external monitoring stack required.

view_locks — lists all active IRIS locks. Each row shows the lock name, lock type (shared/exclusive), the owning process ID, and the caller's job ID. Useful for debugging contention during long-running transactions or BPL routes that hold locks across Await calls.

iris_admin(action="view_locks")

view_processes — shows running IRIS processes. Columns include process ID, namespace, routine+label (i.e. where the process is executing), CPU time, and whether the process is suspended. The namespace parameter narrows results to a single namespace. Process user names are redacted in the response (PHI precaution) unless the call passes dataPolicy = "allow"; the default is block, which refuses the call outright.

iris_admin(action="view_processes", dataPolicy="redact")
iris_admin(action="view_processes", dataPolicy="redact", namespace="MYAPP")

namespace_mappings — returns the global, package, and routine mappings for a namespace: which packages and globals resolve to which databases. Equivalent to the Namespace Mappings page in Management Portal, useful when debugging <CLASS DOES NOT EXIST> errors that turn out to be mapping gaps.

iris_admin(action="namespace_mappings", namespace="MYAPP")

database_status — calls SYS.Database:FreeSpace and returns size, free space, and journal state for every database. Pass name to narrow to a substring match on the database path.

iris_admin(action="database_status")
iris_admin(action="database_status", name="MYAPP")

journal_search — scans the IRIS journal for global set/kill records in a time window. Gated: requires dataPolicy = "allow" because journal records can contain PHI. The global_pattern substring filter keeps results manageable; max_records caps at 1000.

iris_admin(action="journal_search",
           global_pattern="^MyApp.Order",
           time_range={"from": "2026-01-15T00:00:00Z",
                       "to":   "2026-01-15T23:59:59Z"})

For standalone journal access there is also a journal_search tool (Administration section below) that does not require iris_admin. It is not an alias — the two take different parameter names for the same concepts, and passing one tool's names to the other is rejected with UNKNOWN_PARAMETER:

Concept iris_admin(action="journal_search") standalone journal_search
Time window time_range={"from": …, "to": …} start / end
Result cap max_records (default 100, max 1000) max_entries (1–500)
PHI gate connection dataPolicy connection dataPolicy
Global filter global_pattern global_pattern

global_pattern is the only name they share.


iris_admin

List namespaces, databases, users, roles, and web apps. Read actions have no gate. Write actions require IRIS_WRITE_TOOLS_ENABLED=1.

Read actions (no env gate):

Action Parameters
list_namespaces
list_databases
list_users
list_roles
list_webapps type (string, optional)
get_webapp path (string, required)
check_permission resource (string, required), permission (string, required)
view_locks
view_processes namespace (string, optional)
namespace_mappings namespace (string, optional)
database_status name (string, optional)
list_user_roles username (string, required)
journal_search global_pattern (string), time_range ({from, to} ISO8601), max_records (int, default 100, max 1000)

action is the only required parameter, and its 25 values are advertised as an enum, so a client can complete them and a value outside the list comes back as INVALID_ACTION. server routes the call to a named registered instance. Every other parameter is optional at the schema level and required by the action that reads it, per the tables here.

The parameter set is closed: a filter typed as type_filter or name_filter is rejected with UNKNOWN_PARAMETER naming what the tool does accept. It used to be dropped silently, and the unfiltered result set came back looking like an answer.

view_processes and journal_search redact PHI according to dataPolicy in [policy.<server>], which defaults to block. It used to be a call parameter, which meant a caller could authorize its own bulk journal read by passing dataPolicy: "allow". That key is no longer part of the schema, so passing it is now an UNKNOWN_PARAMETER error rather than a no-op. journal_search needs dataPolicy = "allow" on the connection.

Write actions (require IRIS_WRITE_TOOLS_ENABLED=1):

Action Parameters
create_user username, password (required); full_name, roles (optional)
update_user username (required); password, enabled, roles (optional)
delete_user username (required)
create_namespace name, code_database, data_database (all required)
delete_namespace name (required)
create_webapp path, namespace, enabled (required); dispatch_class (optional)
delete_webapp path (required)
clear_password_change_flag username (default _SYSTEM), password (default SYS), new_password (optional, default same as password)
unlock_user username (required)
fresh_container_setup username (default _SYSTEM), password (default SYS), new_password (optional)
mirror_add_async mirror_name, primary_host (required); primary_port (default 2188), instance_name (default IRIS), async_member_type (0=DR, 1=ReadOnly, 2=ReadWrite; default 0)

Destructive actions (require IRIS_DESTRUCTIVE_TOOLS_ENABLED=1):

Action Parameters
mirror_failover confirm (bool, required — must be true; prevents accidental promotion)

Fresh container setup actions clear the first-boot blockers on a newly-started IRIS community container:

  • clear_password_change_flag — clears the forced-password-change flag on an account by calling %SYSTEM.Security.ChangePassword. When new_password is omitted, the same password is used for both old and new — clears the flag without changing the credential.
  • unlock_user — resets InvalidLoginAttempts to 0 via Security.Users.Modify, unblocking a locked account. Idempotent — safe to call on an account that is not locked.
  • fresh_container_setup — runs clear_password_change_flag then unlock_user in sequence. Returns {success, ready, steps[]}. Continues on per-step errors so callers see which steps succeeded. Call this once on a fresh container before any other iad tool.

Mirror management:

  • mirror_add_async — joins this IRIS instance to an existing mirror set as an async disaster-recovery member by calling SYS.Mirror.JoinMirrorAsAsyncMember in %SYS. Performs a pre-flight IsMember() check; returns ALREADY_MEMBER if already in a mirror. Version mismatches between primary and candidate surface as MIRROR_VERSION_MISMATCH.
  • mirror_failover — promotes this backup member to primary via SYS.Mirror.BecomePrimary. Irreversible without manual recovery. Requires confirm: true to prevent accidental promotion. Pre-flights: returns NOT_MIRROR_MEMBER if not in a mirror, ALREADY_PRIMARY if this instance is already the primary.
iris_admin(action="list_namespaces")
iris_admin(action="list_users")
iris_admin(action="journal_search", global_pattern="^MyApp.*",
           time_range={"from": "2025-01-01T00:00:00Z", "to": "2025-01-02T00:00:00Z"})
iris_admin(action="fresh_container_setup")
iris_admin(action="clear_password_change_flag", username="_SYSTEM", password="SYS")
iris_admin(action="unlock_user", username="_SYSTEM")
iris_admin(action="mirror_add_async", mirror_name="DR_SET", primary_host="10.0.1.5")
iris_admin(action="mirror_failover", confirm=true)

iris_containers

List, select, or start IRIS Docker containers.

Parameter Type Default Notes
action string Required. "list" | "select" | "start"
name string "" Container name; required for select, optional for start

Those two are the only parameters this tool reads. list scans $OBJECTSCRIPT_WORKSPACE (falling back to the process working directory) — there is no workspace_root override. select and start connect as _SYSTEM/SYS on the community edition and take the namespace from the container, so passing namespace, username, password, or edition has no effect.

iris_containers(action="list")
iris_containers(action="select", name="my-iris-container")

WebSocket sessions

Persistent IRIS terminal sessions over WebSocket. Requires IRIS 2023.2+ (Atelier V7 API). Each session keeps a live ObjectScript context between calls — variables set in one iris_ws_exec call are visible in the next.

Session tokens have the form ws:{server}:{NAMESPACE}:{uuid}.

iris_ws_open

Open a new WebSocket terminal session. Returns a session token to pass to subsequent calls.

Parameter Type Default Notes
namespace string "USER" Namespace for the session
server string Named server; omit for default

iris_ws_exec

Execute ObjectScript in an existing session. The session context (variables, open devices) persists across calls.

Parameter Type Default Notes
session string Required. Token from iris_ws_open
code string Required. ObjectScript to run

The per-frame timeout is a fixed 30 seconds, not a parameter. On timeout the session stays open; call iris_ws_close to release it.

iris_ws_close

Close a WebSocket session and free its resources. Passing an already-closed or expired token returns already_closed: true rather than an error.

Parameter Type Default Notes
session string Required. Token from iris_ws_open

Learning agent, skills, and knowledge base

skill

Manage the learning agent skill registry.

Parameter Type Default Notes
action string Required. "list" | "describe" | "search" | "forget" | "propose"
name string For describe/forget
query string For search

action=forget is ☠ destructive-gated — requires destructive_tools_enabled = true.

skill(action="list")
skill(action="describe", name="objectscript-review")
skill(action="search", query="status handling")
skill(action="propose")   # mine recent tool calls into a new skill
skill(action="forget", name="outdated-skill")

skill_community

Browse or install community skills from subscribed GitHub repos.

Parameter Type Default Notes
action string Required. "list" | "install"
package string For action=install

kb / kb_index / kb_recall

Index markdown/text into the IRIS knowledge base, or recall content by keyword.

kb (unified):

Parameter Type Default Notes
action string Required. "index" | "recall"
path string For action=index
query string For action=recall
top_k int 5

kb_index:

Parameter Type Default
workspace_path string

kb_recall:

Parameter Type Default Notes
query string Required.
top_k int 20
kb(action="index", path="/home/user/myapp/docs")
kb(action="recall", query="how to handle %Status errors", top_k=5)

agent_history / agent_stats

Recent tool-call history and learning agent status.

Parameter Type Default
limit int 20
agent_history(limit=50)
agent_stats()

telemetry_query

Query the durable telemetry record.

Parameter Type Default Notes
tool_name string Filter by tool
session_id string
since string ISO8601
until string ISO8601
limit int 500
telemetry_query(tool_name="iris_doc", since="2025-01-01T00:00:00Z")

telemetry_export_trace

Export tool calls as {from, to, via, count, ts} dispatch-trace records.

Parameter Type Default Notes
session_id string
since string ISO8601

Coverage

See iris_coverage and iris_test above for the full parameter reference.

Quick reference:

iris_coverage(mode="check")
iris_coverage(mode="run", package="MyApp", test_path="MyApp.Tests", target_pct=80)
iris_test(pattern="MyApp.Tests", coverage=true, coverage_target_pct=80)

Every response includes testcoverage_available. When the TestCoverage IPM package is installed, cobertura_path writes Cobertura XML output.

VS Code: The InterSystems Testing Manager extension surfaces the same %UnitTest classes in the Test Explorer view. Use iris_test to run and fix tests from Copilot or Claude; use Testing Manager to browse results in the IDE. They share the same server connection.


Tool annotations

Every tool in iad exposes MCP ToolAnnotations — machine-readable hints that MCP clients can act on before calling the tool.

Annotation Set on What it means
read_only_hint 53 tools — all query, inspect, list, history, and comparison tools The tool makes no changes to IRIS state
destructive_hint 7 tools — global_kill, iris_admin, iris_credential_manage, iris_lookup_manage, iris_namespace_create, iris_remove_server, skill_forget The tool can irreversibly delete or overwrite data

MCP clients that respect read_only_hint can run read-only tools in parallel or in background without approval prompts. Clients that respect destructive_hint can surface an extra confirmation step before calling the destructive tools.

These are hints, not enforcement. Enforcement comes from the config gates described below.


Write protection

Two config keys control which tools can write. They form a stack — the destructive tier can only further restrict the write tier, never expand it.

write_tools_enabled

Every write-capable tool returns WRITE_TOOLS_DISABLED when this is false. Set it in .iris-agentic-dev.toml:

write_tools_enabled = true

Editing the file changes the gate on the next tool call, in both directions — no restart.

Environment variable: IRIS_WRITE_TOOLS_ENABLED=1. An operator who exported it before starting iad outranks the file; the file wins over everything else. With neither declared, IRIS SystemMode decides, then the namespace. check_config reports which of those decided in write_tools_source, so an unexpected answer names its own cause.

destructive_tools_enabled

The 7 tools marked ☠ require an additional opt-in. Even with write_tools_enabled = true, they return DESTRUCTIVE_TOOLS_DISABLED unless you also set:

destructive_tools_enabled = true

Default: false, and never inferred — the destructive tier stays off until you declare it. Declaring destructive_tools_enabled = true with write_tools_enabled = false is a contradiction, so iad logs DESTRUCTIVE_REQUIRES_WRITES and exits 2 rather than serving requests with one of the two answers silently discarded.

Why a separate flag? A compile-test workflow needs write_tools_enabled = true, but there's no reason for that same session to be able to kill globals or delete namespaces. Enabling the two tiers independently means an agent that can compile can't accidentally wipe data even if it constructs a destructive call.

Environment variable: IRIS_DESTRUCTIVE_TOOLS_ENABLED=1

There is no per-server write allowlist. Restricting writes to particular named servers is designed in specs/074-write-server-allowlist/ and not implemented; until it ships, a write allowed on this instance is allowed against every server in the pool. Use separate workspaces with their own .iris-agentic-dev.toml if you need different answers per server.

Check order for write tool calls

1. write_tools_enabled        — if false, WRITE_TOOLS_DISABLED
2. destructive_tools_enabled  — if false and tool is ☠, DESTRUCTIVE_TOOLS_DISABLED
3. policy.<server>.allow      — category gate (POLICY_GATE)
4. data safety gates          — PHI, system globals, env template
5. Execute

Steps 1 and 2 run once, in call_tool, before the request reaches the tool — so a tool cannot be added without passing through them. Read-only tools skip steps 1 and 2.


Data safety gates

Gate design by Pierre Abdelsayed (InterSystems Server Manager MCP), ported to Rust.

PHI is Protected Health Information — the patient-identifying data HIPAA governs.

Some tools can reach PHI, and some can reach the globals IRIS uses to store its own code and configuration. Those calls are checked before they run and refused by default. Four checks run in order; the first one that refuses wins.

1. Environment template. A connection declares what kind of instance it points at via mcpTemplate in .iris-agentic-dev.tomldev (the default) permits everything, test blocks code execution and compiles, and live blocks those plus source control. Writes count as execution here: iris_global with action=set or kill, and iris_query with mode=write, are treated as execution even though reads from the same tools are not. Error code: ENV_GATE_BLOCKED.

2. Bulk-PHI tools. journal_search and iris_message_body return whole records, so there is no field to inspect and no safe subset to return. They are refused outright unless the connection sets dataPolicy = "allow", with no per-call override. Error code: DATA_POLICY_BLOCKED.

3. System globals. IRIS keeps compiled classes, routines, roles, users, and interoperability config in globals such as ^oddDEF, ^ROUTINE, ^%Dictionary*, ^ROLE, and ^Ens.Config*. Writing to them can leave the instance unable to compile or start. This blocklist is hardcoded and cannot be switched off — globalBlocklist in your config adds to it, never replaces it. The one exception is dataPolicyKillAllowlist, which exempts named patterns from this check on kill operations only. Error code: SYSTEM_BLOCKLIST.

4. Globals whose names suggest PHI. Reading ^PAPMI*, ^PAADM*, ^MRADM*, ^ORDER*, and similar requires acknowledgePhi: true on the call. This is a speed bump, not a lock — it exists so nobody pulls a patient record into a chat transcript by accident. Error code: PHI_GATE_BLOCKED.

iris_message_body is gated separately by dataPolicy alone: block refuses the call (PHI_POLICY_BLOCKED), allow requires acknowledgePhi: true (PHI_ACK_REQUIRED), and redact returns the body with the standard HL7 v2 patient fields replaced by [REDACTED] (PID-3, 5, 7, 8, 11, 18 and MSH-3). Redaction only recognizes HL7 v2 — anything else comes back as-is, so redact is not a safe default for XML or custom message bodies.


Common error codes

Code Meaning
POLICY_GATE Call blocked by per-connection policy — see allow in .iris-agentic-dev.toml
ENV_GATE_BLOCKED Tool not permitted by this connection's mcpTemplate — see gates
DATA_POLICY_BLOCKED Bulk-PHI tool called without dataPolicy = "allow"
SYSTEM_BLOCKLIST Global is on the system blocklist — not bypassable
PHI_GATE_BLOCKED Global name matches a PHI pattern — pass acknowledgePhi: true
SCOPE_REQUIRED iris_search called without a document scope — pass a documents wildcard list
STALE_CONTENT iris_doc insert/delete_lines expected field didn't match stored content
STORAGE_RESET_REQUIRES_CONFIRMATION iris_doc mode=put would reset an existing Storage block — pass allow_storage_regeneration: true to proceed
CODE_EDIT_BLOCKED iris_execute call matched a code-editing pattern — use iris_doc + iris_compile
CHECKIN_BLOCKED SCM CheckIn called without IRIS_SCM_ALLOW_CHECKIN=1
HTTP_EXECUTION_FAILED Atelier HTTP call failed — check host, port, credentials
CONTAINER_UNREACHABLE docker exec never reached IRIS — the container is missing or stopped; the message names which
IRIS_UNREACHABLE No IRIS connection discoverable — run check_config
INTEROP_ERROR Ensemble/interop HTTP call failed — check production state and container access
SESSION_WS_UNAVAILABLE The instance does not serve the WebSocket terminal endpoint — requires IRIS 2023.2+
SESSION_WS_DISCONNECTED Session token is invalid or already closed — call iris_ws_open to get a new token
SESSION_STALE Session outlived its idle window and was reaped — open a new one
SESSION_TIMEOUT IRIS sent no frame within 30 seconds — the statement is still running on the server
CONFIRM_REQUIRED global_kill requires a confirm_token from global_preview
CONFIRM_EXPIRED Confirmation token is older than 5 minutes — call global_preview again
CONFIRM_MISMATCH Token was issued for a different global or server
WRITE_TOOLS_DISABLED Write tool called without write_tools_enabled = true in .iris-agentic-dev.toml
DESTRUCTIVE_TOOLS_DISABLED Destructive tool (☠) called without destructive_tools_enabled = true
DESTRUCTIVE_REQUIRES_WRITES destructive_tools_enabled = true set while write_tools_enabled = false — invalid config
FETCH_FAILED compare_document could not fetch source from one or both servers
HL7_NOT_AVAILABLE EnsLib.HL7.Schema not installed on this instance