summaryrefslogtreecommitdiffstats
path: root/src/mcp.h
diff options
context:
space:
mode:
authorEthan Werner <mail@ethanwerner.com>2026-10-10 20:55:33 +0000
committerEthan Werner <mail@ethanwerner.com>2026-10-10 20:56:50 +0000
commitf8423ebea413605b42de23f5dd06c5479d3fa1dd (patch)
tree2612a5eb026c59e7aae01f10a1cf62f2ee43042c /src/mcp.h
initial commitHEADmaster
Diffstat (limited to 'src/mcp.h')
-rw-r--r--src/mcp.h112
1 files changed, 112 insertions, 0 deletions
diff --git a/src/mcp.h b/src/mcp.h
new file mode 100644
index 0000000..24a35a9
--- /dev/null
+++ b/src/mcp.h
@@ -0,0 +1,112 @@
+#ifndef LLSH_MCP_H
+#define LLSH_MCP_H
+
+#include "util.h"
+
+/* Mirrors mcp.rs: a minimal Model Context Protocol client.
+ * mcpServers entries are either stdio (command/args/env) or HTTP (url/headers).
+ * Transport framing is JSON-RPC 2.0: one message per line over stdio, a single
+ * request/response (plain JSON or SSE) over HTTP. A wedged server must not hang
+ * the agent: every call is bounded by MCP_TIMEOUT_SECS. */
+
+#define MCP_TIMEOUT_SECS 120L
+#define MCP_PROTOCOL_VERSION "2024-11-05"
+
+/* Effective per-call timeout. MCP_TIMEOUT_SECS unless the LLSH_MCP_TIMEOUT
+ * environment variable sets a positive whole number of seconds. */
+long mcp_timeout_secs(void);
+
+typedef struct {
+ char *key;
+ char *val;
+} mcp_kv;
+
+/* One mcpServers entry. All fields owned (NULL/0 when absent). */
+typedef struct {
+ char *command;
+ char **args;
+ size_t arg_count;
+ mcp_kv *env;
+ size_t env_count;
+ char *url;
+ mcp_kv *headers;
+ size_t header_count;
+} mcp_server_config;
+
+typedef struct mcp_client mcp_client;
+
+/* A tool advertised by a server's tools/list. client is a back-reference. */
+typedef struct {
+ char *name; /* remote name */
+ char *qualified_name; /* exposed as mcp__<server-length>_<server>__<tool> */
+ char *description;
+ char *input_schema; /* JSON text */
+ mcp_client *client;
+} mcp_tool;
+
+/* Everything connected: the flattened tool list plus the owning clients. */
+typedef struct {
+ mcp_tool *tools;
+ size_t tool_count;
+ mcp_client **clients;
+ size_t client_count;
+} mcp_registry;
+
+/* Test-only loopback transport: queued responses + recorded outgoing lines.
+ * Call mcp_memory_init before use; the caller keeps ownership, the client
+ * borrows it for the lifetime of the connection. */
+typedef struct {
+ strlist responses;
+ strlist sent;
+} mcp_memory;
+
+void mcp_memory_init(mcp_memory *m);
+
+/* Parse a raw mcpServers value (JSON text). Returns 1 and fills *out, or 0
+ * (no error) when it declares neither a command nor a url. */
+int mcp_server_config_from_json(const char *json, mcp_server_config *out);
+void mcp_server_config_free(mcp_server_config *c);
+
+/* Spawn/attach a server. Returns NULL with *err on failure. */
+mcp_client *mcp_connect_stdio(const mcp_server_config *cfg, char **err);
+mcp_client *mcp_connect_http(const mcp_server_config *cfg, char **err);
+/* Test-only: wrap already-open stdio fds (closed on free, no child reaped). */
+mcp_client *mcp_connect_fds(int in_fd, int out_fd, char **err);
+/* Test-only: one buffered line read with carry semantics. */
+char *mcp_read_line(mcp_client *c, const char *method, char **err);
+mcp_client *mcp_connect_memory(mcp_memory *mem, char **err);
+void mcp_client_free(mcp_client *c);
+
+/* Handshake: initialize request + notifications/initialized notification. */
+int mcp_initialize(mcp_client *c, char **err);
+
+/* Discover the server's tools. Caller frees with mcp_tools_free (does not
+ * free the clients). */
+int mcp_list_tools(mcp_client *c, mcp_tool **tools, size_t *n, char **err);
+void mcp_tools_free(mcp_tool *tools, size_t n);
+
+/* Invoke a tool; returns its text content or NULL with *err set. */
+char *mcp_call_tool(mcp_client *c, const char *name, const char *args_json,
+ char **err);
+
+/* Low-level JSON-RPC: returns the `result` value as JSON text (owned), or
+ * NULL with *err ("server error {code}: {message}" on JSON-RPC errors). */
+char *mcp_request(mcp_client *c, const char *method, const char *params_json,
+ char **err);
+int mcp_notify(mcp_client *c, const char *method, const char *params_json,
+ char **err);
+
+/* Extract the JSON-RPC response from an HTTP body (plain or SSE). */
+char *mcp_extract_json(const char *content_type, const char *body, char **err);
+
+/* Connect, handshake, and discover every server, in order. Unreachable
+ * servers become warnings (`mcp \`name\`: reason`) and are skipped. */
+void mcp_connect_all(const char *const *names, const char *const *configs,
+ size_t n, mcp_registry *out, strlist *warnings);
+void mcp_registry_free(mcp_registry *r);
+
+/* Dispatch through the owning client, wrapping failures as
+ * `MCP tool \`name\` failed: ...`. */
+char *mcp_tool_invoke(const mcp_tool *t, const char *args_json, char **err);
+
+#endif