summaryrefslogtreecommitdiffstats
path: root/src/mcp.h
blob: 24a35a9f0fe3a5c93bbd93b74836065534537d6b (plain)
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