Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions doc/user/content/reference/system-catalog/mz_internal.md
Original file line number Diff line number Diff line change
Expand Up @@ -542,16 +542,30 @@ each materialized view with a refresh strategy other than `on-commit`.

## `mz_mcp_data_products`

The `mz_mcp_data_products` view exposes data products (indexed materialized views)
available through the Model Context Protocol (MCP) server. Each data product
represents a queryable dataset with a defined schema.
The `mz_mcp_data_products` view lists data products (i.e., indexed materialized
views) that are available through the Model Context Protocol (MCP) server and
that the current user can access. This is a lightweight discovery view. Use
[`mz_mcp_data_product_details`](#mz_mcp_data_product_details) for full column
schema information.

<!-- RELATION_SPEC mz_internal.mz_mcp_data_products -->
| Field | Type | Meaning |
| ------------- | -------- | ---------------------------------------------------------------------------------------- |
| `object_name` | [`text`] | Fully qualified object name (database.schema.name). |
| `cluster` | [`text`] | Cluster where the index is hosted. |
| `description` | [`text`] | Index comment (used as data product description). |

## `mz_mcp_data_product_details`

The `mz_mcp_data_product_details` view extends [`mz_mcp_data_products`](#mz_mcp_data_products)
with a JSON Schema describing each data product's columns and types.

<!-- RELATION_SPEC mz_internal.mz_mcp_data_product_details -->
| Field | Type | Meaning |
| ------------- | -------- | ---------------------------------------------------------------------------------------- |
| `object_name` | [`text`] | Fully qualified object name (database.schema.name). |
| `cluster` | [`text`] | Cluster where the index is hosted. |
| `description` | [`text`] | Index comment (used as data product description). |
| `schema` | [`jsonb`]| JSON Schema describing the object's columns and types. |

## `mz_object_dependencies`
Expand Down
54 changes: 50 additions & 4 deletions src/catalog/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11506,15 +11506,60 @@ FROM
access: vec![PUBLIC_SELECT],
});

/// Data products exposed via MCP (Model Context Protocol) for AI agents.
/// Lightweight data product discovery for MCP (Model Context Protocol).
///
/// This view discovers indexes with comments that can be used as "data products"
/// for AI agent access. Only indexes with SELECT privilege and cluster USAGE
/// privilege are included.
/// Lists indexed views with comments that the current user has privileges on.
/// Used by the `get_data_products` and `read_data_product` MCP tools.
/// Does not include schema details — use `mz_mcp_data_product_details` for that.
pub static MZ_MCP_DATA_PRODUCTS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
name: "mz_mcp_data_products",
schema: MZ_INTERNAL_SCHEMA,
oid: oid::VIEW_MZ_MCP_DATA_PRODUCTS_OID,
desc: RelationDesc::builder()
.with_column("object_name", SqlScalarType::String.nullable(false))
.with_column("cluster", SqlScalarType::String.nullable(false))
.with_column("description", SqlScalarType::String.nullable(true))
.with_key(vec![0, 1, 2])
.finish(),
column_comments: BTreeMap::from_iter([
(
"object_name",
"Fully qualified object name (database.schema.name).",
),
("cluster", "Cluster where the index is hosted."),
(
"description",
"Index comment (used as data product description).",
),
]),
sql: r#"
SELECT DISTINCT
'"' || op.database || '"."' || op.schema || '"."' || op.name || '"' AS object_name,
c.name AS cluster,
cts.comment AS description
FROM mz_internal.mz_show_my_object_privileges op
JOIN mz_objects o ON op.name = o.name AND op.object_type = o.type
JOIN mz_schemas s ON s.name = op.schema AND s.id = o.schema_id
JOIN mz_databases d ON d.name = op.database AND d.id = s.database_id
JOIN mz_indexes i ON i.on_id = o.id
JOIN mz_clusters c ON c.id = i.cluster_id
JOIN mz_internal.mz_show_my_cluster_privileges cp ON cp.name = c.name
LEFT JOIN mz_internal.mz_comments cts ON cts.id = i.id AND cts.object_sub_id IS NULL
WHERE op.privilege_type = 'SELECT'
AND cp.privilege_type = 'USAGE'
"#,
access: vec![PUBLIC_SELECT],
});

/// Full data product details with JSON Schema for MCP agents.
///
/// Extends `mz_mcp_data_products` with column types, index keys, and column
/// comments, formatted as a JSON Schema object. Used by the
/// `get_data_product_details` MCP tool.
pub static MZ_MCP_DATA_PRODUCT_DETAILS: LazyLock<BuiltinView> = LazyLock::new(|| BuiltinView {
name: "mz_mcp_data_product_details",
schema: MZ_INTERNAL_SCHEMA,
oid: oid::VIEW_MZ_MCP_DATA_PRODUCT_DETAILS_OID,
desc: RelationDesc::builder()
.with_column("object_name", SqlScalarType::String.nullable(false))
.with_column("cluster", SqlScalarType::String.nullable(false))
Expand Down Expand Up @@ -14482,6 +14527,7 @@ pub static BUILTINS_STATIC: LazyLock<Vec<Builtin<NameReference>>> = LazyLock::ne
Builtin::ContinualTask(&MZ_WALLCLOCK_LAG_HISTORY_CT),
Builtin::View(&MZ_INDEX_ADVICE),
Builtin::View(&MZ_MCP_DATA_PRODUCTS),
Builtin::View(&MZ_MCP_DATA_PRODUCT_DETAILS),
]);

builtins.extend(notice::builtins());
Expand Down
9 changes: 5 additions & 4 deletions src/environmentd/src/http/mcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ use crate::http::AuthedClient;
use crate::http::sql::{SqlRequest, SqlResponse, SqlResult, execute_request};

// To add a new tool: add entry to tools/list, add handler function, add dispatch case.
// Discovery uses the lightweight view (no JSON schema computation).
const DISCOVERY_QUERY: &str = "SELECT * FROM mz_internal.mz_mcp_data_products";
// Details uses the full view with JSON schema.
const DETAILS_QUERY_PREFIX: &str =
"SELECT * FROM mz_internal.mz_mcp_data_product_details WHERE object_name = ";

/// MCP request errors, mapped to JSON-RPC error codes.
#[derive(Debug, Error)]
Expand Down Expand Up @@ -660,10 +664,7 @@ async fn get_data_product_details(
) -> Result<McpResult, McpRequestError> {
debug!(name = %name, "Executing get_data_product_details");

let query = format!(
"SELECT * FROM mz_internal.mz_mcp_data_products WHERE object_name = {}",
escaped_string_literal(name)
);
let query = format!("{}{}", DETAILS_QUERY_PREFIX, escaped_string_literal(name));

let rows = execute_sql(client, &query).await?;

Expand Down
1 change: 1 addition & 0 deletions src/pgrepr-consts/src/oid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -788,3 +788,4 @@ pub const SOURCE_MZ_CATALOG_RAW_OID: u32 = 17067;
pub const LOG_MZ_CLUSTER_PROMETHEUS_METRICS_OID: u32 = 17068;
pub const FUNC_PARSE_CATALOG_ID_OID: u32 = 17069;
pub const FUNC_PARSE_CATALOG_PRIVILEGES_OID: u32 = 17070;
pub const VIEW_MZ_MCP_DATA_PRODUCT_DETAILS_OID: u32 = 17071;
8 changes: 8 additions & 0 deletions test/sqllogictest/autogenerated/mz_internal.slt
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,13 @@ SELECT name, type, comment FROM objects WHERE schema = 'mz_internal' AND object
object_name text Fully␠qualified␠object␠name␠(database.schema.name).
cluster text Cluster␠where␠the␠index␠is␠hosted.
description text Index␠comment␠(used␠as␠data␠product␠description).

query TTT
SELECT name, type, comment FROM objects WHERE schema = 'mz_internal' AND object = 'mz_mcp_data_product_details' ORDER BY position
----
object_name text Fully␠qualified␠object␠name␠(database.schema.name).
cluster text Cluster␠where␠the␠index␠is␠hosted.
description text Index␠comment␠(used␠as␠data␠product␠description).
schema jsonb JSON␠Schema␠describing␠the␠object's␠columns␠and␠types.

query TTT
Expand Down Expand Up @@ -751,6 +758,7 @@ mz_materialization_dependencies
mz_materialization_lag
mz_materialized_view_refresh_strategies
mz_materialized_view_refreshes
mz_mcp_data_product_details
mz_mcp_data_products
mz_mysql_source_tables
mz_network_policies
Expand Down
4 changes: 4 additions & 0 deletions test/sqllogictest/information_schema_tables.slt
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,10 @@ mz_materialized_view_refreshes
SOURCE
materialize
mz_internal
mz_mcp_data_product_details
VIEW
materialize
mz_internal
mz_mcp_data_products
VIEW
materialize
Expand Down
2 changes: 1 addition & 1 deletion test/sqllogictest/mz_catalog_server_index_accounting.slt
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ mz_message_batch_counts_received_raw_s2_primary_idx CREATE␠INDEX␠"mz_messag
mz_message_batch_counts_sent_raw_s2_primary_idx CREATE␠INDEX␠"mz_message_batch_counts_sent_raw_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_message_batch_counts_sent_raw"␠("channel_id",␠"from_worker_id",␠"to_worker_id")
mz_message_counts_received_raw_s2_primary_idx CREATE␠INDEX␠"mz_message_counts_received_raw_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_message_counts_received_raw"␠("channel_id",␠"from_worker_id",␠"to_worker_id")
mz_message_counts_sent_raw_s2_primary_idx CREATE␠INDEX␠"mz_message_counts_sent_raw_s2_primary_idx"␠IN␠CLUSTER␠[s2]␠ON␠"mz_introspection"."mz_message_counts_sent_raw"␠("channel_id",␠"from_worker_id",␠"to_worker_id")
mz_notices_ind CREATE␠INDEX␠"mz_notices_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s803␠AS␠"mz_internal"."mz_notices"]␠("id")
mz_notices_ind CREATE␠INDEX␠"mz_notices_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s804␠AS␠"mz_internal"."mz_notices"]␠("id")
mz_object_dependencies_ind CREATE␠INDEX␠"mz_object_dependencies_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s465␠AS␠"mz_internal"."mz_object_dependencies"]␠("object_id")
mz_object_history_ind CREATE␠INDEX␠"mz_object_history_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s534␠AS␠"mz_internal"."mz_object_history"]␠("id")
mz_object_lifetimes_ind CREATE␠INDEX␠"mz_object_lifetimes_ind"␠IN␠CLUSTER␠[s2]␠ON␠[s535␠AS␠"mz_internal"."mz_object_lifetimes"]␠("id")
Expand Down
1 change: 1 addition & 0 deletions test/sqllogictest/oid.slt
Original file line number Diff line number Diff line change
Expand Up @@ -1178,3 +1178,4 @@ SELECT oid, name FROM mz_objects WHERE id LIKE 's%' AND oid < 20000 ORDER BY oid
17068 mz_cluster_prometheus_metrics
17069 parse_catalog_id
17070 parse_catalog_privileges
17071 mz_mcp_data_product_details
1 change: 1 addition & 0 deletions test/testdrive/catalog.td
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,7 @@ mz_hydration_statuses ""
mz_index_advice ""
mz_materialization_dependencies ""
mz_materialization_lag ""
mz_mcp_data_product_details ""
mz_mcp_data_products ""
mz_notices ""
mz_notices_redacted ""
Expand Down
Loading