mcp_server_sqlite/tools/
list_foreign_keys_tool.rs1use rmcp::model::{Content, IntoContents};
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize};
9
10use super::ToolError;
11use crate::{mcp::McpServerSqlite, traits::SqliteServerTool};
12
13#[derive(
14 Clone,
15 Copy,
16 Debug,
17 PartialEq,
18 Eq,
19 PartialOrd,
20 Ord,
21 Hash,
22 Default,
23 Serialize,
24 Deserialize,
25 JsonSchema,
26)]
27pub struct ListForeignKeysTool;
32
33impl SqliteServerTool for ListForeignKeysTool {
34 const NAME: &str = "list_foreign_keys";
35
36 type Context = McpServerSqlite;
37 type Error = ToolError<ListForeignKeysError>;
38
39 type Input = ListForeignKeysInput;
40 type Output = ListForeignKeysOutput;
41
42 fn handle(
43 ctx: &Self::Context,
44 input: Self::Input,
45 ) -> Result<Self::Output, Self::Error> {
46 let conn = ctx
47 .connection()
48 .map_err(|source| ToolError::Connection { source })?;
49
50 let pragma_sql = format!(
51 "PRAGMA foreign_key_list({})",
52 enquote_identifier(&input.table_name),
53 );
54
55 let mut stmt = conn.prepare(&pragma_sql).map_err(|source| {
56 ToolError::Tool(ListForeignKeysError::Query { source })
57 })?;
58
59 let foreign_keys = stmt
60 .query_map([], |row| {
61 Ok(ForeignKeyInfo {
62 id: row.get(0)?,
63 from_column: row.get(3)?,
64 to_table: row.get(2)?,
65 to_column: row.get(4)?,
66 on_update: row.get(5)?,
67 on_delete: row.get(6)?,
68 })
69 })
70 .map_err(|source| {
71 ToolError::Tool(ListForeignKeysError::Query { source })
72 })?
73 .collect::<Result<Vec<_>, _>>()
74 .map_err(|source| {
75 ToolError::Tool(ListForeignKeysError::Query { source })
76 })?;
77
78 Ok(ListForeignKeysOutput { foreign_keys })
79 }
80}
81
82fn enquote_identifier(name: &str) -> String {
85 format!("\"{}\"", name.replace('"', "\"\""))
86}
87
88#[derive(
90 Clone,
91 Debug,
92 PartialEq,
93 Eq,
94 PartialOrd,
95 Ord,
96 Hash,
97 Serialize,
98 Deserialize,
99 schemars::JsonSchema,
100)]
101pub struct ListForeignKeysInput {
102 #[schemars(description = "The name of the table to list foreign keys for")]
104 pub table_name: String,
105}
106
107#[derive(
109 Clone,
110 Debug,
111 PartialEq,
112 Eq,
113 PartialOrd,
114 Ord,
115 Hash,
116 Serialize,
117 Deserialize,
118 schemars::JsonSchema,
119)]
120pub struct ListForeignKeysOutput {
121 pub foreign_keys: Vec<ForeignKeyInfo>,
125}
126
127#[derive(
131 Clone,
132 Debug,
133 PartialEq,
134 Eq,
135 PartialOrd,
136 Ord,
137 Hash,
138 Serialize,
139 Deserialize,
140 schemars::JsonSchema,
141)]
142pub struct ForeignKeyInfo {
143 pub id: i64,
146 pub from_column: String,
148 pub to_table: String,
150 pub to_column: String,
152 pub on_update: String,
155 pub on_delete: String,
158}
159
160#[derive(Debug, thiserror::Error)]
162pub enum ListForeignKeysError {
163 #[error("failed to list foreign keys: {source}")]
165 Query {
166 source: rusqlite::Error,
168 },
169}
170
171impl IntoContents for ListForeignKeysError {
174 fn into_contents(self) -> Vec<Content> {
175 vec![Content::text(self.to_string())]
176 }
177}