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
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.
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, not loops
Plan-and-execute and goal-oriented search are strategies over that graph, so a run that replans still checkpoints and still resumes.
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?"))
#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?"))
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()),
)
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"))
writer calls: 1
#Adapters
| Package | What it connects |
|---|---|
moonkoog/mcp | A server's tools as this agent's tools; this agent as an MCP server. |
moonkoog/a2a | An agent as an A2A executor; a remote agent as one tool. |
moonkoog/acp | An agent behind ACP: events become session updates. |
moonkoog/rag | Retrieval as a tool, and retrieval before every call. |
moonkoog/skills | A skill listing in the instructions, a skill's body on demand. |
moonkoog/serve | Threads 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.