SLayer
SLayer is an expressive, embeddable semantic layer for AI agents and humans that lets you query governed data and metrics with plain formulas, searchable metadata, and full model/datasource management via MCP/API tools.
Semantic querying – Run analytical queries against models with measures, dimensions, time groupings, filters, sorting, and SQL/dry-run/explain modes.
Advanced formula transforms – Use aggregations like
sum,avg,count, arithmetic ratios, cumulative sums (cumsum), period-over-period change (change,change_pct), time shifts, lag/lead, last, and rank.Multi-stage queries – Build DAGs where stages reference sibling query outputs for complex, dependency-driven analytics.
Search and discovery –
searchover memories, entity tags, and full text, plusinspectfor individual or batch model/column/measure/datasource details.Memory store – Save, link, search, and delete agent learnings with entity associations for reusable business context.
Semantic model creation/editing – Create models from tables or queries, edit columns/measures/aggregations/joins/filters, delete models, and manage model metadata.
Datasource management – Create, list, describe, edit, delete, and prioritize database connections (Postgres, MySQL, SQLite, BigQuery, Snowflake, etc.).
Auto-ingestion and validation – Discover database tables/schemas, idempotently build models, and diff stored models against live schemas for needed deletions.
Query assistance – Recommend root models and join-qualified paths for requested columns/metrics, including partial coverage for multi-stage splits.
Extensibility – Support inline model extensions, ad-hoc models, saved measures, variables, strict mode, whole-period snapping, and configurable output formats (markdown, JSON, CSV).
Provides semantic layer capabilities for ClickHouse databases, allowing AI agents to query data using measures, dimensions, and filters instead of writing SQL directly, with automatic SQL compilation and dialect handling.
Integrates with dbt's semantic layer concepts and is positioned as an alternative to raw SQL generation by AI agents, with references to dbt's benchmark analysis comparing semantic layers to text-to-SQL approaches.
Provides semantic layer capabilities for MySQL databases, allowing AI agents to query data using measures, dimensions, and filters instead of writing SQL directly, with automatic SQL compilation and dialect handling.
Provides semantic layer capabilities for SQLite databases, allowing AI agents to query data using measures, dimensions, and filters instead of writing SQL directly, with automatic SQL compilation and dialect handling.
SLayer
An expressive, embeddable semantic layer for AI agents and humans.
SLayer enables AI-powered data analytics on top of your warehouse. Agents get a governed, shared surface through which they access your data and metrics and give you reliable answers.
SLayer handles database connectivity (read-only), SQL translation, common data transformations, and row-level security, so LLMs and humans don't have to. Adapt it to your workflows, not the other way around. Manage definitions easily with an agent or by yourself.
SLayer can be used as a standalone tool or imported as a Python library, easily embeddable into any Python app. Use it for powering analytical MCP servers or APIs or simply to query databases semantically.
SLayer is at the core of Motley and is maintained with ♡ by the same team.
How SLayer is different
Traditionally, semantic layers were a part of the BI stack, where every metric and its aggregation had to be predefined. Agents need more flexibility because users ask questions that involve metric combinations (like ratios), transforms (like time shifts), or different aggregations of the same metric (like average instead of sum).
SLayer allows to define a column revenue once and query it using expressions like revenue:sum, revenue:avg, revenue:sum / *:count, time_shift(revenue:sum, -1, 'year') etc.; multi-stage queries are also supported.
SLayer is focused on the common agentic search → inspect → query flow. It has a search tool for efficient discovery and a memory store for linking the relevant business context.
Agents, apps and humans can talk to SLayer via MCP, REST API, CLI, Python, Flight SQL, or Postgres-based SQL API. SLayer supports most popular databases.
SLayer fits next to your existing data stack. It also provides importers for dbt, Cube, and Ossie configs.
See docs for more.
Example
Question (run on the built-in demo Jaffle Shop database): "show monthly revenue by store, with month-over-month % change"
Side by side, here's LLM-generated SQL and the equivalent SLayer query.
Related MCP server: Foggy Data MCP Bridge
Quickstart
We recommend using uv, especially if you don't work in a Python project.
uv tool install 'motley-slayer[all]'If slayer isn't found on PATH afterwards, run uv tool update-shell and reopen your terminal.
Using demo dataset
# With the Jaffle Shop demo preloaded (zero-config quickstart)
claude mcp add slayer_demo -- slayer mcp --demoUsing your own data
Set up your datasource, substituting the correct database, username, hostname, and db_name.
slayer datasources create 'postgresql://user:${DB_PASSWORD}@hostname/db_name'The password will be read by SLayer at init time, not saved to disk nor exposed to Claude.
Then add SLayer to Claude Code:
claude mcp add slayer -- slayer mcp --ingest-on-startupNow SLayer MCP will be visible in Claude Code next time you start it. Make sure to launch Claude Code from a shell where DB_PASSWORD is exported — the MCP subprocess inherits its environment from the launching process.
Read more on how to get started with MCP, CLI, REST API, Python in the docs.
License
MIT
Available Tools
20 toolscreate_datasourceA
Create a database connection, verify it, and auto-ingest models. Use ${ENV_VAR} syntax in credentials to reference environment variables.
Args: name: Unique datasource name. type: Database type — postgres, mysql, sqlite, bigquery, or snowflake. host: Database host (default: localhost). port: Database port (e.g. 5432 for Postgres). database: Database name. username: Database username. password: Database password. connection_string: Full connection string as alternative to individual fields. schema_name: Default schema name. Also used as the single schema for auto-ingestion. schemas: Comma-separated schemas to ingest. Mutually exclusive with schema_name / all_schemas. all_schemas: Ingest every non-system schema. Mutually exclusive with schema_name / schemas. auto_ingest: Automatically ingest models from the database schema (default: true). Set to false to skip.
Example: create_datasource(name="mydb", type="postgres", host="localhost", port=5432, database="app", username="user", password="pass")
| Name | Required | Description | Default |
|---|---|---|---|
| host | No | ||
| name | Yes | ||
| port | No | ||
| type | Yes | ||
| schemas | No | ||
| database | No | ||
| password | No | ||
| username | No | ||
| all_schemas | No | ||
| auto_ingest | No | ||
| schema_name | No | ||
| connection_string | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden. It discloses key side effects: verification of the connection, auto-ingestion of models, environment variable substitution in credentials, and defaults like auto_ingest. It stops short of explaining failure behavior, whether an existing datasource is overwritten, or how connection_string interacts with individual credentials.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose is front-loaded in the first two sentences, followed by a compact Arg list and a useful invocation example. Every element earns its place: the summary, parameter explanations, constraints, defaults, and example. There is no redundant or filler content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, the description is nearly complete: all parameters are explained, constraints are noted, and a concrete example is provided. It does not need to explain return values because an output schema exists. Minor gaps remain around credential precedence when connection_string is used alongside individual fields and the exact behavior when auto_ingest is false.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, but the description compensates fully by documenting all 12 parameters with additional meaning: supported type values, defaults, an example port, schema mutual exclusivity, and connection_string as an alternative. This goes well beyond the bare input schema and gives an agent what it needs to construct valid arguments.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific action and resource: 'Create a database connection, verify it, and auto-ingest models.' This clearly distinguishes it from sibling tools like list_datasources, edit_datasource, and ingest_datasource_models. It also lists supported database types, making the tool's scope unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly conveys when to use the tool: when creating a datasource and optionally ingesting models. It also provides parameter-level usage guidance, such as connection_string being an alternative to individual fields and schemas/schema_name/all_schemas being mutually exclusive. However, it does not explicitly name alternative tools or state when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_modelA
Create a new semantic model, either from a database table or from a query.
Host a column/measure on the model whose row grain is 1:1 with what
it describes — not merely one where its input columns live. Choose
join keys by column Description (author intent); on ties take the
shortest declared join path (long chains through lookup/log tables
fan out rows). Encode definitions in dependency order, referencing
already-defined entities by name rather than re-deriving them inline;
in row-level SQL parenthesise weighted sums in comparisons
((a*w1 + b*w2) > t).
From a table or sql query (provide sql_table or sql): create_model(name="orders", sql_table="public.orders", data_source="mydb", columns=[...], measures=[...])
From a query (provide query): create_model(name="monthly_summary", query={"source_model": "orders", "measures": ["count(*)", "sum(amount)"], "time_dimensions": [{"dimension": "created_at", "granularity": "month"}]}) Columns are auto-introspected from the query result.
Args:
name: Unique model name (lowercase, underscores).
sql_table: Database table name, e.g. "public.orders".
sql: Alternative to sql_table — a custom SQL expression for the model's source.
data_source: Name of the datasource (from list_datasources).
description: What this model represents.
columns: List of column definitions. Each: {"name": "col", "sql": "col", "type": "string"}.
Types: string, number, time, date, boolean. Optional fields: primary_key,
unique (single-column uniqueness that is not the PK; primary_key
already implies it), allowed_aggregations (whitelist), filter
(CASE WHEN inside aggregation), label, description, hidden,
meta.
measures: List of named formula definitions on the model. Each:
{"name": "aov", "formula": "sum(revenue) / count(*)", "label": "...",
"description": "...", "meta": {...}}.
Queries can reference these by bare name (e.g. {"formula": "aov"}).
meta is an optional opaque dict for caller bookkeeping
(e.g. linking the formula back to a source identifier).
query: A SLayer query dict (or list of stage dicts for a multi-stage backing
query). When provided, the query is saved as the model's source_queries
and the model becomes query-backed. Mutually exclusive with sql_table, sql,
columns, and measures.
variables: Default values for {var} placeholders in the backing query.
Saved as query_variables on the model. Only meaningful when query
is provided.
| Name | Required | Description | Default |
|---|---|---|---|
| sql | No | ||
| name | Yes | ||
| query | No | ||
| columns | No | ||
| measures | No | ||
| sql_table | No | ||
| variables | No | ||
| data_source | No | ||
| description | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It discloses that query becomes source_queries, that query is mutually exclusive with other source parameters, and that columns are auto-introspected. It also provides deep guidance on row grain and join key selection, which is exceptionally transparent for a creation tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but well-organized, with examples and a structured parameter list. It front-loads the core purpose and then elaborates. The opening paragraph on row grain is dense but directly relevant to correct usage, so it earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (9 params, two modes, mutual exclusivity), the description covers all necessary details: examples, parameter semantics, auto-introspection, and mutual exclusivity. An output schema exists, so return values are not needed. Nothing essential is missing for an agent to call this correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate fully. It does so by detailing every parameter: name conventions, types for columns and measures, mutual exclusivity, and the role of variables. Examples for both modes make the semantics concrete and actionable.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a precise statement: 'Create a new semantic model, either from a database table or from a query.' It immediately distinguishes two modes and provides concrete examples, making the tool's purpose unambiguous and clearly separated from siblings like edit_model and delete_model.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives two usage modes with examples and notes that data_source comes from list_datasources, implying a prerequisite. However, it does not explicitly state when not to use this tool or contrast it with edit_model, though the purpose makes the primary usage clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_datasourceB
Delete a datasource configuration.
Args: name: Datasource name to delete.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden of behavioral disclosure. It states the deletion action but does not mention whether deletion is irreversible, whether it only removes configuration or also underlying data, whether it cascades to dependent models, or what happens if the datasource name does not exist. For a destructive tool, this is a meaningful gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is short and front-loaded with the core purpose. The Args section is brief but repeats what the schema already structurally conveys. It earns its place by adding parameter context, though it could be slightly tighter.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple one-parameter tool this is close to minimally viable, but the destructive nature of the operation requires more context. The description does not explain the scope of deletion, reversibility, side effects, or error behavior. Without annotations, an agent cannot fully assess the consequences of invoking this tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate for the bare 'name' property in the schema. The Args section clarifies that the name is the datasource name to delete, which provides actionable meaning beyond the schema's generic 'Title: Name'. It fully covers the single required parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Delete a datasource configuration.' This clearly distinguishes it from sibling tools like create_datasource, edit_datasource, and delete_model. The agent knows exactly what action this tool performs.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives no guidance on when to use this tool versus alternatives, such as edit_datasource or delete_model. There are no explicit conditions, prerequisites, or warnings about when deletion is appropriate. Usage is only implied by the tool's name and one-line description.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_modelA
Delete a semantic model.
Args: name: Model name to delete. data_source: Datasource the model belongs to. Required when the same name exists in multiple datasources (otherwise the priority list / single-match rules apply).
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | ||
| data_source | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full responsibility for disclosing behavioral traits. It states that a semantic model is deleted, but it does not mention whether deletion is irreversible, whether it cascades to dependent objects, whether permissions are required, or what side effects may occur. This is a significant gap for a destructive operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose is front-loaded in a single clear sentence, and the Args section adds only relevant parameter details. There is no filler or redundant wording; every line supports correct invocation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the tool's purpose and both parameters, and an output schema exists so return-value details need not be explained. However, for a destructive tool with no annotations, the absence of behavioral consequences and the vague priority-list reference leave the description incomplete for an agent that needs to invoke it safely in edge cases.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, and it does. It explains that `name` identifies the model to delete and that `data_source` serves as a disambiguator when the same name exists in multiple datasources. The reference to priority list / single-match rules adds meaning, though those rules are not fully defined.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb and resource ('Delete a semantic model'), making it immediately clear what the tool does. It naturally distinguishes this from sibling tools like delete_datasource and create_model/edit_model, so there is no ambiguity about its core operation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides useful conditional guidance for when `data_source` is required, but it does not explicitly address when to use delete_model versus alternatives such as delete_datasource. The intended usage is implied by the tool name and first sentence rather than backed by clear selection rules.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
describe_datasourceA
Show datasource details: connection status, available schemas, and (by default) the tables in the given or default schema.
Use this after create_datasource to verify the connection and explore what's queryable before calling ingest_datasource_models.
Args:
name: Datasource name (from list_datasources).
list_tables: If True (default), append a list of tables from the
schema named by schema_name (or the dialect's default
schema when empty).
schema_name: Database schema to list tables from (e.g. "public").
Empty uses the dialect default. Ignored when list_tables=False.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | ||
| list_tables | No | ||
| schema_name | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It accurately explains the default behavior (tables are included by default), the logic for list_tables, and the handling of schema_name including the edge case 'Ignored when list_tables=False.' This goes beyond the schema and gives the agent meaningful behavioral expectations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured: a one-sentence summary, a one-sentence usage context, and a concise Args list. Every sentence adds necessary information, and the most important scoping detail (verify before ingest) is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a describe tool with an output schema, the description covers all needed call context: what it returns at a high level, how to use it in the datasource workflow, and complete parameter semantics. The presence of an output schema lessens the need to document return values in prose, so nothing critical is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must fully compensate for the bare parameter titles. It does: 'name' is sourced from list_datasources, 'list_tables' is explained with its default and effect, and 'schema_name' gets an example ('public'), its default behavior, and its interaction with list_tables. All three parameters are richly documented.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Show datasource details: connection status, available schemas, and (by default) the tables.' It clearly distinguishes this from sibling tools like list_datasources and ingest_datasource_models by framing it as the verification/exploration step between them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use the tool: 'Use this after create_datasource to verify the connection and explore what's queryable before calling ingest_datasource_models.' This gives an agent a clear workflow position and eliminates ambiguity about where this tool fits among siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
edit_datasourceB
Update a datasource's metadata.
Args: name: Datasource name to update. description: New description for the datasource.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | ||
| description | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden of behavioral disclosure. It only says 'Update' and does not explain what happens when 'description' is omitted (leave unchanged vs clear to null), whether the datasource must already exist, or what side effects occur. This ambiguity is significant because the schema gives 'description' a default of null.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is short, front-loaded with the purpose, and includes only a compact Args list that adds necessary parameter semantics. There is no filler or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple two-parameter tool with an output schema, the description is mostly adequate. However, it lacks guidance on optional parameter behavior, error cases, and when to prefer this tool over related datasource/model tools, leaving minor but real gaps for an agent deciding how to invoke it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must define the parameters. It does add meaning: 'Datasource name to update' clarifies that 'name' is an identifier, and 'New description for the datasource' clarifies the intended value. It stops short of explaining null/omission semantics, but still compensates for the bare schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action ('Update') on a resource ('a datasource's metadata') and the Args section clarifies that 'name' identifies the target while 'description' supplies the new value. It is clear enough to distinguish from create/delete/describe datasource tools, though 'metadata' is somewhat broad.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied: use this when you want to update an existing datasource's metadata. However, there is no explicit when-to-use vs alternatives, no mention of when not to use it, and no comparison to sibling tools like edit_model or delete_datasource.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
edit_modelA
Edit an existing model in a single call — update metadata, upsert columns/measures/aggregations/joins, manage filters, and remove entities.
Host a column/measure on the model whose row grain is 1:1 with what
it describes — not merely one where its input columns live. Choose
join keys by column Description (author intent); on ties take the
shortest declared join path (long chains through lookup/log tables
fan out rows). Encode definitions in dependency order, referencing
already-defined entities by name rather than re-deriving them inline;
in row-level SQL parenthesise weighted sums in comparisons
((a*w1 + b*w2) > t).
Args:
model_name: Name of the model to edit.
description: New model description.
data_source: Lookup key — the datasource the model belongs to.
Required when the same name exists in multiple datasources
(otherwise the priority list / single-match rules apply).
new_data_source: Move the model to a different datasource (rare;
renames its storage location). Pass None (default) to
leave the data_source unchanged.
default_time_dimension: Default time dimension (a column of type date/time) for
time-dependent transforms.
sql_table: Database table name. Setting this clears sql and source_queries.
sql: Custom SQL expression for the model source. Setting this clears sql_table and source_queries.
source_queries: Replace the model's backing query with this list of stages.
Each stage is a SlayerQuery dict; non-final stages must have a name.
Setting this clears sql_table and sql, makes the model query-backed,
and refreshes the cached columns and backing_query_sql.
query_variables: Replace the model's default {var} placeholder values for
its backing query. Pass null/None to clear. Only meaningful for
query-backed models.
hidden: Whether this model is hidden from discovery.
meta: Arbitrary JSON metadata for the model (replaces existing meta). Pass null/None to clear.
columns: Columns to create or update (upsert by name). Each dict:
{"name": "col", "type": "string", "sql": "col", "description": "...",
"primary_key": false, "unique": false, "hidden": false,
"allowed_aggregations": ["sum", "avg"],
"filter": "status = 'active'", "label": "..."}.
If a column with this name exists, only the provided fields are updated.
Types: string, number, time, date, boolean.
unique marks single-column uniqueness that is not the primary key
(primary_key already implies it); it is used to infer join
cardinality.
measures: Named formula measures to create or update (upsert by name). Each dict:
{"name": "aov", "formula": "sum(revenue) / count(*)", "label": "...",
"description": "...", "meta": {...}}.
Queries can reference these by bare name (e.g. {"formula": "aov"}).
meta is an optional opaque dict for caller bookkeeping.
aggregations: Aggregations to create or update (upsert by name). Each dict:
{"name": "weighted_avg", "formula": "SUM({value} * {weight}) / NULLIF(SUM({weight}), 0)",
"params": [{"name": "weight", "sql": "quantity"}], "description": "...",
"meta": {...}}.
meta is an optional opaque dict for caller bookkeeping.
joins: Joins to create or update (upsert by target_model). Each dict:
{"target_model": "customers", "join_pairs": [["customer_id", "id"]],
"cardinality": "many_to_one", "description": "...", "meta": {...}}.
A composite key is one join with several join_pairs entries, not
one join per column. cardinality is the join's arity read
source->target, one of one_to_one / one_to_many /
many_to_one / many_to_many; omit it when undetermined. It is
descriptive metadata only — it changes neither join_type nor
query results.
add_filters: SQL filter strings to add (e.g. ["deleted_at IS NULL"]). Duplicates ignored.
remove_filters: SQL filter strings to remove (exact match).
remove: Named entities to delete, keyed by type:
{"columns": ["col_name"], "measures": ["measure_name"],
"aggregations": ["agg_name"], "joins": ["target_model_name"]}.
Removals are processed before upserts.
Example — update a column and add a named measure: edit_model(model_name="orders", columns=[{"name": "status", "type": "string"}], measures=[{"name": "aov", "formula": "sum(revenue) / count(*)"}]) Example — remove a measure: edit_model(model_name="orders", remove={"measures": ["old_metric"]})
| Name | Required | Description | Default |
|---|---|---|---|
| sql | No | ||
| meta | No | ||
| joins | No | ||
| hidden | No | ||
| remove | No | ||
| columns | No | ||
| measures | No | ||
| sql_table | No | ||
| model_name | Yes | ||
| add_filters | No | ||
| data_source | No | ||
| description | No | ||
| aggregations | No | ||
| remove_filters | No | ||
| source_queries | No | ||
| new_data_source | No | ||
| query_variables | No | ||
| default_time_dimension | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral burden, and it does so thoroughly. It discloses mutation semantics, upsert-by-name behavior, mutual clearing between sql_table/sql/source_queries, refresh of cached columns, removal-before-upsert ordering, and the fact that join cardinality is descriptive metadata only.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but earned: it covers 18 parameters, complex side effects, design guidance, and two examples in an organized, front-loaded structure. The purpose statement and key behavioral principles come first, followed by an Args list and examples.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex 18-parameter mutation tool with zero schema descriptions and no annotations, the description is exceptionally complete. It covers parameter semantics, ordering, side effects, disambiguation rules, design principles, and usage examples; the presence of an output schema means return-value documentation is not required.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must fully compensate. Every parameter is explained with real meaning: lookup rules for data_source, side effects for new_data_source, structure examples for columns/measures/aggregations/joins, and the exact clearing interactions among sql/sql_table/source_queries.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource, 'Edit an existing model in a single call,' and enumerates the full set of operations: update metadata, upsert columns/measures/aggregations/joins, manage filters, and remove entities. This clearly distinguishes it from siblings like create_model and delete_model.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The context is clear: this is the tool for editing an existing model, and the examples demonstrate typical use cases. It does not explicitly state when to prefer a sibling tool or when not to use it, but the 'edit existing model' framing makes the intended use unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
forget_memoryA
Delete a memory by id.
Cascades: every other memory's memory:<id> reference to
this id is automatically stripped from its entities list.
Args:
id: The memory_id returned by save_memory. Accepts
strings (the canonical form, including user-supplied
"kb.policy"-style ids) as well as legacy ints
(coerced to their decimal string form).
Raises a friendly error if the id is invalid or the memory does not exist.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and it discloses the key side effect (cascading removal of memory:<id> references from entities lists) and the error behavior for invalid or missing ids. It does not discuss reversibility or permissions, but the destructive action and cascade are clearly stated.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core action, then efficiently explains cascade behavior, parameter formats, and error handling. No sentence is wasted, and the structure is scannable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter deletion tool with an output schema, the description covers the operation, parameter semantics, side effects, and errors. The only minor gap is that it does not describe the output or return value, but that is already covered by the output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% and the description fully compensates: it defines id as the memory_id returned by save_memory, lists accepted forms (canonical strings including kb.policy-style ids and legacy ints), and explains int coercion. This is more precise than the bare 'Id' property in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific action ('Delete a memory by id') with the exact resource and identifier. This clearly distinguishes it from sibling deletion tools like delete_model and delete_datasource because it targets memories.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description establishes clear context: use this when you have a memory_id from save_memory and need to remove that memory. It does not explicitly name alternatives or exclusions, but the resource distinction plus the save_memory relationship makes the appropriate use obvious.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_datasource_priorityA
Return the configured datasource priority list (most-preferred
first), or [] if none is set.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does reasonably well: it states the return value, the ordering convention, and the empty-list default when unset. It does not explicitly say the operation is read-only, but 'Return' and the absence of side-effect language make that sufficiently clear.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence with no wasted words. The core action and resource are front-loaded, and the ordering and default behavior are provided in a compact parenthetical.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter, getter-style tool with an output schema present, this description is complete. It communicates what is returned, the ordering, and the fallback value, leaving no meaningful ambiguity for an agent invoking the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so the baseline of 4 applies. There are no parameter semantics to document, and the description does not need to compensate for any schema gaps.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Return') and a well-defined resource ('configured datasource priority list'), and clarifies ordering as most-preferred first. It is clearly distinguishable from siblings like list_datasources and set_datasource_priority.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies the tool is used when the agent needs the current datasource priority ordering, but it does not explicitly mention when to prefer this over alternatives or any exclusions. The context is clear enough for a simple getter, but no alternative routing is provided.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ingest_datasource_modelsA
Auto-discover tables in a database and create / additively update semantic models from them.
Idempotent (DEV-1356): re-runs are additive only. New columns and joins
are appended to existing models; existing column / join definitions
are never overwritten. After the additive pass, returns the pending
validate_models deletes alongside the additions.
Args: datasource_name: Name of an existing datasource (from list_datasources). include_tables: Comma-separated list of table names to include. If empty, all tables are ingested. schema_name: A single database schema to inspect (e.g. "public"). Empty uses the default schema. schemas: Comma-separated schemas to inspect. Mutually exclusive with schema_name / all_schemas. all_schemas: Ingest every non-system schema. Mutually exclusive with schema_name / schemas.
| Name | Required | Description | Default |
|---|---|---|---|
| schemas | No | ||
| all_schemas | No | ||
| schema_name | No | ||
| include_tables | No | ||
| datasource_name | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral disclosure burden. It clearly states idempotence, that re-runs are additive only, that existing definitions are never overwritten, and that the result includes pending validate_models deletes. This is substantial transparency for a mutating tool, though it does not cover failure modes or permissions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose and the crucial idempotence guarantee, followed by the necessary parameter documentation. The wording around 'pending validate_models deletes' is slightly awkward, but every sentence earns its place given the lack of schema descriptions.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with five parameters, mutual exclusions, and a mutating/additive behavior, the description provides enough context to invoke it correctly: source datasource, table filtering, schema selection, and the additive result. It does not explain return structure in detail, but an output schema exists and the behavioral guarantee is clear.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate with parameter details. The Args section explains each parameter, gives examples, defines empty-string behavior, and documents mutual exclusivity among schema_name, schemas, and all_schemas. This fully compensates for the missing schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action ('Auto-discover tables in a database') and a clear outcome ('create / additively update semantic models'). It clearly differentiates from sibling tools like create_model and edit_model by focusing on automatic discovery rather than manual construction.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context: it operates on an existing datasource from list_datasources and is additive/idempotent, so it is safe to re-run. It does not explicitly enumerate when to choose this over create_model/edit_model, but the auto-discovery language makes the intended use case clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
inspectA
Inspect EXACTLY one entity by reference and kind, a homogeneous
BATCH when reference is a list — or the whole COLLECTION at a kind
when reference is omitted / None.
A clean point-lookup: no fusion / ranking / cypher, and no bundled
memories. Use search instead when you want an entity surfaced in
context (with related memories and ranked neighbours).
Before using a column as a filter, projection, group-by, or join
key, inspect it and read its Description: (the schema author's
intent) and Sample values: (the stored literal forms — a top-N
sample, indicative rather than exhaustive; build text predicates
from these, never a guessed spelling). Never pick a column from its
name alone.
Collection (DEV-1667): omit reference (or pass None / [])
to list a whole kind. entity_type="model" lists all models grouped
by datasource (compact=True: one terse line per model; compact=False:
the full per-model tables). entity_type="datasource" lists all
datasources. Only model / datasource support the collection
view; other kinds raise. This subsumes models_summary /
list_datasources.
Batch (DEV-1612): pass a list of references that all share the one
entity_type. Returns one rendered block per id, in input order,
each echoing its resolved canonical id (a ## <canonical> header in
markdown; a JSON array under format="json"). Per-id resolution
errors are isolated — one bad id does not sink the batch (in JSON it
becomes a {"reference": ..., "error": ...} element). A single
str keeps its byte-for-byte single output; a one-element list is
still batch-framed.
Args:
reference: The entity reference, or a list of references (batch).
Accepts canonical forms (mydb, mydb.orders,
mydb.orders.amount), bare names, join paths
(orders.customers.region → resolved to the owning model),
and memory:<id> for memories. Normalised via the shared
resolver; the normalised canonical id is echoed in the JSON
shape.
entity_type: REQUIRED. One of datasource, model,
column, measure, aggregation, memory.
Disambiguates the 3-part canonical collision (a name
shared by, e.g., a column and an aggregation) and asserts
the resolved kind — a mismatch returns a detailed error.
compact: When true (default): description-only for
column/measure/aggregation/datasource/memory; for
entity_type="model" a cheap schema skeleton (column /
measure / aggregation names + join targets, zero DB calls).
False returns the full render (and, for the datasource kind,
a per-model skeleton for each visible model).
format: "markdown" (default) or "json".
num_rows: Sample-data rows for entity_type="model". Ignored
(with a warning) for other kinds.
show_sql: Include generated SQL for entity_type="model".
Ignored (with a warning) for datasource/memory; a silent
no-op for column/measure/aggregation.
sections: Section subset for entity_type="model". Ignored
(with a warning) for other kinds.
descriptions_max_chars: Truncate description fields to this many
characters. Applies to every kind.
| Name | Required | Description | Default |
|---|---|---|---|
| format | No | markdown | |
| compact | No | ||
| num_rows | No | ||
| sections | No | ||
| show_sql | No | ||
| reference | No | ||
| entity_type | Yes | ||
| descriptions_max_chars | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure, and it does so thoroughly. It reveals the no-fusion/no-ranking/no-cypher nature, batch error isolation with per-id error elements, canonical-id echoing, ignored parameter warnings, compact-mode behavior, and the fact that collection view only works for model/datasource. This goes well beyond typical descriptions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long, but it covers three modes, eight parameters, kind-specific behavior, and error semantics. It is well-structured with clear section headers for single, collection, batch, and Args. Each sentence carries useful information; the ticket references and examples are compact and relevant. The front-loading of the core behavior makes it easy to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (three modes, eight parameters, many kind-specific exceptions), the description leaves little to inference. It explains return shaping for markdown and JSON, error isolation, canonical resolution, parameter warnings, and which entities support collection view. The presence of an output schema means the description does not need to enumerate every render field, and it focuses on the behavioral and selection guidance an agent needs.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has 0% description coverage, so the Args section is the only source of parameter meaning. It fully compensates by explaining reference forms (canonical, bare, join paths, memory), the required entity_type enum, the semantics of compact, format, num_rows, show_sql, sections, and descriptions_max_chars, including which parameters are ignored for which kinds. Every parameter is given practical, non-obvious context.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description leads with a precise statement: Inspect exactly one entity by reference and kind, a homogeneous batch when reference is a list, or a whole collection when reference is omitted. It also distinguishes itself from siblings by naming search as the alternative for in-context lookup and noting that it subsumes models_summary and list_datasources. This is a model of clear purpose and sibling differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use the tool vs. alternatives: 'Use search instead when you want an entity surfaced in context (with related memories and ranked neighbours).' It also provides concrete guidance for collection mode, batch mode, and warns which entity types support collection. The advice to inspect columns before using them in filters/joins adds valuable use-context instructions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
inspect_modelA
DEPRECATED: use the inspect tool. Return a complete-yet-compact view of a semantic model.
Always emitted (regardless of sections): model header + description,
metadata bullets (data_source, sql_table, default_time_dimension,
hidden, row_count), backing-query structure for query-backed models,
and — when show_sql=True — the custom SQL block, model-level
filters, and the cached backing-query SQL.
Section-gated parts (subset selectable via sections):
columns— unified row-level columns table with asampledcolumn (distinct values for string/boolean,min .. maxfor number/date/time, ortop20 ... (N distinct)for high- cardinality categoricals).measures— named-formula library.aggregations— custom aggregation definitions. Theformulacolumn and thesqlfield of eachparams[]entry are gated byshow_sql.joins— join definitions.samples— live sample-data query (COUNT(*)plus one aggregation per column).learnings— learning-only memories whose canonical entities reference this model.
When a section is omitted from sections: columns, measures,
aggregations and joins collapse to a one-line backticked CSV
of names; samples and learnings are dropped entirely.
A footer at the end of the response lists what was trimmed and how
to fetch more.
Args:
model_name: Name of the model to inspect.
num_rows: Max sample-data rows (default: 3).
show_sql: When true, include the generated SQL for the sample-data
query, the custom SQL block, model-level filters, the cached
backing-query SQL, and aggregation formulas/param SQL.
format: Output format — "markdown" (default) or "json".
Case-insensitive.
sections: Subset of ["columns", "measures", "aggregations", "joins", "samples", "learnings"]. Default (None
or empty list) renders all six. Unknown names are ignored
with a warning line at the end of the response. A non-empty
list of only unknown names resolves to no sections (not
all six) — "all sections" is reserved for None/[] so
a typo can't silently trigger the full expensive payload.
descriptions_max_chars: When set, every description field (model,
column, measure, aggregation) longer than this is truncated
with a ... [truncated] suffix. Must be >= 0. None
(default) means no truncation.
| Name | Required | Description | Default |
|---|---|---|---|
| format | No | markdown | |
| compact | No | ||
| num_rows | No | ||
| sections | No | ||
| show_sql | No | ||
| model_name | Yes | ||
| data_source | No | ||
| descriptions_max_chars | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full disclosure burden and does so thoroughly. It details always-emitted content, section-gated behavior, collapse behavior for omitted sections, the footer, unknown-section handling, SQL gating via `show_sql`, and truncation semantics. This gives the agent strong awareness of response shape and edge-case behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but tightly structured and front-loaded with the deprecation warning and core purpose. Every major behavior is organized under clear headers, defaults and edge cases are explicit, and there is no filler or vague wording.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 8-parameter introspection tool with no annotations and an output schema, the description is unusually complete: it explains output composition, section subsetting, collapse behavior, SQL inclusion, format, and truncation. The only real gap is the undocumented `compact` and `data_source` parameters, which prevents a perfect score.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The raw schema has no parameter descriptions, so the description compensates substantially for `model_name`, `num_rows`, `show_sql`, `format`, `sections`, and `descriptions_max_chars`, including a nuanced explanation of unknown `sections` values. However, `compact` and `data_source` are absent from the Args section, leaving their semantics to inference from titles and context.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Return') and resource ('semantic model') and defines the output as a 'complete-yet-compact view.' It also explicitly distinguishes itself from the sibling `inspect` tool by flagging DEPRECATED and directing the agent to use `inspect` instead, leaving no ambiguity about what this tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description is explicit about usage: it begins with 'DEPRECATED: use the inspect tool,' naming the alternative and telling the agent when not to choose this tool. It also gives clear guidance on section selection, defaults, and edge cases, so an agent knows exactly how to request the desired subset of output.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_datasourcesA
List all configured database connections (names and types only, credentials are not shown). Use describe_datasource for connection details and status.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden of disclosing behavior. It states that credentials are not shown, which is an important privacy/security behavior beyond the basic 'list' semantics. The read-only nature is implied by 'List' but not explicitly stated; a small gap, but the redaction disclosure adds meaningful transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two concise sentences with no filler. The main purpose is front-loaded, and the second sentence adds routing guidance. Every word earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a parameterless list tool with an output schema present, the description fully covers what the agent needs to decide whether to call it and what to expect. It also gives a pointer to the appropriate sibling for more detail, making the context complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so there is no parameter documentation needed. The description focuses on the output content instead, clarifying that only names and types are returned, which is the relevant semantic information for an agent.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action ('List all configured database connections') and the scope of the result ('names and types only'), immediately distinguishing it from connection detail operations. It also explicitly differentiates from describe_datasource.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly tells the agent when to use this tool versus an alternative: use describe_datasource for connection details and status. This is clear, direct routing guidance with no ambiguity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
models_summaryA
Brief summary of all (non-hidden) models in a datasource.
DEV-1549: compact-by-default rendering. Under compact=True
each model section emits its name, description, the column count
(Columns: N), the comma-separated measure NAMES
(Measures: a, b, c) and the Joins to: list — no
per-column table, no per-measure formula block. Pass
compact=False to restore the verbose markdown / JSON shape
with full column and measure payloads.
Args: datasource_name: Name of the datasource (from list_datasources). format: Output format — "markdown" (default, compact and LLM-friendly) or "json" (structured array of model summaries). Case-insensitive. compact: Default True — drop per-column / per-measure detail. Set False to surface the full per-model tables.
| Name | Required | Description | Default |
|---|---|---|---|
| format | No | markdown | |
| compact | No | ||
| datasource_name | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well by detailing exactly what compact=True emits (name, description, column count, measure names, joins list) and what it omits. It also discloses the default behavior and the compact=False alternative. It doesn't discuss errors or side effects, but none are expected for a read-only summary tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with a clear opening sentence followed by rendering details and an Args list. The DEV-1549 ticket reference is noise for an AI agent, but all other sentences earn their place by clarifying output behavior and parameter choices.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description fully covers the parameters and output rendering modes, and an output schema exists to define the structured return shape. It could improve by naming sibling alternatives or noting datasource-not-found behavior, but for a summary tool with this complexity it is largely complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, and it does. Each parameter is explained in the Args section: datasource_name references list_datasources, format names valid values and case-insensitivity, and compact explains default and behavioral impact. This adds substantial meaning beyond the bare input schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the tool's purpose: 'Brief summary of all (non-hidden) models in a datasource.' It specifies the resource scope (all non-hidden models) and gives a distinct action, though it doesn't explicitly contrast with siblings like inspect_model or describe_datasource.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context for when the tool would be useful—summarizing all models in a datasource—but it never explicitly states when to prefer this over siblings such as inspect_model or describe_datasource. It does explain usage for compact versus verbose output and format selection, but tool-alternative guidance is implied rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
queryA
Query data from a semantic model. Call inspect(reference=".", entity_type="model") first to see available columns and measures, and search (with the entities you plan to use and/or a free-text question) to surface saved learnings and example queries before finalizing a query.
The query argument takes one of three forms:
Model name (string) — run a query-backed saved model by name, e.g.
"monthly_revenue"(honorsvariables; every other setting comes from the stored query).Query object (dict) — a single query; per-field documentation is on the SlayerQuery schema.
Multi-stage list (list of query objects) — a DAG of stages. Every entry except the last MUST carry a
name; the last entry is the root whose rows are returned. Stages reference one another by that name — as asource_modelor via a join in an inline ModelExtension — and the engine orders them topologically. An inner stage's result columns become plain columns of the outer stage (dotted paths flatten:stores.name->stores__name); a stage may reference only what its own source defines or what a prior stage projected — define before you reference. Use stages when a whole result set must be re-queried, joined, or reused; single-query nesting and computed dimensions already cover re-aggregation.
Expressions — one language, used in measures, computed dimensions, filters, and order. The same expression returns its value as a measure, groups by it as a computed dimension, masks as a filter (routed automatically to WHERE / HAVING / post-aggregation), and sorts in order.
Aggregations are function calls over a column or a same-model scalar expression:
count(*),sum(total),sum(amount - cost),percentile(price, p=0.95). Available: sum, avg (both take window='90d' for trailing time windows), min, max, count, count_distinct, count_distinct_approx, median, percentile(x, p=), weighted_avg(x, weight=col), stddev_samp, stddev_pop, var_samp, var_pop, corr(x, other=col), covar_samp(x, other=col), covar_pop(x, other=col), first(x[, time_col]) / last(x[, time_col]) (earliest/latest record's value per group), plus model-defined custom aggregations. Write count_distinct(x), never count(distinct x).All aggregations support
partition_by=(bare names:partition_by=region,partition_by=[region, city],partition_by=[]for the grand total), computing the aggregate at that coarser grain; the result is broadcast over the missing dimensions.Combine aggregations with arithmetic and transforms — missing dimensions broadcast on both sides. E.g. with dimensions ["city", "region"], the measure {"formula": "sum(total) / sum(total, partition_by=region)", "name": "share_of_region"} is each city's share of its region's total.
Aggregations nest: "avg(sum(total, partition_by=[region, city]), partition_by=[region])" averages the per-city totals within each region. The top-level partition_by must be a subset of the query's dimensions; inner aggregations' partition_by need not be. An outer aggregation's parameters must be determined by the operand's grain — a cell value at that grain, e.g. weight=count(id, partition_by=[region, city]), or a column that grain fixes; any other row column is a typed error.
Transforms wrap aggregated expressions: cumsum(x); change(x) / change_pct(x) (period-over-period delta / % change — calendar-aware and partition-safe, prefer these for growth); time_shift(x, -1[, 'year']) (the shifted value itself, for custom arithmetic); lag(x, n) / lead(x, n) (row-position shift, NULL at edges); first(x) / last(x) (broadcast the earliest/latest bucket's value); consecutive_periods(predicate) (trailing run length; the predicate may be row-level, e.g. status = 'paid'); rank(x), dense_rank(x), percent_rank(x), ntile(x, n=N) (rank family — optional partition_by=, no time dimension needed). All other transforms require a time_dimensions entry. Transforms nest in either order (change(cumsum(x))). Not supported: a row-level column mixed into a composite or nested input of time_shift / change / change_pct, or mixed with another aggregation's value inside one aggregation source.
Cross-model: reference any joined model's field as
model_name.field_name(or a longer dotted path) and the engine figures out the join paths, avoiding fan-outs and chasm traps — each aggregation computes over its own model's rows exactly once; ambiguous routes error naming the candidates, and result keys use the full routed path. An aggregation sliced by a dimension not attributable to it broadcasts its value with a warning — seeto_many_handlingto attribute or error instead.
Method — decompose the question into blocks first: every qualifier, projected column, filter, grouping, unit, rounding, and ordering hint is one block, and each must map to a named column/measure/filter/dimension. Never drop a qualifier because no entity matched — search for it, else encode it as an expression or an inline ModelExtension column; reference already-encoded quantities by name rather than re-deriving their logic. Pin explicitly rather than guessing: which aggregation ("typical" is not automatically avg vs median), the grouping column and raw-vs-standardized labels, each aggregate's scope (all rows vs a filtered subset), sort column + direction + tie-break, NULL handling, units and rounding, exact numeric constants. "How many / count of" -> a scalar count(*); "which / list / show" -> the rows. Project exactly the columns the question names — no extras, none missing.
Filter literals — build every ==/in/like predicate on a text column from that column's sampled values (inspect it), never a guessed spelling; samples are a top-N snapshot, so when a needed literal is absent verify it (e.g. a distinct-values query) rather than assume either way. Compare case/whitespace-insensitively in the FILTER position only, never on a projected, grouped, or join-key column; abbreviations that case-folding can't unify go in the IN-set. Apply only the transformations (TRIM/ROUND/CAST/dedup) the question or a governing definition requires.
Verify — run the exact final query and read the result (show_sql=true when unsure): row count plausible; no dimension-only GROUP BY when you wanted per-record rows (distinct_dimension_values: false); sort column + direction as asked; each aggregate's scope right; NULL behavior intended; string values carry the expected casing. On a wrong result, change ONE variable at a time — two changes per attempt make the outcome uninterpretable.
Query-object fields taking the functional time-granularity form
gran(col) — gran one of second, minute, hour, day, week,
week_sunday, month, quarter, year:
dimensions: group-by columns; a granularity call such as
month(created_at) buckets that timestamp, equivalent to a
time_dimensions entry (and orderable as month(created_at)).
time_dimensions: time-bucketed group-bys — {"dimension": ..., "granularity": ...} dicts, or the string form month(created_at).
main_time_dimension: which time dimension time-ordered transforms key off.
Top-level arguments (siblings of query, NOT fields inside it):
variables: Values for {placeholder} substitutions in filters / model SQL. Also
settable per query object; precedence: runtime (top-level) > named-stage >
outer-query > model.query_variables.
show_sql: When true, include the generated SQL in the response for debugging.
dry_run: When true, generate and return the SQL without executing it.
explain: When true, run EXPLAIN ANALYZE and return the query plan.
format: Output format — "markdown" (default, compact) | "json" | "csv". Case-insensitive.
Without an explicit limit the response is capped at 20 rows with a truncation notice.
Example: query(query={"source_model": "orders", "dimensions": ["status"], "measures": [{"formula": "count(*)"}], "filters": ["status == 'completed'"]})
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | ||
| format | No | markdown | |
| dry_run | No | ||
| explain | No | ||
| show_sql | No | ||
| variables | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral burden and does so exhaustively: default 20-row truncation, automatic WHERE/HAVING/post-aggregation filter routing, broadcast warnings for non-attributable dimensions, topological stage ordering, dotted-path flattening, and precedence rules for variables. It also discloses debug behaviors (show_sql, dry_run, explain) and verification expectations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long, but every section earns its place for a tool of this complexity: purpose and prerequisites are front-loaded, and bold headers with bullet lists make the expression rules, argument forms, filters, and verification steps scannable. The included example concretizes the abstract spec without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, an output schema, and zero annotation coverage, the description is essentially complete: it covers preconditions, input forms, expression syntax, filter construction, stage semantics, debug options, output format, row caps, and a verification protocol. Nothing an agent must know to invoke this tool safely and correctly is left undocumented.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% for top-level parameters, and the description fully compensates by documenting all five top-level siblings (variables, show_sql, dry_run, explain, format) with precedence and behavior, plus the three forms of the query argument in depth. It adds meaning far beyond the schema, including the expression language, gran() forms, and per-field clarification of query-object semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
"Query data from a semantic model" states a specific verb and resource, and the opening lines position this tool as the execution step after inspect and search, naming sibling tools explicitly. The three query argument forms and the worked example further pin down exactly what this tool does versus siblings like inspect_model or search.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says to call inspect(...) first and search(...) before finalizing a query, giving an unambiguous workflow. It also states when to use multi-stage lists versus single-query nesting, and warns against using limit to trim plain lists — clear when-to-use and when-not-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
recommend_root_modelA
Recommend the root model (query source_model) for a set of
model.column / model.metric items, and give each item's
join-qualified reference path from that root.
Introspects the join graph and picks the model from which every
requested item is reachable (LEFT joins are directional; INNER
joins traverse both ways), minimizing total join hops. The returned
paths are ready to drop into a query whose source_model is the
recommended root — e.g. a joined column comes back as
customers.regions.name and a root-owned one as status;
aggregation spellings (sum(revenue) / revenue:sum) are preserved.
When no single model reaches everything, root_model is null and
coverage lists the best partial roots so you can split the
request into a multi-stage query.
Call this once your item list is final, not as a schema browser —
explore with search / inspect first.
Args:
items: entity references (orders.revenue, customers.name,
orders.revenue:sum / sum(orders.revenue), bare aov for a saved metric...).
data_source: optional datasource scope; when omitted, names
resolve via the datasource-priority list. All items must
resolve to a single datasource.
root_hint: optional intended root — a bare model name or
<data_source>.<model> within the resolved datasource.
Honored when it reaches every item (overriding the min-hops
pick, so you can force a bridge model that owns none of the
items); otherwise the auto-pick is used and a warning
explains why. Resolved after the datasource is determined,
so it cannot pick the datasource.
format: "markdown" (default) or "json".
| Name | Required | Description | Default |
|---|---|---|---|
| items | Yes | ||
| format | No | markdown | |
| root_hint | No | ||
| data_source | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden and does so thoroughly: it discloses directional LEFT joins, bidirectional INNER joins, min-hop optimization, root_hint override semantics, the warning when the hint is ignored, and the null-root/coverage fallback. This goes well beyond a basic 'recommends a root' summary.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose, then methodically covers behavior, edge cases, usage timing, and parameters. Every section earns its place; the Args section adds semantic value beyond the schema rather than repeating it.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex join-graph tool with 0% schema parameter coverage, the description is complete: it covers when to use it, all parameter semantics, key edge cases, output examples, and interaction with sibling tools. The existing output schema handles detailed return-structure documentation, so nothing essential is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate — and it does. items gets concrete spellings and examples, data_source gets scope and single-datasource constraints, root_hint gets override behavior and resolution order, and format gets its allowed values and default.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb-and-resource statement: it recommends a root model for a set of items and returns join-qualified reference paths. It also distinguishes itself from schema-browsing siblings by explicitly saying it is not a schema browser and pointing to search/inspect for exploration.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly states when to call the tool: once the item list is final, not during exploration. It also names alternatives (search/inspect) and explains the fallback behavior when no single root reaches every item, which helps an agent decide whether to split the request.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
save_memoryA
Save an agent memory: a free-form note plus the SLayer entities it concerns.
linked_entities accepts either:
a list of entity reference strings — each item is resolved to the canonical
<datasource>.<model>[.<leaf>]form. Bare names use the datasource priority list; ambiguous bare-column matches are rejected.memory:<id>is also valid here (cross-memory references; the target memory must exist).a
SlayerQuery(dict) — entities are auto-extracted fromsource_model,dimensions,time_dimensions,measures, andfilters; resolution warnings are non-fatal. The query itself is stored alongside the learning, so the memory surfaces insearch'sexample_querieslist (vs thememorieslist for entity-list memories).
DEV-1428: id is an optional canonical memory id. Omit to
auto-allocate a monotonic int-shaped id ("1", "2", ...);
supply a string for a stable user-controlled id
("kb.policy.42"). Charset excludes :, /, ?,
#, whitespace. Duplicate id → unconditional upsert,
created_at preserved.
Returns the assigned memory_id (string), the canonical
entities stored, and any non-fatal warnings.
Cascade-on-delete: when a model / datasource / measure is
deleted, every memory:<id> and <ds>.<model>[.<leaf>]
reference under it is automatically stripped from every other
memory's entities list. Memories with zero entities after
the strip are kept (the learning text stands alone).
Search is lenient: stale entity tags in saved memories are filtered out at retrieval time rather than raising.
Args:
learning: The note text. Required, non-empty.
linked_entities: List of entity strings, or an inline
SlayerQuery payload.
id: Optional canonical memory id (see above).
Examples: save_memory( learning="orders.is_returned in {0,1,NULL}; treat NULL as not returned", linked_entities=["orders.is_returned"], )
save_memory(
learning="Paid revenue by status",
linked_entities={
"source_model": "orders",
"measures": [{"formula": "sum(amount)"}],
"filters": ["status = 'paid'"],
},
id="kb.paid-revenue",
)
| Name | Required | Description | Default |
|---|---|---|---|
| id | No | ||
| learning | Yes | ||
| description | No | ||
| linked_entities | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it delivers extensively. It discloses upsert semantics, id auto-allocation rules, charset restrictions, cascade-on-delete behavior, lenient stale-tag filtering, and non-fatal resolution warnings. This is far beyond what an agent could infer from the schema alone.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but well-organized, with a leading one-sentence summary followed by clearly scoped sections and practical examples. The length is justified by the absence of annotations and schema descriptions, though a few sections could be tightened without losing value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex tool with no annotations and rich schema, the description covers purpose, input forms, id behavior, deletion side effects, search integration, and return values. The only notable omission is the optional description parameter, but overall the agent has everything needed to invoke the tool correctly and anticipate consequences.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, and it thoroughly explains learning, linked_entities, and id, including accepted formats, edge cases, and examples. However, the schema also exposes a 'description' parameter that the description never mentions, leaving one of the four parameters undocumented.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a precise verb and resource: 'Save an agent memory: a free-form note plus the SLayer entities it concerns.' It distinguishes the tool from siblings like search and forget_memory, and even explains where saved memories surface in search's results, making its role unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides strong context for when to save a memory, including two distinct input modes (entity list vs SlayerQuery) and explains that query-based memories appear in search's example_queries list. It does not explicitly name alternatives or state when not to use this tool, but the save-versus-search/forget relationship is clear from context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
searchA
Up to three-channel semantic search over memories + canonical entities.
Call this BEFORE query to surface any notes or example
queries previously saved against the entities you're
considering.
Discovery, not detail: hits come back as one-line descriptions —
pick candidate ids here, then read their full bodies with
inspect (batching same-kind ids in one call). A broad
compact=False search drags full renders into cached context on
every later turn for no added signal.
Channel 1 (entity-overlap BM25 over memories): runs when
entities and/or query is supplied. Memories whose
canonical entity tags overlap the resolved input are ranked.
Channel 2 (tantivy full-text over memories ∪ entities): runs
when question is supplied. The in-memory index covers every
memory + every searchable entity (datasource / non-hidden model /
non-hidden column / named measure / aggregation).
Channel 3 (dense embedding similarity, optional): runs when
question is supplied AND the advanced_search extra is
installed AND a provider API key is configured for the active
embedding model. Cosine similarity between the question
embedding and persisted entity/memory embeddings. Skipped with
a single warning into SearchResponse.warnings when any
precondition fails — tantivy + BM25 continue to work.
All hits (memories, example queries, entities) are fused via
Reciprocal Rank Fusion (k=60) into a single ranked
results list capped at max_results.
Empty input (no entities, no query, no question) returns the
newest memories capped at max_results, with a warning.
Args:
entities: Canonical entity reference strings.
query: Optional SlayerQuery (dict). Entities are
auto-extracted to broaden channel-1 input.
question: Free-text query for the tantivy full-text channel.
datasource: Optional datasource name. When set, scope all
three channels to that one datasource. Entity hits are
limited to docs rooted at the datasource (exact match
or dotted-path descendant). Memories surface when any
of their tagged entities is rooted at the datasource —
a memory spanning multiple datasources surfaces from
each. BM25 / IDF stats reflect only the filtered subset.
Unknown datasource raises ValueError.
max_results: Maximum total number of hits to return (default 10).
cypher_filter: Optional openCypher MATCH query returning
… AS id that pre-filters all three channels to the
returned canonical IDs — narrow to one kind so
max_results isn't spent on an RRF-fused mix of
memories, columns, measures, and models. When
advanced_search is not installed, only simple
MATCH (n:Label1:Label2) RETURN n.id AS id patterns are
supported as a kind filter (multi-label uses union
semantics; allowed labels: Memory, Datasource, Model,
ModelColumn, Measure, Aggregation — use ModelColumn,
not Column, which resolves only on the naive fallback).
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | ||
| compact | No | ||
| entities | No | ||
| question | No | ||
| datasource | No | ||
| max_results | No | ||
| cypher_filter | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so thoroughly: it discloses empty-input behavior, the warning-on-skip behavior for the dense channel, `ValueError` on unknown datasource, RRF fusion, and the context-cache cost of `compact=False`. This goes well beyond the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but densely packed and well-structured: a top-line purpose, ordering instruction, channel breakdown, fusion/empty-input behavior, then parameter docs. Every sentence carries operational value for a complex 7-parameter tool, so the length is earned.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, no annotations, and an output schema present, the description covers all invocation concerns: channel preconditions, result fusion, error cases, scoping semantics, and cypher_filter limitations. An agent has enough to call this correctly in varied contexts.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must explain every parameter. It provides detailed semantics for `entities`, `query`, `question`, `datasource`, `max_results`, and `cypher_filter`, and explains `compact` in prose. This fully compensates for the schema's lack of descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening line names a specific operation and resource: 'three-channel semantic search over memories + canonical entities.' It also positions the tool against siblings by stating it is discovery-level and that full bodies should be read via `inspect`, making its role unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly instructs agents to call this before `query`, tells them to use `inspect` for details, warns against broad `compact=False`, and explains when each channel runs. It also gives concrete cypher_filter guidance, leaving little room for mis-selection.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_datasource_priorityA
Configure how SLayer disambiguates bare model names that exist in multiple datasources.
When two datasources both define a model named users, calling
edit_model("users") (no data_source=) is ambiguous. SLayer
walks this priority list and picks the first datasource that has
the requested name. If none of the candidates appear in the list,
an AmbiguousModelError is raised.
Args:
priority: Datasource names, most-preferred first. Each entry
must already exist (run list_datasources first). Pass
an empty list to clear the priority.
| Name | Required | Description | Default |
|---|---|---|---|
| priority | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries full behavioral disclosure and does so well: it explains the priority-list walk, first-match selection, AmbiguousModelError when no candidate matches, and empty-list clearing behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose, then uses a concrete ambiguous-model example to make the behavior intuitive. Every part—purpose, example, resolution rules, parameter semantics—earns its place without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple one-parameter configuration tool, the description covers the scenario, prerequisites, error behavior, and clearing semantics. The output schema can handle return details, and no critical information is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema only specifies 'priority' as an array of strings, so the description is essential. It adds ordering semantics (most-preferred first), the requirement that names already exist, and the empty-list clearing behavior.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Configure how SLayer disambiguates bare model names.' The description clearly distinguishes this from sibling tools like get_datasource_priority and list_datasources by focusing on the configuration/set behavior.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides clear context for when this tool is needed—when bare model names exist in multiple datasources—and gives an actionable prerequisite (run list_datasources first). It does not explicitly contrast with get_datasource_priority, but the read/write pairing is implied by the sibling names.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
validate_modelsA
Diff persisted SLayer models against the live database schema(s).
Returns a JSON-serialized list of pending delete operations (column drops, measure drops, join drops, filter removals, whole models) needed to keep stored models valid against the current live state. Read-only — does not modify storage.
Args: data_source: Datasource name to validate. When omitted, every datasource is validated concurrently and results are concatenated.
| Name | Required | Description | Default |
|---|---|---|---|
| data_source | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so well: it explicitly states 'Read-only — does not modify storage', describes what the returned list contains, and discloses the behavior when data_source is omitted (validates all datasources concurrently and concatenates results).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose, followed by return behavior, safety note, and parameter semantics. Every sentence earns its place; no filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with one optional parameter and an output schema, the description is complete: it explains the input, the output shape, the no-modification guarantee, and the default all-datasources behavior. Nothing needed to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It fully explains the single parameter: 'Datasource name to validate', including the omission behavior and result concatenation. This adds meaning well beyond the raw schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Diff persisted SLayer models against the live database schema(s)'. It also clarifies the concrete output—a JSON list of pending delete operations—which distinguishes it from siblings like query, inspect_model, and delete_model.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context for when to use the tool: to validate persisted models against live schemas and preview required deletions. It does not explicitly name alternative tools or exclusions, but the purpose is specific enough that an agent can select it appropriately among the sibling tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
2 tool updates
v0.10.2- Changed
query27 fields changed- changed
Input schema / $defs / Column / properties / db_type / descriptionPrevious value: -"Raw database type string (e.g. 'point', 'jsonb'), retained when the declared DataType loses information. Populated by ingestion for UNKNOWN (opaque) columns; None for mapped types, where the declared DataType already carries everything we need."New value: +"Raw database type string (e.g. 'point', 'DECIMAL(18, 2)'), retained when the declared DataType loses information. Populated by ingestion for UNKNOWN (opaque) and exact NUMERIC/DECIMAL columns; None when the mapped type carries everything needed." - added
Input schema / $defs / ColumnRefAdded value: +{ + "description": "A column reference: bare name or dotted join path (``customers.regions.name``); a short form (``regions.name``) auto-routes when exactly one route exists.", + "properties": { + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Label" + }, + "model": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Model" + }, + "name": { + "title": "Name", + "type": "string" + } + }, + "required": [ + "name" + ], + "title": "ColumnRef", + "type": "object" +} - added
Input schema / $defs / ComputedDimensionAdded value: +{ + "additionalProperties": false, + "description": "A dimension computed by an ``expression``; an aggregation inside must carry ``partition_by=`` to fix its grain. Best given an explicit ``name``.", + "properties": { + "expression": { + "title": "Expression", + "type": "string" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Name" + } + }, + "required": [ + "expression" + ], + "title": "ComputedDimension", + "type": "object" +} - removed
Input schema / $defs / DataType / descriptionRemoved value: -"SLayer data types — values match sqlglot's ``exp.DataType.Type``\nbyte-for-byte so SQL generation can ``CAST`` to the declared type without\na translation map. (DEV-1361.)\n\n``UNKNOWN`` is the explicit *opaque* type: the column's database type was\ndetected but SLayer cannot operate on it — it has no default btree/hash\noperator class, which is exactly what ``GROUP BY`` / ``DISTINCT`` require\n(``json``, ``xml``, the geometric / PostGIS types, range types, ...).\nComparable types keep working as ``TEXT`` even when unmapped — ``jsonb``,\n``uuid``, ``bytea``, arrays and ``tsvector`` are all groupable and are\ndeliberately *not* opaque. ``slayer.engine.ingestion._OPAQUE_SA_TYPE_NAMES``\nis the source of truth for that classification. Such a column is\n**stored and displayed** — its raw DB type string is kept on\n``Column.db_type`` — but it is never used in ``GROUP BY``, ``DISTINCT``,\naggregation, or ``CAST``, because those operations fail at the database\n(e.g. \"could not identify an equality operator for type point\"). Use the\n:attr:`is_opaque` property rather than comparing against the member\ndirectly. ``UNKNOWN`` is also a real ``sqlglot`` type name, so the\nbyte-equality invariant above still holds." - added
Input schema / $defs / ModelExtension / additionalPropertiesAdded value: +false - changed
Input schema / $defs / ModelExtension / properties / columns / anyOfPrevious value: -[ - { - "items": {}, - "type": "array" - }, - { - "type": "null" - } -]New value: +[ + { + "items": { + "$ref": "#/$defs/Column" + }, + "type": "array" + }, + { + "type": "null" + } +] - changed
Input schema / $defs / ModelExtension / properties / joins / anyOfPrevious value: -[ - { - "items": {}, - "type": "array" - }, - { - "type": "null" - } -]New value: +[ + { + "items": { + "$ref": "#/$defs/ModelJoin" + }, + "type": "array" + }, + { + "type": "null" + } +] - added
Input schema / $defs / ModelJoin / properties / nameAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Name" +} - changed
Input schema / $defs / ModelMeasure / descriptionPrevious value: -"A named formula evaluating to an aggregated value (grammar: ``slayer/core/formula.py``)."New value: +"A named aggregated value: ``formula`` is an aggregation expression — inline in a query's measures or saved on a model and referenced by bare name. ``name`` sets the result key, referenceable in filters and order by either the name or the formula text." - added
Input schema / $defs / OrderItemAdded value: +{ + "additionalProperties": false, + "description": "A sort key: ``column`` is a result column name or an expression string; ``direction`` asc|desc.", + "properties": { + "column": { + "$ref": "#/$defs/ColumnRef" + }, + "direction": { + "default": "asc", + "title": "Direction", + "type": "string" + }, + "raw_formula": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Internal — captured automatically from expression strings; do not set.", + "title": "Raw Formula" + } + }, + "required": [ + "column" + ], + "title": "OrderItem", + "type": "object" +} - changed
Input schema / $defs / SlayerModel / properties / version / defaultPrevious value: -9New value: +10 - added
Input schema / $defs / SlayerQueryAdded value: +{ + "additionalProperties": false, + "description": "User-facing query object — what to retrieve from a model, as names/references, no SQL.", + "properties": { + "dimensions": { + "anyOf": [ + { + "items": { + "anyOf": [ + { + "$ref": "#/$defs/ColumnRef" + }, + { + "$ref": "#/$defs/ComputedDimension" + } + ] + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Group-by columns — names / dotted paths, or computed expressions ({\"expression\": ..., \"name\": ...}); one result row per distinct value combination.", + "title": "Dimensions" + }, + "distinct_dimension_values": { + "default": true, + "description": "Default true: dimension-only queries return distinct dimension combinations (GROUP BY the projected dimensions). Set false for raw per-record rows — requires empty `measures` and no measure reference in `filters`/`order`. For rows plus a count, keep the default and add `count(*)`.", + "title": "Distinct Dimension Values", + "type": "boolean" + }, + "filters": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Condition strings, AND-ed; each routes automatically to WHERE / HAVING / post-aggregation. May contain aggregations, transforms, and {variable} placeholders.", + "title": "Filters" + }, + "limit": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Max rows to return. Use only for top-N / 'the single most X' requests — never to trim a plain list (an uncapped MCP response is truncated at 20 rows with an explicit notice).", + "title": "Limit" + }, + "main_time_dimension": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Name of the time dimension that time-ordered transforms (change, lag, ...) key off; overrides auto-detection when the query has multiple time dimensions.", + "title": "Main Time Dimension" + }, + "measures": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/ModelMeasure" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Values to return: aggregation-expression formulas (see the query tool description). A bare name references a saved model measure.", + "title": "Measures" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Stage name in a multi-stage list; other stages reference it as their source_model.", + "title": "Name" + }, + "offset": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Rows to skip.", + "title": "Offset" + }, + "order": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/OrderItem" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Sort keys; column is a result column name or an aggregation-bearing expression string.", + "title": "Order" + }, + "source_model": { + "anyOf": [ + { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "#/$defs/ModelExtension" + }, + { + "$ref": "#/$defs/SlayerModel" + } + ] + }, + { + "type": "null" + } + ], + "default": null, + "description": "The query's population: a saved model name, an inline ModelExtension ({\"source_name\": ..., plus optional \"columns\"/\"measures\"/\"joins\"}), or a full inline model. Omit to infer the smallest model determining every queried dimension, time dimension, and row-level filter column (the choice is reported in response metadata).", + "title": "Source Model" + }, + "time_dimensions": { + "anyOf": [ + { + "items": { + "anyOf": [ + { + "$ref": "#/$defs/TimeDimension" + }, + { + "type": "string" + } + ] + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Time-bucketed group-bys — one result row per bucket. Each entry is a TimeDimension dict or the functional string gran(col), e.g. \"month(created_at)\".", + "title": "Time Dimensions" + }, + "to_many_handling": { + "default": "broadcast", + "description": "What happens when an aggregation is sliced by a dimension not attributable to it: broadcast (default — repeat the value across the cells, with a warning) | associate (aggregate per cell over the distinct associated entities) | error (refuse).", + "enum": [ + "broadcast", + "associate", + "error" + ], + "title": "To Many Handling", + "type": "string" + }, + "variables": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "{placeholder} values scoped to this query object / stage; the tool-level variables argument overrides.", + "title": "Variables" + }, + "version": { + "default": 4, + "title": "Version", + "type": "integer" + }, + "whole_periods_only": { + "default": false, + "description": "Snap date filters to whole time buckets and drop the current incomplete bucket.", + "title": "Whole Periods Only", + "type": "boolean" + } + }, + "title": "SlayerQuery", + "type": "object" +} - added
Input schema / $defs / TimeDimensionAdded value: +{ + "description": "Group-by on ``dimension`` truncated to ``granularity``; optional ``date_range`` [start, end] (ISO dates).", + "properties": { + "date_range": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Date Range" + }, + "dimension": { + "$ref": "#/$defs/ColumnRef" + }, + "granularity": { + "$ref": "#/$defs/TimeGranularity" + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Label" + } + }, + "required": [ + "dimension", + "granularity" + ], + "title": "TimeDimension", + "type": "object" +} - added
Input schema / $defs / TimeGranularityAdded value: +{ + "enum": [ + "second", + "minute", + "hour", + "day", + "week", + "week_sunday", + "month", + "quarter", + "year" + ], + "title": "TimeGranularity", + "type": "string" +} - removed
Input schema / properties / dimensionsRemoved value: -{ - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Dimensions" -} - removed
Input schema / properties / distinct_dimension_valuesRemoved value: -{ - "default": true, - "title": "Distinct Dimension Values", - "type": "boolean" -} - removed
Input schema / properties / filtersRemoved value: -{ - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Filters" -} - removed
Input schema / properties / limitRemoved value: -{ - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Limit" -} - removed
Input schema / properties / measuresRemoved value: -{ - "anyOf": [ - { - "items": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Measures" -} - removed
Input schema / properties / offsetRemoved value: -{ - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Offset" -} - removed
Input schema / properties / orderRemoved value: -{ - "anyOf": [ - { - "items": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Order" -} - added
Input schema / properties / queryAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/$defs/SlayerQuery" + }, + { + "items": { + "$ref": "#/$defs/SlayerQuery" + }, + "type": "array" + } + ], + "title": "Query" +} - removed
Input schema / properties / source_modelRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "$ref": "#/$defs/ModelExtension" - }, - { - "$ref": "#/$defs/SlayerModel" - } - ], - "title": "Source Model" -} - removed
Input schema / properties / strictRemoved value: -{ - "default": false, - "title": "Strict", - "type": "boolean" -} - removed
Input schema / properties / time_dimensionsRemoved value: -{ - "anyOf": [ - { - "items": { - "additionalProperties": true, - "type": "object" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Time Dimensions" -} - removed
Input schema / properties / whole_periods_onlyRemoved value: -{ - "default": false, - "title": "Whole Periods Only", - "type": "boolean" -} - changed
Input schema / requiredPrevious value: -[ - "source_model" -]New value: +[ + "query" +]
- Removed
query_nested
21 tool updates
v0.10.0- First observed
create_datasource - First observed
create_model - First observed
delete_datasource - First observed
delete_model - First observed
describe_datasource - First observed
edit_datasource - First observed
edit_model - First observed
forget_memory - First observed
get_datasource_priority - First observed
ingest_datasource_models - First observed
inspect - First observed
inspect_model - First observed
list_datasources - First observed
models_summary - First observed
query - First observed
query_nested - First observed
recommend_root_model - First observed
save_memory - First observed
search - First observed
set_datasource_priority - First observed
validate_models
TDQS
Scored across 20 tools
Most tools have clearly distinct purposes (datasource lifecycle, model CRUD, querying, search/memory). However, inspect_model and models_summary are explicitly superseded by the broader inspect tool, creating redundancy that could cause misselection. Descriptions clearly mark these overlaps, but the presence of deprecated tools adds mild ambiguity.
The predominant pattern is verb_noun (create_model, delete_datasource, set_datasource_priority, etc.). Deviations include bare verbs like 'search' and 'inspect', and noun-led 'models_summary'. The deprecated 'inspect_model' also creates an inconsistency with 'inspect'. Overall, the convention is mostly consistent with a few outliers.
At 20 tools, this is on the heavy side but reasonable for a server covering datasource management, model CRUD, querying, and memory/search. It sits at the borderline between well-scoped (3-15) and slightly over (16-25), and the breadth of functionality justifies the count, though a few redundant tools could be trimmed.
The surface covers datasource lifecycle (create/describe/edit/delete/priority), model lifecycle (create/edit/delete/inspect/validate/ingest), querying, and search/memory. Minor gaps include lack of datasource credential updates and model renaming, but these are workaroundable. Deprecated tools add redundancy but don't create functional dead ends.
Maintenance
Related MCP Connectors
The grounded data layer for any LLM: governed SQL, metrics, lineage and catalog over your data.
- busabaseOAuthcom.busabase
Database for your AI agent. Turn its output into data, docs, skills, and apps you can actually use.
Verified, sourced, real-time intelligence layer for AI agents.
Your company's brain for AI agents. Cited, permission-aware knowledge across every system.
Related MCP Servers
- AlicenseNot gradedqualityFmaintenanceSecurely connects AI agents to multiple databases simultaneously while enabling collaborative learning from team query patterns, all while keeping data private by running locally.25 npm870MIT
- AlicenseNot gradedqualityDmaintenanceA semantic layer query engine with MCP support, enabling AI assistants to query structured data through natural language and declarative interfaces.2Apache 2.0

EnrichMCPofficial
AlicenseNot gradedqualityDmaintenanceTurns your data model into a semantic layer for AI agents, automatically generating typed, discoverable tools with entity relationships and schema discovery.644Apache 2.0- AlicenseNot gradedqualityCmaintenanceEnables AI agents to understand and query your database safely by providing a semantic layer of metadata, with tools to search, explain, validate, and generate safe SQL.2MIT