moonkoog

An agent framework for MoonBit. A run is a state graph: it checkpoints, it resumes, and it stops where you tell it to. Around that graph sit the prebuilt agents, the middleware pipeline, planning, teams, and the adapters that put an agent behind MCP, A2A, ACP and the Agent Protocol.

$ moon add moonbitstack/moonkoog
graph

One engine

The loop, the checkpoint, the interrupt, the sub-agent and the team are all the same graph. There is no second orchestration layer to learn.

pipeline

Middleware as a chain

Each model call and tool call passes through a chain that takes the rest as a function. Retry, fallback, limits, summarising and redaction are one shape.

plans

Plans, not loops

Plan-and-execute and goal-oriented search are strategies over that graph, so a run that replans still checkpoints and still resumes.

wire

Every direction

An agent served over MCP, reached over A2A, exported over ACP, or answering the Agent Protocol. Each adapter is a translation, not a fork.

#First run

A provider, a model and one question. The provider here is scripted, which is what a test โ€” or this page โ€” uses when there is no key.

let provider = @fake.Fake::text("A state graph is a program made of nodes.")
let agent = @moonkoog.create_agent(
  provider,
  @moonkoog.AgentConfig::new("scripted").with_instructions("Answer in one sentence."),
)
println(agent.run("What is a state graph?"))
A state graph is a program made of nodes.

#Tools

A tool states its schema and its body. The model asks for it by name, the run executes it, the result goes back into the conversation.

let agent = @moonkoog.create_agent(
  provider,
  @moonkoog.AgentConfig::new("scripted").with_tools([@moonkoog.calculator()]),
)
println(agent.run("What is 6 times 7?"))
The answer is 42.
tool said: 42

#The pipeline

Middleware wraps the calls a run makes. This run retries a gateway error, caps how many calls it may spend, and scrubs an address before the provider sees it.

let agent = @moonkoog.create_agent(
  provider,
  @moonkoog.AgentConfig::new("scripted")
  .with_middleware(@middleware.ModelRetry::new(attempts=2, base_ms=1))
  .with_middleware(@middleware.ModelCallLimit::new(limit=3))
  .with_middleware(@middleware.Pii::standard()),
)
answer: answered on the second try
calls made: 2
first message: write to [email]

#Teams

A member of a team is a nested graph over the same conversation. It hands the conversation on with a tool, and the team's router follows.

let team = @moonkoog.create_agent(
  provider,
  @moonkoog.swarm(@moonkoog.AgentConfig::new("scripted", name="team"), [researcher, writer]),
)
println(team.run("write a line about graphs"))
team answer: The writer wrote this.
writer calls: 1

#Adapters

PackageWhat it connects
moonkoog/mcpA server's tools as this agent's tools; this agent as an MCP server.
moonkoog/a2aAn agent as an A2A executor; a remote agent as one tool.
moonkoog/acpAn agent behind ACP: events become session updates.
moonkoog/ragRetrieval as a tool, and retrieval before every call.
moonkoog/skillsA skill listing in the instructions, a skill's body on demand.
moonkoog/serveThreads and runs over the Agent Protocol.

#Where to go next

The guide walks from a first run to a team behind a protocol. The API reference lists every public item. The examples run offline, on a scripted provider.