{"server":"md.mostlyright/datasets","count":6,"changes":[{"tool":"query_table","kind":"changed","observed_at":"2026-10-09T20:19:54.916Z","fields":["description","inputSchema"],"description_before":"A bounded, structured query over one table's current version. NO SQL: send columns, filters, order_by, aggregates, group_by and limit as JSON. Example: {\"table_id\": \"0f2f...\", \"columns\": [\"observed_at\", \"air_temp_f\"], \"filters\": [{\"column\": \"air_temp_f\", \"operator\": \"gte\", \"value\": 80}], \"order_by\": [{\"column\": \"observed_at\", \"direction\": \"desc\"}], \"limit\": 50}. Ceilings: 64 columns, 8 filters, 2 sort keys, 4 aggregates, 4 group keys, 10000 rows a page (25 when limit is omitted), 8 MiB of JSON. Every page answers with next_cursor; send it back as cursor (same columns, filters and order_by) for the next page until it is null, and you have read the whole table on one immutable version. Operators, all ANDed: eq, neq, in (an array of at most 20 values), gt, gte, lt, lte, is_null and is_not_null (no value), between (exactly two non-null bounds, inclusive at both ends), and contains, starts_with and ends_with (one non-empty string, case-sensitive, text columns only). Dates and timestamps compare as ISO strings, so a month is one between or a gte plus an lt. GROUPED AGGREGATES: send group_by beside aggregates for one row per distinct combination, keyed by the group column names and the aggregate aliases. Example: {\"table_id\": \"0f2f...\", \"columns\": [\"station\"], \"group_by\": [\"station\"], \"aggregates\": [{\"function\": \"avg\", \"column\": \"air_temp_f\", \"as\": \"avg_temp\"}], \"order_by\": [{\"column\": \"avg_temp\", \"direction\": \"desc\"}], \"limit\": 10}. An aggregate answers under its `as`, or under {function}_{column or \"all\"}_{position} without one. group_by needs at least one aggregate, no two result columns may share a name and names are compared without case (group_by [\"city\"] refuses an alias of \"CITY\", and two aggregates cannot share one alias), and order_by may only name a group column or an alias. limit counts GROUPS, execution.truncated means the limit was reached so there may be more groups, and a grouped answer has no next page: next_cursor is null and offset and cursor stay refused beside aggregates. Without group_by an aggregate query returns one row for the whole table and cannot be ordered. Requires an mr_use_ workspace key (Authorization: Bearer) or an OAuth connection. The first query over a dataset connects it to the workspace; connect_dataset makes that explicit but is not required. Returns rows plus table.version_id and table.content_digest; cite those. For the whole table in one request, download the Parquet instead.","description_after":"A bounded, structured query over one table's current version. NO SQL: send columns, filters, order_by, aggregates, group_by and limit as JSON. Example: {\"table_id\": \"0f2f...\", \"columns\": [\"observed_at\", \"air_temp_f\"], \"filters\": [{\"column\": \"air_temp_f\", \"operator\": \"gte\", \"value\": 80}], \"order_by\": [{\"column\": \"observed_at\", \"direction\": \"desc\"}], \"limit\": 50}. Ceilings: 64 columns, 8 filters, 2 sort keys, 4 aggregates, 4 group keys, 10000 rows a page (25 when limit is omitted), 8 MiB of JSON. Every page answers with next_cursor; send it back as cursor (same columns, filters and order_by) for the next page until it is null, and you have read the whole table on one immutable version. Operators, all ANDed: eq, neq, in (an array of at most 20 values), gt, gte, lt, lte, is_null and is_not_null (no value), between (exactly two non-null bounds, inclusive at both ends), and contains, starts_with and ends_with (one non-empty string, case-sensitive, text columns only). Dates and timestamps compare as ISO strings, so a month is one between or a gte plus an lt. GROUPED AGGREGATES: send group_by beside aggregates for one row per distinct combination, keyed by the group column names and the aggregate aliases. Example: {\"table_id\": \"0f2f...\", \"columns\": [\"station\"], \"group_by\": [\"station\"], \"aggregates\": [{\"function\": \"avg\", \"column\": \"air_temp_f\", \"as\": \"avg_temp\"}], \"order_by\": [{\"column\": \"avg_temp\", \"direction\": \"desc\"}], \"limit\": 10}. An aggregate answers under its `as`, or under {function}_{column or \"all\"}_{position} without one. group_by needs at least one aggregate, no two result columns may share a name and names are compared without case (group_by [\"city\"] refuses an alias of \"CITY\", and two aggregates cannot share one alias), and order_by may only name a group column or an alias. limit counts GROUPS, execution.truncated means the limit was reached so there may be more groups, and a grouped answer has no next page: next_cursor is null and offset and cursor stay refused beside aggregates. Without group_by an aggregate query returns one row for the whole table and cannot be ordered. TEXT SEARCH: when get_table_schema returns capabilities.search, add search: {\"text\": \"...\", \"columns\": its keyword_columns (or some of them), \"spec_digest\": its spec_digest} to match rows containing every term, beside any filters. execution.search_mode says how it ran and execution.search_scope what the count covers. A refusal saying search is unavailable means the table's version changed: call get_table_schema again. Requires an mr_use_ workspace key (Authorization: Bearer) or an OAuth connection. The first query over a dataset connects it to the workspace; connect_dataset makes that explicit but is not required. Returns rows plus table.version_id and table.content_digest; cite those, and timings: the milliseconds each server step took. For the whole table in one request, download the Parquet instead.","description_similarity":0.85,"before_hash":"4835e0449eee70736b723a665b700e2c","after_hash":"2d0108d48857c467dd1134b2aa96048f"},{"tool":"get_table_schema","kind":"changed","observed_at":"2026-10-09T20:19:54.916Z","fields":["description"],"description_before":"One table's columns (name, type, and any published profile such as null counts, distinct counts or ranges), its immutable version_id, and its capabilities: whether anonymous sampling, keyed querying and Parquet download are enabled. Example: {\"table_id\": \"0f2f6bfa-4a63-4f75-9a0b-1a7d9c5b2e10\"}. Read this before writing a query_table call: the column names it lists are the only ones the query grammar accepts.","description_after":"One table's columns (name, type, and any published profile such as null counts, distinct counts or ranges), its immutable version_id, and its capabilities: whether anonymous sampling, keyed querying and Parquet download are enabled, and, when the publisher declared text search, capabilities.search (spec_digest and keyword_columns for query_table's search). Example: {\"table_id\": \"0f2f6bfa-4a63-4f75-9a0b-1a7d9c5b2e10\"}. Read this before writing a query_table call: the column names it lists are the only ones the query grammar accepts.","description_similarity":0.855,"before_hash":"d832ae49eb49e71290de9ac07429c6bb","after_hash":"d55b0e53e796943c809c2891f7c636e9"},{"tool":"list_source_credentials","kind":"changed","observed_at":"2026-10-08T19:46:57.531Z","fields":["description","inputSchema"],"description_before":"The NAMES of the API keys and passwords this workspace has stored for its sources, with their status and when they were added. Never a value — no secret ever crosses this server. Takes no arguments. Returns {credentials: [{name, status, created_at, rotated_at, rotation_generation}], count, paste_url}. A recipe references a credential by name, so this is how you learn which names exist. If the one you need is missing, ask the person to paste it at the paste_url — you cannot add it and must not ask them to send it to you.","description_after":"Lists named secret metadata and saved workspace connections without secret values. Pass dataset_id to check dataset grants. Follow connections.next_offset with connection_offset to read another bounded page. Returns credentials, connections (status, safe items, grants_status), and a structured source_credentials setup request for the chat UI. Stored or active means enrolled, not a verified provider login. If setup or dataset access is missing, stop and ask the person to use the secure setup card or settings links; never request a key, password, private key or verification code in chat. After the person returns, call this again before resuming. Connection recipes use connection_id, version_digest and source_parameters; values never belong in a recipe.","description_similarity":0.168,"before_hash":"7ca7114f8b63b3ed88c4f7f55bd5b5fe","after_hash":"81e45182f1441627af9833207bc431b1"},{"tool":"update_dataset","kind":"changed","observed_at":"2026-09-29T17:30:41.735Z","fields":["description"],"description_before":"A changed nonblank description automatically generates a factual dataset name and topic tags. Supply name only when deliberately overriding the generated title. Drafts and unchanged descriptions do not generate metadata. Changes a dataset's display name, its description, or both. Reads the current version first and sends it as the precondition, so a change made elsewhere in between is refused rather than overwritten. The opening paragraph is the search snippet and the answer-engine summary, so it says what this is, then what it is for, then the facts, and never opens with the grain, the mechanism or a station code. Example: {\"dataset_id\": \"…\", \"description\": \"Denver weather history since 2020: every airport report from Denver International (KDEN) with the official daily high and low. Built for daily temperature forecasting and for checking the weather at any hour. One row per report, about 30 a day, refreshed each morning with the previous day added.\"}. Returns {dataset_id, name, description, version, dashboard_url}. A workspace admits one dataset per name; a name already taken is refused. Nothing rebuilds — this is metadata only. If public_projection_synced is false, retry with {dataset_id, sync_only: true} to update the public page without repeating the metadata write.","description_after":"A changed nonblank description generates a factual dataset name and topic tags, except a name or tags someone chose in an earlier update, which stay. The name given at create_dataset is a working title and does not count as chosen. Send name with the description to keep a title you want; it then stays until someone sends another. Drafts and unchanged descriptions do not generate metadata. Changes a dataset's display name, its description, or both. Reads the current version first and sends it as the precondition, so a change made elsewhere in between is refused rather than overwritten. The opening paragraph is the search snippet and the answer-engine summary, so it says what this is, then what it is for, then the facts, and never opens with the grain, the mechanism or a station code. Example: {\"dataset_id\": \"…\", \"description\": \"Denver weather history since 2020: every airport report from Denver International (KDEN) with the official daily high and low. Built for daily temperature forecasting and for checking the weather at any hour. One row per report, about 30 a day, refreshed each morning with the previous day added.\"}. Returns {dataset_id, name, description, version, dashboard_url}. A workspace admits one dataset per name; a name already taken is refused. Nothing rebuilds — this is metadata only. If public_projection_synced is false, retry with {dataset_id, sync_only: true} to update the public page without repeating the metadata write.","description_similarity":0.827,"before_hash":"aeb7776d9692d4bbc3059fbbd6307713","after_hash":"0fce410d05d9f368093746605baef4b3"},{"tool":"create_dataset","kind":"changed","observed_at":"2026-09-29T17:30:41.735Z","fields":["description"],"description_before":"Creates an empty dataset in your workspace and returns its ids. A dataset is the container a recipe binds to; it exists before it has a description, a recipe or a single row, which is the point — the page opens on it and fills in. The name leads with the subject a searcher would type and then the place, never with a grain word, a mechanism, a publisher or a station code. Example: {\"name\": \"Denver weather history since 2020\", \"description\": \"Denver weather history since 2020: every airport report from Denver International (KDEN) with the official daily high and low.\"}. Returns {dataset_id, cloud_dataset_id, name, description, dashboard_url}. dataset_id is the id every other build tool takes; cloud_dataset_id is only for the dashboard URL. Costs nothing to run and builds nothing. Next: write a recipe and call register_recipe with this dataset_id in its dataset block. If the user handed you a dataset id from its page, build into that id and do not call this. While a dataset the user just created for their request is still empty, this returns that dataset (reused_open_request: true) rather than creating a second one. Pass separate: true only when the user has asked for another, separate dataset.","description_after":"Creates an empty dataset in your workspace and returns its ids. A dataset is the container a recipe binds to; it exists before it has a description, a recipe or a single row, which is the point — the page opens on it and fills in. The name leads with the subject a searcher would type and then the place, never with a grain word, a mechanism, a publisher or a station code. When this creates a dataset, the name given here is a working title: the first update_dataset that sends a changed, nonblank description replaces it with a generated one, unless that call sends the name too. Example: {\"name\": \"Denver weather history since 2020\", \"description\": \"Denver weather history since 2020: every airport report from Denver International (KDEN) with the official daily high and low.\"}. Returns {dataset_id, cloud_dataset_id, name, description, dashboard_url}. dataset_id is the id every other build tool takes; cloud_dataset_id is only for the dashboard URL. Costs nothing to run and builds nothing. Next: write a recipe and call register_recipe with this dataset_id in its dataset block. If the user handed you a dataset id from its page, build into that id and do not call this. While a dataset the user just created for their request is still empty, this returns that dataset (reused_open_request: true) rather than creating a second one. Pass separate: true only when the user has asked for another, separate dataset.","description_similarity":0.895,"before_hash":"7fbb25214925f078f13ed6b2891f49c9","after_hash":"fbccc1ab47535b857d582900aacf1edd"},{"tool":"create_dataset","kind":"changed","observed_at":"2026-09-29T11:52:28.668Z","fields":["description","inputSchema"],"description_before":"Creates an empty dataset in your workspace and returns its ids. A dataset is the container a recipe binds to; it exists before it has a description, a recipe or a single row, which is the point — the page opens on it and fills in. The name leads with the subject a searcher would type and then the place, never with a grain word, a mechanism, a publisher or a station code. Example: {\"name\": \"Denver weather history since 2020\", \"description\": \"Denver weather history since 2020: every airport report from Denver International (KDEN) with the official daily high and low.\"}. Returns {dataset_id, cloud_dataset_id, name, description, dashboard_url}. dataset_id is the id every other build tool takes; cloud_dataset_id is only for the dashboard URL. Costs nothing to run and builds nothing. Next: write a recipe and call register_recipe with this dataset_id in its dataset block.","description_after":"Creates an empty dataset in your workspace and returns its ids. A dataset is the container a recipe binds to; it exists before it has a description, a recipe or a single row, which is the point — the page opens on it and fills in. The name leads with the subject a searcher would type and then the place, never with a grain word, a mechanism, a publisher or a station code. Example: {\"name\": \"Denver weather history since 2020\", \"description\": \"Denver weather history since 2020: every airport report from Denver International (KDEN) with the official daily high and low.\"}. Returns {dataset_id, cloud_dataset_id, name, description, dashboard_url}. dataset_id is the id every other build tool takes; cloud_dataset_id is only for the dashboard URL. Costs nothing to run and builds nothing. Next: write a recipe and call register_recipe with this dataset_id in its dataset block. If the user handed you a dataset id from its page, build into that id and do not call this. While a dataset the user just created for their request is still empty, this returns that dataset (reused_open_request: true) rather than creating a second one. Pass separate: true only when the user has asked for another, separate dataset.","description_similarity":0.757,"before_hash":"94bce55a0f8fd743025b972554a14ed8","after_hash":"7fbb25214925f078f13ed6b2891f49c9"}],"next_before":null,"next_actions":[]}