Skip to content
mcp-data-platform composable mcp data platform
v1.x part of txn2 ↗

Tools API Reference

Complete specification for all MCP tools provided by mcp-data-platform.

Error contract

Every failed tool call returns a uniform, self-describing error so an agent can tell a correctable mistake from a platform problem and act on it. A failure sets isError: true and carries both a human-readable text message and a machine-readable structuredContent.error object:

{
  "isError": true,
  "content": [
    { "type": "text", "text": "the \"asset_id\" parameter is required (code: missing_required_parameter) Hint: Supply \"asset_id\" and retry. This is a problem with the call's arguments, not a platform fault." }
  ],
  "structuredContent": {
    "error": {
      "code": "missing_required_parameter",
      "category": "client_input",
      "message": "the \"asset_id\" parameter is required",
      "hint": "Supply \"asset_id\" and retry. This is a problem with the call's arguments, not a platform fault."
    }
  }
}
Field Meaning
code Stable, machine-readable identifier the agent may branch on (for example missing_required_parameter, invalid_arguments, not_found, unauthorized, setup_required, internal_error).
category Broad class (see below) telling the agent whose fault the failure is.
message The specific failure.
hint The corrective action, when the caller can take one.

Categories

Category Whose fault What to do
client_input The call Fix the arguments and retry.
not_found The call The named resource does not exist; correct the reference.
authentication_failed The caller's identity Provide valid credentials.
authorization_denied The caller's identity The persona is not permitted; request access.
user_declined The user A consent prompt was declined.
setup_required Session state Call the required setup tool first.
feature_unavailable Deployment config The feature is not enabled on this deployment; do not present it as an outage.
internal The platform Not the caller's fault; do not retry with modified input.
tool_error Unclassified A tool failure that has not been given a finer category; the message is still descriptive.

The contract is uniform by construction: a normalization layer guarantees every error result carries this envelope even when an individual tool returns only a bare message, so an agent never receives an opaque, undifferentiated string. The category is also recorded on the audit log (error_category) for operators.

Unknown arguments are refused, not ignored

The platform's own tools publish input schemas that are closed to unknown top-level arguments ("additionalProperties": false). A misnamed argument fails at the tool boundary, before the handler runs, with code: invalid_arguments, category: client_input, and a message naming the offending property — for example, passing parameters to api_invoke_endpoint instead of query_params:

{
  "error": {
    "code": "invalid_arguments",
    "category": "client_input",
    "message": "validating \"arguments\": validating root: unexpected additional properties [\"parameters\"]",
    "hint": "The arguments do not match the tool's input schema. Read the tool's schema, correct or drop the named property, and retry. This is a problem with the call's arguments, not a platform fault."
  }
}

Nested maps stay open where they carry a foreign namespace: query_params, headers, and body on the api_* tools accept arbitrary keys, because those names belong to the upstream API rather than to the tool. Tools proxied from an upstream MCP server through a gateway connection keep the upstream's own schema, strict or not.

Trino Tools

trino_query

Execute a read-only SQL query against the Trino cluster. Write operations (INSERT, UPDATE, DELETE, CREATE, DROP, etc.) are rejected before reaching Trino. Annotated with ReadOnlyHint: true for MCP client auto-approval.

Parameters:

Parameter Type Required Default Description
query string Yes - SQL query to execute (read-only)
limit integer No 1000 Maximum rows to return (capped by max_limit config)
connection string No the kind's default: Trino connection name

Response Schema:

{
  "columns": [
    {"name": "column_name", "type": "varchar"}
  ],
  "rows": [
    ["value1", "value2"]
  ],
  "row_count": 100,
  "execution_time_ms": 250,
  "query_id": "20240115_123456_00001_xxxxx"
}

Enrichment (when enabled):

{
  "semantic_context": {
    "description": "Table description from DataHub",
    "owners": [{"name": "Team Name", "type": "group"}],
    "tags": ["tag1", "tag2"],
    "domain": {"name": "Domain Name"},
    "quality_score": 0.95,
    "deprecation": null
  }
}

Error Codes:

Code Cause
SYNTAX_ERROR Invalid SQL syntax
TABLE_NOT_FOUND Referenced table doesn't exist
PERMISSION_DENIED Insufficient privileges
TIMEOUT Query exceeded timeout
WRITE_REJECTED Write SQL rejected (use trino_execute instead)

trino_execute

Execute any SQL against the Trino cluster, including write operations (INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, etc.). Annotated with DestructiveHint: true so MCP clients prompt for confirmation.

read_only is set per instance, and the block applies to the connection the call names — or to the default connection when it names none. A call routed to an instance with read_only: true is refused; the other instances of the same toolkit still accept writes.

Parameters:

Parameter Type Required Default Description
query string Yes - SQL query to execute
limit integer No 1000 Maximum rows to return (capped by max_limit config)
connection string No the kind's default: Trino connection name

Response Schema: Same as trino_query.


trino_explain

Get the execution plan for a SQL query.

Parameters:

Parameter Type Required Default Description
query string Yes - SQL query to explain
connection string No the kind's default: Trino connection name

Response Schema:

{
  "plan": "Query Plan\n- TableScan[table = ...]\n  ...",
  "format": "text"
}

trino_browse

Browse the Trino catalog hierarchy. Omit all parameters to list catalogs. Provide catalog to list schemas. Provide catalog and schema to list tables.

Parameters:

Parameter Type Required Default Description
catalog string No - Catalog name. Omit to list all catalogs
schema string No - Schema name. Requires catalog. Omit to list schemas
pattern string No - LIKE pattern to filter tables (only when listing tables)
connection string No the kind's default: Trino connection name

Response Schema (list catalogs):

{
  "catalogs": ["hive", "iceberg", "memory"]
}

Response Schema (list schemas):

{
  "catalog": "hive",
  "schemas": ["default", "sales", "marketing"]
}

Response Schema (list tables):

{
  "catalog": "hive",
  "schema": "sales",
  "tables": [
    {"name": "orders", "type": "TABLE"},
    {"name": "customers", "type": "TABLE"},
    {"name": "daily_revenue", "type": "VIEW"}
  ]
}

trino_describe_table

Get table schema and metadata.

Parameters:

Parameter Type Required Default Description
table string Yes - Table name (can be catalog.schema.table)
connection string No the kind's default: Trino connection name

Response Schema:

{
  "table": {
    "catalog": "hive",
    "schema": "sales",
    "name": "orders"
  },
  "columns": [
    {
      "name": "order_id",
      "type": "bigint",
      "nullable": false,
      "comment": "Unique order identifier"
    }
  ],
  "partitioning": ["order_date"],
  "properties": {
    "format": "PARQUET"
  }
}

trino_list_connections

List configured Trino connections.

Parameters: None

Response Schema:

{
  "connections": [
    {
      "name": "primary",
      "display_name": "Production",
      "host": "trino.example.com",
      "catalog": "hive",
      "schema": "default"
    }
  ]
}

DataHub Tools

Search for entities in the catalog.

Parameters:

Parameter Type Required Default Description
query string Yes - Search query
type string No - Entity type: dataset, dashboard, chart, dataflow
platform string No - Platform filter: trino, snowflake, s3, etc.
limit integer No 10 Maximum results (capped by max_limit config)
connection string No first configured DataHub connection name

Response Schema:

{
  "results": [
    {
      "urn": "urn:li:dataset:(urn:li:dataPlatform:trino,hive.sales.orders,PROD)",
      "name": "orders",
      "description": "Customer orders",
      "platform": "trino",
      "type": "dataset",
      "owners": ["Data Team"],
      "tags": ["pii", "financial"]
    }
  ],
  "total": 150,
  "has_more": true
}

Enrichment (when enabled):

{
  "query_context": {
    "urn:li:dataset:...": {
      "queryable": true,
      "connection": "primary",
      "table_identifier": {
        "catalog": "hive",
        "schema": "sales",
        "table": "orders"
      },
      "sample_query": "SELECT * FROM hive.sales.orders LIMIT 10"
    }
  }
}

Reading a catalog entity by URN

The DataHub toolkit registers no by-URN read tool. A dataset, glossary term, tag, domain, or data product is read in full with fetch on its urn:li:... reference; the retired datahub_get_entity, datahub_get_schema, datahub_get_queries, datahub_get_glossary_term, and datahub_get_data_product tools are not registered under any name.

A fetched dataset (fetch urn:li:dataset:...) returns the catalog's full record as the document content:

{
  "urn": "urn:li:dataset:(urn:li:dataPlatform:trino,warehouse.sales.orders,PROD)",
  "name": "orders",
  "type": "DATASET",
  "platform": "trino",
  "sub_types": ["table"],
  "description": "Customer orders from e-commerce platform",
  "owners": [{"urn": "urn:li:corpuser:ana", "type": "user", "name": "ana"}],
  "tags": ["pii", "financial"],
  "glossary_terms": [{"urn": "urn:li:glossaryTerm:Order", "name": "Order"}],
  "domain": {"urn": "urn:li:domain:sales", "name": "Sales"},
  "custom_properties": {"refresh_schedule": "daily"},
  "created": "2024-01-01T00:00:00Z",
  "schema": {
    "version": 3,
    "fields": [
      {"field_path": "order_id", "type": "NUMBER", "native_type": "bigint", "nullable": false,
       "description": "Unique order identifier", "tags": ["pii"]}
    ],
    "primary_keys": ["order_id"],
    "foreign_keys": []
  },
  "queries": [
    {"urn": "urn:li:query:q1", "name": "completed orders",
     "statement": "SELECT * FROM orders WHERE status = 'completed'", "source": "MANUAL",
     "created_by": "urn:li:corpuser:ana", "created": "2024-01-15T10:00:00Z"}
  ],
  "total_queries": 1,
  "related_documents": [{"urn": "urn:li:document:d1", "title": "Orders runbook"}],
  "query_availability": {
    "available": true,
    "query_table": "warehouse.sales.orders",
    "connection": "primary",
    "estimated_rows": 1250000
  }
}

unavailable names any part of the record (schema, queries, related_documents) the catalog could not serve on that read, so an absent part is distinguishable from an empty one. query_availability is present when a query provider is configured; the document's verifiable field names the same table when the dataset is queryable.

A fetched glossary term (fetch urn:li:glossaryTerm:...) returns:

{
  "urn": "urn:li:glossaryTerm:Revenue",
  "kind": "glossary_term",
  "name": "Revenue",
  "description": "Total monetary value from sales transactions",
  "parent_node": "urn:li:glossaryNode:FinancialMetrics",
  "owners": [{"urn": "urn:li:corpuser:cfo", "type": "user", "name": "cfo"}],
  "custom_properties": {"calculation": "SUM(line_item_amount)"},
  "datasets": [{"urn": "urn:li:dataset:...", "name": "warehouse.sales.invoices"}],
  "more_datasets": false
}

A tag or domain returns the same shape without parent_node, owners, and custom_properties.

A fetched data product (fetch urn:li:dataProduct:...) returns:

{
  "urn": "urn:li:dataProduct:customer360",
  "kind": "data_product",
  "name": "Customer 360",
  "description": "Unified customer view combining all customer data sources",
  "domain": {"urn": "urn:li:domain:marketing", "name": "Marketing"},
  "owners": [{"urn": "urn:li:corpgroup:marketing-data", "type": "group", "name": "Marketing Data Team"}],
  "custom_properties": {"sla": "99.9%", "refresh": "hourly"},
  "datasets": [
    {"urn": "urn:li:dataset:customers", "name": "customers"},
    {"urn": "urn:li:dataset:customer_events", "name": "customer_events"}
  ]
}

Member datasets outside the caller's connection boundary are withheld and counted in datasets_withheld, with notice explaining it.


datahub_get_lineage

Get upstream or downstream lineage for an entity. Set level=column for column-level lineage showing which upstream columns feed each downstream column. Default (dataset) returns dataset-level relationships with direction and depth control.

Parameters:

Parameter Type Required Default Description
urn string Yes - Entity URN
level string No dataset Granularity: dataset or column
direction string No downstream upstream or downstream (dataset level only)
depth integer No 3 Maximum traversal depth, max 5 (dataset level only)
connection string No first configured DataHub connection name

Response Schema (dataset level):

{
  "root": "urn:li:dataset:...",
  "direction": "downstream",
  "entities": [
    {
      "urn": "urn:li:dataset:...",
      "name": "daily_orders_agg",
      "type": "dataset",
      "depth": 1
    }
  ],
  "relationships": [
    {
      "source": "urn:li:dataset:orders",
      "target": "urn:li:dataset:daily_orders_agg",
      "type": "TRANSFORMED"
    }
  ]
}

Response Schema (column level):

{
  "root": "urn:li:dataset:...",
  "column_lineage": [
    {
      "downstream": {
        "urn": "urn:li:dataset:daily_orders_agg",
        "column": "total_revenue"
      },
      "upstreams": [
        {
          "urn": "urn:li:dataset:orders",
          "column": "total_amount"
        }
      ]
    }
  ]
}

datahub_browse

Browse the DataHub catalog by category. Set what=tags to list tags, what=domains to list data domains, or what=data_products to list data products.

Parameters:

Parameter Type Required Default Description
what string Yes - What to browse: tags, domains, or data_products
filter string No - Optional filter string (tags only)
connection string No first configured DataHub connection name

Response Schema (tags):

{
  "tags": [
    {"urn": "urn:li:tag:pii", "name": "pii", "description": "Contains PII"},
    {"urn": "urn:li:tag:financial", "name": "financial", "description": "Financial data"}
  ]
}

Response Schema (domains):

{
  "domains": [
    {
      "urn": "urn:li:domain:sales",
      "name": "Sales",
      "description": "Sales and revenue data",
      "entity_count": 45
    }
  ]
}

Response Schema (data_products):

{
  "data_products": [
    {
      "urn": "urn:li:dataProduct:customer360",
      "name": "Customer 360",
      "description": "Unified customer view",
      "domain": "urn:li:domain:marketing",
      "assets": 12
    }
  ]
}

datahub_list_connections

List configured DataHub connections.

Parameters: None

Response Schema:

{
  "connections": [
    {
      "name": "primary",
      "display_name": "Primary Catalog",
      "url": "https://datahub.example.com"
    }
  ]
}

datahub_create

Create a new entity or resource in DataHub. Uses the what discriminator to select the entity type. Only available when read_only: false.

Annotated with DestructiveHint: false, IdempotentHint: false, OpenWorldHint: true.

Parameters:

Parameter Type Required Default Description
what string Yes - Entity type to create (see table below)
name string Varies - Entity name (required for most types)
connection string No first configured DataHub connection name

Additional parameters vary by what value — see the mcp-datahub documentation for full parameter details per entity type.

what Creates Key fields
tag Tag name
domain Domain name
glossary_term Glossary term name
data_product Data product name, domain_urn
document Context document (1.4.x+) name
application Application name
query Saved query value (SQL)
incident Incident name, incident_type, entity_urns
structured_property Structured property qualified_name, value_type, entity_types
data_contract Data contract dataset_urns

Response Schema:

{
  "urn": "urn:li:tag:new-tag",
  "message": "Created tag 'new-tag'"
}

datahub_update

Update metadata on an existing DataHub entity. Uses the what discriminator to select what to update, with an optional action for add/remove operations. Only available when read_only: false.

Annotated with DestructiveHint: false, IdempotentHint: true, OpenWorldHint: true.

Parameters:

Parameter Type Required Default Description
what string Yes - What to update (see table below)
urn string Varies - Entity URN to update
action string Varies - add or remove (required for tags, glossary terms, links, owners)
connection string No first configured DataHub connection name

Additional parameters vary by what value — see the mcp-datahub documentation for full parameter details.

what action Description
description — Set entity description from value
column_description — Set schema field description from value
tag add/remove Add or remove a tag
glossary_term add/remove Add or remove a glossary term
link add/remove Add or remove a link
owner add/remove Add or remove an owner
domain set/remove Set or remove domain assignment
structured_properties set/remove Set or remove structured property values
structured_property — Update a structured property definition
incident_status — Update incident status
incident — Update incident details
query — Update query properties
document_contents — Update document title/text (1.4.x+)
document_status — Update document status (1.4.x+)
document_related_entities — Update document related entities (1.4.x+)
document_sub_type — Update document sub-type (1.4.x+)
data_contract — Upsert a data contract

Response Schema:

{
  "urn": "urn:li:dataset:...",
  "message": "Updated description on urn:li:dataset:..."
}

datahub_delete

Delete an entity or resource from DataHub. Uses the what discriminator to select the entity type. Only available when read_only: false.

Annotated with DestructiveHint: true, IdempotentHint: true, OpenWorldHint: true.

Parameters:

Parameter Type Required Default Description
what string Yes - Entity type to delete (see below)
urn string Yes - Entity URN to delete
connection string No first configured DataHub connection name

Supported what values: query, tag, domain, glossary_entity, data_product, application, document, structured_property.

Response Schema:

{
  "urn": "urn:li:tag:old-tag",
  "message": "Deleted tag 'old-tag'"
}

S3 Tools

Two tools (#1591): s3_list lists buckets or one bucket's objects, s3_object performs one action over a (bucket, key). Both are registered on every connection; put, copy and delete on a connection configured read_only: true are refused with an error naming the connection.

s3_list

List the buckets of a connection, or the objects in one bucket.

Parameters:

Parameter Type Required Default Description
bucket string No - Bucket to list objects in; omit to list buckets
prefix string No - Key prefix filter
delimiter string No - Delimiter for hierarchy (typically /)
max_keys integer No 1000 Objects per page (1-1000)
continuation_token string No - Token from the previous page
connection string No first configured S3 connection name

Response Schema (no bucket):

{
  "buckets": [
    {
      "name": "data-lake",
      "creation_date": "2024-01-01T00:00:00Z"
    }
  ],
  "count": 1
}

Response Schema (bucket named):

{
  "bucket": "data-lake",
  "prefix": "sales/orders/",
  "delimiter": "/",
  "objects": [
    {
      "key": "sales/orders/2024/01/data.parquet",
      "size": 52428800,
      "last_modified": "2024-01-15T10:30:00Z",
      "etag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
      "storage_class": "STANDARD"
    }
  ],
  "common_prefixes": ["sales/orders/2024/02/"],
  "count": 1,
  "is_truncated": false
}

s3_object

Act on one object.

Parameters:

Parameter Type Required Default Description
action string Yes - get, metadata, put, copy, delete, presign
bucket string Yes - Bucket name
key string Yes - Object key
content string for put - Content as text, or base64 with is_base64
content_type string No application/octet-stream MIME type (put)
is_base64 boolean No false content is base64-encoded binary (put)
metadata object No - Custom metadata (put; on copy replaces the source's)
dest_bucket string No same bucket Destination bucket (copy)
dest_key string for copy - Destination key (copy)
method string No GET GET or PUT (presign)
expires_in integer No 3600 Seconds until the URL expires, max 604800 (presign)
connection string No first configured S3 connection name

Response Schema (get):

{
  "bucket": "data-lake",
  "key": "config/settings.json",
  "size": 25,
  "content_type": "application/json",
  "last_modified": "2024-01-15T10:30:00Z",
  "etag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
  "content": "{\"setting\": \"value\"}",
  "is_base64": false
}

Note: get is limited by max_get_size; binary content is returned base64-encoded with is_base64: true.

Response Schema (metadata):

{
  "bucket": "data-lake",
  "key": "sales/orders/data.parquet",
  "size": 52428800,
  "content_type": "application/octet-stream",
  "content_length": 52428800,
  "last_modified": "2024-01-15T10:30:00Z",
  "etag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
  "metadata": {
    "created-by": "etl-pipeline"
  }
}

Response Schema (put):

{
  "bucket": "data-lake",
  "key": "uploads/file.json",
  "size": 1024,
  "etag": "\"d41d8cd98f00b204e9800998ecf8427e\""
}

Note: put is limited by max_put_size.

Response Schema (copy):

{
  "source_bucket": "data-lake",
  "source_key": "original/file.json",
  "dest_bucket": "data-lake",
  "dest_key": "backup/file.json",
  "etag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
  "last_modified": "2024-01-15T10:30:00Z"
}

Response Schema (delete):

{
  "bucket": "data-lake",
  "key": "uploads/file.json",
  "deleted": true
}

Response Schema (presign):

{
  "bucket": "data-lake",
  "key": "uploads/file.json",
  "url": "https://bucket.s3.amazonaws.com/key?X-Amz-...",
  "method": "GET",
  "expires_in_seconds": 3600,
  "expires_at": "2024-01-15T11:00:00Z"
}

Read-only refusal: on a connection configured read_only: true, put, copy and delete return an error result:

Error: connection "archive" is read-only: s3_object action "delete" is not permitted on it

Knowledge Tools

For the full governance workflow, see Knowledge Capture.

memory_capture

Record domain knowledge shared during a session (memory toolkit). Available to all personas when the memory layer is enabled (memory defaults on when a database is configured; memory.enabled: false disables capture).

Parameters:

Parameter Type Required Default Description
type string Yes - Sink-class. Live: personal_preference, episodic_event. Reviewed (creates a pending insight): business_knowledge, schema_entity, operational_rule
content string Yes - The knowledge to record (10-4000 characters)
confidence string No medium Confidence level: high, medium, low
entity_urns array No [] DataHub URNs this knowledge relates to (max 10)
related_columns array No [] Columns related to this knowledge (max 20)
suggested_actions array No [] Proposed catalog changes (max 5)

Suggested Action Schema:

Field Type Required Description
action_type string Yes One of: update_description, add_tag, add_glossary_term, flag_quality_issue, add_documentation, add_curated_query
target string Yes Target of the change (entity name, column name, or URL)
detail string Yes Change detail (new description, tag name, term name, query name, etc.)
query_sql string Conditional SQL statement (required for add_curated_query)
query_description string No Optional description for add_curated_query

Response Schema:

{
  "insight_id": "a1b2c3d4e5f67890a1b2c3d4e5f67890",
  "status": "pending",
  "message": "Insight captured. It will be reviewed by a data catalog administrator."
}

apply_knowledge

Review, synthesize, and apply captured insights to the data catalog. Admin-only. Requires knowledge.apply.enabled: true.

Parameters:

Parameter Type Required Description
action string Yes One of: bulk_review, review, synthesize, apply, approve, reject, rollback, list_changesets, bulk_untag
entity_urn string Conditional Required for review, synthesize, apply, list_changesets; optional for rollback (validates the changeset belongs to this entity)
tag_urn string Conditional Required for bulk_untag; the tag (name or urn:li:tag:...) to remove from every entity that carries it
insight_ids array Conditional Required for approve, reject; optional for synthesize, apply
changes array Conditional Required for apply with sink: "datahub"
sink string No Where an apply writes: datahub (default, applies changes to entity_urn), knowledge_page (promotes to a canonical page via page), or agent_instructions (promotes an operating rule into the deployment's own instructions via instructions). The capture-time sink_class is a hint, not a binding
page object Conditional Required for sink: "knowledge_page": {slug, title, summary, body, tags, references, force_new}. Found-or-created by slug, so a repeat promotion consolidates. The response carries portal_url, where the page is read, so the address handed to a person is one the platform composed rather than one the caller guessed
instructions object Conditional Required for sink: "agent_instructions" (see below)
changeset_id string Conditional Required for rollback
confirm bool No Required when require_confirmation is enabled (for apply and rollback)
review_notes string No Notes for approve/reject actions
itemize bool No With bulk_review, also return the pending insights themselves (full insight_text body, captured_by, sink_class, created_at, suggested_actions_count, ...; full suggested_actions omitted, fetch for it), paginated by offset/limit. The insights window is byte-budgeted (page_size_capped: true flags a short page, continue with next_offset) and by_entity is capped (by_entity_truncated: true) so the response stays under the output limit
limit int No Page size for itemized bulk_review (default 20, max 100)
offset int No Page start for itemized bulk_review; pass the previous next_offset to continue

Instructions Schema (for sink: "agent_instructions"):

Field Type Required Description
section string Yes Heading the rule lives under, and the find-or-create key. One line, no #, at most 120 characters
body string Yes The rule text. Over 1,500 bytes it is written to a knowledge page and the section keeps a one-line mcp:knowledge_page:<slug> index entry pointing at it; the response then carries the page's page_id and its portal_url
slug string No Slug for the page a diverted body lands on (default: a slug derived from section)
title string No Title for the page a diverted body lands on (default: section)
summary string No One line saying what reading the page answers; it becomes the index entry the instructions keep
tags array No Tags for the page a diverted body lands on; the origin sink-class is added automatically
references array No Entity references to attach to that page, in the forms page.references accepts

The promotion writes only its own section, leaving every other section byte-identical; records a changeset under target ai:<section> that list_changesets lists and rollback reverts; is refused when it names a tool the deployment does not register; and is refused when it would push the layer past its byte limit. It needs a database-backed config store. See Knowledge.

Change Schema (for apply action):

Field Type Required Description
change_type string Yes One of: update_description, add_tag, remove_tag, add_glossary_term, flag_quality_issue, add_documentation, add_curated_query, set_structured_property, remove_structured_property, raise_incident, resolve_incident, add_context_document, update_context_document, remove_context_document, delete_tag, set_custom_property, remove_custom_property
target string Yes Target of the change (see below)
detail string Yes Change detail (see below)
query_sql string Conditional SQL statement (required for add_curated_query). For update_context_document, the new title
query_description string No Optional description for add_curated_query. For add_context_document/update_context_document, the document category

Target and detail by change type:

Change Type Target Detail
update_description column:<fieldPath> for column-level, empty for entity-level (or the tag URN via entity_urn to fix a tag's own definition) Description text
add_tag / remove_tag Ignored Tag name or URN
add_glossary_term Ignored Term name or URN
flag_quality_issue Ignored Quality issue description (sets the QualityIssue tag; with a detail, also raises a DataHub incident carrying it)
add_documentation URL Link description
add_curated_query Ignored Query name
set_structured_property Property qualified name or URN Value or JSON array
remove_structured_property Property qualified name or URN Removal reason
raise_incident Incident title Description
resolve_incident Incident URN Resolution message
add_context_document Document title Document content
update_context_document Document ID New content
remove_context_document Document ID Ignored
delete_tag Ignored (entity_urn is the tag URN) Ignored
set_custom_property customProperties key Value
remove_custom_property customProperties key Ignored

delete_tag deletes a tag definition entirely and is irreversible; set_custom_property/remove_custom_property edit an entity's legacy customProperties (datasets, dashboards, charts, dataFlows, dataJobs, containers, dataProducts, domains, glossaryTerms, glossaryNodes) and, like structured properties, are recorded but not auto-revertible. A single apply may set multiple properties or remove multiple properties, but not both on the same entity (the shared aspect is written non-atomically); use separate apply calls.

Actions:

Action Description Required Params
bulk_review Counts of all pending insights; pass optional itemize: true (with limit/offset) to enumerate the queue, each with its full insight_text body, captured_by, sink_class, and suggested_actions_count (response bounded per page; page_size_capped/by_entity_truncated flag any cut) None
review Insights for a specific entity with current DataHub metadata entity_urn
approve Transition insights to approved status insight_ids
reject Transition insights to rejected status insight_ids
synthesize Structured change proposals from approved insights entity_urn
apply Write to the sink sink selects, with changeset tracking entity_urn + changes (datahub), page (knowledge_page), or instructions (agent_instructions)
list_changesets List a target's changesets (id, timestamp, actor, change type, rollback status). The target is a catalog URN, kp:<slug> for a page, or ai:<section> for an instruction section entity_urn
rollback Revert a changeset's changes to their before-image changeset_id, confirm
bulk_untag Remove a tag from every entity a catalog search finds carrying it, recording one changeset (not auto-revertible) tag_urn, confirm

rollback reverts the changes an apply made: it removes added tags/glossary terms/documentation links (keeping any that pre-existed in the before-image), restores a changed description, transitions the source insights to rolled_back, and marks the changeset rolled back. It is refused if the changeset is already rolled back, if a newer changeset has since modified the same aspect, or if the changeset touched change types whose prior state was not captured or is irreversible (column descriptions, structured properties, custom properties, incidents, curated queries, context documents, prompts, delete_tag, bulk_untag).

Response Schema (apply):

{
  "changeset_id": "cs_x1y2z3a4b5c6d7e8f9a0b1c2d3e4f5a6",
  "entity_urn": "urn:li:dataset:(urn:li:dataPlatform:trino,hive.sales.orders,PROD)",
  "changes_applied": 2,
  "insights_marked_applied": 1,
  "revertible": true,
  "resulting_state": {
    "description": "Order records with gross margin amounts (before returns)",
    "tags": ["urn:li:tag:gross-margin"],
    "glossary_terms": [],
    "owners": []
  },
  "message": "Changes applied to DataHub. Roll back with action=rollback changeset_id=cs_x1y2z3a4b5c6d7e8f9a0b1c2d3e4f5a6. changes_applied counts requested changes; verify against resulting_state below."
}

revertible is a boolean reflecting whether the changeset can be rolled back automatically (computed with the same all-or-nothing gate rollback enforces; any single unrevertible change makes the whole changeset false, so there is no partial rollback). When it is false, an unrevertible_change_types array names the change types with no before-image and the message states why instead of advertising a rollback. list_changesets entries carry the same two fields (and report false once a changeset has been rolled back).

See Governance Workflow for detailed examples of each action.


Portal Tools

The portal toolkit persists AI-generated assets to S3 with PostgreSQL metadata. Requires portal.enabled: true.

save_asset

Save AI-generated content (JSX dashboard, HTML report, SVG chart, etc.) to the asset portal as a versioned asset. Captures provenance: the calls the asset was built from, read from the audit log at write time. See Provenance.

Parameters:

Parameter Type Required Default Description
name string Yes - Display name (max 255 chars)
content string Yes - Artifact content
content_type string Yes - MIME type: text/html, text/jsx, image/svg+xml, text/markdown, application/json, application/x-ndjson, text/csv, text/tab-separated-values, application/xml, application/yaml, application/sql, text/plain

A generic content_type (text/plain or application/octet-stream) is replaced by the type detected from the content, so a JSON payload saved under a catch-all type still opens in the JSON viewer. A specific declaration is always honored. Detection can only reclassify content into passive families: it never promotes a payload to text/html, text/jsx or image/svg+xml. See Content Types and Viewers. | description | string | No | "" | Description (max 2000 chars) | | tags | array | No | [] | Tags for categorization (max 20 tags, each max 100 chars) | | sources | array | No | session window | The calls this asset was built from, as the call_id (or mcp:call:<id> reference) each query and API invocation returns. Replaces the default window; only the caller's own calls resolve (max 100) |

Response Schema:

{
  "asset_id": "a1b2c3d4e5f67890a1b2c3d4e5f67890",
  "portal_url": "https://portal.example.com/portal/assets/a1b2c3d4e5f67890a1b2c3d4e5f67890",
  "message": "Artifact saved successfully.",
  "provenance_captured": true,
  "calls_recorded": 5
}

Storage layout:

Content is stored in S3 at {s3_prefix}{user_id}/{asset_id}/content.{ext} where the extension is derived from the content type.


manage_asset

List, retrieve, update, delete, or share saved assets. All mutations enforce ownership: users can only modify their own assets.

Parameters:

Parameter Type Required Default Description
action string Yes - One of: list, get, update, delete, search, share, list_shares, revoke_share
asset_id string Conditional - Required for get, update, delete, share, list_shares
content string No - New content (for update — replaces S3 object)
name string No - New name (for update)
description string No - New description (for update)
tags array No - New tags (for update)
content_type string No - New content type (for update, only when replacing content)
sources array No session window The calls behind a content edit (update, patch), as call_id values or mcp:call:<id> references. Recorded as a new capture alongside the ones earlier versions carry
query string Conditional - Free-text relevance query (required for search)
limit integer No 50 Max results for list (max 200); ranked search defaults to 20 (max 100)
recipient string No - Who to share with (share): an email address, or a name resolved against the known-users directory. Omit for a link share
permission string No viewer viewer or editor (share). A link share is always viewer
access_mode string No authenticated Who a link share admits (share, no recipient): authenticated or public
expires_in string Conditional - Duration bounding a public link (24h). Required for access_mode: public, refused for every other share
share_id string Conditional - Share to end (required for revoke_share)

Actions:

Action Description Required Params
list Show current user's assets None
get Retrieve full asset metadata asset_id
update Change metadata or replace content asset_id
delete Soft-delete an asset asset_id
search Rank the caller's own assets by relevance to query (hybrid vector + lexical, lexical-only fallback). Returns each match with a score plus a ranking field; scoped server-side to the caller's own assets by owner_id (the library's ownership key) and fails closed without an identity. query
share Grant access to an asset you own. With recipient: a restricted share addressed to that person, who is emailed the link. Without: a link, authenticated by default. Owner (or admin) only; anonymous callers refused. asset_id
list_shares The shares that currently grant access to the asset — revoked and expired ones excluded — with recipient, permission, access mode, view URL, and access count asset_id
revoke_share End one share by ID. Its token stops opening the asset immediately share_id

Registering an asset as a queryable table is the separate manage_table tool, which serves an uploaded resource through the same action.

Response Schema (list):

{
  "assets": [
    {
      "id": "a1b2c3d4e5f67890a1b2c3d4e5f67890",
      "owner_id": "[email protected]",
      "name": "Revenue Dashboard",
      "description": "Monthly revenue breakdown",
      "content_type": "text/html",
      "s3_bucket": "portal-assets",
      "s3_key": "artifacts/user/asset-id/content.html",
      "size_bytes": 4096,
      "tags": ["dashboard", "revenue"],
      "provenance": {
        "user_id": "[email protected]",
        "session_id": "dps_abc123",
        "captures": [
          {
            "tool": "save_asset",
            "captured_at": "2026-01-15T10:05:00Z",
            "version": 1,
            "session_id": "dps_abc123",
            "event_ids": ["8kQ2f1uVQ2S1p0aT4Hn2Zw"],
            "calls": [
              {
                "event_id": "8kQ2f1uVQ2S1p0aT4Hn2Zw",
                "kind": "sql",
                "tool": "trino_query",
                "connection": "warehouse",
                "statement": "SELECT region, revenue FROM sales.quarterly",
                "purpose": "Totalling Q4 revenue by region for the board deck.",
                "outcome": "success",
                "duration_ms": 1840,
                "timestamp": "2026-01-15T10:00:00Z"
              }
            ]
          }
        ]
      },
      "created_at": "2024-01-15T10:05:00Z",
      "updated_at": "2024-01-15T10:05:00Z"
    }
  ],
  "total": 1
}

Response Schema (update/delete):

{
  "asset_id": "a1b2c3d4e5f67890a1b2c3d4e5f67890",
  "message": "Asset updated successfully."
}

Error Codes:

Condition Error Message
Missing asset_id asset_id is required for {action} action
Asset not found asset not found: ...
Wrong owner you can only {action} your own assets
Invalid action invalid action "...": must be one of: list, get, update, delete

manage_table

Make a stored CSV, JSON-lines or Parquet file readable as a query-engine table over the directory the file already sits in, so trino_query can join it to warehouse tables. Nothing is copied or ingested. A CSV's columns are all VARCHAR; a JSON-lines file's are typed from its values and a Parquet file's are the types its footer declares (Registered Tables).

The file is named by its reference, the string a search hit and a fetch document carry, so one action serves every kind of stored file and no argument names the kind.

Parameters:

Parameter Type Required Default Description
action string Yes - One of: register, list, unregister
reference string Conditional - The file: mcp:resource:<id> (uploaded reference material) or mcp:asset:<id> (saved asset). Required for register and list
connection string Conditional - Trino connection whose scratch schema holds the table (required for register)
table_name string No filename slug Name for the registered table; persona-prefixed either way
follow boolean No true Whether the table follows the file: each revision or version written over it moves the table onto the new contents. false pins the table to the version it is registered over
repair boolean No false Save a corrected version of a file that cannot be read as a table the way it is stored, and register that. The choice is kept on the registration: a following table corrects each later version carrying the same kind of defect and moves onto the corrected version
registration_id string Conditional - Registration to drop (required for unregister), read off a row of table_registrations

Actions:

Action Description Required Params
register Create an external table over the file. The response carries each column's declared type in column_types, and a sample statement: for a CSV, whose columns are all VARCHAR, it shows the CAST a typed join needs reference, connection
list The registrations over this file, under table_registrations: each with its registration_id, columns and column_types, whether it follows the file (follow), whether the file has moved on since (stale), and why a following table is behind (follow_error). Check here before registering: registering the same name again replaces the row and changes its id reference
unregister Drop one registered table. The file itself is unchanged registration_id

Response Schema (register):

{
  "reference": "mcp:resource:res_01HK7R9F",
  "registration_id": "reg_9f2c1d4b8a3e5602",
  "connection": "scratch",
  "query_table": "scratch.uploads.analyst_vendor_keys",
  "columns": ["store_id", "vendor_code", "rebate_pct"],
  "column_types": [
    {"name": "store_id", "type": "VARCHAR"},
    {"name": "vendor_code", "type": "VARCHAR"},
    {"name": "rebate_pct", "type": "VARCHAR"}
  ],
  "sample_sql": "SELECT ... CAST(u.store_id AS integer) ...",
  "registered_by": "[email protected]",
  "stale": false,
  "follow": true,
  "format": "csv",
  "message": "Registered as scratch.uploads.analyst_vendor_keys on connection scratch. Every column of a CSV is VARCHAR, so a join to a typed column needs a CAST. The table follows the file: each revision or version written moves it onto the new contents. Register with follow=false for a table pinned to this version."
}

Response Schema (list):

{
  "reference": "mcp:resource:res_01HK7R9F",
  "table_registrations": [
    {
      "registration_id": "reg_9f2c1d4b8a3e5602",
      "connection": "scratch",
      "query_table": "scratch.uploads.analyst_vendor_keys",
      "columns": ["store_id", "vendor_code", "rebate_pct"],
      "sample_sql": "SELECT ... CAST(u.store_id AS integer) ...",
      "registered_by": "[email protected]",
      "stale": false,
      "follow": true,
      "repair": false
    }
  ],
  "total": 1
}

The same registrations reach a search hit and a fetch document under tables, projected for the caller who only wants to write the query rather than change a registration. table_changes, which a content write reports, is neither: it is one sentence per table saying what that write did to it. See Registered Tables.

Error Codes:

Condition Error Message
Missing reference reference is required for {action}: pass the mcp:resource: or mcp:asset: reference from a search hit, verbatim.
Missing connection connection is required for register: ...
Missing registration_id registration_id is required for unregister: ...
Not a stored-file reference reference "..." is not a reference to a stored file: ...
Missing, deleted, or not yours that reference names no stored file you can register
No signed-in identity Registering a table needs a signed-in identity. ...
Deployment cannot register This deployment cannot register tables: it needs a Trino connection with a scratch catalog and schema configured. ...

Who may register is authority to change the file, not to read it: an asset by its owner or an administrator, a resource by its uploader or an administrator of its scope. See Registered Tables.


manage_resource

Manage a file in the managed resource library. A managed resource is the only kind of file a saved asset can reference, so this is what makes the data half of a referencing asset refreshable by the platform rather than only by a person at an upload form: a scheduled script rewrites the CSV a dashboard reads, and the dashboard is not touched.

Two of the five actions write content, and three round out the lifecycle around them: get answers what is filed at an address, list reports a folder, and delete removes a file. Without them a caller had to remember the id of everything it ever wrote, because create refuses an address that is already taken, replace_content needs a reference, and search is relevance-ranked rather than a lookup.

Content crosses the wire in one of two fields. content carries text — CSV, JSON, Markdown, SVG — and content_base64 carries base64-encoded bytes for a binary file such as a PNG or a PDF. Exactly one is given, and both are capped at the deployment's portal.max_content_size (10 MB by default), the same cap save_asset applies.

A create declares what the bytes are in content_type; a create that does not is refused. The type is not detected on this path, because the families an agent writes most cannot be named from content: SVG, HTML, JSX and Markdown are all stored text/plain when nothing is declared, and text/plain under nosniff is a broken image or an unrendered document wherever an asset references the file. A replace_content keeps the type the resource already carries unless it declares a new one, so a refresh cannot reclassify a file under every reference to it. The types the platform stores are listed on the built-in knowledge page mcp:knowledge_page:platform-content-types-for-stored-files and in Content Types and Viewers.

Parameters:

Parameter Type Required Default Description
action string Yes - One of: create, replace_content, get, list, delete
reference string Conditional - The resource to act on: mcp:resource:<id>, from a search hit, a fetch document, or a create. Required for replace_content; for get and delete it is the alternative to naming the address
if_exists string No fail What a create does when the address already holds a file: fail refuses it, replace records the next version of what is there
force boolean No false Delete a file even though something still points at it
limit integer No 100 How many files one page of a list holds; 100 is also the largest page
offset integer No 0 How many files to skip before a list's page
content string Conditional - The file as text. This or content_base64
content_base64 string Conditional - The file as base64-encoded bytes. This or content
content_type string Conditional - Media type the bytes are (required for create). Not detected: SVG, HTML, JSX and Markdown all read as plain text to a byte sniffer. replace_content keeps the type the resource already carries when it is omitted; a file stored under a generic type is re-detected from its bytes
filename string Conditional - Name of the file, normalized to lowercase with spaces replaced. Required for create, and for a get or delete that names the address. replace_content ignores it
display_name string Conditional - Name shown in the resource library (required for create)
path string Conditional - The folder path the file is filed under inside its library, for example datasets or datasets/media-manager/shows. Required for create, and for a get or delete that names the address; for list it is the folder the listing is rooted at, and everything beneath it is included. Slash-separated; each folder name is lowercase letters, digits and hyphens starting with a letter or digit, at most 31 characters; at most 8 folders deep and 200 characters overall
description string Conditional - What the file is and what reads it (required for create)
tags string[] No [] Tags for filtering in the library
scope string No user user, persona, or global. For list, omitting it means every library you can see rather than only your own
scope_id string No you Persona name for scope=persona; must be empty for scope=global
change_summary string No Content replaced via manage_resource Why the content changed, shown in the version history beside the revision

Actions:

Action Description Required Params
create File new content as a managed resource and report its mcp:// URI and its mcp:resource: reference. With if_exists=replace it records the next version of whatever is already at that address instead of refusing filename, display_name, path, description, content_type, content
replace_content Write new content over an existing resource, keeping its id, URI and filename, and record the change as its next version reference, content
get Report what is filed at an address, or what a reference names, without the bytes. Nothing there is found: false rather than an error, on either branch path + filename, or reference
list Report the files under a folder, newest first, with the total the page was cut from -
delete Remove a file and its version history, refusing while something still points at it path + filename, or reference

Response Schema (create):

{
  "resource_id": "a1b2c3d4e5f67890a1b2c3d4e5f67890",
  "reference": "mcp:resource:a1b2c3d4e5f67890a1b2c3d4e5f67890",
  "uri": "mcp://user/550e8400-e29b-41d4-a716-446655440000/datasets/weather-daily.csv",
  "filename": "weather-daily.csv",
  "display_name": "Daily Weather",
  "scope": "user",
  "scope_id": "550e8400-e29b-41d4-a716-446655440000",
  "category": "datasets",
  "content_type": "text/csv",
  "size_bytes": 2481,
  "message": "Created. Reference it from an asset by passing the uri above in save_asset's 'references', ..."
}

get returns found, what it looked up -- the uri for an address, the reference for a reference, each reported whether or not anything answers it -- and a resource record carrying everything a fetch of the reference would except the bytes. Finding nothing is an answer and not a failure on both branches: an empty address and a reference naming a file that is gone or outside what the caller can see are each found: false, the same way fetch answers a dangling reference, so a script deciding between replace_content and create reads a deleted file as absent rather than as a failed call. A read that failed stays a tool error, because creating on a failed read files a second copy of a file that is already there. list returns path, resources as that same record shape, total and offset. delete returns deleted, and on a refusal the holds counts and the table_registrations that stopped it, in the shape manage_table action=list reports.

replace_content returns the same shape plus version, the number the content was recorded as, and table_changes when a table is registered over the file: one sentence per table, saying it followed onto the new version (scratch.uploads.analyst_stores on scratch now reads version 7.) or is pinned and now behind it, with the same sentences appended to message. A create reports no version: it records version 1 only where the deployment keeps a version trail, and a number the history may not hold is worse than none. See Following the file.

Error Codes:

Condition Error Message
Missing content type on a create content_type is required for create: name the media type the bytes are ...
Missing content content is required: pass the file as text in 'content', or as base64-encoded bytes in 'content_base64' ...
Both content fields pass content or content_base64, not both: ...
Malformed base64 content_base64 is not valid base64: ...
Over the size cap content size N exceeds maximum M bytes. A file this large has to be uploaded through the portal's resource library ...
Scope refused you cannot write to the global scope, which is administrators only: managed-resource write refused
Missing reference reference is required for replace_content: pass the mcp:resource:<id> reference ...
Reference of another kind reference "..." names a target of type "asset", not "resource". ...
Missing, deleted, or not visible (replace_content and delete; get answers found: false instead) there is no managed resource "<id>" you can see: no such managed resource
No version trail this deployment keeps no version history for managed resources, so content cannot be replaced: managed-resource write unavailable
No signed-in identity Writing a managed resource needs a signed-in identity. ...
No managed-resource layer This deployment has no managed-resource library to write to: ... Nothing was saved.
Neither a reference nor an address name the file to act on: either its mcp:resource:<id> reference, or the address it is filed at as scope, path and filename. Nothing was changed
An if_exists the tool does not take if_exists "overwrite" is not one this tool takes: pass "fail" ... or "replace" ...
Delete refused by what points at the file Not deleted: 2 assets reference this file, 1 prompt attaches this file. ... call this again with force=true to delete it and break them.
Delete whose dependency check failed could not establish what depends on this file, so it was not deleted. Try again, or pass force=true ...
Delete refused by scope you cannot delete a file in the "analyst" persona scope, which is that persona's administrators only: managed-resource write refused

Creating is scope authority — your own user scope, a persona you administer, or the global scope as a platform administrator — and a refusal names the scope rather than the file, because where it was filed is what the caller has to change. Replacing and deleting are the authority to change that file: its uploader, or an administrator of its scope. Deleting is not a stronger authority than overwriting, because a replacement already leaves nothing of the previous content beyond the version trail a delete takes with it.

A delete is refused while an asset references the file, a prompt attaches it, or a query-engine table is registered over it. Neither of the first two is a foreign key — deleting the file deliberately leaves the row behind so the thing that depended on it reports the material as missing — so nothing in the database stops the delete and the tool does. A knowledge page is not among them: the platform refuses an mcp:resource: citation on a shared page, because a resource is visibility-scoped, so no page can point at one. The refusal counts each kind rather than naming them, because each of those records carries an audience the caller is not necessarily in; the portal's Used-by panel on the file resolves those audiences. The tables are reported as table_registrations, the rows manage_table action=list answers with, because manage_table already names them to the same caller. force: true deletes anyway and drops any table over the file. A resource you cannot see is answered as absent, whether it is missing, deleted, or somebody else's. A managed-script run is judged as the person it acts for: it authenticates as a principal that owns no file, so a create with no scope named files into its version author's library and a replacement reaches what that person uploaded (Script security). See Asset References.