mdlang
Documentation

Start here

How it works

Three stages, each with a clear boundary: what you author, what the build step produces, and what happens on a call.

The schema lives in your repo, a codegen step produces a fully-typed client, and the runtime executes against your config.

If that sounds like Prisma, that’s deliberate — mdlang borrows its shape. The difference is what sits at the end of the pipeline: instead of a database, an agent with a specific set of tools.

1 · You write

actions/*.md

Frontmatter declares the name, arguments, output shape, and allowed tools. The body becomes the agent's task instruction.

2 · Build step

npx mdlang generate

Each action becomes a .ts module holding an input schema, an output schema, and a config object, plus an index.ts exposing a typed action(name, args).

3 · Runtime

await action(name, args)

Arguments are validated, a LangChain agent is built with your model, only that action's tools are attached, and a structured response comes back.

What generate does

For each action file, codegen emits a module containing three exports: the input schema built from args, the output schema built from output, and a config object carrying the instruction and the tool allowlist. Alongside them it writes an index.ts exposing a typed action(name, args) function plus ActionMap and ActionName.

actions/generated/├── index.ts                typed action() wrapper + ActionMap, ActionName└── create-user.ts          input schema, output schema, runtime config

Codegen also connects to every MCP server in your config and checks each tool an action references. This is the step that catches a renamed tool: you find out at build time, with the server and tool named in the warning, rather than when a request fails in production.

Why it survives bundling

The generated client wires your config, your system prompt, and every action module through static imports. Nothing is looked up on the filesystem and nothing is transpiled at call time.

That’s what lets an action run inside a Next.js route, an esbuild bundle, or a serverless function — environments where reading a .md file off disk at runtime would fail.

What happens on a call

  • Your arguments are validated against the generated input schema. Anything that doesn’t match throws immediately, with the failing fields named.
  • Arguments with a default are filled in before the agent runs.
  • A LangChain agent is constructed with your configured model, your MDLANG.mdsystem prompt, and the action’s instruction.
  • Only the tools the action declared are attached. MCP servers it names are connected, then torn down when the run finishes.
  • The response is parsed against the output schema, and any remaining $ context references are resolved before the value is returned.