use crate::cli::args::{GlobalArgs, SchemaCommand};
use crate::cli::output::OutputWriter;
use crate::error::{self, ExitStatus, GdeltError, Result};
use serde_json::json;
pub async fn handle_schema(cmd: SchemaCommand, global: &GlobalArgs) -> Result<ExitStatus> {
match cmd {
SchemaCommand::Commands => list_commands(global),
SchemaCommand::Command(args) => describe_command(&args.command, global),
SchemaCommand::Output(args) => describe_output(&args.command, global),
SchemaCommand::Codes => list_exit_codes(global),
SchemaCommand::Tools => list_mcp_tools(global),
}
}
fn list_commands(global: &GlobalArgs) -> Result<ExitStatus> {
let output = OutputWriter::new(global);
let commands = json!({
"commands": [
{
"name": "doc",
"description": "DOC 2.0 API - Search news articles and documents",
"subcommands": [
{"name": "search", "description": "Search for articles"},
{"name": "timeline", "description": "Get timeline data (volume/tone over time)"},
{"name": "wordcloud", "description": "Generate word cloud data"},
{"name": "themes", "description": "List available GKG themes"}
]
},
{
"name": "geo",
"description": "GEO 2.0 API - Geographic search and mapping",
"subcommands": [
{"name": "search", "description": "Search with geographic context"},
{"name": "points", "description": "Get point data for locations"},
{"name": "heatmap", "description": "Generate heatmap data"},
{"name": "aggregate", "description": "Country/region aggregation"}
]
},
{
"name": "tv",
"description": "TV 2.0 API - Television news search",
"subcommands": [
{"name": "search", "description": "Search TV captions"},
{"name": "clips", "description": "Get video clips"},
{"name": "timeline", "description": "TV coverage timeline"},
{"name": "stations", "description": "List stations"}
]
},
{
"name": "tvai",
"description": "TV AI API - AI-enhanced TV analysis",
"subcommands": [
{"name": "search", "description": "Search AI-analyzed content"},
{"name": "concepts", "description": "Search by visual concepts"},
{"name": "visual", "description": "Search by visual entities"}
]
},
{
"name": "data",
"description": "Download and manage historical GDELT data",
"subcommands": [
{"name": "download events", "description": "Download events data"},
{"name": "download gkg", "description": "Download GKG data"},
{"name": "download mentions", "description": "Download mentions data"},
{"name": "sync", "description": "Sync to latest data"},
{"name": "status", "description": "Show download status"},
{"name": "list", "description": "List available date ranges"},
{"name": "delete", "description": "Delete local data"}
]
},
{
"name": "events",
"description": "Query local events database",
"subcommands": [
{"name": "query", "description": "Query events with filters"},
{"name": "lookup", "description": "Lookup event by ID"},
{"name": "actors", "description": "Query by actor"},
{"name": "countries", "description": "Query by country"},
{"name": "codes list", "description": "List CAMEO codes"},
{"name": "codes search", "description": "Search codes by description"},
{"name": "codes describe", "description": "Describe a specific code"}
]
},
{
"name": "gkg",
"description": "Query local Global Knowledge Graph",
"subcommands": [
{"name": "query", "description": "Query GKG records"},
{"name": "themes", "description": "Query by themes"},
{"name": "persons", "description": "Query person mentions"},
{"name": "organizations", "description": "Query organization mentions"},
{"name": "locations", "description": "Query location mentions"}
]
},
{
"name": "db",
"description": "Database management",
"subcommands": [
{"name": "stats", "description": "Show database statistics"},
{"name": "vacuum", "description": "Optimize database"},
{"name": "export", "description": "Export data to file"},
{"name": "import", "description": "Import data from file"},
{"name": "query", "description": "Run raw SQL query"}
]
},
{
"name": "analytics",
"description": "Built-in analytics",
"subcommands": [
{"name": "trends", "description": "Trend analysis"},
{"name": "entities", "description": "Entity extraction"},
{"name": "sentiment", "description": "Sentiment analysis"},
{"name": "compare", "description": "Compare topics (A vs B)"},
{"name": "report daily", "description": "Generate daily report"},
{"name": "report weekly", "description": "Generate weekly report"},
{"name": "report custom", "description": "Generate custom report"}
]
},
{
"name": "config",
"description": "Configuration management",
"subcommands": [
{"name": "show", "description": "Show current configuration"},
{"name": "get", "description": "Get a configuration value"},
{"name": "set", "description": "Set a configuration value"},
{"name": "reset", "description": "Reset configuration to defaults"},
{"name": "validate", "description": "Validate configuration file"}
]
},
{
"name": "schema",
"description": "Schema introspection for agents",
"subcommands": [
{"name": "commands", "description": "List all commands"},
{"name": "command", "description": "Get schema for a specific command"},
{"name": "output", "description": "Get output schema for a command"},
{"name": "codes", "description": "List all exit codes"},
{"name": "tools", "description": "List MCP tool definitions"}
]
},
{
"name": "serve",
"description": "Start MCP server (foreground)"
},
{
"name": "daemon",
"description": "Daemon management",
"subcommands": [
{"name": "start", "description": "Start the daemon"},
{"name": "stop", "description": "Stop the daemon"},
{"name": "restart", "description": "Restart the daemon"},
{"name": "status", "description": "Show daemon status"},
{"name": "logs", "description": "View daemon logs"},
{"name": "install", "description": "Install as system service"},
{"name": "uninstall", "description": "Uninstall system service"}
]
},
{
"name": "skill",
"description": "Claude Code skill management",
"subcommands": [
{"name": "install", "description": "Install Claude Code skill"},
{"name": "uninstall", "description": "Uninstall skill"},
{"name": "update", "description": "Update skill files"},
{"name": "status", "description": "Show skill status"}
]
},
{
"name": "completions",
"description": "Generate shell completions"
}
]
});
output.write_value(&commands)?;
Ok(ExitStatus::Success)
}
fn describe_command(command: &str, global: &GlobalArgs) -> Result<ExitStatus> {
let output = OutputWriter::new(global);
let schema = match command {
"doc search" => json!({
"command": "doc search",
"description": "Search for news articles using GDELT DOC 2.0 API",
"arguments": [
{"name": "query", "type": "string", "required": true, "description": "Search query (supports GDELT query syntax)"}
],
"options": [
{"name": "--timespan", "short": "-t", "type": "string", "default": "24h", "description": "Time span to search (e.g., 24h, 7d, 3m)"},
{"name": "--start", "type": "string", "description": "Start datetime (YYYYMMDDHHMMSS)"},
{"name": "--end", "type": "string", "description": "End datetime (YYYYMMDDHHMMSS)"},
{"name": "--max-records", "short": "-n", "type": "integer", "default": 75, "description": "Maximum records to return"},
{"name": "--sort", "short": "-s", "type": "enum", "values": ["date-desc", "date-asc", "tone-desc", "tone-asc", "hybrid-rel"], "default": "hybrid-rel"},
{"name": "--lang", "short": "-l", "type": "string", "description": "Filter by source language (ISO 639-1)"},
{"name": "--country", "short": "-c", "type": "string", "description": "Filter by source country (FIPS code)"},
{"name": "--domain", "short": "-d", "type": "string", "description": "Filter by domain"},
{"name": "--theme", "type": "string", "description": "Filter by GKG theme"},
{"name": "--tone-min", "type": "float", "description": "Minimum tone score (-10 to 10)"},
{"name": "--tone-max", "type": "float", "description": "Maximum tone score (-10 to 10)"}
],
"examples": [
"gdelt doc search 'climate change'",
"gdelt doc search 'ukraine' --timespan 7d --lang en",
"gdelt doc search 'election' --country US --sort date-desc"
]
}),
"events query" => json!({
"command": "events query",
"description": "Query events from local database",
"options": [
{"name": "--actor", "short": "-a", "type": "string", "description": "Filter by actor code"},
{"name": "--event-code", "short": "-e", "type": "string", "description": "Filter by event code (supports wildcards like '14*')"},
{"name": "--quad-class", "type": "integer", "description": "Filter by quad class (1-4)"},
{"name": "--country", "short": "-c", "type": "string", "description": "Filter by country"},
{"name": "--start", "type": "string", "description": "Start date (YYYY-MM-DD)"},
{"name": "--end", "type": "string", "description": "End date (YYYY-MM-DD)"},
{"name": "--goldstein-min", "type": "float", "description": "Minimum Goldstein scale (-10 to 10)"},
{"name": "--goldstein-max", "type": "float", "description": "Maximum Goldstein scale"},
{"name": "--limit", "short": "-n", "type": "integer", "default": 100},
{"name": "--offset", "type": "integer", "default": 0}
],
"examples": [
"gdelt events query --country US --start 2024-01-01",
"gdelt events query --event-code '14*' --limit 50",
"gdelt events query --actor GOV --quad-class 4"
]
}),
"db query" => json!({
"command": "db query",
"description": "Run raw SQL query on local database",
"arguments": [
{"name": "query", "type": "string", "required": true, "description": "SQL query to execute"}
],
"examples": [
"gdelt db query 'SELECT COUNT(*) FROM events'",
"gdelt db query 'SELECT actor1_code, COUNT(*) as cnt FROM events GROUP BY actor1_code ORDER BY cnt DESC LIMIT 10'"
]
}),
_ => {
return Err(GdeltError::NotFound(format!("Unknown command: {}", command)));
}
};
output.write_value(&schema)?;
Ok(ExitStatus::Success)
}
fn describe_output(command: &str, global: &GlobalArgs) -> Result<ExitStatus> {
let output = OutputWriter::new(global);
let schema = match command {
"doc search" => json!({
"command": "doc search",
"output_schema": {
"type": "object",
"properties": {
"articles": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {"type": "string"},
"title": {"type": "string"},
"seendate": {"type": "string"},
"domain": {"type": "string"},
"language": {"type": "string"},
"sourcecountry": {"type": "string"},
"tone": {"type": "number"}
}
}
}
}
}
}),
"events query" => json!({
"command": "events query",
"output_schema": {
"type": "array",
"items": {
"type": "object",
"properties": {
"global_event_id": {"type": "integer"},
"sql_date": {"type": "integer"},
"actor1_code": {"type": "string"},
"actor1_name": {"type": "string"},
"actor2_code": {"type": "string"},
"actor2_name": {"type": "string"},
"event_code": {"type": "string"},
"quad_class": {"type": "integer"},
"goldstein_scale": {"type": "number"},
"num_mentions": {"type": "integer"},
"avg_tone": {"type": "number"},
"action_geo_fullname": {"type": "string"},
"source_url": {"type": "string"}
}
}
}
}),
"db stats" => json!({
"command": "db stats",
"output_schema": {
"type": "object",
"properties": {
"events_count": {"type": "integer"},
"gkg_count": {"type": "integer"},
"mentions_count": {"type": "integer"},
"events_date_range": {"type": "array", "items": {"type": "integer"}},
"file_size_bytes": {"type": "integer"}
}
}
}),
_ => {
return Err(GdeltError::NotFound(format!("Unknown command: {}", command)));
}
};
output.write_value(&schema)?;
Ok(ExitStatus::Success)
}
fn list_exit_codes(global: &GlobalArgs) -> Result<ExitStatus> {
let output = OutputWriter::new(global);
let codes = error::all_exit_codes();
output.write_value(&serde_json::to_value(codes)?)?;
Ok(ExitStatus::Success)
}
fn list_mcp_tools(global: &GlobalArgs) -> Result<ExitStatus> {
let output = OutputWriter::new(global);
let tools = json!({
"tools": [
{
"name": "gdelt_doc_search",
"description": "Search GDELT news articles",
"inputSchema": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search query"},
"timespan": {"type": "string", "description": "Time span (e.g., 24h, 7d)"},
"max_records": {"type": "integer", "description": "Maximum results"},
"lang": {"type": "string", "description": "Language filter"},
"country": {"type": "string", "description": "Country filter"}
},
"required": ["query"]
}
},
{
"name": "gdelt_events_query",
"description": "Query local GDELT events database",
"inputSchema": {
"type": "object",
"properties": {
"actor": {"type": "string", "description": "Actor code filter"},
"event_code": {"type": "string", "description": "Event code filter"},
"country": {"type": "string", "description": "Country filter"},
"start_date": {"type": "string", "description": "Start date (YYYY-MM-DD)"},
"end_date": {"type": "string", "description": "End date (YYYY-MM-DD)"},
"limit": {"type": "integer", "description": "Maximum results"}
}
}
},
{
"name": "gdelt_geo_search",
"description": "Geographic search for GDELT data",
"inputSchema": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search query"},
"timespan": {"type": "string", "description": "Time span"},
"country": {"type": "string", "description": "Country filter"},
"max_points": {"type": "integer", "description": "Maximum points"}
},
"required": ["query"]
}
},
{
"name": "gdelt_db_query",
"description": "Run SQL query on local GDELT database",
"inputSchema": {
"type": "object",
"properties": {
"sql": {"type": "string", "description": "SQL query"}
},
"required": ["sql"]
}
}
]
});
output.write_value(&tools)?;
Ok(ExitStatus::Success)
}