From aac1d785395d90f14a62895b3dd809e69575108c Mon Sep 17 00:00:00 2001 From: wangfengxiang <31534139+wangfengxiang@users.noreply.github.com> Date: Thu, 3 Sep 2026 01:53:08 +0800 Subject: [PATCH] feat(mcp): list stored routines and fetch routine source --- .../editor/EditorSettingsDialog.vue | 2 + apps/desktop/src/i18n/locales/en.ts | 2 + apps/desktop/src/i18n/locales/es.ts | 2 + apps/desktop/src/i18n/locales/it.ts | 2 + apps/desktop/src/i18n/locales/ja.ts | 2 + apps/desktop/src/i18n/locales/ko.ts | 2 + apps/desktop/src/i18n/locales/pt-BR.ts | 2 + apps/desktop/src/i18n/locales/zh-CN.ts | 2 + apps/desktop/src/i18n/locales/zh-TW.ts | 2 + crates/dbx-mcp/src/backend.rs | 149 ++++++++++++++++++ crates/dbx-mcp/src/server.rs | 146 ++++++++++++++++- crates/dbx-mcp/tests/protocol.rs | 4 +- docs/content/docs/mcp.cn.mdx | 4 +- docs/content/docs/mcp.mdx | 4 +- packages/mcp-server/README.md | 6 +- 15 files changed, 321 insertions(+), 10 deletions(-) diff --git a/apps/desktop/src/components/editor/EditorSettingsDialog.vue b/apps/desktop/src/components/editor/EditorSettingsDialog.vue index 8224f83f62..f9ec783686 100644 --- a/apps/desktop/src/components/editor/EditorSettingsDialog.vue +++ b/apps/desktop/src/components/editor/EditorSettingsDialog.vue @@ -2286,6 +2286,8 @@ const mcpToolOptions = [ { name: "dbx_list_databases", labelKey: "settings.mcpToolListDatabases" }, { name: "dbx_list_tables", labelKey: "settings.mcpToolListTables" }, { name: "dbx_describe_table", labelKey: "settings.mcpToolDescribeTable" }, + { name: "dbx_list_routines", labelKey: "settings.mcpToolListRoutines" }, + { name: "dbx_get_routine_source", labelKey: "settings.mcpToolGetRoutineSource" }, { name: "dbx_get_schema_context", labelKey: "settings.mcpToolGetSchemaContext" }, { name: "dbx_execute_query", labelKey: "settings.mcpToolExecuteQuery" }, { name: "dbx_open_session", labelKey: "settings.mcpToolOpenSession" }, diff --git a/apps/desktop/src/i18n/locales/en.ts b/apps/desktop/src/i18n/locales/en.ts index 429a3c2cf8..91f446a330 100644 --- a/apps/desktop/src/i18n/locales/en.ts +++ b/apps/desktop/src/i18n/locales/en.ts @@ -7215,6 +7215,8 @@ export default { mcpToolListDatabases: "List databases", mcpToolListTables: "List tables", mcpToolDescribeTable: "Describe table", + mcpToolListRoutines: "List routines", + mcpToolGetRoutineSource: "Routine source", mcpToolGetSchemaContext: "Schema context", mcpToolExecuteQuery: "Execute SQL / Mongo commands", mcpToolOpenSession: "Open query session", diff --git a/apps/desktop/src/i18n/locales/es.ts b/apps/desktop/src/i18n/locales/es.ts index 34ddfe27bd..cfa884e76c 100644 --- a/apps/desktop/src/i18n/locales/es.ts +++ b/apps/desktop/src/i18n/locales/es.ts @@ -6827,6 +6827,8 @@ export default withEnglishFallback({ mcpToolListDatabases: "Listar bases de datos", mcpToolListTables: "Listar tablas", mcpToolDescribeTable: "Estructura de tabla", + mcpToolListRoutines: "Listar rutinas", + mcpToolGetRoutineSource: "Código de rutina", mcpToolGetSchemaContext: "Contexto de Schema", mcpToolExecuteQuery: "Ejecutar SQL / comandos Mongo", mcpToolOpenSession: "Abrir sesión de consulta", diff --git a/apps/desktop/src/i18n/locales/it.ts b/apps/desktop/src/i18n/locales/it.ts index 818ded1e66..0ad9bcc8fb 100644 --- a/apps/desktop/src/i18n/locales/it.ts +++ b/apps/desktop/src/i18n/locales/it.ts @@ -6827,6 +6827,8 @@ export default withEnglishFallback({ mcpToolListDatabases: "Elenca database", mcpToolListTables: "Elenca tabelle", mcpToolDescribeTable: "Struttura tabella", + mcpToolListRoutines: "Elenca routine", + mcpToolGetRoutineSource: "Sorgente routine", mcpToolGetSchemaContext: "Contesto Schema", mcpToolExecuteQuery: "Esegui SQL / comandi Mongo", mcpToolOpenSession: "Apri sessione di query", diff --git a/apps/desktop/src/i18n/locales/ja.ts b/apps/desktop/src/i18n/locales/ja.ts index 4a17fb4193..b6522b9cf2 100644 --- a/apps/desktop/src/i18n/locales/ja.ts +++ b/apps/desktop/src/i18n/locales/ja.ts @@ -6834,6 +6834,8 @@ export default withEnglishFallback({ mcpToolListDatabases: "データベースを一覧", mcpToolListTables: "テーブルを一覧", mcpToolDescribeTable: "テーブル構造", + mcpToolListRoutines: "ルーチン一覧", + mcpToolGetRoutineSource: "ルーチンソース", mcpToolGetSchemaContext: "Schema コンテキスト", mcpToolExecuteQuery: "SQL / Mongo コマンドを実行", mcpToolOpenSession: "クエリセッションを開く", diff --git a/apps/desktop/src/i18n/locales/ko.ts b/apps/desktop/src/i18n/locales/ko.ts index 12b5f2ad50..12e7ea9896 100644 --- a/apps/desktop/src/i18n/locales/ko.ts +++ b/apps/desktop/src/i18n/locales/ko.ts @@ -6575,6 +6575,8 @@ export default withEnglishFallback({ mcpToolListDatabases: "데이터베이스 나열", mcpToolListTables: "테이블 나열", mcpToolDescribeTable: "테이블 구조", + mcpToolListRoutines: "루틴 목록", + mcpToolGetRoutineSource: "루틴 소스", mcpToolGetSchemaContext: "Schema 컨텍스트", mcpToolExecuteQuery: "SQL / Mongo 명령 실행", mcpToolOpenSession: "쿼리 세션 열기", diff --git a/apps/desktop/src/i18n/locales/pt-BR.ts b/apps/desktop/src/i18n/locales/pt-BR.ts index ef5ea50638..191ba538cc 100644 --- a/apps/desktop/src/i18n/locales/pt-BR.ts +++ b/apps/desktop/src/i18n/locales/pt-BR.ts @@ -6829,6 +6829,8 @@ export default withEnglishFallback({ mcpToolListDatabases: "Listar bancos de dados", mcpToolListTables: "Listar tabelas", mcpToolDescribeTable: "Estrutura da tabela", + mcpToolListRoutines: "Listar rotinas", + mcpToolGetRoutineSource: "Código da rotina", mcpToolGetSchemaContext: "Contexto do Schema", mcpToolExecuteQuery: "Executar SQL / comandos Mongo", mcpToolOpenSession: "Abrir sessão de consulta", diff --git a/apps/desktop/src/i18n/locales/zh-CN.ts b/apps/desktop/src/i18n/locales/zh-CN.ts index 685f239013..9c5ef7d129 100644 --- a/apps/desktop/src/i18n/locales/zh-CN.ts +++ b/apps/desktop/src/i18n/locales/zh-CN.ts @@ -7199,6 +7199,8 @@ export default withEnglishFallback({ mcpToolListDatabases: "列出数据库", mcpToolListTables: "列出表", mcpToolDescribeTable: "表结构", + mcpToolListRoutines: "存储过程列表", + mcpToolGetRoutineSource: "存储过程源码", mcpToolGetSchemaContext: "Schema 上下文", mcpToolExecuteQuery: "执行 SQL / Mongo 命令", mcpToolOpenSession: "打开查询会话", diff --git a/apps/desktop/src/i18n/locales/zh-TW.ts b/apps/desktop/src/i18n/locales/zh-TW.ts index 61f4ea84f1..01411e75f8 100644 --- a/apps/desktop/src/i18n/locales/zh-TW.ts +++ b/apps/desktop/src/i18n/locales/zh-TW.ts @@ -6169,6 +6169,8 @@ export default withEnglishFallback({ mcpToolListDatabases: "列出資料庫", mcpToolListTables: "列出資料表", mcpToolDescribeTable: "資料表結構", + mcpToolListRoutines: "預存程序列表", + mcpToolGetRoutineSource: "預存程序原始碼", mcpToolGetSchemaContext: "Schema 上下文", mcpToolExecuteQuery: "執行 SQL / Mongo 命令", mcpToolOpenSession: "開啟查詢工作階段", diff --git a/crates/dbx-mcp/src/backend.rs b/crates/dbx-mcp/src/backend.rs index 8520733b0b..a9a2c949b1 100644 --- a/crates/dbx-mcp/src/backend.rs +++ b/crates/dbx-mcp/src/backend.rs @@ -228,6 +228,32 @@ pub trait DbxBackend: Send + Sync { let _ = (connection, database, schema, table); Err("Column metadata is not supported by this backend.".to_string()) } + /// List stored routines (procedures/functions) for a schema. `routine_types` + /// filters by object type ("PROCEDURE"/"FUNCTION"); `None` returns both. + async fn list_routines( + &self, + connection: &ConnectionConfig, + database: &str, + schema: &str, + routine_types: Option<&[String]>, + ) -> Result, String> { + let _ = (connection, database, schema, routine_types); + Err("Routine metadata is not supported by this backend.".to_string()) + } + /// Fetch a stored routine's full source text. `object_type` is + /// `PROCEDURE` or `FUNCTION` (SCREAMING_SNAKE_CASE, as in the desktop API). + async fn get_routine_source( + &self, + connection: &ConnectionConfig, + database: &str, + schema: &str, + name: &str, + object_type: &str, + signature: Option<&str>, + ) -> Result { + let _ = (connection, database, schema, name, object_type, signature); + Err("Routine source is not supported by this backend.".to_string()) + } async fn execute_redis_command( &self, connection: &ConnectionConfig, @@ -544,6 +570,21 @@ fn local_plugin_dir(settings: &DesktopSettings, data_dir: &Path) -> PathBuf { .unwrap_or_else(|| data_dir.join("plugins")) } +/// Whether an object listing entry is a stored routine (procedure or function). +fn is_routine_object(object_type: &str) -> bool { + object_type.eq_ignore_ascii_case("PROCEDURE") || object_type.eq_ignore_ascii_case("FUNCTION") +} + +/// Parse a routine type string ("PROCEDURE"/"FUNCTION", case-insensitive) into +/// the core `ObjectSourceKind` used by `get_object_source_core`. +fn parse_routine_kind(object_type: &str) -> Result { + match object_type.trim().to_ascii_uppercase().as_str() { + "PROCEDURE" => Ok(dbx_core::db::ObjectSourceKind::Procedure), + "FUNCTION" => Ok(dbx_core::db::ObjectSourceKind::Function), + other => Err(format!("Unsupported routine type \"{other}\"; use PROCEDURE or FUNCTION.")), + } +} + fn local_agent_dir(settings: &DesktopSettings, data_dir: &Path) -> PathBuf { let legacy_driver_base = settings.driver_store_dir.as_ref().filter(|value| !value.trim().is_empty()).map(PathBuf::from); @@ -691,6 +732,53 @@ impl DbxBackend for LocalBackend { dbx_core::schema::get_columns_core(&self.state, &connection.id, database, schema, table).await } + async fn list_routines( + &self, + connection: &ConnectionConfig, + database: &str, + schema: &str, + routine_types: Option<&[String]>, + ) -> Result, String> { + let default_types = ["PROCEDURE".to_string(), "FUNCTION".to_string()]; + let object_types = routine_types.unwrap_or(default_types.as_slice()); + let objects = dbx_core::schema::list_objects_core( + &self.state, + &connection.id, + database, + schema, + None, + None, + None, + Some(object_types), + None, + ) + .await?; + Ok(objects.into_iter().filter(|object| is_routine_object(&object.object_type)).collect()) + } + + async fn get_routine_source( + &self, + connection: &ConnectionConfig, + database: &str, + schema: &str, + name: &str, + object_type: &str, + signature: Option<&str>, + ) -> Result { + let kind = parse_routine_kind(object_type)?; + dbx_core::schema::get_object_source_core( + &self.state, + &connection.id, + database, + schema, + name, + kind, + signature, + None, + ) + .await + } + async fn collect_docs_snapshot( &self, connection: &ConnectionConfig, @@ -1127,6 +1215,67 @@ impl DbxBackend for WebBackend { .map_err(|error| format!("Invalid column list response: {error}")) } + async fn list_routines( + &self, + connection: &ConnectionConfig, + database: &str, + schema: &str, + routine_types: Option<&[String]>, + ) -> Result, String> { + self.ensure_connected(connection).await?; + let types = routine_types.map(|types| types.join(",")).unwrap_or_else(|| "PROCEDURE,FUNCTION".to_string()); + self.request( + reqwest::Method::GET, + &format!( + "/api/schema/objects?connection_id={}&database={}&schema={}&object_types={}", + url_encode(&connection.id), + url_encode(database), + url_encode(schema), + url_encode(&types) + ), + None, + ) + .await? + .json::>() + .await + .map(|objects| objects.into_iter().filter(|object| is_routine_object(&object.object_type)).collect()) + .map_err(|error| format!("Invalid routine list response: {error}")) + } + + async fn get_routine_source( + &self, + connection: &ConnectionConfig, + database: &str, + schema: &str, + name: &str, + object_type: &str, + signature: Option<&str>, + ) -> Result { + let kind = parse_routine_kind(object_type)?; + let kind_name = match kind { + dbx_core::db::ObjectSourceKind::Procedure => "PROCEDURE", + dbx_core::db::ObjectSourceKind::Function => "FUNCTION", + _ => unreachable!("parse_routine_kind only yields routines"), + }; + self.ensure_connected(connection).await?; + let mut query = format!( + "/api/schema/object-source?connection_id={}&database={}&schema={}&table={}&object_type={}", + url_encode(&connection.id), + url_encode(database), + url_encode(schema), + url_encode(name), + url_encode(kind_name), + ); + if let Some(signature) = signature.filter(|value| !value.trim().is_empty()) { + query.push_str(&format!("&signature={}", url_encode(signature))); + } + self.request(reqwest::Method::GET, &query, None) + .await? + .json() + .await + .map_err(|error| format!("Invalid routine source response: {error}")) + } + async fn collect_docs_snapshot( &self, connection: &ConnectionConfig, diff --git a/crates/dbx-mcp/src/server.rs b/crates/dbx-mcp/src/server.rs index 1edb1a6467..9f58b17ffe 100644 --- a/crates/dbx-mcp/src/server.rs +++ b/crates/dbx-mcp/src/server.rs @@ -73,6 +73,42 @@ pub struct DescribeTableRequest { pub schema: Option, } +#[derive(Debug, Deserialize, schemars::JsonSchema)] +pub struct ListRoutinesRequest { + #[serde(flatten)] + pub selector: ConnectionSelector, + #[schemars(description = "Database name")] + #[schemars(extend("type" = "string"))] + pub database: Option, + #[schemars(description = "Schema name")] + #[schemars(extend("type" = "string"))] + pub schema: Option, + #[schemars( + description = "Optional routine type filter: PROCEDURE or FUNCTION. Omit to list both. Unsupported databases return an empty list." + )] + #[schemars(extend("type" = "string"))] + pub routine_type: Option, +} + +#[derive(Debug, Deserialize, schemars::JsonSchema)] +pub struct GetRoutineSourceRequest { + #[serde(flatten)] + pub selector: ConnectionSelector, + #[schemars(description = "Routine name (as returned by dbx_list_routines)")] + pub name: String, + #[schemars(description = "Routine type: PROCEDURE or FUNCTION")] + pub object_type: String, + #[schemars(description = "Optional routine signature for databases that overload routine names (e.g. Oracle)")] + #[schemars(extend("type" = "string"))] + pub signature: Option, + #[schemars(description = "Database name")] + #[schemars(extend("type" = "string"))] + pub database: Option, + #[schemars(description = "Schema name")] + #[schemars(extend("type" = "string"))] + pub schema: Option, +} + #[derive(Debug, Deserialize, schemars::JsonSchema)] pub struct ExecuteQueryRequest { #[serde(flatten)] @@ -438,6 +474,76 @@ impl DbxMcpServer { } } + #[tool(name = "dbx_list_routines", description = "List stored procedures and functions for a database schema")] + async fn list_routines(&self, Parameters(request): Parameters) -> CallToolResult { + if let Err(error) = self.ensure_tool_allowed("dbx_list_routines").await { + return error; + } + let resolved = match self.resolve_connection(&request.selector).await { + Ok(resolved) => resolved, + Err(error) => return error, + }; + let database = match self.resolve_database(request.database, &resolved) { + Ok(database) => database, + Err(error) => return error, + }; + let schema = match self.resolve_schema(request.schema) { + Ok(schema) => schema, + Err(error) => return error, + }; + let routine_types = match request.routine_type.as_deref() { + None => None, + Some(kind) => match normalize_routine_type(kind) { + Ok(kind) => Some(vec![kind]), + Err(error) => return tool_error("ROUTINE_LIST_ERROR", error), + }, + }; + match self.backend.list_routines(&resolved.connection, &database, &schema, routine_types.as_deref()).await { + Ok(routines) if routines.is_empty() => text("No routines found."), + Ok(routines) => text(format_routines(&routines)), + Err(error) => tool_error("ROUTINE_LIST_ERROR", error), + } + } + + #[tool(name = "dbx_get_routine_source", description = "Get the full source text of a stored procedure or function")] + async fn get_routine_source(&self, Parameters(request): Parameters) -> CallToolResult { + if let Err(error) = self.ensure_tool_allowed("dbx_get_routine_source").await { + return error; + } + let resolved = match self.resolve_connection(&request.selector).await { + Ok(resolved) => resolved, + Err(error) => return error, + }; + let database = match self.resolve_database(request.database, &resolved) { + Ok(database) => database, + Err(error) => return error, + }; + let schema = match self.resolve_schema(request.schema) { + Ok(schema) => schema, + Err(error) => return error, + }; + let object_type = match normalize_routine_type(&request.object_type) { + Ok(kind) => kind, + Err(error) => return tool_error("ROUTINE_SOURCE_ERROR", error), + }; + match self + .backend + .get_routine_source( + &resolved.connection, + &database, + &schema, + &request.name, + &object_type, + request.signature.as_deref(), + ) + .await + { + Ok(source) if source.source.trim().is_empty() => text("Routine source is empty."), + Ok(source) => text(source.source), + Err(error) => tool_error("ROUTINE_SOURCE_ERROR", error), + } + } + #[tool( name = "dbx_execute_query", description = "Execute a SQL query on a database connection (max 100 rows returned)" @@ -1703,6 +1809,32 @@ fn format_connections(connections: &[ConnectionSummary]) -> String { output } +/// Normalize a user-supplied routine type ("procedure", "PROCEDURE", ...) to +/// the canonical SCREAMING form used by the object listing protocol. +fn normalize_routine_type(value: &str) -> Result { + match value.trim().to_ascii_uppercase().as_str() { + "PROCEDURE" => Ok("PROCEDURE".to_string()), + "FUNCTION" => Ok("FUNCTION".to_string()), + other => Err(format!("Unsupported routine type \"{other}\"; use PROCEDURE or FUNCTION.")), + } +} + +fn format_routines(routines: &[dbx_core::db::ObjectInfo]) -> String { + let rows = routines + .iter() + .map(|routine| { + vec![ + routine.name.clone(), + routine.object_type.clone(), + routine.schema.clone().unwrap_or_default(), + routine.signature.clone().unwrap_or_default(), + routine.comment.clone().unwrap_or_default(), + ] + }) + .collect::>(); + markdown_table(&["Routine", "Type", "Schema", "Signature", "Comment"], &rows) +} + fn format_columns(columns: &[dbx_core::db::ColumnInfo]) -> String { let rows = columns .iter() @@ -1943,13 +2075,15 @@ mod tests { let tools = server.tool_router.list_all(); let names = tools.iter().map(|tool| tool.name.as_ref()).collect::>(); #[cfg(feature = "mq-admin")] - assert_eq!(tools.len(), 15); + assert_eq!(tools.len(), 17); #[cfg(not(feature = "mq-admin"))] - assert_eq!(tools.len(), 14); + assert_eq!(tools.len(), 16); assert!(names.contains(&"dbx_list_connections")); assert!(names.contains(&"dbx_list_databases")); assert!(names.contains(&"dbx_list_tables")); assert!(names.contains(&"dbx_describe_table")); + assert!(names.contains(&"dbx_list_routines")); + assert!(names.contains(&"dbx_get_routine_source")); assert!(names.contains(&"dbx_execute_query")); assert!(names.contains(&"dbx_add_connection")); assert!(names.contains(&"dbx_duplicate_connection")); @@ -2060,6 +2194,8 @@ mod tests { let mut checks: Vec<(&str, &[&str])> = vec![ ("dbx_list_tables", &["database", "schema"]), ("dbx_describe_table", &["database", "schema"]), + ("dbx_list_routines", &["database", "schema", "routine_type"]), + ("dbx_get_routine_source", &["database", "schema", "signature"]), ("dbx_execute_query", &["database", "session_id", "cell_char_offset", "cell_char_limit"]), ("dbx_open_session", &["database"]), ("dbx_open_table", &["database", "schema"]), @@ -2183,14 +2319,16 @@ mod tests { ); let names = server.tool_router.list_all().into_iter().map(|tool| tool.name).collect::>(); #[cfg(feature = "mq-admin")] - assert_eq!(names.len(), 10); + assert_eq!(names.len(), 12); #[cfg(not(feature = "mq-admin"))] - assert_eq!(names.len(), 9); + assert_eq!(names.len(), 11); assert!(!names.iter().any(|name| name == "dbx_add_connection")); assert!(!names.iter().any(|name| name == "dbx_duplicate_connection")); assert!(!names.iter().any(|name| name == "dbx_remove_connection")); assert!(!names.iter().any(|name| name == "dbx_open_table")); assert!(!names.iter().any(|name| name == "dbx_execute_and_show")); + assert!(names.iter().any(|name| name == "dbx_list_routines")); + assert!(names.iter().any(|name| name == "dbx_get_routine_source")); assert!(names.iter().any(|name| name == "dbx_open_session")); assert!(names.iter().any(|name| name == "dbx_close_session")); #[cfg(feature = "mq-admin")] diff --git a/crates/dbx-mcp/tests/protocol.rs b/crates/dbx-mcp/tests/protocol.rs index 39b789a32d..0226022094 100644 --- a/crates/dbx-mcp/tests/protocol.rs +++ b/crates/dbx-mcp/tests/protocol.rs @@ -285,9 +285,9 @@ async fn initializes_lists_tools_and_calls_a_tool() { let tools = client.peer().list_tools(None).await.expect("list tools"); let names = tools.tools.iter().map(|tool| tool.name.as_ref()).collect::>(); #[cfg(feature = "mq-admin")] - assert_eq!(names.len(), 15); + assert_eq!(names.len(), 17); #[cfg(not(feature = "mq-admin"))] - assert_eq!(names.len(), 14); + assert_eq!(names.len(), 16); assert!(names.contains(&"dbx_list_connections")); assert!(names.contains(&"dbx_list_databases")); assert!(names.contains(&"dbx_duplicate_connection")); diff --git a/docs/content/docs/mcp.cn.mdx b/docs/content/docs/mcp.cn.mdx index 948fdbb001..b0ace67460 100644 --- a/docs/content/docs/mcp.cn.mdx +++ b/docs/content/docs/mcp.cn.mdx @@ -87,7 +87,7 @@ DeepSeek Harness 通过 Cordis 插件条目加载 MCP Server,不读取 `mcpSer ## 工具列表 -DBX MCP 当前提供 15 个工具: +DBX MCP 当前提供 17 个工具: | 工具 | 说明 | | --- | --- | @@ -98,6 +98,8 @@ DBX MCP 当前提供 15 个工具: | `dbx_remove_connection` | 从 DBX 存储删除连接 | | `dbx_list_tables` | 列出表、视图或集合 | | `dbx_describe_table` | 返回列定义和表元数据 | +| `dbx_list_routines` | 列出 Schema 中的存储过程和函数,支持可选的 `routine_type` 过滤(PROCEDURE 或 FUNCTION) | +| `dbx_get_routine_source` | 按名称返回存储过程或函数的源码,重载场景可用可选的 `signature` 区分 | | `dbx_get_schema_context` | 返回适合 AI 使用的紧凑 Schema 上下文 | | `dbx_execute_query` | 执行 SQL 或支持的 MongoDB shell 命令,最多返回 100 行 | | `dbx_open_session` | 为 SQL 连接打开固定后端连接的有状态查询会话 | diff --git a/docs/content/docs/mcp.mdx b/docs/content/docs/mcp.mdx index 1a439c665e..0e3fed0c1d 100644 --- a/docs/content/docs/mcp.mdx +++ b/docs/content/docs/mcp.mdx @@ -87,7 +87,7 @@ Run `dsh web --dump-config` to verify the composed configuration, then restart D ## Tools -DBX MCP currently provides 15 tools: +DBX MCP currently provides 17 tools: | Tool | Description | | --- | --- | @@ -98,6 +98,8 @@ DBX MCP currently provides 15 tools: | `dbx_remove_connection` | Remove a connection from DBX storage | | `dbx_list_tables` | List tables, views, or collections | | `dbx_describe_table` | Return columns and table metadata | +| `dbx_list_routines` | List stored procedures and functions in a schema, with an optional `routine_type` filter (PROCEDURE or FUNCTION) | +| `dbx_get_routine_source` | Return the source of a stored procedure or function by name, with an optional `signature` for overloaded names | | `dbx_get_schema_context` | Return compact schema context for an AI model | | `dbx_execute_query` | Execute SQL or a supported MongoDB shell command, returning at most 100 rows | | `dbx_open_session` | Open a stateful SQL query session pinned to one backend connection | diff --git a/packages/mcp-server/README.md b/packages/mcp-server/README.md index 3167d3ff94..fb002a1213 100644 --- a/packages/mcp-server/README.md +++ b/packages/mcp-server/README.md @@ -17,7 +17,7 @@ The MCP protocol, connection loading, SQL safety, schema access, Redis support, ## Features -- **15 MCP tools** for connection and database discovery, schemas, SQL, Redis, sessions, messages, and DBX UI integration +- **17 MCP tools** for connection and database discovery, schemas, SQL, Redis, sessions, messages, and DBX UI integration - **Precompiled native binaries** with no local Rust, Cargo, Python, or C/C++ build requirement - **No `better-sqlite3` runtime dependency** and no Node native-addon ABI coupling - **Local, Web, and Docker modes** using the same tool interface @@ -149,6 +149,8 @@ Ask the MCP client to: | `dbx_remove_connection` | Remove a connection from DBX storage | | `dbx_list_tables` | List tables, views, collections, or message queue topics | | `dbx_describe_table` | Return columns and table metadata | +| `dbx_list_routines` | List stored procedures and functions in a schema, with an optional `routine_type` filter (PROCEDURE or FUNCTION) | +| `dbx_get_routine_source` | Return the source of a stored procedure or function by name, with an optional `signature` for overloaded names | | `dbx_get_schema_context` | Return compact schema context suitable for an AI model | | `dbx_execute_query` | Execute SQL or a supported MongoDB shell command, returning at most 100 rows | | `dbx_open_session` | Open a stateful SQL query session pinned to one backend connection | @@ -433,7 +435,7 @@ MCP 协议、连接读取、SQL 安全检查、Schema、Redis、MongoDB、Web ### 主要能力 -- 15 个 MCP 工具,涵盖连接和数据库发现、Schema、SQL、Redis、会话、消息队列和 DBX 桌面集成 +- 17 个 MCP 工具,涵盖连接和数据库发现、Schema、SQL、Redis、会话、消息队列和 DBX 桌面集成 - 不依赖 `better-sqlite3`,没有 Node 原生模块 ABI 问题 - 支持本地 DBX、DBX Web 和 Docker - 可选 Streamable HTTP 传输,使用 Bearer Token 保护;stdio 仍为默认方式