// The guide's three servers answer the same requests. This program prints the
// answers the page records, and fails when one of the three differs.
///|
let meta : Json = {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {},
}
///|
fn message(id : Int32, op : String, params : Map[String, Json]) -> Json {
let params = params.copy()
params["_meta"] = meta
{
"jsonrpc": "2.0",
"id": Json(id),
"method": Json(op),
"params": Json(params),
}
}
///|
/// The names under `key` of a listing, in order.
fn names(result : Map[String, Json], key : String, field : String) -> String {
guard result.get(key) is Some(Array(items)) else { return "" }
items
.filter_map(fn(item) {
if item is Object(fields) && fields.get(field) is Some(String(name)) {
Some(name)
} else {
None
}
})
.join(",")
}
///|
/// The text of the first block of `key`, which holds a list of things with a
/// `text` field.
fn first_text(result : Map[String, Json], key : String) -> String {
guard result.get(key) is Some(Array(items)) &&
items.length() > 0 &&
items[0] is Object(item) &&
item.get("text") is Some(String(text)) else {
return ""
}
text
}
///|
/// A body on one line, with its line breaks written as `\n`.
fn one_line(text : String) -> String {
text.split("\n").join("\\n")
}
///|
/// A reply, in the one line the page shows.
fn describe(reply : Json) -> String {
guard reply is Object(o) else { return "no reply" }
if o.get("error") is Some(Object(error)) {
return match error.get("code") {
Some(Number(code, ..)) => "error \{code.to_int()}"
_ => "error"
}
}
guard o.get("result") is Some(Object(result)) else { return "no result" }
if result.get("tools") is Some(_) {
return names(result, "tools", "name")
}
if result.get("prompts") is Some(_) {
return names(result, "prompts", "name")
}
if result.get("resources") is Some(_) {
return names(result, "resources", "uri")
}
if result.get("resourceTemplates") is Some(_) {
return names(result, "resourceTemplates", "uriTemplate")
}
if result.get("contents") is Some(_) {
return one_line(first_text(result, "contents"))
}
if result.get("messages") is Some(_) {
guard result.get("messages") is Some(Array(messages)) &&
messages.length() > 0 &&
messages[0] is Object(message) &&
message.get("content") is Some(Object(content)) &&
content.get("text") is Some(String(text)) else {
return ""
}
return one_line(text)
}
one_line(first_text(result, "content"))
}
///|
/// One request of the guide: what the page calls it, the method, the params.
fn cases() -> Array[(String, String, Map[String, Json])] {
let empty : Map[String, Json] = Map([])
[
("tools/list", "tools/list", empty),
(
"tools/call add {\"a\":2,\"b\":3}",
"tools/call",
{ "name": "add", "arguments": { "a": 2, "b": 3 } },
),
(
"tools/call greet {\"name\":\"leo\",\"language\":\"zh\"}",
"tools/call",
{ "name": "greet", "arguments": { "name": "leo", "language": "zh" } },
),
("prompts/list", "prompts/list", empty),
(
"prompts/get review {\"code\":\"fn f() { 1 }\"}",
"prompts/get",
{ "name": "review", "arguments": { "code": "fn f() { 1 }" } },
),
("resources/list", "resources/list", empty),
("resources/templates/list", "resources/templates/list", empty),
(
"resources/read {\"uri\":\"config://app\"}",
"resources/read",
{ "uri": "config://app" },
),
(
"resources/read {\"uri\":\"users://7/profile\"}",
"resources/read",
{ "uri": "users://7/profile" },
),
(
"tools/call count {\"up_to\":2}",
"tools/call",
{ "name": "count", "arguments": { "up_to": 2 } },
),
(
"tools/call unknown {\"name\":\"nope\"}",
"tools/call",
{ "name": "nope", "arguments": empty },
),
]
}
///|
fn common_cases() -> Array[(String, String, Map[String, Json])] {
cases().filter(fn(entry) { !entry.0.has_prefix("tools/call count") })
}
///|
fn context_cases() -> Array[(String, String, Map[String, Json])] {
cases().filter(fn(entry) { entry.0.has_prefix("tools/call count") })
}
///|
/// The answers of one server to `requests`, one line per request.
async fn answers(
mcp : @moonmcp.Server,
requests : Array[(String, String, Map[String, Json])],
) -> Array[String] {
let lines : Array[String] = []
let mut id = 0
for entry in requests {
let (label, op, params) = entry
id = id + 1
let reply = mcp.handle(message(id, op, params)) catch { _ => None }
lines.push(label + " -> " + describe_or(reply))
}
lines
}
///|
fn describe_or(reply : Json?) -> String {
match reply {
Some(reply) => describe(reply)
None => "no reply"
}
}
///|
/// The answers of the extended servers, which hold the greeting back.
async fn extended_answers(mcp : @moonmcp.Server) -> Array[String] {
let lines : Array[String] = []
let listed = mcp.handle(message(1, "tools/list", Map([]))) catch { _ => None }
lines.push("tools/list -> " + describe_or(listed))
let called = mcp.handle(
message(2, "tools/call", { "name": "add", "arguments": { "a": 2, "b": 3 } }),
) catch {
_ => None
}
lines.push("tools/call add -> " + describe_or(called))
lines
}
///|
/// Print `lines`, and the differences `other` has from them.
fn report(name : String, lines : Array[String], other : Array[String]) -> Bool {
let same = lines == other
if !same {
println("DIFFERS: " + name)
for i, line in other {
if i >= lines.length() || line != lines[i] {
println(" " + line)
}
}
}
same
}
///|
async fn main {
let common = common_cases()
let base = answers(@ann.server(), common)
println("[common]")
for line in base {
println(line)
}
let mut same = report("functions", base, answers(@fns.server(), common))
same = report("classes", base, answers(@cls.server(), common)) && same
let context = context_cases()
let context_base = answers(@fns.with_counter(), context)
println("[context]")
for line in context_base {
println(line)
}
same = report("classes", context_base, answers(@cls.with_counter(), context)) &&
same
let extended = extended_answers(@fns.extended())
println("[extended]")
for line in extended {
println(line)
}
same = report("extended classes", extended, extended_answers(@cls.extended())) &&
same
println("[same] \{same}")
}