Skip to main content

rustapi_mcp/
types.rs

1//! Core MCP data types (tool definitions, requests, responses, capabilities).
2//!
3//! These types are intentionally high-level for the foundation phase.
4//! They will be expanded with full JSON-RPC message shapes when we implement transports.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9/// A capability that this MCP server advertises during `initialize`.
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
11#[serde(rename_all = "camelCase")]
12pub enum McpCapability {
13    /// The server can list and invoke tools.
14    Tools,
15    /// Future: resources, prompts, sampling, etc.
16    #[serde(other)]
17    Other,
18}
19
20/// A tool description that will be sent to MCP clients in `tools/list`.
21#[derive(Debug, Clone, Serialize, Deserialize)]
22#[serde(rename_all = "camelCase")]
23pub struct McpTool {
24    /// Stable name of the tool (usually derived from path + method or a slug).
25    pub name: String,
26
27    /// Human readable description (comes from OpenAPI `summary` / `description` when available).
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub description: Option<String>,
30
31    /// JSON Schema for the input parameters (reused from `rustapi-openapi` / `Schema` derive).
32    pub input_schema: serde_json::Value,
33
34    /// Optional JSON Schema for the output (when we have good response schemas).
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub output_schema: Option<serde_json::Value>,
37
38    /// Tags associated with this tool (used for filtering via `McpConfig`).
39    #[serde(default, skip_serializing_if = "Vec::is_empty")]
40    pub tags: Vec<String>,
41
42    /// Framework-level permission classification ("read" | "write").
43    /// This is the key part of native scoping — agents can see the blast radius.
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub permission: Option<String>,
46
47    /// Whether this tool should trigger a confirmation prompt on the agent side.
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub requires_confirmation: Option<bool>,
50}
51
52/// Request from an MCP client to call a tool.
53#[derive(Debug, Clone, Deserialize)]
54#[serde(rename_all = "camelCase")]
55pub struct ToolCallRequest {
56    /// Name of the tool to invoke.
57    pub name: String,
58    /// Arguments passed to the tool (will be turned into extractors later).
59    #[serde(default)]
60    pub arguments: HashMap<String, serde_json::Value>,
61}
62
63/// Successful result of a tool call.
64#[derive(Debug, Clone, Serialize)]
65#[serde(rename_all = "camelCase")]
66pub struct ToolCallResponse {
67    /// The actual content returned by the underlying handler (serialized appropriately).
68    pub content: serde_json::Value,
69
70    /// Whether this result should be treated as an error by the agent (even if HTTP 2xx).
71    #[serde(default)]
72    pub is_error: bool,
73
74    /// Optional metadata (token counts when using TOON, execution path, etc.).
75    #[serde(skip_serializing_if = "Option::is_none")]
76    pub meta: Option<serde_json::Value>,
77}
78
79impl ToolCallResponse {
80    /// Construct a successful tool response.
81    pub fn success(content: impl Serialize) -> Self {
82        Self {
83            content: serde_json::to_value(content).unwrap_or(serde_json::json!({})),
84            is_error: false,
85            meta: None,
86        }
87    }
88
89    /// Construct an error tool response (from the agent's perspective).
90    pub fn error(message: impl Into<String>) -> Self {
91        Self {
92            content: serde_json::json!({ "error": message.into() }),
93            is_error: true,
94            meta: None,
95        }
96    }
97}