Files
mesh-sdk/c/mesh_sdk.c
T
jochen 78817c949e A tools bundle speaks MCP over stdio: the loop in TypeScript, and skeleton SDKs for Python, Go, Rust and C (hq ADR 0187)
The node's runtime launches a tools bundle as a process and speaks MCP over stdio to it, so a tool
may be written in any language and the transport stays in the runtime (ADR 0039's refusal, kept).
Each SDK is the loop and the tool type and nothing else: `@novox/mesh-sdk/stdio` (serveStdio, and
serveRegisteredOverStdio for a bundle that already registers the in-process way), python/, go/,
rust/ and c/ beside it, each with an example answering `greet` and the seat verb `node-lamp.on`.
Skeletons, by the operator's direction: the bare minimum one bundle per language needs to be built
and answer, proven here by each example answering tools/list and tools/call on stdio.
2026-10-02 20:34:19 +02:00

84 lines
3.9 KiB
C

#include "mesh_sdk.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void say(json_t *m) {
json_object_set_new(m, "jsonrpc", json_string("2.0"));
char *text = json_dumps(m, JSON_COMPACT);
fprintf(stdout, "%s\n", text);
fflush(stdout);
free(text);
json_decref(m);
}
static json_t *as_schema(json_t *given) {
if (!given || !json_is_object(given) || json_object_size(given) == 0)
return json_pack("{s:s,s:{}}", "type", "object", "properties");
if (json_object_get(given, "type") || json_object_get(given, "properties"))
return json_incref(given);
return json_pack("{s:s,s:O}", "type", "object", "properties", given);
}
int mesh_serve(const char *name, const mesh_tool *tools, size_t count) {
char *line = NULL;
size_t cap = 0;
ssize_t got;
while ((got = getline(&line, &cap, stdin)) >= 0) {
json_error_t err;
json_t *req = json_loads(line, 0, &err);
if (!req) continue;
json_t *id = json_object_get(req, "id");
const char *method = json_string_value(json_object_get(req, "method"));
json_t *params = json_object_get(req, "params");
if (!method) { json_decref(req); continue; }
if (strcmp(method, "initialize") == 0) {
say(json_pack("{s:O,s:{s:s,s:{s:{}},s:{s:s,s:s}}}", "id", id ? id : json_null(), "result",
"protocolVersion", MESH_SDK_PROTOCOL, "capabilities", "tools",
"serverInfo", "name", name, "version", "1"));
} else if (strcmp(method, "notifications/initialized") == 0) {
} else if (strcmp(method, "ping") == 0) {
if (id) say(json_pack("{s:O,s:{}}", "id", id, "result"));
} else if (strcmp(method, "tools/list") == 0) {
json_t *listed = json_array();
for (size_t i = 0; i < count; i++)
json_array_append_new(listed, json_pack("{s:s,s:s,s:o}", "name", tools[i].name,
"description", tools[i].description, "inputSchema", as_schema(tools[i].input)));
say(json_pack("{s:O,s:{s:o}}", "id", id ? id : json_null(), "result", "tools", listed));
} else if (strcmp(method, "tools/call") == 0) {
const char *wanted = params ? json_string_value(json_object_get(params, "name")) : NULL;
const mesh_tool *tool = NULL;
for (size_t i = 0; wanted && i < count; i++)
if (strcmp(tools[i].name, wanted) == 0) tool = &tools[i];
if (!tool) {
char message[256];
snprintf(message, sizeof message, "%s has no tool %s", name, wanted ? wanted : "");
say(json_pack("{s:O,s:{s:i,s:s}}", "id", id ? id : json_null(), "error", "code", -32602, "message", message));
} else {
json_t *args = params ? json_object_get(params, "arguments") : NULL;
json_t *empty = args ? NULL : json_object();
char *error = NULL;
json_t *result = tool->run(args ? args : empty, &error);
if (empty) json_decref(empty);
if (result) {
char *text = json_dumps(result, JSON_COMPACT);
say(json_pack("{s:O,s:{s:[{s:s,s:s}]}}", "id", id ? id : json_null(), "result", "content", "type", "text", "text", text));
free(text);
json_decref(result);
} else {
say(json_pack("{s:O,s:{s:[{s:s,s:s}],s:b}}", "id", id ? id : json_null(), "result", "content",
"type", "text", "text", error ? error : "the tool failed", "isError", 1));
free(error);
}
}
} else if (id) {
char message[256];
snprintf(message, sizeof message, "no %s", method);
say(json_pack("{s:O,s:{s:i,s:s}}", "id", id, "error", "code", -32601, "message", message));
}
json_decref(req);
}
free(line);
return ferror(stdin) ? -1 : 0;
}