Skip to main content
Glama
questdb

mcp-server-questdb

Official

apply_notebook_state

Replace a notebook's entire cell layout, values, and settings in one atomic call to bulk-edit multiple cells, restructure notebooks, or build one from scratch.

Instructions

Bulk-apply the entire desired state of a notebook in one atomic call. Use this for bulk edits spanning multiple cells or creating a notebook from scratch. Use update_cell or set_cell_* for small operations. Use INSTEAD OF chained add_cell + update_cell + set_cell_mode + set_cell_chart_config only when composing a multi-cell layout from scratch, changing many cells at once, or restructuring an existing notebook. The cells array is the COMPLETE desired list: cells in the current notebook whose id is missing from your request are DELETED. For new cells, omit id and one will be generated. Each cell carries exactly one of value (full verbatim SQL) or preserve_value: true (keep the existing cell's SQL, results, and run history unchanged). A changed value carries results over by content: a statement whose text is unchanged keeps its result, an edited or added one starts empty, and a rewrite that leaves nothing unchanged clears the cell's results — prefer preserve_value for every cell whose SQL you are not changing, and NEVER send a value reconstructed from a preview or a truncated get_cell read. Charts in mode='draw' render automatically — do not call run_cell afterwards. Cells with resolved mode='run' (explicit, or omitted: new defaults to 'run', existing preserves) auto-execute after the apply — EXCEPT cells whose statements include DDL/DML (INSERT/UPDATE/CREATE/DROP/...): those are NEVER auto-executed (their runs entry gets skipped: true), so applying state can never trigger a write's side effects. Take consent from the user, then call run_cell explicitly to execute them. Markdown cells (type:"markdown") are rendered prose and are likewise never auto-run. Auto-executed read-only cells run their statements in PARALLEL (one failure skips nothing; a statement rejected at validation is skipped with its validation error). Each cell also accepts auto_refresh — the same per-cell override set_cell_autorefresh writes. The response includes a runs: [{cellId, success, queryCount?, results?, error?, skipped?}] array — results is the per-statement status list ("success" / "cancelled" / "ERROR: <message>"); a top-level error is set only when the run was refused before any statement executed. The response also carries results_cleared: the ids of cells whose whole result this apply discarded (view:"editor", or a changed value that kept no statement unchanged); it is empty when nothing was cleared, and a cell that keeps some statement results is not listed. Always call get_workspace_state first; the state-freshness gate applies.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
cellsYesComplete desired cell list, in order. Cell at index N gets position N. Missing existing-cell ids are deleted.
buffer_idYes
variablesYesOrdered notebook-scoped global variables to be referenced as @var in the query (the DECLARE block surfaced in the Variables popover). Each item is {"name": "from", "value": "dateadd('d', -7, now())"}; names have no leading '@'. Order matters: if one variable references another, place the dependency first and the dependent variable later. Values are sent as a notebook-scoped `DECLARE` block prepended to each cell statement (or merged into the cell's own `DECLARE` block when present). Globals are server-resolved at parse time, so operator precedence and lexical shadowing follow QuestDB's `DECLARE` semantics. For non-`SELECT` statement forms (`INSERT`/`CREATE`/`UPDATE`/`ALTER`/…) globals are not injected; declare locally inside the inner `SELECT` if needed. Pass null to preserve current; pass [] to clear all.
layout_modeYesNotebook layout mode after this apply. Null preserves current.
maximized_cell_idYesSpotlight one cell id, or null to clear. Pass null to clear.
auto_refresh_defaultYesNotebook-level auto-refresh default after this apply. Cells with no per-cell auto_refresh inherit it. Null preserves current.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed19 schema fields changedv0.5.0
    • changedInput schema / properties / auto_refresh_default / anyOf
      Previous value: -[
      -  {
      -    "type": [
      -      "boolean",
      -      "null"
      -    ]
      -  },
      -  {
      -    "enum": [
      -      "1s",
      -      "5s",
      -      "10s",
      -      "30s",
      -      "1m"
      -    ],
      -    "type": "string"
      -  }
      -]New value: +[
      +  {
      +    "type": [
      +      "boolean",
      +      "null"
      +    ]
      +  },
      +  {
      +    "description": "Fixed interval: digits plus ms, s or m, from 50ms to 60m, e.g. \"250ms\", \"5s\", \"15m\".",
      +    "pattern": "^[1-9][0-9]*(ms|s|m)$",
      +    "type": "string"
      +  }
      +]
    • changedInput schema / properties / cells / items / properties / auto_refresh / anyOf
      Previous value: -[
      -  {
      -    "type": [
      -      "boolean",
      -      "null"
      -    ]
      -  },
      -  {
      -    "enum": [
      -      "1s",
      -      "5s",
      -      "10s",
      -      "30s",
      -      "1m"
      -    ],
      -    "type": "string"
      -  }
      -]New value: +[
      +  {
      +    "type": [
      +      "boolean",
      +      "null"
      +    ]
      +  },
      +  {
      +    "description": "Fixed interval: digits plus ms, s or m, from 50ms to 60m, e.g. \"250ms\", \"5s\", \"15m\".",
      +    "pattern": "^[1-9][0-9]*(ms|s|m)$",
      +    "type": "string"
      +  }
      +]
    • changedInput schema / properties / cells / items / properties / auto_refresh / description
      Previous value: -"Per-cell auto-refresh value: true = adaptive poll, false = off, or a fixed interval string (\"1s\"/\"5s\"/\"10s\"/\"30s\"/\"1m\"). Omitted or null stores NO override — the cell inherits the notebook's auto_refresh_default."New value: +"Per-cell auto-refresh value: true = adaptive poll, false = off, or a fixed interval string (digits plus ms, s or m, from 50ms to 60m, e.g. \"250ms\", \"5s\", \"15m\"). Omitted or null stores NO override — the cell inherits the notebook's auto_refresh_default."
    • addedInput schema / properties / cells / items / properties / editor_height
      Added value: +{
      +  "anyOf": [
      +    {
      +      "maximum": 2400,
      +      "minimum": 56,
      +      "type": "number"
      +    },
      +    {
      +      "enum": [
      +        "auto"
      +      ],
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "description": "Editor pane height in CSS pixels; for markdown, rendered content height. Null preserves an existing setting (default auto for a new cell); \"auto\" restores content-driven sizing. SQL editors are at least 72px, markdown at least 56px, and every pane at most 2400px. Use 86px for a polished markdown title."
      +}
    • changedInput schema / properties / cells / items / properties / grid / description
      Previous value: -"Grid position when layout_mode='grid'. The 12 columns apply to width only (w ≤ 12). Rendered cell box is h*10 + (h-1)*20 px (do NOT estimate with h*30); a fixed 44px header leaves (h*30 - 64)px of content; chart plots pad a further ~40px top and ~56-86px bottom. EXAMPLES: markdown h:3 -> 70px box / 26px text (the minimum); markdown h:5 -> 130px / 86px text (a title); chart h:10 -> 280px / ~140px of plot."New value: +"Grid position when layout_mode='grid'. The 12 columns apply to width only (w ≤ 12); height is derived from editor_height, result_height, and view."
    • removedInput schema / properties / cells / items / properties / grid / properties / h
      Removed value: -{
      -  "type": "integer"
      -}
    • addedInput schema / properties / cells / items / properties / grid / properties / w / maximum
      Added value: +12
    • addedInput schema / properties / cells / items / properties / grid / properties / w / minimum
      Added value: +1
    • addedInput schema / properties / cells / items / properties / grid / properties / x / maximum
      Added value: +11
    • addedInput schema / properties / cells / items / properties / grid / properties / x / minimum
      Added value: +0
    • addedInput schema / properties / cells / items / properties / grid / properties / y / minimum
      Added value: +0
    • changedInput schema / properties / cells / items / properties / grid / required
      Previous value: -[
      -  "x",
      -  "y",
      -  "w",
      -  "h"
      -]New value: +[
      +  "x",
      +  "y",
      +  "w"
      +]
    • addedInput schema / properties / cells / items / properties / highlight_config
      Added value: +{
      +  "additionalProperties": false,
      +  "description": "Highlight rules for the cell's result grids, applied to every statement's grid by column name. Omitting clears the cell's rules (full PUT) — copy the current `highlight_config` from <notebook_context> to keep them.",
      +  "properties": {
      +    "identity_columns": {
      +      "description": "Columns whose values identify the same row across refreshes, e.g. [\"symbol\",\"side\"]. Needed only by previous rules; may be empty otherwise. A grid missing any of them gets no comparison. Never the designated timestamp for latest-row queries.",
      +      "items": {
      +        "type": "string"
      +      },
      +      "type": "array"
      +    },
      +    "rules": {
      +      "items": {
      +        "additionalProperties": false,
      +        "properties": {
      +          "applies_to": {
      +            "description": "What a match colors: cell = only the matching cell, row = every cell of the row. null = cell. List order decides per cell; a row rule listed first paints the whole row, a cell rule listed first keeps its cell.",
      +            "enum": [
      +              "cell",
      +              "row",
      +              null
      +            ],
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "base_color": {
      +            "description": "steps: color for values below the lowest step. null = amber.",
      +            "enum": [
      +              "red",
      +              "teal",
      +              "amber",
      +              "lime",
      +              "orange",
      +              "purple",
      +              "green",
      +              "pink",
      +              "blue",
      +              "olive",
      +              null
      +            ],
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "color": {
      +            "description": "Cell color for previous and value rules; for between with fill gradient, the color at the `value` end. null = teal. Use green/red for up/down (the gain/loss pair).",
      +            "enum": [
      +              "red",
      +              "teal",
      +              "amber",
      +              "lime",
      +              "orange",
      +              "purple",
      +              "green",
      +              "pink",
      +              "blue",
      +              "olive",
      +              null
      +            ],
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "column": {
      +            "description": "Result column the rule targets. null = every numeric column (ignored by newRow).",
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "display": {
      +            "description": "UI labels: Flash = temporary (briefly highlight matching cells, then fade out); Permanent = always (keep matching cells highlighted until the next result). Use these UI labels when describing the display mode to the user, but send temporary or always in this field. null = temporary for previous rules, always otherwise.",
      +            "enum": [
      +              "temporary",
      +              "always",
      +              null
      +            ],
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "enabled": {
      +            "description": "false keeps the rule but skips it. null = enabled.",
      +            "type": [
      +              "boolean",
      +              "null"
      +            ]
      +          },
      +          "fill": {
      +            "description": "value between only. solid = one color inside the range (default). gradient = shade from `color` at `value` to `high_color` at `to`, mixed in between and clamped beyond the ends; matches every numeric cell. With value and to both null the scale always spans the column's current min and max.",
      +            "enum": [
      +              "solid",
      +              "gradient",
      +              null
      +            ],
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "high_color": {
      +            "description": "value between with fill gradient: the color at the `to` end. null = green.",
      +            "enum": [
      +              "red",
      +              "teal",
      +              "amber",
      +              "lime",
      +              "orange",
      +              "purple",
      +              "green",
      +              "pink",
      +              "blue",
      +              "olive",
      +              null
      +            ],
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "kind": {
      +            "description": "previous = compare with the same row in the previous result (needs identity_columns). newRow = a row whose identity was not in the previous result (needs identity_columns); it always paints the whole row and takes only color and display, so send column null. value = compare with a fixed value; op between with fill gradient shades by position in the range. steps = ascending thresholds, one color each, value >= from, highest wins; base_color below the first.",
      +            "enum": [
      +              "previous",
      +              "value",
      +              "steps",
      +              "newRow"
      +            ],
      +            "type": "string"
      +          },
      +          "op": {
      +            "description": "previous: gt|lt|changed|changedBy. value: gt|gte|lt|lte|eq|between|isNull|contains|matches. null for steps and newRow. Per column type: numeric takes everything except contains/matches; timestamp takes gt/lt/changed, the ordering ops, eq, between (solid fill) and isNull; text takes changed, eq, isNull, contains and matches; boolean takes changed, eq and isNull; other types (arrays, uuid, …) take changed and isNull. A condition that does not fit the column never matches.",
      +            "enum": [
      +              "gt",
      +              "gte",
      +              "lt",
      +              "lte",
      +              "changed",
      +              "changedBy",
      +              "eq",
      +              "between",
      +              "isNull",
      +              "contains",
      +              "matches",
      +              null
      +            ],
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "steps": {
      +            "description": "steps: thresholds; a value takes the highest step it reaches (value >= from). Below the lowest step it takes base_color.",
      +            "items": {
      +              "additionalProperties": false,
      +              "properties": {
      +                "color": {
      +                  "enum": [
      +                    "red",
      +                    "teal",
      +                    "amber",
      +                    "lime",
      +                    "orange",
      +                    "purple",
      +                    "green",
      +                    "pink",
      +                    "blue",
      +                    "olive"
      +                  ],
      +                  "type": "string"
      +                },
      +                "from": {
      +                  "type": "number"
      +                }
      +              },
      +              "required": [
      +                "from",
      +                "color"
      +              ],
      +              "type": "object"
      +            },
      +            "type": [
      +              "array",
      +              "null"
      +            ]
      +          },
      +          "text": {
      +            "description": "value contains: case-insensitive substring (eq on text ignores case too). value matches: an RE2 regular expression (linear time; no backreferences or lookarounds), e.g. ^EUR; there are no /…/flags, put (?i) in front to ignore case, e.g. (?i)eur.",
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "threshold": {
      +            "description": "previous changedBy: minimum change to match, inclusive (|change| >= threshold), 0 or more; 0 = any change, unchanged cells never match.",
      +            "type": [
      +              "number",
      +              "null"
      +            ]
      +          },
      +          "to": {
      +            "description": "value between: the upper bound, inclusive; a number with more than 15 significant digits as a string. null = auto: the column's current maximum, recomputed on every result.",
      +            "type": [
      +              "number",
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "unit": {
      +            "description": "previous changedBy: threshold unit. null = percent.",
      +            "enum": [
      +              "absolute",
      +              "percent",
      +              null
      +            ],
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "value": {
      +            "description": "value rules: the comparison value (gt/gte/lt/lte/eq), or the lower bound for between. Plain value, no quotes; a number with more than 15 significant digits (a LONG id past 2^53, a high-scale DECIMAL) as a string, so JSON keeps every digit. Timestamps as ISO strings only (YYYY-MM-DD[THH:mm[:ss[.fraction]]], up to 9 fraction digits), read as UTC unless they carry a zone; other date forms are rejected. For between, null = auto: the column's current minimum, recomputed on every result.",
      +            "type": [
      +              "number",
      +              "string",
      +              "null"
      +            ]
      +          }
      +        },
      +        "required": [
      +          "kind",
      +          "column",
      +          "enabled",
      +          "display",
      +          "applies_to",
      +          "color",
      +          "op",
      +          "value",
      +          "to",
      +          "threshold",
      +          "unit",
      +          "text",
      +          "steps",
      +          "base_color",
      +          "fill",
      +          "high_color"
      +        ],
      +        "type": "object"
      +      },
      +      "type": "array"
      +    }
      +  },
      +  "required": [
      +    "identity_columns",
      +    "rules"
      +  ],
      +  "type": [
      +    "object",
      +    "null"
      +  ]
      +}
    • removedInput schema / properties / cells / items / properties / is_view_maximized
      Removed value: -{
      -  "description": "Whether the cell's result view (chart OR table) fills the cell, hiding the editor. Null defaults to true when mode='draw'.",
      -  "type": [
      -    "boolean",
      -    "null"
      -  ]
      -}
    • changedInput schema / properties / cells / items / properties / mode / description
      Previous value: -"Cell mode. Null defaults to 'run' for new cells, preserves current for existing."New value: +"Requested result mode. Null defaults to 'run' for new cells and preserves the current mode for existing cells, except that view='editor' is authoritative and clears the presented mode. An explicit 'run' or 'draw' cannot be combined with view='editor'."
    • addedInput schema / properties / cells / items / properties / result_height
      Added value: +{
      +  "anyOf": [
      +    {
      +      "maximum": 2400,
      +      "minimum": 100,
      +      "type": "number"
      +    },
      +    {
      +      "enum": [
      +        "auto"
      +      ],
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "description": "Result/chart pane height in CSS pixels. Null preserves an existing setting (default auto for a new cell); \"auto\" restores result-driven sizing. Charts require at least 296px; tables 100px; maximum 2400px. Ignored for markdown; keep it null."
      +}
    • changedInput schema / properties / cells / items / properties / type / description
      Previous value: -"Cell kind. \"markdown\" makes this a rendered prose cell — `value` is markdown source, and `mode`/`chart_config` MUST be null (rejected otherwise). It is never executed or auto-run. Cell kind is STICKY: null/omitted preserves an existing cell's kind (it does NOT reset markdown cells to SQL); new cells default to \"sql\"."New value: +"Cell kind. \"markdown\" makes this a rendered prose cell — `value` is markdown source, and `mode`/`chart_config` MUST be null (rejected otherwise). It is never executed or auto-run. Cell kind is FIXED for the life of a cell: null/omitted preserves an existing cell's kind, and sending the other kind for an existing id is rejected — delete the cell and add a new one instead. New cells default to \"sql\"."
    • addedInput schema / properties / cells / items / properties / view
      Added value: +{
      +  "anyOf": [
      +    {
      +      "enum": [
      +        "editor",
      +        "result",
      +        "editor_result"
      +      ],
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "description": "The cell's pane view. Null preserves an existing setting. result and editor_result store the arrangement. editor is authoritative: it DISCARDS an existing cell's result and snapshot, clears its presented mode, and hides the result pane (the toggle-off gesture; the cell is then listed in results_cleared). Send mode:null with view:'editor'; an explicit mode 'run' or 'draw' is rejected as contradictory. New draw cells default to result; other new SQL cells default to editor_result but report editor with mode null until a result exists. Ignored for markdown; keep it null."
      +}
    • changedInput schema / properties / cells / items / required
      Previous value: -[
      -  "id",
      -  "name",
      -  "value",
      -  "preserve_value",
      -  "type",
      -  "mode",
      -  "auto_refresh",
      -  "is_view_maximized",
      -  "chart_config",
      -  "grid"
      -]New value: +[
      +  "id",
      +  "name",
      +  "value",
      +  "preserve_value",
      +  "type",
      +  "mode",
      +  "auto_refresh",
      +  "editor_height",
      +  "result_height",
      +  "view",
      +  "chart_config",
      +  "grid",
      +  "highlight_config"
      +]
  2. Changed4 schema fields changedv0.3.1
    • addedInput schema / properties / auto_refresh_default
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": [
      +        "boolean",
      +        "null"
      +      ]
      +    },
      +    {
      +      "enum": [
      +        "1s",
      +        "5s",
      +        "10s",
      +        "30s",
      +        "1m"
      +      ],
      +      "type": "string"
      +    }
      +  ],
      +  "description": "Notebook-level auto-refresh default after this apply. Cells with no per-cell auto_refresh inherit it. Null preserves current."
      +}
    • changedInput schema / properties / cells / items / properties / auto_refresh / description
      Previous value: -"Auto-refresh for draw cells: true = adaptive poll, false = off, or a fixed interval string (\"1s\"/\"5s\"/\"10s\"/\"30s\"/\"1m\"). Null defaults to true (adaptive) when mode='draw'."New value: +"Per-cell auto-refresh value: true = adaptive poll, false = off, or a fixed interval string (\"1s\"/\"5s\"/\"10s\"/\"30s\"/\"1m\"). Omitted or null stores NO override — the cell inherits the notebook's auto_refresh_default."
    • changedInput schema / properties / cells / items / properties / grid / description
      Previous value: -"Grid position when layout_mode='grid'. x/y/w/h in 12-column units (w ≤ 12)."New value: +"Grid position when layout_mode='grid'. The 12 columns apply to width only (w ≤ 12). Rendered cell box is h*10 + (h-1)*20 px (do NOT estimate with h*30); a fixed 44px header leaves (h*30 - 64)px of content; chart plots pad a further ~40px top and ~56-86px bottom. EXAMPLES: markdown h:3 -> 70px box / 26px text (the minimum); markdown h:5 -> 130px / 86px text (a title); chart h:10 -> 280px / ~140px of plot."
    • changedInput schema / required
      Previous value: -[
      -  "buffer_id",
      -  "layout_mode",
      -  "maximized_cell_id",
      -  "variables",
      -  "cells"
      -]New value: +[
      +  "buffer_id",
      +  "layout_mode",
      +  "auto_refresh_default",
      +  "maximized_cell_id",
      +  "variables",
      +  "cells"
      +]
  3. First observedv0.3.0

TDQS

A4.8/5.0
Behavior5/5

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 atomic deletion semantics, preserve_value behavior, result-carryover rules, automatic chart rendering, auto-execution exclusions for DDL/DML and markdown, parallel execution, response shape (runs, results_cleared), and the state-freshness gate.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but front-loaded with the core purpose and routing guidance, and most sentences carry operational detail needed for a destructive bulk API. Some clauses are dense and could be tightened, but the size is justified by the tool's complexity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex bulk mutation tool with no output schema, the description covers deletion semantics, auto-execution rules, consent requirements, response fields, and state freshness. It leaves no major behavioral gap an agent would need to call the tool safely.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 83%, so the baseline is 3, but the description adds substantial meaning beyond the schema: the cells array is a complete desired list where missing ids are deleted, value and preserve_value are mutually exclusive alternatives, and apply is a full PUT that clears omitted chart/highlight/name settings.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a precise verb (bulk-apply), resource (notebook state), and atomic scope, and explicitly contrasts itself with update_cell, set_cell_*, and chained add_cell/update_cell operations. An agent can tell exactly what this tool replaces without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives explicit when-to-use conditions (bulk edits across cells, notebook from scratch, multi-cell layout, many-cell changes, restructuring) and when-not-to-use alternatives (small operations via update_cell or set_cell_*). It also mandates calling get_workspace_state first and taking consent before run_cell.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.