cMCP 0.4.1
Model Context Protocol library in pure C11
Loading...
Searching...
No Matches
cmcp_types.h
Go to the documentation of this file.
1/**
2 * @file cmcp_types.h
3 * @brief JSON-RPC 2.0 message shapes, capability structs, dispatch
4 * types. Shared between server and client.
5 *
6 * The "schema" of cMCP at the C-struct level: how a JSON-RPC
7 * request/response/notification looks in memory, what the server-
8 * and client-side capability objects carry, the JSON-RPC error-code
9 * constants. Most callers reach these through `cmcp_server.h` /
10 * `cmcp_client.h` rather than allocating these types directly.
11 */
12#ifndef CMCP_TYPES_H
13#define CMCP_TYPES_H
14
15#include <stddef.h>
16#include "cmcp_json.h"
17
18/* ------------------------------------------------------------------ */
19/* MCP capability declarations */
20/* ------------------------------------------------------------------ */
21/* These are the subset of MCP capabilities cMCP currently models. Each
22 * field is a boolean: 1 if the peer offers this capability, 0 if not.
23 * Negotiated at initialize-time and never re-negotiated within a
24 * session. Phase 1.4 wires the structs and the handshake; later phases
25 * fill in the behavior gated by each flag. */
26
27typedef struct {
28 int tools_list_changed; /* tools/list_changed notifications */
29 int resources_subscribe; /* resources/subscribe + updated */
30 int resources_list_changed; /* resources/list_changed */
31 int prompts_list_changed; /* prompts/list_changed */
32 int logging; /* logging methods */
34
35typedef struct {
36 int sampling; /* sampling/createMessage */
37 int sampling_tools; /* sampling.tools sub-cap (MCP 2025-11-25
38 * SEP-1577): host's model can invoke tools
39 * the server hands it during sampling */
40 int elicitation; /* elicitation/create */
41 int elicitation_form; /* elicitation.form sub-cap (MCP 2025-11-25
42 * SEP-1036): host accepts schema-driven
43 * form elicitations (the legacy default) */
44 int elicitation_url; /* elicitation.url sub-cap (SEP-1036): host
45 * accepts URL-redirect elicitations */
46 int roots_list_changed; /* roots/list_changed */
48
49/* ------------------------------------------------------------------ */
50/* MCP log severity levels (RFC 5424 syslog, per spec) */
51/* ------------------------------------------------------------------ */
52/* Numerically ordered: a message with level >= the server-side floor
53 * is emitted; below it is dropped. `debug` is the most verbose,
54 * `emergency` the most severe. Wire names are the lowercase strings
55 * below; cmcp_log_level_from_name / _to_name convert. */
66
67/* Returns the wire string ("debug", "info", ...) for `lvl`, or NULL
68 * if `lvl` is out of range. Pointer is to static storage. */
70
71/* Parses `name` into a level. Returns 0 on success and writes
72 * *out_level; returns -1 on unknown/NULL name. */
73int cmcp_log_level_from_name(const char *name, cmcp_log_level_t *out_level);
74
75/* JSON-RPC 2.0 standard error codes. Parenthesised so they survive
76 * use in macro-expansion contexts where the surrounding tokens could
77 * otherwise re-bind the leading `-` (e.g. `x - CMCP_RPC_PARSE_ERROR`
78 * would otherwise expand to `x - -32700`, which still parses but is
79 * the kind of thing static analysers, rightly, flag). */
80#define CMCP_RPC_PARSE_ERROR (-32700)
81#define CMCP_RPC_INVALID_REQUEST (-32600)
82#define CMCP_RPC_METHOD_NOT_FOUND (-32601)
83#define CMCP_RPC_INVALID_PARAMS (-32602)
84#define CMCP_RPC_INTERNAL_ERROR (-32603)
85
86/* JSON-RPC reserves -32000..-32099 for implementation-defined server
87 * errors. cMCP and MCP application errors live in this range. */
88#define CMCP_RPC_SERVER_ERROR_MIN (-32099)
89#define CMCP_RPC_SERVER_ERROR_MAX (-32000)
90
91/* ------------------------------------------------------------------ */
92/* JSON-RPC ID */
93/* ------------------------------------------------------------------ */
94
95typedef enum {
96 CMCP_ID_NONE, /* no id field (notification) */
97 CMCP_ID_NULL, /* explicit JSON null (parse-error responses) */
101
102typedef struct {
104 long long i; /* CMCP_ID_INT */
105 char *s; /* CMCP_ID_STRING — owned */
106 size_t s_len;
107} cmcp_id_t;
108
111void cmcp_id_init_int(cmcp_id_t *id, long long i);
112int cmcp_id_init_string(cmcp_id_t *id, const char *s, size_t n);
113int cmcp_id_copy(cmcp_id_t *dst, const cmcp_id_t *src);
115int cmcp_id_equal(const cmcp_id_t *a, const cmcp_id_t *b);
116
117/* ------------------------------------------------------------------ */
118/* JSON-RPC error object */
119/* ------------------------------------------------------------------ */
120
121typedef struct {
122 int code;
123 char *message; /* owned */
124 cmcp_json_t *data; /* owned, may be NULL */
126
127/* Free a heap-owned cmcp_rpc_error_t (message + data + the struct
128 * itself). Safe to call with NULL. Hosts that obtained the error from
129 * cmcp_client_tool_call (CMCP_TOOL_ERR_PROTOCOL path) use this; in-
130 * tree code generally owns errors as part of a cmcp_rpc_message_t and
131 * lets cmcp_rpc_message_clear handle them. */
133
134/* ------------------------------------------------------------------ */
135/* JSON-RPC message (discriminated union) */
136/* ------------------------------------------------------------------ */
137
143
144typedef struct {
146 cmcp_id_t id; /* REQUEST, RESPONSE */
147 char *method; /* REQUEST, NOTIFICATION — owned */
148 cmcp_json_t *params; /* REQUEST, NOTIFICATION — owned, may be NULL */
149 cmcp_json_t *result; /* RESPONSE success — owned, may be NULL */
150 cmcp_rpc_error_t *error; /* RESPONSE error — owned, may be NULL */
152
155
156/* ------------------------------------------------------------------ */
157/* Encode / decode */
158/* ------------------------------------------------------------------ */
159
160/* Parse JSON-RPC text into one or more messages.
161 *
162 * On success, returns CMCP_OK; *out_msgs is a malloc'd array of length
163 * *out_count (1 for a single message, N for a JSON-RPC batch). Free
164 * with cmcp_rpc_messages_free().
165 *
166 * NOTE: MCP (since 2025-06-18, still the case in 2025-11-25) removes
167 * batch support at the protocol layer, but this framing layer parses
168 * batches for completeness; higher layers should reject batches with
169 * INVALID_REQUEST when applicable. */
170int cmcp_rpc_parse(const char *text, size_t len,
171 cmcp_rpc_message_t **out_msgs, size_t *out_count);
173
174/* Convert one parsed JSON value into a message. Caller must clear *out
175 * even on failure (init it first via cmcp_rpc_message_init). */
176int cmcp_rpc_from_json(const cmcp_json_t *json, cmcp_rpc_message_t *out);
177
178/* Convert a message into a fresh cmcp_json_t tree. */
180
181/* Emit one message as a JSON string with stable key ordering
182 * (caller frees with free()). */
184char *cmcp_rpc_emit_batch(const cmcp_rpc_message_t *msgs, size_t count);
185
186/* ------------------------------------------------------------------ */
187/* Construction helpers */
188/* ------------------------------------------------------------------ */
189
190/* All make_* helpers take ownership of params/result/data on success. */
192 const char *method, cmcp_json_t *params);
194 const char *id_str, size_t id_len,
195 const char *method, cmcp_json_t *params);
197 const char *method, cmcp_json_t *params);
199 cmcp_json_t *result);
201 int code, const char *message, cmcp_json_t *data);
202
203/* ------------------------------------------------------------------ */
204/* In-flight request table (client-side ID matching) */
205/* ------------------------------------------------------------------ */
206
207typedef struct cmcp_rpc_pending cmcp_rpc_pending_t;
208
211
212/* Reserve a fresh monotonic positive integer ID and associate it with
213 * userdata. Returns the new ID on success (always > 0). Returns 0 on
214 * allocation failure (CMCP_ENOMEM). Returns -1 if the in-flight cap
215 * is exhausted (CMCP_EAGAIN); see cmcp_rpc_pending_set_max_inflight. */
216long long cmcp_rpc_pending_register(cmcp_rpc_pending_t *t, void *userdata);
217
218/* Look up an ID and remove it. Returns 1 and writes *out_userdata on
219 * hit; returns 0 if not found. out_userdata may be NULL. */
221 void **out_userdata);
222
224
225/* Configure the in-flight cap (Tier 6 axis 6.5.3). Surplus calls to
226 * register fail with id -1 (CMCP_EAGAIN). 0 = unbounded. By default
227 * the table reads CMCP_RPC_MAX_INFLIGHT (default 1024) at construction.
228 * Thread-safe. */
231
232/* ------------------------------------------------------------------ */
233/* Dispatch */
234/* ------------------------------------------------------------------ */
235
236/* Handler for an incoming request or notification.
237 *
238 * For REQUEST: out_response is pre-initialised as an empty response
239 * mirroring the request's ID. Handler must set out_response->result
240 * OR out_response->error before returning.
241 *
242 * For NOTIFICATION: out_response is NULL.
243 *
244 * Return CMCP_OK on success; non-zero on failure (treated as
245 * INTERNAL_ERROR for requests, ignored for notifications). */
247 cmcp_rpc_message_t *out_response,
248 void *userdata);
249
250typedef struct {
251 const char *method;
253 void *userdata;
255
256/* Dispatch one message against the route table.
257 *
258 * REQUEST: looks up route by method, invokes handler, populates
259 * *out_response with success or error. If method is unknown,
260 * populates out_response with a -32601 error. Returns CMCP_OK.
261 *
262 * NOTIFICATION: looks up route and invokes handler (out_response is
263 * NULL). Returns CMCP_OK whether or not the method was found.
264 * out_response (if non-NULL) is left untouched.
265 *
266 * RESPONSE: returns CMCP_EINVAL — match via pending table instead. */
268 const cmcp_rpc_route_t *routes, size_t n_routes,
269 cmcp_rpc_message_t *out_response);
270
271#endif
Hand-rolled JSON value tree, parser, and emitter.
const char * cmcp_log_level_to_name(cmcp_log_level_t lvl)
char * cmcp_rpc_emit_batch(const cmcp_rpc_message_t *msgs, size_t count)
int(* cmcp_rpc_handler_fn)(const cmcp_rpc_message_t *in, cmcp_rpc_message_t *out_response, void *userdata)
Definition cmcp_types.h:246
int cmcp_rpc_make_response(cmcp_rpc_message_t *m, const cmcp_id_t *id, cmcp_json_t *result)
void cmcp_rpc_pending_free(cmcp_rpc_pending_t *t)
cmcp_id_kind_t
Definition cmcp_types.h:95
@ CMCP_ID_NONE
Definition cmcp_types.h:96
@ CMCP_ID_INT
Definition cmcp_types.h:98
@ CMCP_ID_NULL
Definition cmcp_types.h:97
@ CMCP_ID_STRING
Definition cmcp_types.h:99
int cmcp_rpc_dispatch(const cmcp_rpc_message_t *in, const cmcp_rpc_route_t *routes, size_t n_routes, cmcp_rpc_message_t *out_response)
void cmcp_id_clear(cmcp_id_t *id)
void cmcp_id_init_null(cmcp_id_t *id)
cmcp_json_t * cmcp_rpc_to_json(const cmcp_rpc_message_t *m)
cmcp_log_level_t
Definition cmcp_types.h:56
@ CMCP_LOG_LEVEL_INFO
Definition cmcp_types.h:58
@ CMCP_LOG_LEVEL_WARNING
Definition cmcp_types.h:60
@ CMCP_LOG_LEVEL_ERROR
Definition cmcp_types.h:61
@ CMCP_LOG_LEVEL_CRITICAL
Definition cmcp_types.h:62
@ CMCP_LOG_LEVEL_EMERGENCY
Definition cmcp_types.h:64
@ CMCP_LOG_LEVEL_ALERT
Definition cmcp_types.h:63
@ CMCP_LOG_LEVEL_DEBUG
Definition cmcp_types.h:57
@ CMCP_LOG_LEVEL_NOTICE
Definition cmcp_types.h:59
void cmcp_id_init_int(cmcp_id_t *id, long long i)
void cmcp_rpc_messages_free(cmcp_rpc_message_t *msgs, size_t count)
long long cmcp_rpc_pending_register(cmcp_rpc_pending_t *t, void *userdata)
size_t cmcp_rpc_pending_count(cmcp_rpc_pending_t *t)
int cmcp_id_init_string(cmcp_id_t *id, const char *s, size_t n)
int cmcp_id_equal(const cmcp_id_t *a, const cmcp_id_t *b)
void cmcp_rpc_error_free(cmcp_rpc_error_t *e)
int cmcp_rpc_parse(const char *text, size_t len, cmcp_rpc_message_t **out_msgs, size_t *out_count)
int cmcp_rpc_pending_take(cmcp_rpc_pending_t *t, long long id, void **out_userdata)
char * cmcp_rpc_emit(const cmcp_rpc_message_t *m)
int cmcp_rpc_make_notification(cmcp_rpc_message_t *m, const char *method, cmcp_json_t *params)
int cmcp_rpc_from_json(const cmcp_json_t *json, cmcp_rpc_message_t *out)
int cmcp_rpc_make_request_str(cmcp_rpc_message_t *m, const char *id_str, size_t id_len, const char *method, cmcp_json_t *params)
int cmcp_rpc_make_error(cmcp_rpc_message_t *m, const cmcp_id_t *id, int code, const char *message, cmcp_json_t *data)
cmcp_msg_kind_t
Definition cmcp_types.h:138
@ CMCP_MSG_NOTIFICATION
Definition cmcp_types.h:141
@ CMCP_MSG_RESPONSE
Definition cmcp_types.h:140
@ CMCP_MSG_REQUEST
Definition cmcp_types.h:139
cmcp_rpc_pending_t * cmcp_rpc_pending_new(void)
int cmcp_rpc_make_request(cmcp_rpc_message_t *m, long long id, const char *method, cmcp_json_t *params)
void cmcp_rpc_message_init(cmcp_rpc_message_t *m)
int cmcp_id_copy(cmcp_id_t *dst, const cmcp_id_t *src)
void cmcp_rpc_message_clear(cmcp_rpc_message_t *m)
void cmcp_rpc_pending_set_max_inflight(cmcp_rpc_pending_t *t, size_t cap)
int cmcp_log_level_from_name(const char *name, cmcp_log_level_t *out_level)
struct cmcp_rpc_pending cmcp_rpc_pending_t
Definition cmcp_types.h:207
size_t cmcp_rpc_pending_max_inflight(cmcp_rpc_pending_t *t)
void cmcp_id_init_none(cmcp_id_t *id)
size_t s_len
Definition cmcp_types.h:106
char * s
Definition cmcp_types.h:105
cmcp_id_kind_t kind
Definition cmcp_types.h:103
long long i
Definition cmcp_types.h:104
cmcp_json_t * data
Definition cmcp_types.h:124
cmcp_json_t * result
Definition cmcp_types.h:149
cmcp_msg_kind_t kind
Definition cmcp_types.h:145
cmcp_rpc_error_t * error
Definition cmcp_types.h:150
cmcp_json_t * params
Definition cmcp_types.h:148
const char * method
Definition cmcp_types.h:251
cmcp_rpc_handler_fn handler
Definition cmcp_types.h:252