Skip to main content

tap_mcp/tools/
agent_tools.rs

1//! Agent management tools
2
3use super::schema;
4use super::{default_limit, error_text_response, success_text_response, ToolHandler};
5use crate::error::{Error, Result};
6use crate::mcp::protocol::{CallToolResult, Tool};
7use crate::tap_integration::TapIntegration;
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10use std::sync::Arc;
11use tracing::{debug, error, info};
12
13/// Tool for creating new TAP agents
14pub struct CreateAgentTool {
15    tap_integration: Arc<TapIntegration>,
16}
17
18/// Parameters for creating an agent
19#[derive(Debug, Deserialize)]
20#[serde(deny_unknown_fields)]
21struct CreateAgentParams {
22    #[serde(default)]
23    label: Option<String>,
24}
25
26/// Response for creating an agent
27#[derive(Debug, Serialize)]
28struct CreateAgentResponse {
29    #[serde(rename = "@id")]
30    id: String,
31    #[serde(skip_serializing_if = "Option::is_none")]
32    label: Option<String>,
33    created_at: String,
34}
35
36impl CreateAgentTool {
37    pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
38        Self { tap_integration }
39    }
40
41    fn tap_integration(&self) -> &TapIntegration {
42        &self.tap_integration
43    }
44}
45
46#[async_trait::async_trait]
47impl ToolHandler for CreateAgentTool {
48    async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
49        let params: CreateAgentParams = match arguments {
50            Some(args) => serde_json::from_value(args)
51                .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
52            None => {
53                return Ok(error_text_response(
54                    "Missing required parameters".to_string(),
55                ))
56            }
57        };
58
59        debug!("Creating new agent with auto-generated DID");
60
61        // Create an ephemeral agent
62        use std::sync::Arc;
63        use tap_agent::storage::KeyStorage;
64        use tap_agent::{
65            did::{DIDGenerationOptions, DIDKeyGenerator, KeyType},
66            TapAgent,
67        };
68
69        // Generate a new key
70        let generator = DIDKeyGenerator::new();
71        let generated_key = generator
72            .generate_did(DIDGenerationOptions {
73                key_type: KeyType::Ed25519,
74            })
75            .map_err(|e| Error::tool_execution(format!("Failed to generate DID: {}", e)))?;
76
77        debug!("Generated new DID for agent: {}", generated_key.did);
78
79        // Create the agent from the generated key
80        let (agent, generated_did) = TapAgent::from_private_key(
81            &generated_key.private_key,
82            generated_key.key_type,
83            false, // debug mode
84        )
85        .await
86        .map_err(|e| Error::tool_execution(format!("Failed to create agent: {}", e)))?;
87
88        // Save the key to storage with the label
89        let mut key_storage = match KeyStorage::load_default() {
90            Ok(storage) => storage,
91            Err(e) => {
92                debug!("Could not load existing key storage ({}), creating new", e);
93                KeyStorage::new()
94            }
95        };
96
97        // Create a StoredKey with the label
98        let stored_key = if let Some(ref label) = params.label {
99            tap_agent::storage::KeyStorage::from_generated_key_with_label(&generated_key, label)
100        } else {
101            tap_agent::storage::KeyStorage::from_generated_key(&generated_key)
102        };
103
104        // Add the key to storage
105        key_storage.add_key(stored_key);
106
107        debug!("Current keys in storage: {}", key_storage.keys.len());
108        for (did, key) in &key_storage.keys {
109            debug!("  - {}: {}", did, key.label);
110        }
111
112        // Save the storage
113        match key_storage.save_default() {
114            Ok(_) => {
115                info!(
116                    "Successfully saved agent key to storage with label: {:?}",
117                    params.label
118                );
119            }
120            Err(e) => {
121                error!("Failed to save key storage: {}", e);
122                return Err(Error::tool_execution(format!(
123                    "Failed to save key storage: {}",
124                    e
125                )));
126            }
127        }
128
129        // Register the agent with the TapNode
130        match self
131            .tap_integration()
132            .node()
133            .register_agent(Arc::new(agent))
134            .await
135        {
136            Ok(()) => {
137                info!("Created and registered agent with DID: {}", generated_did);
138
139                let response = CreateAgentResponse {
140                    id: generated_did,
141                    label: params.label,
142                    created_at: chrono::Utc::now().to_rfc3339(),
143                };
144
145                let response_json = serde_json::to_string_pretty(&response).map_err(|e| {
146                    Error::tool_execution(format!("Failed to serialize response: {}", e))
147                })?;
148
149                debug!("CreateAgent response JSON: {}", response_json);
150                Ok(success_text_response(response_json))
151            }
152            Err(e) => {
153                error!("Failed to create agent: {}", e);
154                Ok(error_text_response(format!(
155                    "Failed to create agent: {}",
156                    e
157                )))
158            }
159        }
160    }
161
162    fn get_definition(&self) -> Tool {
163        Tool {
164            name: "tap_create_agent".to_string(),
165            description: "Creates a new TAP agent with auto-generated DID and stores the keys in ~/.tap/keys.json. Returns the generated DID. Roles and party associations are specified per transaction, not during agent creation.".to_string(),
166            input_schema: schema::create_agent_schema(),
167        }
168    }
169}
170
171/// Tool for listing TAP agents
172pub struct ListAgentsTool {
173    tap_integration: Arc<TapIntegration>,
174}
175
176/// Parameters for listing agents
177#[derive(Debug, Deserialize)]
178struct ListAgentsParams {
179    #[serde(default = "default_limit")]
180    limit: u32,
181    #[serde(default)]
182    offset: u32,
183}
184
185/// Response for listing agents
186#[derive(Debug, Serialize)]
187struct ListAgentsResponse {
188    agents: Vec<ListAgentInfo>,
189    total: usize,
190}
191
192#[derive(Debug, Serialize)]
193struct ListAgentInfo {
194    #[serde(rename = "@id")]
195    id: String,
196    #[serde(skip_serializing_if = "Option::is_none")]
197    label: Option<String>,
198    policies: Vec<Value>,
199    metadata: Value,
200}
201
202impl ListAgentsTool {
203    pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
204        Self { tap_integration }
205    }
206
207    fn tap_integration(&self) -> &TapIntegration {
208        &self.tap_integration
209    }
210}
211
212#[async_trait::async_trait]
213impl ToolHandler for ListAgentsTool {
214    async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
215        let params: ListAgentsParams = match arguments {
216            Some(args) => serde_json::from_value(args)
217                .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
218            None => ListAgentsParams {
219                limit: default_limit(),
220                offset: 0,
221            },
222        };
223
224        debug!(
225            "Listing agents with limit={}, offset={}",
226            params.limit, params.offset
227        );
228
229        match self.tap_integration().list_agents().await {
230            Ok(agents) => {
231                let total = agents.len();
232
233                // Apply pagination
234                let paginated_agents: Vec<_> = agents
235                    .into_iter()
236                    .skip(params.offset as usize)
237                    .take(params.limit as usize)
238                    .map(|agent| ListAgentInfo {
239                        id: agent.id,
240                        label: agent.metadata.get("label").cloned(),
241                        policies: agent
242                            .policies
243                            .into_iter()
244                            .map(serde_json::Value::String)
245                            .collect(),
246                        metadata: if agent.metadata.is_empty() {
247                            serde_json::Value::Null
248                        } else {
249                            serde_json::to_value(agent.metadata).unwrap_or(serde_json::Value::Null)
250                        },
251                    })
252                    .collect();
253
254                let response = ListAgentsResponse {
255                    agents: paginated_agents,
256                    total,
257                };
258
259                let response_json = serde_json::to_string_pretty(&response).map_err(|e| {
260                    Error::tool_execution(format!("Failed to serialize response: {}", e))
261                })?;
262
263                Ok(success_text_response(response_json))
264            }
265            Err(e) => {
266                error!("Failed to list agents: {}", e);
267                Ok(error_text_response(format!("Failed to list agents: {}", e)))
268            }
269        }
270    }
271
272    fn get_definition(&self) -> Tool {
273        Tool {
274            name: "tap_list_agents".to_string(),
275            description: "Lists all configured agents from ~/.tap/keys.json. Agents are identified by their DIDs. Roles and party associations are transaction-specific and not stored with agents.".to_string(),
276            input_schema: schema::list_agents_schema(),
277        }
278    }
279}