Model Context Protocol servers for MoonBit.
async fn main {
let mcp = @moonmcp.Server::new("demo", version="1.0.0")
mcp.tool(
"add",
args => {
guard args.get("a") is Some(Number(a, ..)) &&
args.get("b") is Some(Number(b, ..)) else {
fail("a and b must be numbers")
}
{ "sum": Json(a + b) }
},
description="Add two numbers",
input_schema={
"type": "object",
"properties": { "a": { "type": "number" }, "b": { "type": "number" } },
"required": ["a", "b"],
},
)
mcp.resource("config://app", () => "{\"theme\":\"dark\"}", mime_type="application/json")
@mooncat.serve(mcp.to_asgi(), port=12000) catch {
e => println("stopped: \{e}")
}
}| Call | Offers |
|---|---|
| tool(name, handler, input_schema=, output_schema=, description=, annotations=) | A tool. The handler returns any JSON value: an object becomes structured content as well as text. |
| resource(uri, read, mime_type=) | A resource read as text. |
| template(uri_template, read) | Every resource an RFC 6570 template names; read gets the matched parameters. |
| prompt(name, render, arguments=) | A prompt rendered as one user message. |
| add_tool, add_resource, add_template, add_prompt | A component built in full with Tool::new, Resource::new, ResourceTemplate::new or Prompt::new. |
#mcp.tool(description="Add two numbers")
fn add(a : Double, b : Double) -> Double {
a + b
}
#mcp.tool(name="word_count")
fn count(text : String, unique~ : Bool = false) -> Int {
...
}
#mcp.resource("users://{id}/profile")
fn profile(id : Int) -> String {
"profile of user \{id}"
}
#mcp.prompt(description="Ask for a code review")
fn review(code : String, focus? : String) -> String {
...
}rule(name: "mcp", command: "moonx moonbitstack/moonmcp/cmd/mcp@0.1.1 -C \"$mod_dir\" \"$input\" -o \"$output\"")dev_build(rule: "mcp", input: "tools.mbt", output: "tools.g.mbt")
formatter(ignore: [ "tools.g.mbt" ])| Transport | Call | Notes |
|---|---|---|
| Streamable HTTP | to_asgi(path="/mcp", origins=[]) | An ASGI application. One POST per message, answered with one JSON object or 202. Version, method and name headers are checked against the body; a foreign Origin is refused with 403. |
| stdio | run_stdio() | One message per line on standard input and output. Native. |
| Any line stream | serve_lines(read, write) | The stdio loop over streams of your own. |
| In process | handle(message) | One JSON-RPC message in, the reply out; None for a notification. |
| Example | Shows |
|---|---|
| demo | One server with two tools, a resource, a resource template and a prompt |
| http | That server over Streamable HTTP: moon run src/examples/http --target native |
| stdio | That server over stdio: moon run src/examples/stdio --target native |
| annotations | Tools, resources, a template and a prompt written as #mcp.* annotations, served over stdio |
| Package | What it holds |
|---|---|
| moonmcp | The names a server is written with: Server, Tool, Resource, Prompt and the rest |
| server | Server: registration, dispatch for both protocol eras, Streamable HTTP, stdio; decode, which reads an argument as a parameter's type |
| cmd/mcp | The generator behind #mcp.tool, #mcp.resource and #mcp.prompt |
| types | The wire model: content blocks, tools, prompts, resources, capabilities, results |
| tools, resources, prompts | The components a server offers |
| exceptions | FastMCPError and the MCP error codes it maps to |
| component, tasks, settings, config, utilities | Component metadata, task configuration, settings, the mcp.json client configuration, media helpers |
moon add moonbitstack/moonmcp