mdlang
Documentation

Writing actions

Context variables

The agent does not need to see your data in order to route it. Arguments enter a run as named variables, and the runtime substitutes real values in the moment before a tool call executes.

The model works with the names of your values. The runtime supplies the values.

How substitution works

Every argument becomes a $-prefixed entry in the run’s context. When the model calls a tool and passes a variable name, the runtime swaps in the value on the way out.

args { password: "correct-horse…" }   →   ctx { $password: "correct-horse…" }

In the transcript

hashPassword({ raw: "$password" })

What the runtime sends

hashPassword({ raw: "correct-horse…" })

What that buys you:

  • Payloads stay out of your prompt, and therefore out of any provider’s logs.
  • Token counts stay flat regardless of how large the values are — a short id and a large document cost the same to route.

Capturing results back into context

Tool results can flow the same way. Declare which fields of a response to capture and they become new variables, without the payload ever reaching the model.

responseMaps — for MCP tools

mdlang.config.ts
mcpServers: {  Postgres: {    command: "npx",    args: ["-y", "some-postgres-mcp-server"],    // After `insert` runs, capture two fields into context.    responseMaps: {      insert: { $userId: "rows[0].id", $planType: "rows[0].plan" },    },  },},

With a responseMap in place, the agent is told only ok. captured: $userId, $planType instead of the payload. The next tool call — sending the welcome email — can reference $userId without the row having passed through the model.

contextOutput — for custom tools

mdlang.config.ts
custom: {  hashPassword: Object.assign(    tool(async ({ raw }) => bcrypt.hash(raw, 10), { /* … */ }),    { contextOutput: { $hashedPassword: "." } }, // "." captures the whole result  ),},

Path expressions

Both responseMaps and contextOutput use the same path syntax to pick fields out of a result.

ExpressionSelects
fieldA top-level field.
nested.fieldA field one or more levels down.
array[0].fieldA field of a specific element.
array[].fieldThat field from every element — extracts a column.
.The whole result. An empty string does the same.