Skip to main content
Glama

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
DB_HOSTNoDatabase host for PostgreSQL, MySQL, etc. Used in datasource YAML files with environment variable references like ${DB_HOST}.
DB_NAMENoDatabase name. Used in datasource YAML files with environment variable references like ${DB_NAME}.
DB_PORTNoDatabase port (e.g., 5432 for PostgreSQL, 3306 for MySQL). Used in datasource YAML files with environment variable references.
DB_USERNoDatabase username. Used in datasource YAML files with environment variable references like ${DB_USER}.
DB_PASSWORDNoDatabase password. Used in datasource YAML files with environment variable references like ${DB_PASSWORD}.
DATABASE_URLNoAlternative to individual DB_* variables: a full database connection URL (e.g., postgresql://user:password@host:port/database). Can be used in datasource YAML files with environment variable references.

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": false
}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
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" (honors variables; 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 a source_model or 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 — see to_many_handling to 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'"]})

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.

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 a sampled column (distinct values for string/boolean, min .. max for number/date/time, or top20 ... (N distinct) for high- cardinality categoricals).

  • measures — named-formula library.

  • aggregations — custom aggregation definitions. The formula column and the sql field of each params[] entry are gated by show_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.

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.

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.

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"]})

create_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")

list_datasourcesA

List all configured database connections (names and types only, credentials are not shown). Use describe_datasource for connection details and status.

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.

edit_datasourceB

Update a datasource's metadata.

Args: name: Datasource name to update. description: New description for the datasource.

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).

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.

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".

delete_datasourceB

Delete a datasource configuration.

Args: name: Datasource name to delete.

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.

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.

get_datasource_priorityA

Return the configured datasource priority list (most-preferred first), or [] if none is set.

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 from source_model, dimensions, time_dimensions, measures, and filters; resolution warnings are non-fatal. The query itself is stored alongside the learning, so the memory surfaces in search's example_queries list (vs the memories list 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",
)
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.

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).

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

TDQS

A3.9/5.0

Scored across 20 tools

Disambiguation4/5

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.

Naming Consistency4/5

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.

Tool Count3/5

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.

Completeness4/5

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

ActivityActive
ResponsivenessWithin a week