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.
84 lines
3.9 KiB
C
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;
|
|
}
|