#![cfg(feature = "mcp")]
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct DocSearchInput {
pub query: String,
#[serde(default = "default_timespan")]
pub timespan: String,
#[serde(default = "default_max_records")]
pub max_records: u32,
pub lang: Option<String>,
pub country: Option<String>,
}
fn default_timespan() -> String {
"24h".to_string()
}
fn default_max_records() -> u32 {
25
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct EventsQueryInput {
pub actor: Option<String>,
pub event_code: Option<String>,
pub country: Option<String>,
pub start: Option<String>,
pub end: Option<String>,
#[serde(default = "default_limit")]
pub limit: u32,
}
fn default_limit() -> u32 {
100
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct GkgQueryInput {
pub theme: Option<String>,
pub person: Option<String>,
pub organization: Option<String>,
pub start: Option<String>,
pub end: Option<String>,
#[serde(default = "default_limit")]
pub limit: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct AnalyticsTrendsInput {
pub topics: Vec<String>,
#[serde(default = "default_analytics_timespan")]
pub timespan: String,
#[serde(default)]
pub detect_anomalies: bool,
}
fn default_analytics_timespan() -> String {
"30d".to_string()
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct AnalyticsSentimentInput {
pub topic: String,
#[serde(default = "default_analytics_timespan")]
pub timespan: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct GeoSearchInput {
pub query: String,
#[serde(default = "default_geo_timespan")]
pub timespan: String,
pub country: Option<String>,
#[serde(default = "default_geo_max_points")]
pub max_points: u32,
}
fn default_geo_timespan() -> String {
"7d".to_string()
}
fn default_geo_max_points() -> u32 {
250
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct TvSearchInput {
pub query: String,
#[serde(default = "default_geo_timespan")]
pub timespan: String,
pub station: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct ToolResult<T> {
pub success: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<T>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
impl<T> ToolResult<T> {
pub fn success(data: T) -> Self {
Self {
success: true,
data: Some(data),
error: None,
}
}
pub fn error(msg: impl Into<String>) -> Self {
Self {
success: false,
data: None,
error: Some(msg.into()),
}
}
}
pub fn list_tools() -> Vec<ToolInfo> {
vec![
ToolInfo {
name: "gdelt_doc_search".to_string(),
description: "Search news articles using the GDELT DOC 2.0 API".to_string(),
input_schema: schemars::schema_for!(DocSearchInput),
},
ToolInfo {
name: "gdelt_events_query".to_string(),
description: "Query events from the local GDELT database".to_string(),
input_schema: schemars::schema_for!(EventsQueryInput),
},
ToolInfo {
name: "gdelt_gkg_query".to_string(),
description: "Query Global Knowledge Graph data".to_string(),
input_schema: schemars::schema_for!(GkgQueryInput),
},
ToolInfo {
name: "gdelt_analytics_trends".to_string(),
description: "Analyze trends for topics over time".to_string(),
input_schema: schemars::schema_for!(AnalyticsTrendsInput),
},
ToolInfo {
name: "gdelt_analytics_sentiment".to_string(),
description: "Analyze sentiment for a topic".to_string(),
input_schema: schemars::schema_for!(AnalyticsSentimentInput),
},
ToolInfo {
name: "gdelt_geo_search".to_string(),
description: "Search with geographic context".to_string(),
input_schema: schemars::schema_for!(GeoSearchInput),
},
ToolInfo {
name: "gdelt_tv_search".to_string(),
description: "Search television news coverage".to_string(),
input_schema: schemars::schema_for!(TvSearchInput),
},
]
}
#[derive(Debug, Clone, Serialize)]
pub struct ToolInfo {
pub name: String,
pub description: String,
pub input_schema: schemars::schema::RootSchema,
}