1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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
|