use crate::{
embedded::{EmbeddedCore, IngestEvent},
error::Result,
};
use serde_json::Value;
use std::future::Future;
pub struct McpToolTracker<'a> {
core: &'a EmbeddedCore,
}
impl<'a> McpToolTracker<'a> {
pub fn new(core: &'a EmbeddedCore) -> Self {
Self { core }
}
pub async fn emit_tool_result(
&self,
workflow_id: &str,
tool_name: &str,
duration_ms: u64,
) -> Result<()> {
self.core
.ingest(IngestEvent {
entity_id: workflow_id,
event_type: "mcp.tool.result",
payload: serde_json::json!({
"tool_name": tool_name,
"duration_ms": duration_ms,
}),
metadata: None,
tenant_id: None,
})
.await
}
pub async fn emit_tool_error(
&self,
workflow_id: &str,
tool_name: &str,
error: &str,
) -> Result<()> {
self.core
.ingest(IngestEvent {
entity_id: workflow_id,
event_type: "mcp.tool.error",
payload: serde_json::json!({
"tool_name": tool_name,
"error": error,
}),
metadata: None,
tenant_id: None,
})
.await
}
pub async fn track<F, Fut>(&self, workflow_id: &str, tool_name: &str, f: F) -> Result<Value>
where
F: FnOnce() -> Fut,
Fut: Future<Output = Result<Value>>,
{
let start = std::time::Instant::now();
let result = f().await;
let duration_ms = start.elapsed().as_millis() as u64;
match &result {
Ok(_) => {
self.emit_tool_result(workflow_id, tool_name, duration_ms)
.await?;
}
Err(e) => {
self.core
.ingest(IngestEvent {
entity_id: workflow_id,
event_type: "mcp.tool.error",
payload: serde_json::json!({
"tool_name": tool_name,
"error": e.to_string(),
"duration_ms": duration_ms,
}),
metadata: None,
tenant_id: None,
})
.await?;
}
}
result
}
}