chore: support MCP schema 2025-06-18 (#1621)
This updates the schema in `generate_mcp_types.py` from `2025-03-26` to `2025-06-18`, regenerates `mcp-types/src/lib.rs`, and then updates all the code that uses `mcp-types` to honor the changes. Ran ``` npx @modelcontextprotocol/inspector just codex mcp ``` and verified that I was able to invoke the `codex` tool, as expected. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/1621). * #1623 * #1622 * __->__ #1621
This commit is contained in:
@@ -967,15 +967,17 @@ async fn run_task(sess: Arc<Session>, sub_id: String, input: Vec<InputItem>) {
|
|||||||
) => {
|
) => {
|
||||||
items_to_record_in_conversation_history.push(item);
|
items_to_record_in_conversation_history.push(item);
|
||||||
let (content, success): (String, Option<bool>) = match result {
|
let (content, success): (String, Option<bool>) = match result {
|
||||||
Ok(CallToolResult { content, is_error }) => {
|
Ok(CallToolResult {
|
||||||
match serde_json::to_string(content) {
|
content,
|
||||||
Ok(content) => (content, *is_error),
|
is_error,
|
||||||
Err(e) => {
|
structured_content: _,
|
||||||
warn!("Failed to serialize MCP tool call output: {e}");
|
}) => match serde_json::to_string(content) {
|
||||||
(e.to_string(), Some(true))
|
Ok(content) => (content, *is_error),
|
||||||
}
|
Err(e) => {
|
||||||
|
warn!("Failed to serialize MCP tool call output: {e}");
|
||||||
|
(e.to_string(), Some(true))
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
Err(e) => (e.clone(), Some(true)),
|
Err(e) => (e.clone(), Some(true)),
|
||||||
};
|
};
|
||||||
items_to_record_in_conversation_history.push(
|
items_to_record_in_conversation_history.push(
|
||||||
@@ -1353,7 +1355,7 @@ async fn handle_function_call(
|
|||||||
let params = match parse_container_exec_arguments(arguments, sess, &call_id) {
|
let params = match parse_container_exec_arguments(arguments, sess, &call_id) {
|
||||||
Ok(params) => params,
|
Ok(params) => params,
|
||||||
Err(output) => {
|
Err(output) => {
|
||||||
return output;
|
return *output;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
handle_container_exec_with_params(params, sess, sub_id, call_id).await
|
handle_container_exec_with_params(params, sess, sub_id, call_id).await
|
||||||
@@ -1396,7 +1398,7 @@ fn parse_container_exec_arguments(
|
|||||||
arguments: String,
|
arguments: String,
|
||||||
sess: &Session,
|
sess: &Session,
|
||||||
call_id: &str,
|
call_id: &str,
|
||||||
) -> Result<ExecParams, ResponseInputItem> {
|
) -> Result<ExecParams, Box<ResponseInputItem>> {
|
||||||
// parse command
|
// parse command
|
||||||
match serde_json::from_str::<ShellToolCallParams>(&arguments) {
|
match serde_json::from_str::<ShellToolCallParams>(&arguments) {
|
||||||
Ok(shell_tool_call_params) => Ok(to_exec_params(shell_tool_call_params, sess)),
|
Ok(shell_tool_call_params) => Ok(to_exec_params(shell_tool_call_params, sess)),
|
||||||
@@ -1409,7 +1411,7 @@ fn parse_container_exec_arguments(
|
|||||||
success: None,
|
success: None,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
Err(output)
|
Err(Box::new(output))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,10 +135,12 @@ impl McpConnectionManager {
|
|||||||
experimental: None,
|
experimental: None,
|
||||||
roots: None,
|
roots: None,
|
||||||
sampling: None,
|
sampling: None,
|
||||||
|
elicitation: None,
|
||||||
},
|
},
|
||||||
client_info: Implementation {
|
client_info: Implementation {
|
||||||
name: "codex-mcp-client".to_owned(),
|
name: "codex-mcp-client".to_owned(),
|
||||||
version: env!("CARGO_PKG_VERSION").to_owned(),
|
version: env!("CARGO_PKG_VERSION").to_owned(),
|
||||||
|
title: Some("Codex".into()),
|
||||||
},
|
},
|
||||||
protocol_version: mcp_types::MCP_SCHEMA_VERSION.to_owned(),
|
protocol_version: mcp_types::MCP_SCHEMA_VERSION.to_owned(),
|
||||||
};
|
};
|
||||||
@@ -288,6 +290,8 @@ mod tests {
|
|||||||
r#type: "object".to_string(),
|
r#type: "object".to_string(),
|
||||||
},
|
},
|
||||||
name: tool_name.to_string(),
|
name: tool_name.to_string(),
|
||||||
|
output_schema: None,
|
||||||
|
title: None,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,10 +57,12 @@ async fn main() -> Result<()> {
|
|||||||
experimental: None,
|
experimental: None,
|
||||||
roots: None,
|
roots: None,
|
||||||
sampling: None,
|
sampling: None,
|
||||||
|
elicitation: None,
|
||||||
},
|
},
|
||||||
client_info: Implementation {
|
client_info: Implementation {
|
||||||
name: "codex-mcp-client".to_owned(),
|
name: "codex-mcp-client".to_owned(),
|
||||||
version: env!("CARGO_PKG_VERSION").to_owned(),
|
version: env!("CARGO_PKG_VERSION").to_owned(),
|
||||||
|
title: Some("Codex".to_string()),
|
||||||
},
|
},
|
||||||
protocol_version: MCP_SCHEMA_VERSION.to_owned(),
|
protocol_version: MCP_SCHEMA_VERSION.to_owned(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -108,7 +108,10 @@ pub(crate) fn create_tool_for_codex_tool_call_param() -> Tool {
|
|||||||
|
|
||||||
Tool {
|
Tool {
|
||||||
name: "codex".to_string(),
|
name: "codex".to_string(),
|
||||||
|
title: Some("Codex".to_string()),
|
||||||
input_schema: tool_input_schema,
|
input_schema: tool_input_schema,
|
||||||
|
// TODO(mbolin): This should be defined.
|
||||||
|
output_schema: None,
|
||||||
description: Some(
|
description: Some(
|
||||||
"Run a Codex session. Accepts configuration parameters matching the Codex Config struct.".to_string(),
|
"Run a Codex session. Accepts configuration parameters matching the Codex Config struct.".to_string(),
|
||||||
),
|
),
|
||||||
@@ -179,6 +182,7 @@ mod tests {
|
|||||||
let tool_json = serde_json::to_value(&tool).expect("tool serializes");
|
let tool_json = serde_json::to_value(&tool).expect("tool serializes");
|
||||||
let expected_tool_json = serde_json::json!({
|
let expected_tool_json = serde_json::json!({
|
||||||
"name": "codex",
|
"name": "codex",
|
||||||
|
"title": "Codex",
|
||||||
"description": "Run a Codex session. Accepts configuration parameters matching the Codex Config struct.",
|
"description": "Run a Codex session. Accepts configuration parameters matching the Codex Config struct.",
|
||||||
"inputSchema": {
|
"inputSchema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use codex_core::protocol::Op;
|
|||||||
use codex_core::protocol::Submission;
|
use codex_core::protocol::Submission;
|
||||||
use codex_core::protocol::TaskCompleteEvent;
|
use codex_core::protocol::TaskCompleteEvent;
|
||||||
use mcp_types::CallToolResult;
|
use mcp_types::CallToolResult;
|
||||||
use mcp_types::CallToolResultContent;
|
use mcp_types::ContentBlock;
|
||||||
use mcp_types::JSONRPC_VERSION;
|
use mcp_types::JSONRPC_VERSION;
|
||||||
use mcp_types::JSONRPCMessage;
|
use mcp_types::JSONRPCMessage;
|
||||||
use mcp_types::JSONRPCResponse;
|
use mcp_types::JSONRPCResponse;
|
||||||
@@ -44,12 +44,13 @@ pub async fn run_codex_tool_session(
|
|||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let result = CallToolResult {
|
let result = CallToolResult {
|
||||||
content: vec![CallToolResultContent::TextContent(TextContent {
|
content: vec![ContentBlock::TextContent(TextContent {
|
||||||
r#type: "text".to_string(),
|
r#type: "text".to_string(),
|
||||||
text: format!("Failed to start Codex session: {e}"),
|
text: format!("Failed to start Codex session: {e}"),
|
||||||
annotations: None,
|
annotations: None,
|
||||||
})],
|
})],
|
||||||
is_error: Some(true),
|
is_error: Some(true),
|
||||||
|
structured_content: None,
|
||||||
};
|
};
|
||||||
let _ = outgoing
|
let _ = outgoing
|
||||||
.send(JSONRPCMessage::Response(JSONRPCResponse {
|
.send(JSONRPCMessage::Response(JSONRPCResponse {
|
||||||
@@ -88,8 +89,6 @@ pub async fn run_codex_tool_session(
|
|||||||
tracing::error!("Failed to submit initial prompt: {e}");
|
tracing::error!("Failed to submit initial prompt: {e}");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut last_agent_message: Option<String> = None;
|
|
||||||
|
|
||||||
// Stream events until the task needs to pause for user interaction or
|
// Stream events until the task needs to pause for user interaction or
|
||||||
// completes.
|
// completes.
|
||||||
loop {
|
loop {
|
||||||
@@ -98,17 +97,15 @@ pub async fn run_codex_tool_session(
|
|||||||
let _ = outgoing.send(codex_event_to_notification(&event)).await;
|
let _ = outgoing.send(codex_event_to_notification(&event)).await;
|
||||||
|
|
||||||
match &event.msg {
|
match &event.msg {
|
||||||
EventMsg::AgentMessage(AgentMessageEvent { message }) => {
|
|
||||||
last_agent_message = Some(message.clone());
|
|
||||||
}
|
|
||||||
EventMsg::ExecApprovalRequest(_) => {
|
EventMsg::ExecApprovalRequest(_) => {
|
||||||
let result = CallToolResult {
|
let result = CallToolResult {
|
||||||
content: vec![CallToolResultContent::TextContent(TextContent {
|
content: vec![ContentBlock::TextContent(TextContent {
|
||||||
r#type: "text".to_string(),
|
r#type: "text".to_string(),
|
||||||
text: "EXEC_APPROVAL_REQUIRED".to_string(),
|
text: "EXEC_APPROVAL_REQUIRED".to_string(),
|
||||||
annotations: None,
|
annotations: None,
|
||||||
})],
|
})],
|
||||||
is_error: None,
|
is_error: None,
|
||||||
|
structured_content: None,
|
||||||
};
|
};
|
||||||
let _ = outgoing
|
let _ = outgoing
|
||||||
.send(JSONRPCMessage::Response(JSONRPCResponse {
|
.send(JSONRPCMessage::Response(JSONRPCResponse {
|
||||||
@@ -121,12 +118,13 @@ pub async fn run_codex_tool_session(
|
|||||||
}
|
}
|
||||||
EventMsg::ApplyPatchApprovalRequest(_) => {
|
EventMsg::ApplyPatchApprovalRequest(_) => {
|
||||||
let result = CallToolResult {
|
let result = CallToolResult {
|
||||||
content: vec![CallToolResultContent::TextContent(TextContent {
|
content: vec![ContentBlock::TextContent(TextContent {
|
||||||
r#type: "text".to_string(),
|
r#type: "text".to_string(),
|
||||||
text: "PATCH_APPROVAL_REQUIRED".to_string(),
|
text: "PATCH_APPROVAL_REQUIRED".to_string(),
|
||||||
annotations: None,
|
annotations: None,
|
||||||
})],
|
})],
|
||||||
is_error: None,
|
is_error: None,
|
||||||
|
structured_content: None,
|
||||||
};
|
};
|
||||||
let _ = outgoing
|
let _ = outgoing
|
||||||
.send(JSONRPCMessage::Response(JSONRPCResponse {
|
.send(JSONRPCMessage::Response(JSONRPCResponse {
|
||||||
@@ -137,27 +135,19 @@ pub async fn run_codex_tool_session(
|
|||||||
.await;
|
.await;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
EventMsg::TaskComplete(TaskCompleteEvent {
|
EventMsg::TaskComplete(TaskCompleteEvent { last_agent_message }) => {
|
||||||
last_agent_message: _,
|
let text = match last_agent_message {
|
||||||
}) => {
|
Some(msg) => msg.clone(),
|
||||||
let result = if let Some(msg) = last_agent_message {
|
None => "".to_string(),
|
||||||
CallToolResult {
|
};
|
||||||
content: vec![CallToolResultContent::TextContent(TextContent {
|
let result = CallToolResult {
|
||||||
r#type: "text".to_string(),
|
content: vec![ContentBlock::TextContent(TextContent {
|
||||||
text: msg,
|
r#type: "text".to_string(),
|
||||||
annotations: None,
|
text,
|
||||||
})],
|
annotations: None,
|
||||||
is_error: None,
|
})],
|
||||||
}
|
is_error: None,
|
||||||
} else {
|
structured_content: None,
|
||||||
CallToolResult {
|
|
||||||
content: vec![CallToolResultContent::TextContent(TextContent {
|
|
||||||
r#type: "text".to_string(),
|
|
||||||
text: String::new(),
|
|
||||||
annotations: None,
|
|
||||||
})],
|
|
||||||
is_error: None,
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let _ = outgoing
|
let _ = outgoing
|
||||||
.send(JSONRPCMessage::Response(JSONRPCResponse {
|
.send(JSONRPCMessage::Response(JSONRPCResponse {
|
||||||
@@ -177,6 +167,9 @@ pub async fn run_codex_tool_session(
|
|||||||
EventMsg::AgentReasoningDelta(_) => {
|
EventMsg::AgentReasoningDelta(_) => {
|
||||||
// TODO: think how we want to support this in the MCP
|
// TODO: think how we want to support this in the MCP
|
||||||
}
|
}
|
||||||
|
EventMsg::AgentMessage(AgentMessageEvent { .. }) => {
|
||||||
|
// TODO: think how we want to support this in the MCP
|
||||||
|
}
|
||||||
EventMsg::Error(_)
|
EventMsg::Error(_)
|
||||||
| EventMsg::TaskStarted
|
| EventMsg::TaskStarted
|
||||||
| EventMsg::TokenCount(_)
|
| EventMsg::TokenCount(_)
|
||||||
@@ -200,12 +193,15 @@ pub async fn run_codex_tool_session(
|
|||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let result = CallToolResult {
|
let result = CallToolResult {
|
||||||
content: vec![CallToolResultContent::TextContent(TextContent {
|
content: vec![ContentBlock::TextContent(TextContent {
|
||||||
r#type: "text".to_string(),
|
r#type: "text".to_string(),
|
||||||
text: format!("Codex runtime error: {e}"),
|
text: format!("Codex runtime error: {e}"),
|
||||||
annotations: None,
|
annotations: None,
|
||||||
})],
|
})],
|
||||||
is_error: Some(true),
|
is_error: Some(true),
|
||||||
|
// TODO(mbolin): Could present the error in a more
|
||||||
|
// structured way.
|
||||||
|
structured_content: None,
|
||||||
};
|
};
|
||||||
let _ = outgoing
|
let _ = outgoing
|
||||||
.send(JSONRPCMessage::Response(JSONRPCResponse {
|
.send(JSONRPCMessage::Response(JSONRPCResponse {
|
||||||
|
|||||||
@@ -70,9 +70,7 @@ pub async fn run_main(codex_linux_sandbox_exe: Option<PathBuf>) -> IoResult<()>
|
|||||||
JSONRPCMessage::Request(r) => processor.process_request(r),
|
JSONRPCMessage::Request(r) => processor.process_request(r),
|
||||||
JSONRPCMessage::Response(r) => processor.process_response(r),
|
JSONRPCMessage::Response(r) => processor.process_response(r),
|
||||||
JSONRPCMessage::Notification(n) => processor.process_notification(n),
|
JSONRPCMessage::Notification(n) => processor.process_notification(n),
|
||||||
JSONRPCMessage::BatchRequest(b) => processor.process_batch_request(b),
|
|
||||||
JSONRPCMessage::Error(e) => processor.process_error(e),
|
JSONRPCMessage::Error(e) => processor.process_error(e),
|
||||||
JSONRPCMessage::BatchResponse(b) => processor.process_batch_response(b),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,9 @@ use crate::codex_tool_config::create_tool_for_codex_tool_call_param;
|
|||||||
use codex_core::config::Config as CodexConfig;
|
use codex_core::config::Config as CodexConfig;
|
||||||
use mcp_types::CallToolRequestParams;
|
use mcp_types::CallToolRequestParams;
|
||||||
use mcp_types::CallToolResult;
|
use mcp_types::CallToolResult;
|
||||||
use mcp_types::CallToolResultContent;
|
|
||||||
use mcp_types::ClientRequest;
|
use mcp_types::ClientRequest;
|
||||||
|
use mcp_types::ContentBlock;
|
||||||
use mcp_types::JSONRPC_VERSION;
|
use mcp_types::JSONRPC_VERSION;
|
||||||
use mcp_types::JSONRPCBatchRequest;
|
|
||||||
use mcp_types::JSONRPCBatchResponse;
|
|
||||||
use mcp_types::JSONRPCError;
|
use mcp_types::JSONRPCError;
|
||||||
use mcp_types::JSONRPCErrorError;
|
use mcp_types::JSONRPCErrorError;
|
||||||
use mcp_types::JSONRPCMessage;
|
use mcp_types::JSONRPCMessage;
|
||||||
@@ -145,41 +143,11 @@ impl MessageProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle a batch of requests and/or notifications.
|
|
||||||
pub(crate) fn process_batch_request(&mut self, batch: JSONRPCBatchRequest) {
|
|
||||||
tracing::info!("<- batch request containing {} item(s)", batch.len());
|
|
||||||
for item in batch {
|
|
||||||
match item {
|
|
||||||
mcp_types::JSONRPCBatchRequestItem::JSONRPCRequest(req) => {
|
|
||||||
self.process_request(req);
|
|
||||||
}
|
|
||||||
mcp_types::JSONRPCBatchRequestItem::JSONRPCNotification(note) => {
|
|
||||||
self.process_notification(note);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handle an error object received from the peer.
|
/// Handle an error object received from the peer.
|
||||||
pub(crate) fn process_error(&mut self, err: JSONRPCError) {
|
pub(crate) fn process_error(&mut self, err: JSONRPCError) {
|
||||||
tracing::error!("<- error: {:?}", err);
|
tracing::error!("<- error: {:?}", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle a batch of responses/errors.
|
|
||||||
pub(crate) fn process_batch_response(&mut self, batch: JSONRPCBatchResponse) {
|
|
||||||
tracing::info!("<- batch response containing {} item(s)", batch.len());
|
|
||||||
for item in batch {
|
|
||||||
match item {
|
|
||||||
mcp_types::JSONRPCBatchResponseItem::JSONRPCResponse(resp) => {
|
|
||||||
self.process_response(resp);
|
|
||||||
}
|
|
||||||
mcp_types::JSONRPCBatchResponseItem::JSONRPCError(err) => {
|
|
||||||
self.process_error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_initialize(
|
fn handle_initialize(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: RequestId,
|
id: RequestId,
|
||||||
@@ -224,6 +192,7 @@ impl MessageProcessor {
|
|||||||
server_info: mcp_types::Implementation {
|
server_info: mcp_types::Implementation {
|
||||||
name: "codex-mcp-server".to_string(),
|
name: "codex-mcp-server".to_string(),
|
||||||
version: mcp_types::MCP_SCHEMA_VERSION.to_string(),
|
version: mcp_types::MCP_SCHEMA_VERSION.to_string(),
|
||||||
|
title: Some("Codex".to_string()),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -333,12 +302,13 @@ impl MessageProcessor {
|
|||||||
if name != "codex" {
|
if name != "codex" {
|
||||||
// Tool not found – return error result so the LLM can react.
|
// Tool not found – return error result so the LLM can react.
|
||||||
let result = CallToolResult {
|
let result = CallToolResult {
|
||||||
content: vec![CallToolResultContent::TextContent(TextContent {
|
content: vec![ContentBlock::TextContent(TextContent {
|
||||||
r#type: "text".to_string(),
|
r#type: "text".to_string(),
|
||||||
text: format!("Unknown tool '{name}'"),
|
text: format!("Unknown tool '{name}'"),
|
||||||
annotations: None,
|
annotations: None,
|
||||||
})],
|
})],
|
||||||
is_error: Some(true),
|
is_error: Some(true),
|
||||||
|
structured_content: None,
|
||||||
};
|
};
|
||||||
self.send_response::<mcp_types::CallToolRequest>(id, result);
|
self.send_response::<mcp_types::CallToolRequest>(id, result);
|
||||||
return;
|
return;
|
||||||
@@ -350,7 +320,7 @@ impl MessageProcessor {
|
|||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let result = CallToolResult {
|
let result = CallToolResult {
|
||||||
content: vec![CallToolResultContent::TextContent(TextContent {
|
content: vec![ContentBlock::TextContent(TextContent {
|
||||||
r#type: "text".to_owned(),
|
r#type: "text".to_owned(),
|
||||||
text: format!(
|
text: format!(
|
||||||
"Failed to load Codex configuration from overrides: {e}"
|
"Failed to load Codex configuration from overrides: {e}"
|
||||||
@@ -358,6 +328,7 @@ impl MessageProcessor {
|
|||||||
annotations: None,
|
annotations: None,
|
||||||
})],
|
})],
|
||||||
is_error: Some(true),
|
is_error: Some(true),
|
||||||
|
structured_content: None,
|
||||||
};
|
};
|
||||||
self.send_response::<mcp_types::CallToolRequest>(id, result);
|
self.send_response::<mcp_types::CallToolRequest>(id, result);
|
||||||
return;
|
return;
|
||||||
@@ -365,12 +336,13 @@ impl MessageProcessor {
|
|||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let result = CallToolResult {
|
let result = CallToolResult {
|
||||||
content: vec![CallToolResultContent::TextContent(TextContent {
|
content: vec![ContentBlock::TextContent(TextContent {
|
||||||
r#type: "text".to_owned(),
|
r#type: "text".to_owned(),
|
||||||
text: format!("Failed to parse configuration for Codex tool: {e}"),
|
text: format!("Failed to parse configuration for Codex tool: {e}"),
|
||||||
annotations: None,
|
annotations: None,
|
||||||
})],
|
})],
|
||||||
is_error: Some(true),
|
is_error: Some(true),
|
||||||
|
structured_content: None,
|
||||||
};
|
};
|
||||||
self.send_response::<mcp_types::CallToolRequest>(id, result);
|
self.send_response::<mcp_types::CallToolRequest>(id, result);
|
||||||
return;
|
return;
|
||||||
@@ -378,7 +350,7 @@ impl MessageProcessor {
|
|||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
let result = CallToolResult {
|
let result = CallToolResult {
|
||||||
content: vec![CallToolResultContent::TextContent(TextContent {
|
content: vec![ContentBlock::TextContent(TextContent {
|
||||||
r#type: "text".to_string(),
|
r#type: "text".to_string(),
|
||||||
text:
|
text:
|
||||||
"Missing arguments for codex tool-call; the `prompt` field is required."
|
"Missing arguments for codex tool-call; the `prompt` field is required."
|
||||||
@@ -386,6 +358,7 @@ impl MessageProcessor {
|
|||||||
annotations: None,
|
annotations: None,
|
||||||
})],
|
})],
|
||||||
is_error: Some(true),
|
is_error: Some(true),
|
||||||
|
structured_content: None,
|
||||||
};
|
};
|
||||||
self.send_response::<mcp_types::CallToolRequest>(id, result);
|
self.send_response::<mcp_types::CallToolRequest>(id, result);
|
||||||
return;
|
return;
|
||||||
@@ -398,7 +371,7 @@ impl MessageProcessor {
|
|||||||
// Spawn an async task to handle the Codex session so that we do not
|
// Spawn an async task to handle the Codex session so that we do not
|
||||||
// block the synchronous message-processing loop.
|
// block the synchronous message-processing loop.
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
// Run the Codex session and stream events back to the client.
|
// Run the Codex session and stream events Fck to the client.
|
||||||
crate::codex_tool_runner::run_codex_tool_session(id, initial_prompt, config, outgoing)
|
crate::codex_tool_runner::run_codex_tool_session(id, initial_prompt, config, outgoing)
|
||||||
.await;
|
.await;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Types for Model Context Protocol. Inspired by https://crates.io/crates/lsp-types.
|
Types for Model Context Protocol. Inspired by https://crates.io/crates/lsp-types.
|
||||||
|
|
||||||
As documented on https://modelcontextprotocol.io/specification/2025-03-26/basic:
|
As documented on https://modelcontextprotocol.io/specification/2025-06-18/basic:
|
||||||
|
|
||||||
- TypeScript schema is the source of truth: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-03-26/schema.ts
|
- TypeScript schema is the source of truth: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-06-18/schema.ts
|
||||||
- JSON schema is amenable to automated tooling: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-03-26/schema.json
|
- JSON schema is amenable to automated tooling: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-06-18/schema.json
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from pathlib import Path
|
|||||||
# Helper first so it is defined when other functions call it.
|
# Helper first so it is defined when other functions call it.
|
||||||
from typing import Any, Literal
|
from typing import Any, Literal
|
||||||
|
|
||||||
SCHEMA_VERSION = "2025-03-26"
|
SCHEMA_VERSION = "2025-06-18"
|
||||||
JSONRPC_VERSION = "2.0"
|
JSONRPC_VERSION = "2.0"
|
||||||
|
|
||||||
STANDARD_DERIVE = "#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]\n"
|
STANDARD_DERIVE = "#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]\n"
|
||||||
@@ -222,20 +222,7 @@ def add_definition(name: str, definition: dict[str, Any], out: list[str]) -> Non
|
|||||||
any_of = definition.get("anyOf", [])
|
any_of = definition.get("anyOf", [])
|
||||||
if any_of:
|
if any_of:
|
||||||
assert isinstance(any_of, list)
|
assert isinstance(any_of, list)
|
||||||
if name == "JSONRPCMessage":
|
out.extend(define_any_of(name, any_of, description))
|
||||||
# Special case for JSONRPCMessage because its definition in the
|
|
||||||
# JSON schema does not quite match how we think about this type
|
|
||||||
# definition in Rust.
|
|
||||||
deep_copied_any_of = json.loads(json.dumps(any_of))
|
|
||||||
deep_copied_any_of[2] = {
|
|
||||||
"$ref": "#/definitions/JSONRPCBatchRequest",
|
|
||||||
}
|
|
||||||
deep_copied_any_of[5] = {
|
|
||||||
"$ref": "#/definitions/JSONRPCBatchResponse",
|
|
||||||
}
|
|
||||||
out.extend(define_any_of(name, deep_copied_any_of, description))
|
|
||||||
else:
|
|
||||||
out.extend(define_any_of(name, any_of, description))
|
|
||||||
return
|
return
|
||||||
|
|
||||||
type_prop = definition.get("type", None)
|
type_prop = definition.get("type", None)
|
||||||
@@ -609,6 +596,8 @@ def rust_prop_name(name: str, is_optional: bool) -> RustProp:
|
|||||||
prop_name = "r#type"
|
prop_name = "r#type"
|
||||||
elif name == "ref":
|
elif name == "ref":
|
||||||
prop_name = "r#ref"
|
prop_name = "r#ref"
|
||||||
|
elif name == "enum":
|
||||||
|
prop_name = "r#enum"
|
||||||
elif snake_case := to_snake_case(name):
|
elif snake_case := to_snake_case(name):
|
||||||
prop_name = snake_case
|
prop_name = snake_case
|
||||||
is_rename = True
|
is_rename = True
|
||||||
|
|||||||
2517
codex-rs/mcp-types/schema/2025-06-18/schema.json
Normal file
2517
codex-rs/mcp-types/schema/2025-06-18/schema.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@ use serde::Serialize;
|
|||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
pub const MCP_SCHEMA_VERSION: &str = "2025-03-26";
|
pub const MCP_SCHEMA_VERSION: &str = "2025-06-18";
|
||||||
pub const JSONRPC_VERSION: &str = "2.0";
|
pub const JSONRPC_VERSION: &str = "2.0";
|
||||||
|
|
||||||
/// Paired request/response types for the Model Context Protocol (MCP).
|
/// Paired request/response types for the Model Context Protocol (MCP).
|
||||||
@@ -35,6 +35,12 @@ fn default_jsonrpc() -> String {
|
|||||||
pub struct Annotations {
|
pub struct Annotations {
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub audience: Option<Vec<Role>>,
|
pub audience: Option<Vec<Role>>,
|
||||||
|
#[serde(
|
||||||
|
rename = "lastModified",
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "Option::is_none"
|
||||||
|
)]
|
||||||
|
pub last_modified: Option<String>,
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub priority: Option<f64>,
|
pub priority: Option<f64>,
|
||||||
}
|
}
|
||||||
@@ -50,6 +56,14 @@ pub struct AudioContent {
|
|||||||
pub r#type: String, // &'static str = "audio"
|
pub r#type: String, // &'static str = "audio"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Base interface for metadata with name (identifier) and title (display name) properties.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
pub struct BaseMetadata {
|
||||||
|
pub name: String,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub title: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct BlobResourceContents {
|
pub struct BlobResourceContents {
|
||||||
pub blob: String,
|
pub blob: String,
|
||||||
@@ -58,6 +72,17 @@ pub struct BlobResourceContents {
|
|||||||
pub uri: String,
|
pub uri: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
pub struct BooleanSchema {
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub default: Option<bool>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub description: Option<String>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub r#type: String, // &'static str = "boolean"
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub enum CallToolRequest {}
|
pub enum CallToolRequest {}
|
||||||
|
|
||||||
@@ -75,29 +100,17 @@ pub struct CallToolRequestParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The server's response to a tool call.
|
/// The server's response to a tool call.
|
||||||
///
|
|
||||||
/// Any errors that originate from the tool SHOULD be reported inside the result
|
|
||||||
/// object, with `isError` set to true, _not_ as an MCP protocol-level error
|
|
||||||
/// response. Otherwise, the LLM would not be able to see that an error occurred
|
|
||||||
/// and self-correct.
|
|
||||||
///
|
|
||||||
/// However, any errors in _finding_ the tool, an error indicating that the
|
|
||||||
/// server does not support tool calls, or any other exceptional conditions,
|
|
||||||
/// should be reported as an MCP error response.
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct CallToolResult {
|
pub struct CallToolResult {
|
||||||
pub content: Vec<CallToolResultContent>,
|
pub content: Vec<ContentBlock>,
|
||||||
#[serde(rename = "isError", default, skip_serializing_if = "Option::is_none")]
|
#[serde(rename = "isError", default, skip_serializing_if = "Option::is_none")]
|
||||||
pub is_error: Option<bool>,
|
pub is_error: Option<bool>,
|
||||||
}
|
#[serde(
|
||||||
|
rename = "structuredContent",
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
default,
|
||||||
#[serde(untagged)]
|
skip_serializing_if = "Option::is_none"
|
||||||
pub enum CallToolResultContent {
|
)]
|
||||||
TextContent(TextContent),
|
pub structured_content: Option<serde_json::Value>,
|
||||||
ImageContent(ImageContent),
|
|
||||||
AudioContent(AudioContent),
|
|
||||||
EmbeddedResource(EmbeddedResource),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<CallToolResult> for serde_json::Value {
|
impl From<CallToolResult> for serde_json::Value {
|
||||||
@@ -127,6 +140,8 @@ pub struct CancelledNotificationParams {
|
|||||||
/// Capabilities a client may support. Known capabilities are defined here, in this schema, but this is not a closed set: any client can define its own, additional capabilities.
|
/// Capabilities a client may support. Known capabilities are defined here, in this schema, but this is not a closed set: any client can define its own, additional capabilities.
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct ClientCapabilities {
|
pub struct ClientCapabilities {
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub elicitation: Option<serde_json::Value>,
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub experimental: Option<serde_json::Value>,
|
pub experimental: Option<serde_json::Value>,
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
@@ -194,6 +209,7 @@ pub enum ClientResult {
|
|||||||
Result(Result),
|
Result(Result),
|
||||||
CreateMessageResult(CreateMessageResult),
|
CreateMessageResult(CreateMessageResult),
|
||||||
ListRootsResult(ListRootsResult),
|
ListRootsResult(ListRootsResult),
|
||||||
|
ElicitResult(ElicitResult),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
@@ -208,9 +224,18 @@ impl ModelContextProtocolRequest for CompleteRequest {
|
|||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct CompleteRequestParams {
|
pub struct CompleteRequestParams {
|
||||||
pub argument: CompleteRequestParamsArgument,
|
pub argument: CompleteRequestParamsArgument,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub context: Option<CompleteRequestParamsContext>,
|
||||||
pub r#ref: CompleteRequestParamsRef,
|
pub r#ref: CompleteRequestParamsRef,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Additional, optional context for completions
|
||||||
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
pub struct CompleteRequestParamsContext {
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub arguments: Option<serde_json::Value>,
|
||||||
|
}
|
||||||
|
|
||||||
/// The argument's information
|
/// The argument's information
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct CompleteRequestParamsArgument {
|
pub struct CompleteRequestParamsArgument {
|
||||||
@@ -222,7 +247,7 @@ pub struct CompleteRequestParamsArgument {
|
|||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum CompleteRequestParamsRef {
|
pub enum CompleteRequestParamsRef {
|
||||||
PromptReference(PromptReference),
|
PromptReference(PromptReference),
|
||||||
ResourceReference(ResourceReference),
|
ResourceTemplateReference(ResourceTemplateReference),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The server's response to a completion/complete request
|
/// The server's response to a completion/complete request
|
||||||
@@ -248,6 +273,16 @@ impl From<CompleteResult> for serde_json::Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum ContentBlock {
|
||||||
|
TextContent(TextContent),
|
||||||
|
ImageContent(ImageContent),
|
||||||
|
AudioContent(AudioContent),
|
||||||
|
ResourceLink(ResourceLink),
|
||||||
|
EmbeddedResource(EmbeddedResource),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub enum CreateMessageRequest {}
|
pub enum CreateMessageRequest {}
|
||||||
|
|
||||||
@@ -325,6 +360,48 @@ impl From<CreateMessageResult> for serde_json::Value {
|
|||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct Cursor(String);
|
pub struct Cursor(String);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
pub enum ElicitRequest {}
|
||||||
|
|
||||||
|
impl ModelContextProtocolRequest for ElicitRequest {
|
||||||
|
const METHOD: &'static str = "elicitation/create";
|
||||||
|
type Params = ElicitRequestParams;
|
||||||
|
type Result = ElicitResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
pub struct ElicitRequestParams {
|
||||||
|
pub message: String,
|
||||||
|
#[serde(rename = "requestedSchema")]
|
||||||
|
pub requested_schema: ElicitRequestParamsRequestedSchema,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A restricted subset of JSON Schema.
|
||||||
|
/// Only top-level properties are allowed, without nesting.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
pub struct ElicitRequestParamsRequestedSchema {
|
||||||
|
pub properties: serde_json::Value,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub required: Option<Vec<String>>,
|
||||||
|
pub r#type: String, // &'static str = "object"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The client's response to an elicitation request.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
pub struct ElicitResult {
|
||||||
|
pub action: String,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub content: Option<serde_json::Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ElicitResult> for serde_json::Value {
|
||||||
|
fn from(value: ElicitResult) -> Self {
|
||||||
|
// Leave this as it should never fail
|
||||||
|
#[expect(clippy::unwrap_used)]
|
||||||
|
serde_json::to_value(value).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The contents of a resource, embedded into a prompt or tool call result.
|
/// The contents of a resource, embedded into a prompt or tool call result.
|
||||||
///
|
///
|
||||||
/// It is up to the client how best to render embedded resources for the benefit
|
/// It is up to the client how best to render embedded resources for the benefit
|
||||||
@@ -346,6 +423,18 @@ pub enum EmbeddedResourceResource {
|
|||||||
|
|
||||||
pub type EmptyResult = Result;
|
pub type EmptyResult = Result;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
pub struct EnumSchema {
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub r#enum: Vec<String>,
|
||||||
|
#[serde(rename = "enumNames", default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub enum_names: Option<Vec<String>>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub r#type: String, // &'static str = "string"
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub enum GetPromptRequest {}
|
pub enum GetPromptRequest {}
|
||||||
|
|
||||||
@@ -389,10 +478,12 @@ pub struct ImageContent {
|
|||||||
pub r#type: String, // &'static str = "image"
|
pub r#type: String, // &'static str = "image"
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes the name and version of an MCP implementation.
|
/// Describes the name and version of an MCP implementation, with an optional title for UI representation.
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct Implementation {
|
pub struct Implementation {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub title: Option<String>,
|
||||||
pub version: String,
|
pub version: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,24 +533,6 @@ impl ModelContextProtocolNotification for InitializedNotification {
|
|||||||
type Params = Option<serde_json::Value>;
|
type Params = Option<serde_json::Value>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
pub enum JSONRPCBatchRequestItem {
|
|
||||||
JSONRPCRequest(JSONRPCRequest),
|
|
||||||
JSONRPCNotification(JSONRPCNotification),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type JSONRPCBatchRequest = Vec<JSONRPCBatchRequestItem>;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
pub enum JSONRPCBatchResponseItem {
|
|
||||||
JSONRPCResponse(JSONRPCResponse),
|
|
||||||
JSONRPCError(JSONRPCError),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type JSONRPCBatchResponse = Vec<JSONRPCBatchResponseItem>;
|
|
||||||
|
|
||||||
/// A response to a request that indicates an error occurred.
|
/// A response to a request that indicates an error occurred.
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct JSONRPCError {
|
pub struct JSONRPCError {
|
||||||
@@ -483,10 +556,8 @@ pub struct JSONRPCErrorError {
|
|||||||
pub enum JSONRPCMessage {
|
pub enum JSONRPCMessage {
|
||||||
Request(JSONRPCRequest),
|
Request(JSONRPCRequest),
|
||||||
Notification(JSONRPCNotification),
|
Notification(JSONRPCNotification),
|
||||||
BatchRequest(JSONRPCBatchRequest),
|
|
||||||
Response(JSONRPCResponse),
|
Response(JSONRPCResponse),
|
||||||
Error(JSONRPCError),
|
Error(JSONRPCError),
|
||||||
BatchResponse(JSONRPCBatchResponse),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A notification which does not expect a response.
|
/// A notification which does not expect a response.
|
||||||
@@ -777,6 +848,19 @@ pub struct Notification {
|
|||||||
pub params: Option<serde_json::Value>,
|
pub params: Option<serde_json::Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
pub struct NumberSchema {
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub description: Option<String>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub maximum: Option<i64>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub minimum: Option<i64>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub r#type: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct PaginatedRequest {
|
pub struct PaginatedRequest {
|
||||||
pub method: String,
|
pub method: String,
|
||||||
@@ -817,6 +901,17 @@ impl ModelContextProtocolRequest for PingRequest {
|
|||||||
type Result = Result;
|
type Result = Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Restricted schema definitions that only allow primitive types
|
||||||
|
/// without nested objects or arrays.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum PrimitiveSchemaDefinition {
|
||||||
|
StringSchema(StringSchema),
|
||||||
|
NumberSchema(NumberSchema),
|
||||||
|
BooleanSchema(BooleanSchema),
|
||||||
|
EnumSchema(EnumSchema),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub enum ProgressNotification {}
|
pub enum ProgressNotification {}
|
||||||
|
|
||||||
@@ -851,6 +946,8 @@ pub struct Prompt {
|
|||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub title: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes an argument that a prompt can accept.
|
/// Describes an argument that a prompt can accept.
|
||||||
@@ -861,6 +958,8 @@ pub struct PromptArgument {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub required: Option<bool>,
|
pub required: Option<bool>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub title: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
@@ -877,23 +976,16 @@ impl ModelContextProtocolNotification for PromptListChangedNotification {
|
|||||||
/// resources from the MCP server.
|
/// resources from the MCP server.
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct PromptMessage {
|
pub struct PromptMessage {
|
||||||
pub content: PromptMessageContent,
|
pub content: ContentBlock,
|
||||||
pub role: Role,
|
pub role: Role,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
pub enum PromptMessageContent {
|
|
||||||
TextContent(TextContent),
|
|
||||||
ImageContent(ImageContent),
|
|
||||||
AudioContent(AudioContent),
|
|
||||||
EmbeddedResource(EmbeddedResource),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Identifies a prompt.
|
/// Identifies a prompt.
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct PromptReference {
|
pub struct PromptReference {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub title: Option<String>,
|
||||||
pub r#type: String, // &'static str = "ref/prompt"
|
pub r#type: String, // &'static str = "ref/prompt"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -958,6 +1050,8 @@ pub struct Resource {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub size: Option<i64>,
|
pub size: Option<i64>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub title: Option<String>,
|
||||||
pub uri: String,
|
pub uri: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -969,6 +1063,26 @@ pub struct ResourceContents {
|
|||||||
pub uri: String,
|
pub uri: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A resource that the server is capable of reading, included in a prompt or tool call result.
|
||||||
|
///
|
||||||
|
/// Note: resource links returned by tools are not guaranteed to appear in the results of `resources/list` requests.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
pub struct ResourceLink {
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub annotations: Option<Annotations>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub description: Option<String>,
|
||||||
|
#[serde(rename = "mimeType", default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub mime_type: Option<String>,
|
||||||
|
pub name: String,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub size: Option<i64>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub r#type: String, // &'static str = "resource_link"
|
||||||
|
pub uri: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub enum ResourceListChangedNotification {}
|
pub enum ResourceListChangedNotification {}
|
||||||
|
|
||||||
@@ -977,13 +1091,6 @@ impl ModelContextProtocolNotification for ResourceListChangedNotification {
|
|||||||
type Params = Option<serde_json::Value>;
|
type Params = Option<serde_json::Value>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A reference to a resource or resource template definition.
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
|
||||||
pub struct ResourceReference {
|
|
||||||
pub r#type: String, // &'static str = "ref/resource"
|
|
||||||
pub uri: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A template description for resources available on the server.
|
/// A template description for resources available on the server.
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct ResourceTemplate {
|
pub struct ResourceTemplate {
|
||||||
@@ -994,10 +1101,19 @@ pub struct ResourceTemplate {
|
|||||||
#[serde(rename = "mimeType", default, skip_serializing_if = "Option::is_none")]
|
#[serde(rename = "mimeType", default, skip_serializing_if = "Option::is_none")]
|
||||||
pub mime_type: Option<String>,
|
pub mime_type: Option<String>,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub title: Option<String>,
|
||||||
#[serde(rename = "uriTemplate")]
|
#[serde(rename = "uriTemplate")]
|
||||||
pub uri_template: String,
|
pub uri_template: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A reference to a resource or resource template definition.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
pub struct ResourceTemplateReference {
|
||||||
|
pub r#type: String, // &'static str = "ref/resource"
|
||||||
|
pub uri: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub enum ResourceUpdatedNotification {}
|
pub enum ResourceUpdatedNotification {}
|
||||||
|
|
||||||
@@ -1140,6 +1256,7 @@ pub enum ServerRequest {
|
|||||||
PingRequest(PingRequest),
|
PingRequest(PingRequest),
|
||||||
CreateMessageRequest(CreateMessageRequest),
|
CreateMessageRequest(CreateMessageRequest),
|
||||||
ListRootsRequest(ListRootsRequest),
|
ListRootsRequest(ListRootsRequest),
|
||||||
|
ElicitRequest(ElicitRequest),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
@@ -1172,6 +1289,21 @@ pub struct SetLevelRequestParams {
|
|||||||
pub level: LoggingLevel,
|
pub level: LoggingLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
pub struct StringSchema {
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub description: Option<String>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub format: Option<String>,
|
||||||
|
#[serde(rename = "maxLength", default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub max_length: Option<i64>,
|
||||||
|
#[serde(rename = "minLength", default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub min_length: Option<i64>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub r#type: String, // &'static str = "string"
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub enum SubscribeRequest {}
|
pub enum SubscribeRequest {}
|
||||||
|
|
||||||
@@ -1213,6 +1345,25 @@ pub struct Tool {
|
|||||||
#[serde(rename = "inputSchema")]
|
#[serde(rename = "inputSchema")]
|
||||||
pub input_schema: ToolInputSchema,
|
pub input_schema: ToolInputSchema,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
#[serde(
|
||||||
|
rename = "outputSchema",
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "Option::is_none"
|
||||||
|
)]
|
||||||
|
pub output_schema: Option<ToolOutputSchema>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub title: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An optional JSON Schema object defining the structure of the tool's output returned in
|
||||||
|
/// the structuredContent field of a CallToolResult.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
pub struct ToolOutputSchema {
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub properties: Option<serde_json::Value>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub required: Option<Vec<String>>,
|
||||||
|
pub r#type: String, // &'static str = "object"
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A JSON Schema object defining the expected parameters for the tool.
|
/// A JSON Schema object defining the expected parameters for the tool.
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ fn deserialize_initialize_request() {
|
|||||||
"method": "initialize",
|
"method": "initialize",
|
||||||
"params": {
|
"params": {
|
||||||
"capabilities": {},
|
"capabilities": {},
|
||||||
"clientInfo": { "name": "acme-client", "version": "1.2.3" },
|
"clientInfo": { "name": "acme-client", "title": "Acme", "version": "1.2.3" },
|
||||||
"protocolVersion": "2025-03-26"
|
"protocolVersion": "2025-06-18"
|
||||||
}
|
}
|
||||||
}"#;
|
}"#;
|
||||||
|
|
||||||
@@ -37,8 +37,8 @@ fn deserialize_initialize_request() {
|
|||||||
method: "initialize".into(),
|
method: "initialize".into(),
|
||||||
params: Some(json!({
|
params: Some(json!({
|
||||||
"capabilities": {},
|
"capabilities": {},
|
||||||
"clientInfo": { "name": "acme-client", "version": "1.2.3" },
|
"clientInfo": { "name": "acme-client", "title": "Acme", "version": "1.2.3" },
|
||||||
"protocolVersion": "2025-03-26"
|
"protocolVersion": "2025-06-18"
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -57,12 +57,14 @@ fn deserialize_initialize_request() {
|
|||||||
experimental: None,
|
experimental: None,
|
||||||
roots: None,
|
roots: None,
|
||||||
sampling: None,
|
sampling: None,
|
||||||
|
elicitation: None,
|
||||||
},
|
},
|
||||||
client_info: Implementation {
|
client_info: Implementation {
|
||||||
name: "acme-client".into(),
|
name: "acme-client".into(),
|
||||||
|
title: Some("Acme".to_string()),
|
||||||
version: "1.2.3".into(),
|
version: "1.2.3".into(),
|
||||||
},
|
},
|
||||||
protocol_version: "2025-03-26".into(),
|
protocol_version: "2025-06-18".into(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ use image::GenericImageView;
|
|||||||
use image::ImageReader;
|
use image::ImageReader;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use mcp_types::EmbeddedResourceResource;
|
use mcp_types::EmbeddedResourceResource;
|
||||||
|
use mcp_types::ResourceLink;
|
||||||
use ratatui::prelude::*;
|
use ratatui::prelude::*;
|
||||||
use ratatui::style::Color;
|
use ratatui::style::Color;
|
||||||
use ratatui::style::Modifier;
|
use ratatui::style::Modifier;
|
||||||
@@ -331,8 +332,7 @@ impl HistoryCell {
|
|||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
match result {
|
match result {
|
||||||
Ok(mcp_types::CallToolResult { content, .. }) => {
|
Ok(mcp_types::CallToolResult { content, .. }) => {
|
||||||
if let Some(mcp_types::CallToolResultContent::ImageContent(image)) = content.first()
|
if let Some(mcp_types::ContentBlock::ImageContent(image)) = content.first() {
|
||||||
{
|
|
||||||
let raw_data =
|
let raw_data =
|
||||||
match base64::engine::general_purpose::STANDARD.decode(&image.data) {
|
match base64::engine::general_purpose::STANDARD.decode(&image.data) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
@@ -405,21 +405,21 @@ impl HistoryCell {
|
|||||||
|
|
||||||
for tool_call_result in content {
|
for tool_call_result in content {
|
||||||
let line_text = match tool_call_result {
|
let line_text = match tool_call_result {
|
||||||
mcp_types::CallToolResultContent::TextContent(text) => {
|
mcp_types::ContentBlock::TextContent(text) => {
|
||||||
format_and_truncate_tool_result(
|
format_and_truncate_tool_result(
|
||||||
&text.text,
|
&text.text,
|
||||||
TOOL_CALL_MAX_LINES,
|
TOOL_CALL_MAX_LINES,
|
||||||
num_cols as usize,
|
num_cols as usize,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
mcp_types::CallToolResultContent::ImageContent(_) => {
|
mcp_types::ContentBlock::ImageContent(_) => {
|
||||||
// TODO show images even if they're not the first result, will require a refactor of `CompletedMcpToolCall`
|
// TODO show images even if they're not the first result, will require a refactor of `CompletedMcpToolCall`
|
||||||
"<image content>".to_string()
|
"<image content>".to_string()
|
||||||
}
|
}
|
||||||
mcp_types::CallToolResultContent::AudioContent(_) => {
|
mcp_types::ContentBlock::AudioContent(_) => {
|
||||||
"<audio content>".to_string()
|
"<audio content>".to_string()
|
||||||
}
|
}
|
||||||
mcp_types::CallToolResultContent::EmbeddedResource(resource) => {
|
mcp_types::ContentBlock::EmbeddedResource(resource) => {
|
||||||
let uri = match resource.resource {
|
let uri = match resource.resource {
|
||||||
EmbeddedResourceResource::TextResourceContents(text) => {
|
EmbeddedResourceResource::TextResourceContents(text) => {
|
||||||
text.uri
|
text.uri
|
||||||
@@ -430,6 +430,9 @@ impl HistoryCell {
|
|||||||
};
|
};
|
||||||
format!("embedded resource: {uri}")
|
format!("embedded resource: {uri}")
|
||||||
}
|
}
|
||||||
|
mcp_types::ContentBlock::ResourceLink(ResourceLink { uri, .. }) => {
|
||||||
|
format!("link: {uri}")
|
||||||
|
}
|
||||||
};
|
};
|
||||||
lines.push(Line::styled(line_text, Style::default().fg(Color::Gray)));
|
lines.push(Line::styled(line_text, Style::default().fg(Color::Gray)));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user