do-memory-mcp 0.1.31

Model Context Protocol (MCP) server for AI agents
Documentation
use super::{Content, MemoryMCPServer, Value, get_client_id};
use serde_json::json;

pub async fn handle_bulk_episodes(
    server: &mut MemoryMCPServer,
    arguments: Option<Value>,
) -> anyhow::Result<Vec<Content>> {
    use uuid::Uuid;

    let args: Value = arguments.unwrap_or(json!({}));
    let client_id = get_client_id(&args);

    let episode_ids_value = args
        .get("episode_ids")
        .ok_or_else(|| anyhow::anyhow!("Missing 'episode_ids' parameter"))?;

    let episode_ids: Vec<Uuid> = match episode_ids_value {
        Value::String(s) => s
            .split(',')
            .map(|id| {
                let id = id.trim();
                Uuid::parse_str(id).map_err(|_| anyhow::anyhow!("Invalid UUID: {}", id))
            })
            .collect::<anyhow::Result<Vec<_>>>()?,
        Value::Array(arr) => arr
            .iter()
            .map(|v| {
                let id = v
                    .as_str()
                    .ok_or_else(|| anyhow::anyhow!("Expected string in episode_ids array"))?;
                Uuid::parse_str(id).map_err(|_| anyhow::anyhow!("Invalid UUID: {}", id))
            })
            .collect::<anyhow::Result<Vec<_>>>()?,
        _ => {
            return Err(anyhow::anyhow!(
                "episode_ids must be a string (comma-separated) or array of UUIDs"
            ));
        }
    };

    let result = server.get_episodes_by_ids(&episode_ids).await;

    let episode_count = result.as_ref().map(|r| r.len()).unwrap_or(0);
    let success = result.is_ok();
    server
        .audit_logger()
        .log_bulk_episodes(&client_id, episode_count, success)
        .await;

    let mut episodes_json = Vec::with_capacity(result.as_ref().map(|r| r.len()).unwrap_or(0));
    for ep in result?.iter() {
        episodes_json.push(
            serde_json::to_value(ep)
                .map_err(|e| anyhow::anyhow!("Failed to serialize episode: {}", e))?,
        );
    }

    #[derive(serde::Serialize)]
    struct BulkEpisodeResult {
        requested_count: usize,
        found_count: usize,
        missing_count: usize,
        episodes: Vec<serde_json::Value>,
    }

    let bulk_result = BulkEpisodeResult {
        requested_count: episode_ids.len(),
        found_count: episodes_json.len(),
        missing_count: episode_ids.len() - episodes_json.len(),
        episodes: episodes_json,
    };

    Ok(vec![Content::Text {
        text: serde_json::to_string_pretty(&bulk_result)?,
    }])
}

pub async fn handle_create_episode(
    server: &mut MemoryMCPServer,
    arguments: Option<Value>,
) -> anyhow::Result<Vec<Content>> {
    let args = arguments.ok_or_else(|| anyhow::anyhow!("Missing arguments"))?;
    let client_id = get_client_id(&args);
    let task_description = args
        .get("task_description")
        .and_then(|v| v.as_str())
        .unwrap_or("")
        .to_string();

    let result = server.create_episode_tool(args).await;
    let episode_id = result
        .as_ref()
        .ok()
        .and_then(|r| r.get("episode_id"))
        .and_then(|v| v.as_str())
        .unwrap_or("unknown");
    let success = result.is_ok();
    let error = result.as_ref().err().map(|e| e.to_string());
    server
        .audit_logger()
        .log_episode_creation(
            &client_id,
            episode_id,
            &task_description,
            success,
            error.as_deref(),
        )
        .await;

    Ok(vec![Content::Text {
        text: serde_json::to_string_pretty(&result?)?,
    }])
}

pub async fn handle_add_episode_step(
    server: &mut MemoryMCPServer,
    arguments: Option<Value>,
) -> anyhow::Result<Vec<Content>> {
    let args = arguments.ok_or_else(|| anyhow::anyhow!("Missing arguments"))?;
    let client_id = get_client_id(&args);
    let episode_id = args
        .get("episode_id")
        .and_then(|v| v.as_str())
        .unwrap_or("unknown")
        .to_string();
    let step_number = args
        .get("step_number")
        .and_then(|v| v.as_u64())
        .unwrap_or(0) as u32;
    let tool = args
        .get("tool")
        .and_then(|v| v.as_str())
        .unwrap_or("unknown")
        .to_string();

    let result = server.add_episode_step_tool(args).await;
    let success = result.is_ok();
    server
        .audit_logger()
        .log_episode_step(&client_id, &episode_id, step_number, &tool, success)
        .await;

    Ok(vec![Content::Text {
        text: serde_json::to_string_pretty(&result?)?,
    }])
}

pub async fn handle_complete_episode(
    server: &mut MemoryMCPServer,
    arguments: Option<Value>,
) -> anyhow::Result<Vec<Content>> {
    let args = arguments.ok_or_else(|| anyhow::anyhow!("Missing arguments"))?;
    let client_id = get_client_id(&args);
    let episode_id = args
        .get("episode_id")
        .and_then(|v| v.as_str())
        .unwrap_or("unknown")
        .to_string();
    let outcome = args
        .get("outcome_type")
        .and_then(|v| v.as_str())
        .unwrap_or("unknown")
        .to_string();

    let result = server.complete_episode_tool(args).await;
    let success = result.is_ok();
    server
        .audit_logger()
        .log_episode_completion(&client_id, &episode_id, &outcome, success)
        .await;

    Ok(vec![Content::Text {
        text: serde_json::to_string_pretty(&result?)?,
    }])
}

pub async fn handle_get_episode(
    server: &mut MemoryMCPServer,
    arguments: Option<Value>,
) -> anyhow::Result<Vec<Content>> {
    let args = arguments.ok_or_else(|| anyhow::anyhow!("Missing arguments"))?;
    let result = server.get_episode_tool(args).await?;
    Ok(vec![Content::Text {
        text: serde_json::to_string_pretty(&result)?,
    }])
}

pub async fn handle_delete_episode(
    server: &mut MemoryMCPServer,
    arguments: Option<Value>,
) -> anyhow::Result<Vec<Content>> {
    let args = arguments.ok_or_else(|| anyhow::anyhow!("Missing arguments"))?;
    let client_id = get_client_id(&args);
    let episode_id = args
        .get("episode_id")
        .and_then(|v| v.as_str())
        .unwrap_or("unknown")
        .to_string();

    let result = server.delete_episode_tool(args).await;
    let success = result.is_ok();
    let error = result.as_ref().err().map(|e| e.to_string());
    server
        .audit_logger()
        .log_episode_deletion(&client_id, &episode_id, success, error.as_deref())
        .await;

    Ok(vec![Content::Text {
        text: serde_json::to_string_pretty(&result?)?,
    }])
}

pub async fn handle_update_episode(
    server: &mut MemoryMCPServer,
    arguments: Option<Value>,
) -> anyhow::Result<Vec<Content>> {
    let args = arguments.ok_or_else(|| anyhow::anyhow!("Missing arguments"))?;
    let client_id = get_client_id(&args);
    let episode_id = args
        .get("episode_id")
        .and_then(|v| v.as_str())
        .unwrap_or("unknown")
        .to_string();

    let result = server.update_episode_tool(args).await;
    let success = result.is_ok();
    let error = result.as_ref().err().map(|e| e.to_string());
    server
        .audit_logger()
        .log_episode_modification(&client_id, &episode_id, "update", success, error.as_deref())
        .await;

    Ok(vec![Content::Text {
        text: serde_json::to_string_pretty(&result?)?,
    }])
}

pub async fn handle_get_episode_timeline(
    server: &mut MemoryMCPServer,
    arguments: Option<Value>,
) -> anyhow::Result<Vec<Content>> {
    let args = arguments.ok_or_else(|| anyhow::anyhow!("Missing arguments"))?;
    let result = server.get_episode_timeline_tool(args).await?;
    Ok(vec![Content::Text {
        text: serde_json::to_string_pretty(&result)?,
    }])
}