use serde_json::Value;
use crate::client::ConfluenceClient;
use crate::formatters::format_space;
use crate::mcp::{CallToolResult, ToolDefinition};
use crate::types::{AnyPage, PageListResponse, PageListResponseV1, SpaceListResponse};
use super::schema;
pub fn definitions() -> Vec<ToolDefinition> {
vec![
ToolDefinition {
name: "list_spaces".to_string(),
description: "List all accessible Confluence spaces".to_string(),
input_schema: schema(&[
("limit", "number", false, "Maximum number of spaces to return (default: 25)"),
("type", "string", false, "Filter by space type: global, personal"),
]),
},
ToolDefinition {
name: "get_space_pages".to_string(),
description: "Get all pages in a Confluence space".to_string(),
input_schema: schema(&[
("spaceKey", "string", true, "The space key"),
("limit", "number", false, "Maximum number of pages to return (default: 25)"),
("status", "string", false, "Page status: current, archived, draft (default: current)"),
]),
},
]
}
pub async fn list_spaces(client: &ConfluenceClient, args: &Value) -> CallToolResult {
let limit = args.get("limit").and_then(|v| v.as_i64()).unwrap_or(25);
let space_type = args.get("type").and_then(|v| v.as_str());
let endpoint = if client.config().is_cloud {
let mut url = format!("/spaces?limit={limit}");
if let Some(t) = space_type {
url.push_str(&format!("&type={t}"));
}
url
} else {
let mut url = format!("/space?limit={limit}");
if let Some(t) = space_type {
url.push_str(&format!("&type={t}"));
}
url
};
match client.get::<SpaceListResponse>(&endpoint).await {
Ok(result) => {
if result.results.is_empty() {
return CallToolResult::text("No spaces found.");
}
let formatted: Vec<String> = result.results.iter().map(|s| format_space(s)).collect();
CallToolResult::text(format!(
"Found {} space(s):\n\n{}",
result.results.len(),
formatted.join("\n\n---\n\n")
))
}
Err(e) => CallToolResult::text(format!("Error listing spaces: {e}")),
}
}
pub async fn get_space_pages(client: &ConfluenceClient, args: &Value) -> CallToolResult {
let space_key = match args.get("spaceKey").and_then(|v| v.as_str()) {
Some(k) => k,
None => return CallToolResult::text("Error: spaceKey is required."),
};
let limit = args.get("limit").and_then(|v| v.as_i64()).unwrap_or(25);
let status = args
.get("status")
.and_then(|v| v.as_str())
.unwrap_or("current");
if client.config().is_cloud {
let spaces_result: Result<SpaceListResponse, _> =
client.get(&format!("/spaces?keys={space_key}")).await;
match spaces_result {
Ok(sr) => {
if sr.results.is_empty() {
return CallToolResult::text(format!(
"Space with key \"{space_key}\" not found."
));
}
let space_id = &sr.results[0].id;
match client
.get::<PageListResponse>(&format!(
"/spaces/{space_id}/pages?limit={limit}&status={status}"
))
.await
{
Ok(result) => format_page_list(&result.results, space_key),
Err(e) => CallToolResult::text(format!("Error getting space pages: {e}")),
}
}
Err(e) => CallToolResult::text(format!("Error getting space pages: {e}")),
}
} else {
match client
.get::<PageListResponseV1>(&format!(
"/content?spaceKey={space_key}&limit={limit}&status={status}&expand=version,space"
))
.await
{
Ok(result) => format_page_list_v1(&result.results, space_key),
Err(e) => CallToolResult::text(format!("Error getting space pages: {e}")),
}
}
}
fn format_page_list(pages: &[crate::types::ConfluencePage], space_key: &str) -> CallToolResult {
if pages.is_empty() {
return CallToolResult::text(format!("No pages found in space {space_key}."));
}
let formatted: Vec<String> = pages
.iter()
.map(|p| crate::formatters::format_page(&AnyPage::V2(clone_page_v2(p))))
.collect();
CallToolResult::text(format!(
"Found {} page(s) in space {space_key}:\n\n{}",
pages.len(),
formatted.join("\n\n---\n\n")
))
}
fn format_page_list_v1(
pages: &[crate::types::ConfluencePageV1],
space_key: &str,
) -> CallToolResult {
if pages.is_empty() {
return CallToolResult::text(format!("No pages found in space {space_key}."));
}
let formatted: Vec<String> = pages
.iter()
.map(|p| crate::formatters::format_page(&AnyPage::V1(clone_page_v1(p))))
.collect();
CallToolResult::text(format!(
"Found {} page(s) in space {space_key}:\n\n{}",
pages.len(),
formatted.join("\n\n---\n\n")
))
}
fn clone_page_v2(p: &crate::types::ConfluencePage) -> crate::types::ConfluencePage {
serde_json::from_value(serde_json::to_value(p).unwrap()).unwrap()
}
fn clone_page_v1(p: &crate::types::ConfluencePageV1) -> crate::types::ConfluencePageV1 {
serde_json::from_value(serde_json::to_value(p).unwrap()).unwrap()
}