diff options
Diffstat (limited to 'src/mcp.h')
| -rw-r--r-- | src/mcp.h | 112 |
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 |