Model Context Protocol
Use the tools and resources on any Model Context Protocol server over streamable HTTP, with sessions, event-stream answers and paged lists handled, and give a route the JSON-RPC answers that make your own program an MCP server for an assistant to call.
Version 1.0.0 · 21 functions · 3 crates
Functions
What use mcp.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use mcp_connect, mcp_tools from mcp (Quillo 0.6).
mcp_connect(url: str, options: any) → anyor an errorConnects to the server at url: the initialize handshake and the initialized note. Options: token (a Bearer token), headers (a map), client_name, client_version, capabilities, protocol_version, timeout_ms. Gives a session to pass to the other functions: { url, session_id, protocol_version, server, capabilities, instructions, ... }.
mcp_tools(session: any) → listor an errorEvery tool on the server, all pages: { name, description, inputSchema, ... }.
mcp_call(session: any, name: str, arguments: any) → anyor an errorCalls a tool. Gives { text, content, structured, is_error }: a tool that failed answers with is_error true and says why in text, while an unknown tool or bad arguments is a protocol error, an error value here.
mcp_resources(session: any) → listor an errorEvery resource on the server, all pages: { uri, name, mimeType, ... }.
mcp_resource_templates(session: any) → listor an errorEvery resource template (URIs with holes), all pages.
mcp_read(session: any, uri: str) → anyor an errorReads a resource. Gives { text, contents }, text joining the text parts; binary parts stay as blob (base64) in contents.
mcp_prompts(session: any) → listor an errorEvery prompt the server offers, all pages.
mcp_prompt(session: any, name: str, arguments: any) → anyor an errorOne prompt filled in with arguments (texts): { description, messages }.
mcp_ping(session: any) → boolor an errorAsks the server whether it is alive.
mcp_close(session: any) → boolor an errorEnds the session on the server. A server may answer 405 to say it does not let clients do that; that counts as closed.
mcp_parse(body: str) → anyReads a posted JSON-RPC message: { kind: "request", id, method, params }, { kind: "notification", method, params }, { kind: "response" }, or { kind: "invalid", answer }, answer being the JSON-RPC error to send with a 400. A batch is refused: MCP has none since 2025-06-18.
mcp_result(id: any, result: any) → anyThe JSON-RPC answer with result.
mcp_error(id: any, code: int, message: str) → anyThe JSON-RPC error answer: -32601 method not found, -32602 invalid params, -32603 internal error, -32002 resource not found.
mcp_initialize(id: any, params: any, server: any) → anyThe answer to initialize: the client's version when this plugin knows it, else the newest; server is { name, version, instructions, tools, resources, prompts } (true for each the route answers).
mcp_tool(name: str, description: str, input_schema: any) → anyAn entry for mcp_tools_result: a tool's name, what it does, and a JSON Schema of its input.
mcp_tools_result(id: any, tools: list) → anyThe answer to tools/list.
mcp_call_result(id: any, value: any, is_error: bool) → anyThe answer to tools/call: value a text, or anything else as JSON (given also as structuredContent when a map). is_error true tells the model the tool failed, and it can try again.
mcp_resources_result(id: any, resources: list) → anyThe answer to resources/list: resources are { uri, name, mimeType, description }.
mcp_read_result(id: any, uri: str, mime_type: str, text: str) → anyThe answer to resources/read with one text resource.
mcp_new_session() → stror an errorA hard to guess session id for a server that keeps sessions, in the visible ASCII the protocol allows: give it as the Mcp-Session-Id header of the initialize answer.
mcp_origin_ok(origin: str, allowed: list) → boolWhether a request's Origin header may reach this server, which the protocol asks a server to check so a web page cannot talk to a local one. No Origin (not a browser) passes; allowed lists exact origins.
Get it
$ quillo install mcp # mcp.qwer into this folder$ quillo install mcp --example # and its example, mcp.qweOr with curl:
$ curl -O https://quillolang.org/plugins/files/mcp.qwer$ curl -O https://quillolang.org/plugins/files/mcp.qweThen, in your program, all of it or only what you call:
use mcp.qweruse mcp_connect, mcp_tools from mcp // Quillo 0.6: only theseIt uses these crates, which cargo fetches the first time the plugin is built:
ureq = "2"serde_json = { version = "1", features = ["preserve_order"] }getrandom = "0.2"Example
// The Model Context Protocol from both ends. As a client: a server's tools and resources listed and// used. As a server: POST /mcp answers the same requests, so an assistant can call this program.//// MCP_URL=http://127.0.0.1:3001/mcp MCP_TOKEN=... quillo run mcp.qwe// then open http://localhost:8080/tools, /call, /resources, /prompts and /failures// and point an MCP client at http://localhost:8080/mcp//// MCP_URL is any server speaking streamable HTTP.use "mcp.qwer"fn sign_in() { mcp_connect(env("MCP_URL", ""), { token: env("MCP_TOKEN", ""), client_name: "quillo-example" })}route GET /tools { let s = sign_in()? let tools = mcp_tools(s)? mcp_close(s)? { server: s.server.name, version: s.protocol_version, names: map(tools, (t) => t.name) }}route GET /call { let s = sign_in()? let good = mcp_call(s, "add", { a: 2, b: 40 })? let streamed = mcp_call(s, "slow_add", { a: 1, b: 2 })? let failed = mcp_call(s, "divide", { a: 1, b: 0 })? mcp_close(s)? { text: good.text, structured: good.structured, streamed: streamed.text, failed: failed.text, is_error: failed.is_error }}route GET /resources { let s = sign_in()? let list = mcp_resources(s)? let doc = mcp_read(s, list[0].uri)? { uris: map(list, (r) => r.uri), text: doc.text, templates: map(mcp_resource_templates(s)?, (t) => t.uriTemplate) }}route GET /prompts { let s = sign_in()? { names: map(mcp_prompts(s)?, (p) => p.name), filled: mcp_prompt(s, "greet", { name: "Ada" })?.messages[0].content.text, alive: mcp_ping(s)? }}route GET /failures { let s = sign_in()? let gone = s mcp_close(gone)? { unknown_tool: mcp_call(sign_in()?, "no_such_tool", {}).error, missing_file: mcp_read(sign_in()?, "file:///none").error, closed: mcp_tools(gone).error, no_token: mcp_connect(env("MCP_URL", ""), {}).error, nobody: mcp_connect("http://127.0.0.1:9/mcp", {}).error != null, }}// This program as a server: one tool and one resource, for any MCP client.let notes = { "note://welcome": "Welcome to the shop." }route POST /mcp { if !mcp_origin_ok(request.headers.origin ?? "", ["http://localhost:8080"]) { reply 403 "this origin may not call this server" } let m = mcp_parse(request.body) if m.kind == "invalid" { reply 400 m.answer } if m.kind != "request" { reply 202 "" } let id = m.id if m.method == "initialize" { return mcp_initialize(id, m.params, { name: "shop", version: "1.0.0", tools: true, resources: true }) } if m.method == "ping" { return mcp_result(id, {}) } if m.method == "tools/list" { return mcp_tools_result(id, [mcp_tool("price", "The price of a product by sku", { type: "object", properties: { sku: { type: "string" } }, required: ["sku"] })]) } if m.method == "tools/call" { if m.params.name != "price" { return mcp_error(id, -32602, `Unknown tool: ${m.params.name}`) } let sku = m.params.arguments.sku if sku != "A1" { return mcp_call_result(id, `no product ${sku}`, true) } return mcp_call_result(id, { sku: sku, cents: 1999 }, false) } if m.method == "resources/list" { return mcp_resources_result(id, [{ uri: "note://welcome", name: "Welcome note", mimeType: "text/plain" }]) } if m.method == "resources/read" { let text = notes[m.params.uri] if text == null { return mcp_error(id, -32002, "Resource not found") } return mcp_read_result(id, m.params.uri, "text/plain", text) } mcp_error(id, -32601, `Method not found: ${m.method}`)}route GET /session-id = { length: len(mcp_new_session()?) }$ quillo run mcp.qwe$ curl 'localhost:8080/tools'$ curl 'localhost:8080/call'$ curl 'localhost:8080/resources'$ curl 'localhost:8080/prompts'$ curl 'localhost:8080/failures'$ curl 'localhost:8080/mcp'$ curl 'localhost:8080/session-id'It talks to the Redis that REDIS_URL names, else Redis on this machine. The site's test runs it against a stand-in.
Source
//! A Model Context Protocol client over streamable HTTP, with JSON and event-stream answers and paged lists, for tools and resources on another server; and the JSON-RPC answers a route gives to serve those requests itself.//! version: 1.0.0//!//! [dependencies]//! ureq = "2"//! serde_json = { version = "1", features = ["preserve_order"] }//! getrandom = "0.2"use serde_json::{json, Value};use std::io::{BufRead, BufReader};use std::time::Duration;// The newest revision this plugin speaks, then the older ones it can also answer.const VERSIONS: &[&str] = &["2025-11-25", "2025-06-18", "2025-03-26"];fn txt(o: &Value, k: &str) -> Option<String> { o.get(k).and_then(|v| v.as_str()).filter(|s| !s.is_empty()).map(str::to_string)}fn timeout(s: &Value) -> Duration { Duration::from_millis(s.get("timeout_ms").and_then(|v| v.as_i64()).unwrap_or(30_000).clamp(100, 600_000) as u64)}fn next_id() -> i64 { use std::sync::atomic::{AtomicI64, Ordering}; static N: AtomicI64 = AtomicI64::new(1); N.fetch_add(1, Ordering::Relaxed)}fn prepare(s: &Value, method: &str) -> Result<ureq::Request, String> { let url = txt(s, "url").ok_or("the session has no url: use what mcp_connect gave")?; let agent = ureq::AgentBuilder::new().timeout_connect(Duration::from_secs(10)).timeout_read(timeout(s)).build(); let mut r = agent.request(method, &url).set("Accept", "application/json, text/event-stream").set("User-Agent", "quillo-mcp/1.0"); if let Some(t) = txt(s, "token") { r = r.set("Authorization", &format!("Bearer {}", t)); } if let Some(i) = txt(s, "session_id") { r = r.set("Mcp-Session-Id", &i); } if let Some(v) = txt(s, "protocol_version") { r = r.set("MCP-Protocol-Version", &v); } if let Some(Value::Object(h)) = s.get("headers") { for (k, v) in h { if let Some(t) = v.as_str() { r = r.set(k, t); } } } Ok(r)}fn transport_error(s: &Value, e: ureq::Error) -> String { let url = txt(s, "url").unwrap_or_default(); match e { ureq::Error::Status(404, _) if txt(s, "session_id").is_some() => "the server no longer knows this session: connect again".into(), ureq::Error::Status(401, _) => format!("{} answered 401: this server wants a token (the option `token`)", url), ureq::Error::Status(code, r) => format!("{} answered {}: {}", url, code, r.into_string().unwrap_or_default().chars().take(300).collect::<String>()), e => format!("cannot reach {}: {}", url, e), }}// An answer is one JSON message, or an event stream that ends with the message of this id.fn read_answer(resp: ureq::Response, id: i64) -> Result<Value, String> { let ctype = resp.content_type().to_string(); if ctype == "text/event-stream" { let mut lines = BufReader::new(resp.into_reader()); let mut line = String::new(); let mut data = String::new(); loop { line.clear(); let n = lines.read_line(&mut line).map_err(|e| format!("the stream broke: {}", e))?; let t = line.trim_end_matches(['\r', '\n']); if n == 0 || t.is_empty() { if !data.is_empty() { let v: Value = serde_json::from_str(&data).map_err(|e| format!("a message on the stream is not JSON: {}", e))?; data.clear(); // Progress notes and the server's own requests may come first. if v["id"] == json!(id) && v.get("method").is_none() { return Ok(v); } } if n == 0 { return Err("the stream ended with no answer to the request".into()); } continue; } if let Some(d) = t.strip_prefix("data:") { if !data.is_empty() { data.push('\n'); } data.push_str(d.strip_prefix(' ').unwrap_or(d)); } } } let t = resp.into_string().map_err(|e| format!("reading the answer: {}", e))?; serde_json::from_str(&t).map_err(|_| format!("the answer is not JSON: {:?}", t.chars().take(200).collect::<String>()))}fn rpc(s: &Value, method: &str, params: Value) -> Result<Value, String> { let id = next_id(); let msg = json!({ "jsonrpc": "2.0", "id": id, "method": method, "params": params }); let resp = prepare(s, "POST")?.set("Content-Type", "application/json").send_string(&msg.to_string()).map_err(|e| transport_error(s, e))?; result_of(read_answer(resp, id)?, method)}fn result_of(v: Value, method: &str) -> Result<Value, String> { if let Some(e) = v.get("error") { return Err(format!("{}: {} ({})", method, e["message"].as_str().unwrap_or("error"), e["code"])); } v.get("result").cloned().ok_or(format!("the answer to {} has no result", method))}fn notify(s: &Value, method: &str) -> Result<(), String> { let msg = json!({ "jsonrpc": "2.0", "method": method }); prepare(s, "POST")?.set("Content-Type", "application/json").send_string(&msg.to_string()).map_err(|e| transport_error(s, e))?; Ok(())}/// Connects to the server at `url`: the initialize handshake and the `initialized` note. Options: token (a Bearer token), headers (a map), client_name, client_version, capabilities, protocol_version, timeout_ms. Gives a session to pass to the other functions: { url, session_id, protocol_version, server, capabilities, instructions, ... }.pub fn mcp_connect(url: &str, options: serde_json::Value) -> Result<serde_json::Value, String> { let mut s = json!({ "url": url }); for k in ["token", "headers", "timeout_ms"] { if let Some(v) = options.get(k) { s[k] = v.clone(); } } let want = txt(&options, "protocol_version").unwrap_or_else(|| VERSIONS[0].to_string()); let id = next_id(); let msg = json!({ "jsonrpc": "2.0", "id": id, "method": "initialize", "params": { "protocolVersion": want, "capabilities": options.get("capabilities").cloned().unwrap_or(json!({})), "clientInfo": { "name": txt(&options, "client_name").unwrap_or_else(|| "quillo".into()), "version": txt(&options, "client_version").unwrap_or_else(|| "1.0.0".into()) }, } }); let resp = prepare(&s, "POST")?.set("Content-Type", "application/json").send_string(&msg.to_string()).map_err(|e| transport_error(&s, e))?; let session = resp.header("mcp-session-id").map(str::to_string); let r = result_of(read_answer(resp, id)?, "initialize")?; let got = r["protocolVersion"].as_str().unwrap_or("").to_string(); if !VERSIONS.contains(&got.as_str()) { return Err(format!("the server speaks MCP {:?}; this plugin speaks {}", got, VERSIONS.join(", "))); } s["protocol_version"] = json!(got); if let Some(i) = session { s["session_id"] = json!(i); } s["server"] = r["serverInfo"].clone(); s["capabilities"] = r["capabilities"].clone(); s["instructions"] = r.get("instructions").cloned().unwrap_or(Value::Null); notify(&s, "notifications/initialized")?; Ok(s)}// Follows nextCursor to the end of a list.fn all(s: &Value, method: &str, key: &str) -> Result<Vec<Value>, String> { let mut out = vec![]; let mut cursor: Option<String> = None; for _ in 0..1000 { let params = cursor.as_ref().map(|c| json!({ "cursor": c })).unwrap_or(json!({})); let r = rpc(s, method, params)?; out.extend(r[key].as_array().cloned().unwrap_or_default()); match r["nextCursor"].as_str() { Some(c) if !c.is_empty() => cursor = Some(c.to_string()), _ => return Ok(out), } } Err(format!("{} never ended", method))}/// Every tool on the server, all pages: { name, description, inputSchema, ... }.pub fn mcp_tools(session: serde_json::Value) -> Result<Vec<serde_json::Value>, String> { all(&session, "tools/list", "tools")}/// Calls a tool. Gives { text, content, structured, is_error }: a tool that failed answers with is_error true and says why in `text`, while an unknown tool or bad arguments is a protocol error, an error value here.pub fn mcp_call(session: serde_json::Value, name: &str, arguments: serde_json::Value) -> Result<serde_json::Value, String> { let args = if arguments.is_null() { json!({}) } else { arguments }; let r = rpc(&session, "tools/call", json!({ "name": name, "arguments": args }))?; let text: Vec<&str> = r["content"].as_array().map(|l| l.iter().filter(|b| b["type"] == "text").filter_map(|b| b["text"].as_str()).collect()).unwrap_or_default(); Ok(json!({ "text": text.join("\n"), "content": r["content"], "structured": r.get("structuredContent").cloned().unwrap_or(Value::Null), "is_error": r["isError"] == true }))}/// Every resource on the server, all pages: { uri, name, mimeType, ... }.pub fn mcp_resources(session: serde_json::Value) -> Result<Vec<serde_json::Value>, String> { all(&session, "resources/list", "resources")}/// Every resource template (URIs with holes), all pages.pub fn mcp_resource_templates(session: serde_json::Value) -> Result<Vec<serde_json::Value>, String> { all(&session, "resources/templates/list", "resourceTemplates")}/// Reads a resource. Gives { text, contents }, `text` joining the text parts; binary parts stay as `blob` (base64) in `contents`.pub fn mcp_read(session: serde_json::Value, uri: &str) -> Result<serde_json::Value, String> { let r = rpc(&session, "resources/read", json!({ "uri": uri }))?; let text: Vec<&str> = r["contents"].as_array().map(|l| l.iter().filter_map(|b| b["text"].as_str()).collect()).unwrap_or_default(); Ok(json!({ "text": text.join("\n"), "contents": r["contents"] }))}/// Every prompt the server offers, all pages.pub fn mcp_prompts(session: serde_json::Value) -> Result<Vec<serde_json::Value>, String> { all(&session, "prompts/list", "prompts")}/// One prompt filled in with `arguments` (texts): { description, messages }.pub fn mcp_prompt(session: serde_json::Value, name: &str, arguments: serde_json::Value) -> Result<serde_json::Value, String> { let args = if arguments.is_null() { json!({}) } else { arguments }; rpc(&session, "prompts/get", json!({ "name": name, "arguments": args }))}/// Asks the server whether it is alive.pub fn mcp_ping(session: serde_json::Value) -> Result<bool, String> { rpc(&session, "ping", json!({})).map(|_| true)}/// Ends the session on the server. A server may answer 405 to say it does not let clients do that; that counts as closed.pub fn mcp_close(session: serde_json::Value) -> Result<bool, String> { if txt(&session, "session_id").is_none() { return Ok(true); } match prepare(&session, "DELETE")?.call() { Ok(_) | Err(ureq::Error::Status(405, _)) | Err(ureq::Error::Status(404, _)) => Ok(true), Err(e) => Err(transport_error(&session, e)), }}// The serving side: a route gives these the body it got and answers with what comes back./// Reads a posted JSON-RPC message: { kind: "request", id, method, params }, { kind: "notification", method, params }, { kind: "response" }, or { kind: "invalid", answer }, `answer` being the JSON-RPC error to send with a 400. A batch is refused: MCP has none since 2025-06-18.pub fn mcp_parse(body: &str) -> serde_json::Value { let bad = |code: i64, msg: &str, id: Value| json!({ "kind": "invalid", "answer": { "jsonrpc": "2.0", "id": id, "error": { "code": code, "message": msg } } }); let Ok(v) = serde_json::from_str::<Value>(body) else { return bad(-32700, "Parse error", Value::Null) }; if !v.is_object() { return bad(-32600, "Invalid Request: send one JSON-RPC message", Value::Null); } let id = v.get("id").cloned().unwrap_or(Value::Null); if v["jsonrpc"] != "2.0" { return bad(-32600, "Invalid Request: jsonrpc must be \"2.0\"", id); } // MCP's ids are strings or integers, never null. let id_ok = id.is_string() || id.is_i64() || id.is_u64(); match (v.get("method").and_then(|m| m.as_str()), v.get("id")) { (Some(m), None) => json!({ "kind": "notification", "method": m, "params": v.get("params").cloned().unwrap_or(json!({})) }), (Some(m), Some(_)) if id_ok => json!({ "kind": "request", "id": id, "method": m, "params": v.get("params").cloned().unwrap_or(json!({})) }), (Some(_), Some(_)) => bad(-32600, "Invalid Request: an id is a string or an integer", Value::Null), (None, Some(_)) if v.get("result").is_some() || v.get("error").is_some() => json!({ "kind": "response", "id": id }), _ => bad(-32600, "Invalid Request: no method", id), }}/// The JSON-RPC answer with `result`.pub fn mcp_result(id: serde_json::Value, result: serde_json::Value) -> serde_json::Value { json!({ "jsonrpc": "2.0", "id": id, "result": result })}/// The JSON-RPC error answer: -32601 method not found, -32602 invalid params, -32603 internal error, -32002 resource not found.pub fn mcp_error(id: serde_json::Value, code: i64, message: &str) -> serde_json::Value { json!({ "jsonrpc": "2.0", "id": id, "error": { "code": code, "message": message } })}/// The answer to `initialize`: the client's version when this plugin knows it, else the newest; `server` is { name, version, instructions, tools, resources, prompts } (true for each the route answers).pub fn mcp_initialize(id: serde_json::Value, params: serde_json::Value, server: serde_json::Value) -> serde_json::Value { let asked = params["protocolVersion"].as_str().unwrap_or(""); let version = if VERSIONS.contains(&asked) { asked } else { VERSIONS[0] }; let mut caps = json!({}); for k in ["tools", "resources", "prompts"] { if server[k] == true { caps[k] = json!({}); } } let mut r = json!({ "protocolVersion": version, "capabilities": caps, "serverInfo": { "name": server["name"].as_str().unwrap_or("quillo"), "version": server["version"].as_str().unwrap_or("1.0.0") }, }); if let Some(i) = txt(&server, "instructions") { r["instructions"] = json!(i); } mcp_result(id, r)}/// An entry for mcp_tools_result: a tool's name, what it does, and a JSON Schema of its input.pub fn mcp_tool(name: &str, description: &str, input_schema: serde_json::Value) -> serde_json::Value { let schema = if input_schema.is_null() { json!({ "type": "object" }) } else { input_schema }; json!({ "name": name, "description": description, "inputSchema": schema })}/// The answer to `tools/list`.pub fn mcp_tools_result(id: serde_json::Value, tools: Vec<serde_json::Value>) -> serde_json::Value { mcp_result(id, json!({ "tools": tools }))}/// The answer to `tools/call`: `value` a text, or anything else as JSON (given also as structuredContent when a map). `is_error` true tells the model the tool failed, and it can try again.pub fn mcp_call_result(id: serde_json::Value, value: serde_json::Value, is_error: bool) -> serde_json::Value { let text = match &value { Value::String(s) => s.clone(), other => other.to_string(), }; let mut r = json!({ "content": [{ "type": "text", "text": text }] }); if value.is_object() { r["structuredContent"] = value; } if is_error { r["isError"] = json!(true); } mcp_result(id, r)}/// The answer to `resources/list`: `resources` are { uri, name, mimeType, description }.pub fn mcp_resources_result(id: serde_json::Value, resources: Vec<serde_json::Value>) -> serde_json::Value { mcp_result(id, json!({ "resources": resources }))}/// The answer to `resources/read` with one text resource.pub fn mcp_read_result(id: serde_json::Value, uri: &str, mime_type: &str, text: &str) -> serde_json::Value { mcp_result(id, json!({ "contents": [{ "uri": uri, "mimeType": mime_type, "text": text }] }))}/// A hard to guess session id for a server that keeps sessions, in the visible ASCII the protocol allows: give it as the Mcp-Session-Id header of the initialize answer.pub fn mcp_new_session() -> Result<String, String> { let mut b = [0u8; 16]; getrandom::getrandom(&mut b).map_err(|e| e.to_string())?; Ok(b.iter().map(|x| format!("{:02x}", x)).collect())}/// Whether a request's Origin header may reach this server, which the protocol asks a server to check so a web page cannot talk to a local one. No Origin (not a browser) passes; `allowed` lists exact origins.pub fn mcp_origin_ok(origin: &str, allowed: Vec<String>) -> bool { origin.is_empty() || allowed.iter().any(|a| a == origin)}Related
anthropic.qwer · 1.0.0Ask Claude through Anthropic's Messages API with no SDK: tools and tool results sent back in the shape the API wants, streams collected into one answer, tokens counted, prompts marked for caching with the cache's hits read back, and 429s and 529s tried again.4 views · 8 downloadsOpenAI openai.qwer · 1.0.0Chat completions, the Responses API and embeddings from OpenAI or any server that speaks its API, chosen by OPENAI_BASE_URL: tool calls parsed and answered, streams put back together piece by piece, 429s retried as the headers say and a spent quota not retried at all.3 views · 10 downloadsGoogle Gemini gemini.qwer · 1.0.0Google's Gemini API with no SDK: generateContent with a system instruction and function calls, streams collected, embedContent and batches of it, token counts, a blocked prompt told apart from a stop for safety, and 429s retried after the delay the error names.3 views · 9 downloadsOllama ollama.qwer · 1.0.0A model on your own machine through Ollama: generate and chat with their streams collected and timings turned into tokens per second, tool calls, JSON output, embeddings, what is installed and loaded, pulls, and a clear word when the server is stopped or the model is missing.2 views · 8 downloadsThread 0 posts
On Model Context Protocol: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.