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}