task_graph_mcp/tools/
schema.rs1use super::{get_bool, get_string, make_tool};
4use crate::db::Database;
5use anyhow::Result;
6use rmcp::model::Tool;
7use serde_json::{Value, json};
8
9pub fn get_tools() -> Vec<Tool> {
11 vec![make_tool(
12 "get_schema",
13 "Get the task-graph database schema. Returns table names, columns (with types), indexes, foreign keys, and optionally the SQL definitions. Useful for agents writing reports or queries.",
14 json!({
15 "table": {
16 "type": "string",
17 "description": "Filter to a specific table name. If not provided, returns all tables."
18 },
19 "include_sql": {
20 "type": "boolean",
21 "description": "Include the SQL CREATE statements (default: false)"
22 }
23 }),
24 vec![],
25 )]
26}
27
28pub fn get_schema(db: &Database, args: Value) -> Result<Value> {
30 let table_filter = get_string(&args, "table");
31 let include_sql = get_bool(&args, "include_sql").unwrap_or(false);
32
33 let schema = db.get_schema(include_sql)?;
34
35 let tables = if let Some(ref table_name) = table_filter {
37 schema
38 .tables
39 .into_iter()
40 .filter(|t| t.name.eq_ignore_ascii_case(table_name))
41 .collect()
42 } else {
43 schema.tables
44 };
45
46 if let Some(table_name) = table_filter
47 && tables.is_empty()
48 {
49 return Ok(json!({
50 "error": format!("Table '{}' not found", table_name),
51 "available_tables": db.get_table_names()?
52 }));
53 }
54
55 Ok(json!({
56 "sqlite_version": schema.sqlite_version,
57 "table_count": tables.len(),
58 "tables": tables.iter().map(|t| {
59 let mut table_obj = json!({
60 "name": t.name,
61 "type": t.table_type,
62 "columns": t.columns.iter().map(|c| {
63 json!({
64 "name": c.name,
65 "type": c.data_type,
66 "nullable": c.nullable,
67 "primary_key": c.primary_key,
68 "default": c.default_value
69 })
70 }).collect::<Vec<_>>()
71 });
72
73 if !t.indexes.is_empty() {
75 table_obj["indexes"] = json!(t.indexes.iter().map(|i| {
76 json!({
77 "name": i.name,
78 "unique": i.unique,
79 "columns": i.columns
80 })
81 }).collect::<Vec<_>>());
82 }
83
84 if !t.foreign_keys.is_empty() {
86 table_obj["foreign_keys"] = json!(t.foreign_keys.iter().map(|fk| {
87 json!({
88 "from": fk.from_column,
89 "references": format!("{}.{}", fk.to_table, fk.to_column),
90 "on_delete": fk.on_delete,
91 "on_update": fk.on_update
92 })
93 }).collect::<Vec<_>>());
94 }
95
96 if let Some(ref sql) = t.sql {
98 table_obj["sql"] = json!(sql);
99 }
100
101 table_obj
102 }).collect::<Vec<_>>()
103 }))
104}