cMCP 0.4.1
Model Context Protocol library in pure C11
Loading...
Searching...
No Matches
cmcp_session.h
Go to the documentation of this file.
1/**
2 * @file cmcp_session.h
3 * @brief Multi-server primitive aggregator on top of N cMCP clients.
4 *
5 * A host that wants to talk to several MCP servers at once (the
6 * common case for agent frameworks) creates one `cmcp_client_t` per
7 * server, hands them all to a `cmcp_session_t`, and then uses the
8 * session for all subsequent calls. The session fans out
9 * `tools/list` / `resources/list` / `prompts/list` to every member
10 * in parallel and merges the results; `tool_call`, `resource_read`,
11 * and `prompt_get` route to the right backend either by qualified
12 * name (`<server>:<tool>` for tools) or by an explicit `(server,
13 * uri|name)` pair (for resources and prompts, since URIs already
14 * contain colons).
15 */
16#ifndef CMCP_SESSION_H
17#define CMCP_SESSION_H
18
19#include "cmcp_client.h"
20#include "cmcp_types.h"
21
22/* cmcp_session_t aggregates many cmcp_client_t handles into a single
23 * tool surface. Tool names are namespaced as "<server>:<tool>" so a
24 * host (openclawd) can present a flat menu to its model while the
25 * session routes calls to the right child server.
26 *
27 * The session takes ownership of clients added to it. Freeing the
28 * session frees every client, which in turn closes its transport and
29 * reaps any spawned child. */
30
31typedef struct cmcp_session cmcp_session_t;
32
35
36/* Add an already-handshaken client under the given server name. The
37 * server name must be non-empty, must not contain ':', and must be
38 * unique within the session. On success the session takes ownership
39 * of c. On failure c is NOT freed and ownership stays with the
40 * caller. */
42 const char *server_name,
43 cmcp_client_t *c);
44
45/* ====================================================================== */
46/* Aggregated tool listing */
47/* ====================================================================== */
48
49typedef struct {
50 char *server; /* host-supplied server name */
51 char *name; /* tool name as the server published it */
52 char *qualified; /* "<server>:<name>" — pass to tool_call */
53 char *description; /* may be NULL */
54 cmcp_json_t *input_schema; /* deep-copy of the tool's schema; may be NULL */
56
57/* Collect tools/list from every client in registration order and
58 * concatenate them. Returns CMCP_OK on full success or partial
59 * success (tools from servers that responded; failed servers
60 * skipped). On allocation failure returns CMCP_ENOMEM and sets
61 * *out_tools = NULL, *out_n = 0. */
63 cmcp_session_tool_t **out_tools,
64 size_t *out_n);
65
67
68/* ====================================================================== */
69/* Routed tool call */
70/* ====================================================================== */
71
72/* qualified is "<server>:<tool>". args is consumed (may be NULL).
73 * On CMCP_OK, *out_response is initialised and owns its fields —
74 * caller must cmcp_rpc_message_clear() it.
75 * Returns CMCP_ENOTFOUND if no client matches the server prefix. */
77 const char *qualified,
78 cmcp_json_t *args,
79 cmcp_rpc_message_t *out_response);
80
81/* Async routed tool call (F3). The whole point of a multi-server session
82 * is parallel tool calls, but cmcp_session_tool_call is synchronous —
83 * a host wanting concurrency had to drop through cmcp_session_get() to
84 * the raw clients and re-do the routing the session already knows (the
85 * P6 host-probe's FRICTION 1). This pair keeps the routing in the
86 * session and hands back the same cmcp_tool_handle_t the client-level
87 * async call uses, so the result reaping is identical and the handle's
88 * id→client binding still prevents cross-server mis-routing (F4).
89 *
90 * cmcp_session_tool_call_async: `qualified` is "<server>:<tool>"; args
91 * is consumed (may be NULL). Resolves the server prefix, dispatches via
92 * the routed client, and returns a handle bound to that client. On any
93 * failure (bad session/qualified, unknown server, dispatch error) the
94 * returned handle is invalid (cmcp_tool_handle_valid == 0) and args is
95 * still consumed.
96 *
97 * cmcp_session_tool_wait: reap a handle from cmcp_session_tool_call_async
98 * (or, equivalently, from cmcp_client_tool_call_async — the handle is
99 * the same type). Thin forwarder to cmcp_client_tool_wait, provided for
100 * call-site symmetry. Always free the result with
101 * cmcp_tool_result_clear. */
103 const char *qualified,
104 cmcp_json_t *args);
105
107
108/* Look up a client by host-supplied server name. Returns NULL on miss.
109 * Useful for sending notifications, custom requests, etc. */
110cmcp_client_t *cmcp_session_get(cmcp_session_t *s, const char *server_name);
111
113
114/* ====================================================================== */
115/* Aggregated resources */
116/* ====================================================================== */
117
118/* URIs may already contain colons (scheme separator) so we don't fold
119 * server-name into them; resource_read takes an explicit (server, uri)
120 * pair instead of a single qualified string. */
121
122typedef struct {
123 char *server; /* host-supplied server name */
124 char *uri; /* resource URI as the server published it */
125 char *name; /* display name */
126 char *description; /* may be NULL */
127 char *mime_type; /* may be NULL */
129
131 cmcp_session_resource_t **out_resources,
132 size_t *out_n);
133
135
136/* Read one resource. server names which client to route to; uri is sent
137 * as params.uri verbatim. On CMCP_OK out_response owns its fields. */
139 const char *server,
140 const char *uri,
141 cmcp_rpc_message_t *out_response);
142
143/* ====================================================================== */
144/* Aggregated prompts */
145/* ====================================================================== */
146
147typedef struct {
148 char *server; /* host-supplied server name */
149 char *name; /* prompt name as the server published it */
150 char *description; /* may be NULL */
151 cmcp_json_t *arguments; /* deep-copy of the prompt's argument array; may be NULL */
153
155 cmcp_session_prompt_t **out_prompts,
156 size_t *out_n);
157
159
160/* Get one prompt. args is consumed (may be NULL). */
162 const char *server,
163 const char *name,
164 cmcp_json_t *args,
165 cmcp_rpc_message_t *out_response);
166
167#endif
Asynchronous MCP client: handshake, list/call/read/subscribe, cancellation, progress,...
struct cmcp_client cmcp_client_t
Definition cmcp_client.h:31
cmcp_tool_handle_t cmcp_session_tool_call_async(cmcp_session_t *s, const char *qualified, cmcp_json_t *args)
void cmcp_session_free(cmcp_session_t *s)
void cmcp_session_prompts_free(cmcp_session_prompt_t *p, size_t n)
int cmcp_session_prompts_list(cmcp_session_t *s, cmcp_session_prompt_t **out_prompts, size_t *out_n)
int cmcp_session_tools_list(cmcp_session_t *s, cmcp_session_tool_t **out_tools, size_t *out_n)
int cmcp_session_resources_list(cmcp_session_t *s, cmcp_session_resource_t **out_resources, size_t *out_n)
void cmcp_session_resources_free(cmcp_session_resource_t *r, size_t n)
int cmcp_session_prompt_get(cmcp_session_t *s, const char *server, const char *name, cmcp_json_t *args, cmcp_rpc_message_t *out_response)
struct cmcp_session cmcp_session_t
cmcp_client_t * cmcp_session_get(cmcp_session_t *s, const char *server_name)
int cmcp_session_resource_read(cmcp_session_t *s, const char *server, const char *uri, cmcp_rpc_message_t *out_response)
void cmcp_session_tools_free(cmcp_session_tool_t *tools, size_t n)
int cmcp_session_tool_call(cmcp_session_t *s, const char *qualified, cmcp_json_t *args, cmcp_rpc_message_t *out_response)
int cmcp_session_add(cmcp_session_t *s, const char *server_name, cmcp_client_t *c)
cmcp_tool_result_t cmcp_session_tool_wait(cmcp_tool_handle_t h)
size_t cmcp_session_count(const cmcp_session_t *s)
cmcp_session_t * cmcp_session_new(void)
JSON-RPC 2.0 message shapes, capability structs, dispatch types.
cmcp_json_t * arguments
cmcp_json_t * input_schema