sim_codec_mcp/
envelope.rs1use sim_citizen::CitizenField;
6use sim_citizen_derive::Citizen;
7use sim_kernel::Expr;
8use sim_kernel::{Result, Symbol};
9
10#[derive(Clone, Debug, PartialEq)]
15pub enum McpEnvelope {
16 Request(McpRequest),
18 Notification(McpNotification),
20 Response(McpResponse),
22 Error(McpErrorEnvelope),
24}
25
26#[derive(Clone, Debug, PartialEq, Citizen)]
28#[citizen(symbol = "mcp/Request", version = 1)]
29pub struct McpRequest {
30 pub id: Expr,
32 pub method: String,
34 pub params: Expr,
36}
37
38#[derive(Clone, Debug, PartialEq, Citizen)]
40#[citizen(symbol = "mcp/Notification", version = 1)]
41pub struct McpNotification {
42 pub method: String,
44 pub params: Expr,
46}
47
48#[derive(Clone, Debug, PartialEq, Citizen)]
50#[citizen(symbol = "mcp/Response", version = 1)]
51pub struct McpResponse {
52 pub id: Expr,
54 pub result: Expr,
56}
57
58#[derive(Clone, Debug, PartialEq, Citizen)]
60#[citizen(symbol = "mcp/ErrorEnvelope", version = 1)]
61pub struct McpErrorEnvelope {
62 pub id: Expr,
64 pub error: McpError,
66}
67
68#[derive(Clone, Debug, PartialEq, Citizen)]
70#[citizen(symbol = "mcp/Error", version = 1)]
71pub struct McpError {
72 pub code: i64,
75 pub message: String,
77 pub data: Expr,
79}
80
81impl Default for McpRequest {
82 fn default() -> Self {
83 Self {
84 id: Expr::String("fixture".to_owned()),
85 method: "tools/list".to_owned(),
86 params: Expr::Map(Vec::new()),
87 }
88 }
89}
90
91impl Default for McpNotification {
92 fn default() -> Self {
93 Self {
94 method: "notifications/initialized".to_owned(),
95 params: Expr::Map(Vec::new()),
96 }
97 }
98}
99
100impl Default for McpResponse {
101 fn default() -> Self {
102 Self {
103 id: Expr::String("fixture".to_owned()),
104 result: Expr::Map(Vec::new()),
105 }
106 }
107}
108
109impl Default for McpErrorEnvelope {
110 fn default() -> Self {
111 Self {
112 id: Expr::String("fixture".to_owned()),
113 error: McpError::default(),
114 }
115 }
116}
117
118impl Default for McpError {
119 fn default() -> Self {
120 Self {
121 code: -32603,
122 message: "fixture error".to_owned(),
123 data: Expr::Nil,
124 }
125 }
126}
127
128impl CitizenField for McpError {
129 fn encode_field(&self) -> Expr {
130 Expr::List(vec![
131 self.code.encode_field(),
132 self.message.encode_field(),
133 self.data.encode_field(),
134 ])
135 }
136
137 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self> {
138 let Expr::List(items) = expr else {
139 return Err(sim_citizen::field_error(field, "expected MCP error list"));
140 };
141 let [code, message, data] = items.as_slice() else {
142 return Err(sim_citizen::field_error(
143 field,
144 format!("expected 3 MCP error field(s), found {}", items.len()),
145 ));
146 };
147 Ok(Self {
148 code: i64::decode_field_expr(code, field)?,
149 message: String::decode_field_expr(message, field)?,
150 data: Expr::decode_field_expr(data, field)?,
151 })
152 }
153}
154
155pub fn mcp_request_class_symbol() -> Symbol {
157 Symbol::qualified("mcp", "Request")
158}
159
160pub fn mcp_notification_class_symbol() -> Symbol {
162 Symbol::qualified("mcp", "Notification")
163}
164
165pub fn mcp_response_class_symbol() -> Symbol {
167 Symbol::qualified("mcp", "Response")
168}
169
170pub fn mcp_error_envelope_class_symbol() -> Symbol {
172 Symbol::qualified("mcp", "ErrorEnvelope")
173}
174
175pub fn mcp_error_class_symbol() -> Symbol {
177 Symbol::qualified("mcp", "Error")
178}
179
180pub(crate) fn is_jsonrpc_id(expr: &Expr) -> bool {
181 matches!(expr, Expr::String(_) | Expr::Number(_) | Expr::Nil)
182}