mcp: execute stdio tool calls

This commit is contained in:
2026-06-20 18:07:21 +09:00
parent 92432ad750
commit 9a2454037f
4 changed files with 689 additions and 12 deletions
+64
View File
@@ -99,6 +99,51 @@ pub struct ListToolsResult {
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CallToolRequest {
pub name: String,
#[serde(default, skip_serializing_if = "Value::is_null")]
pub arguments: Value,
}
impl CallToolRequest {
pub fn new(name: impl Into<String>, arguments: Value) -> Self {
Self {
name: name.into(),
arguments,
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CallToolResult {
#[serde(default)]
pub content: Vec<McpContentBlock>,
#[serde(default)]
pub structured_content: Option<Value>,
#[serde(default)]
pub is_error: bool,
#[serde(default, rename = "_meta")]
pub meta: Option<Value>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
/// One untrusted MCP `tools/call` content block.
///
/// The `type` discriminator is kept explicit and all server-owned fields stay
/// data in `fields`; this crate does not turn rich MCP content into hidden host
/// context.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct McpContentBlock {
#[serde(rename = "type")]
pub kind: String,
#[serde(flatten)]
pub fields: BTreeMap<String, Value>,
}
/// A resolved, explicit local stdio MCP server process specification.
#[derive(Clone)]
pub struct McpStdioServerSpec {
@@ -426,6 +471,25 @@ impl McpStdioClient {
self.request(McpPhase::Running, "tools/list", params).await
}
/// Execute an initialized MCP `tools/call` request.
///
/// The caller is responsible for applying Yoi tool permissions before this
/// method is reached and for bounding/serializing the untrusted result before
/// it is exposed to model-visible tool history.
pub async fn call_tool(
&mut self,
request: CallToolRequest,
) -> Result<CallToolResult, McpClientError> {
let params = serde_json::to_value(request).map_err(|err| {
McpClientError::new(
&self.server_name,
McpPhase::Running,
McpErrorKind::Protocol(format!("failed to serialize tools/call request: {err}")),
)
})?;
self.request(McpPhase::Running, "tools/call", params).await
}
/// Request pages from `tools/list` up to a host-supplied page/tool bound.
///
/// Bounds are enforced by the host so a server cannot make startup discovery