Skip to main content
Glama

Draw a saved canvas with its figures

well_render_canvas
Read-only

Draw one of the workspace's saved canvases WITH its figures, from the readings you already have.

This tool measures nothing. Every figure on the board is one you measured through that block's own skill and validated through that block's own render tool; this checks that each reading is that tool's output for that block and that its figure can be drawn, and returns the board with each "figure" block carrying that reading as its params. The block draws its counter, its chart, and any partial-read disclosure from that one reading. It stores nothing, except on the turn a person pressed the board's Refresh button: Well then keeps these readings as the board's private snapshot with today's date, and the board page shows them under that date until the next Refresh. You do nothing different on that turn: measure each block through its skill and call this tool as always.

WORKFLOW — run it in this order, and do not shorten it:

  1. well_show_canvas({ canvas_view_id }) → the board's version, and its blocks, each with the feed it reads and the window it asks for.

  2. For EVERY block that names a feed, run that feed's own skill over that block's window, and call its render tool (well_render_cash_position, well_render_burn, well_render_mrr, well_render_runway, well_render_cost_structure, well_render_cash_forecast, well_render_cash_flow_bridge, well_render_logged_time). A logged-time block takes no skill and no window: call well_render_logged_time with no arguments.

  3. well_render_canvas({ canvas_view_id, version, blocks }) → the version well_show_canvas returned, and one entry per feed block carrying the structuredContent that tool returned, forwarded WHOLE.

Forward each reading as it came back, and send nothing beside it. Do not rebuild it, do not round it, and do not compute a figure to put in it. A reading that is not its block's own render tool output, or that covers a different window than its block asks for, is refused. A partial reading draws, and the block states that it is partial. A reading whose figure still cannot be drawn (a runway with no measured span, an amount that is not a number) does not refuse the board: the block draws as one that cannot be drawn, every other block draws, and the result lists it under not_drawn. That is a data state on Well's side, never one the reader can fix: say which block did not draw and that nothing is asked of them, and do not ask them to correct rows or rerun anything.

Every feed block on the canvas needs an entry. A board drawn with one block missing is refused rather than drawn short, because a board missing a figure reads as a board whose figure is zero. Blocks that name no feed — a note — carry their own words already and take no entry. If the canvas changed since you read it, the draw is refused: read it again and measure what it holds now.

Do NOT use this to answer a question about one figure: that is the figure's own render tool, and a board is not an answer to it. Do not use it to save or rearrange a board, which is well_upsert_canvas.

Input Schema

NameRequiredDescriptionDefault
blocksYesOne entry per block on the canvas that names a feed. A note takes none.
versionYesThe canvas version well_show_canvas returned. The readings were measured against that version's blocks.
workspace_idNoTarget workspace. Omit when the token authorizes one workspace. Required when it authorizes several: this read reports one workspace's own figures and will not choose which.
canvas_view_idYesThe canvas to draw. Read it first with well_show_canvas.
conversation_idNoThe conversation id returned by the previous Well result, in its meta under well/conversation_id, in its structuredContent, or in its JSON text block. Pass it back on every call in the same conversation, including a call a card makes, so the chosen workspace and the earlier answers still apply. It decides the conversation on its own: nothing the host states about the session replaces it. Omit it only on the first call of a conversation.
workspace_scopeNoRestrict this call to the family: the token's primary workspace plus the company workspaces under it that you are a member of. Unrelated authorized workspaces drop out of the read; row reads merge across the family, while a per-workspace figure still reports exactly one workspace.

Output Schema

NameRequiredDescriptionDefault
nameNo
errorNo
tilesNo
successYes
versionNo
can_editNo
not_drawnNo
author_nameNo
layout_modeNo
grid_columnsNo
canvas_view_idNo
tiles_resolvedNo
conversation_idNoThe conversation this result belongs to. Pass it back as the conversation_id argument on every later Well call in the same conversation.
newly_connectedNoTools that connected to this workspace since the last result and that no session was told about yet, oldest first. Present only when at least one tool is new. The connected-line rule says when to tell an entry; when it does, one line per entry, in the person's language: "<name> is connected.". Never fold several entries into one sentence. Each entry comes back once, on one result only: never repeat its line later.
origin_template_idNo
resolved_workspaceNoThe workspace that answered, when the caller named none and the token authorizes several.
conversation_id_noteNoPresent only when the server opened a fresh lane, stating that no choice recorded earlier was read.
conversation_id_sourceNoWhere the conversation id came from: the host's own request meta, the caller's argument, or a fresh lane the server opened.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed14 schema fields changed
    • removedOutput schema / properties / author_name / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / properties / author_name / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • removedOutput schema / properties / grid_columns / anyOf
      Removed value: -[
      -  {
      -    "type": "number"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / properties / grid_columns / type
      Added value: +[
      +  "number",
      +  "null"
      +]
    • removedOutput schema / properties / origin_template_id / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / properties / origin_template_id / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • removedOutput schema / properties / resolved_workspace / properties / name / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / properties / resolved_workspace / properties / name / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • removedOutput schema / properties / tiles / items / properties / mark / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / properties / tiles / items / properties / mark / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • removedOutput schema / properties / tiles / items / properties / source / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / properties / tiles / items / properties / source / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • removedOutput schema / properties / tiles / items / properties / title / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / properties / tiles / items / properties / title / type
      Added value: +[
      +  "string",
      +  "null"
      +]
  2. Changed3 schema fields changed
    • addedOutput schema / properties / author_name
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ]
      +}
    • addedOutput schema / properties / can_edit
      Added value: +{
      +  "type": "boolean"
      +}
    • changedOutput schema / properties / newly_connected / description
      Previous value: -"Tools that connected to this workspace since the last result and that no session was told about yet, oldest first. Present only when at least one tool is new. Start the reply with one line per entry, in the person's language: \"<name> is connected.\". Never fold several entries into one sentence. Each entry comes back once, on one result only: never repeat its line later."New value: +"Tools that connected to this workspace since the last result and that no session was told about yet, oldest first. Present only when at least one tool is new. The connected-line rule says when to tell an entry; when it does, one line per entry, in the person's language: \"<name> is connected.\". Never fold several entries into one sentence. Each entry comes back once, on one result only: never repeat its line later."
  3. Changed1 schema field changed
    • addedInput schema / properties / workspace_scope
      Added value: +{
      +  "description": "Restrict this call to the family: the token's primary workspace plus the company workspaces under it that you are a member of. Unrelated authorized workspaces drop out of the read; row reads merge across the family, while a per-workspace figure still reports exactly one workspace.",
      +  "enum": [
      +    "family"
      +  ],
      +  "type": "string"
      +}
  4. Changed1 schema field changed
    • addedOutput schema / properties / newly_connected
      Added value: +{
      +  "description": "Tools that connected to this workspace since the last result and that no session was told about yet, oldest first. Present only when at least one tool is new. Start the reply with one line per entry, in the person's language: \"<name> is connected.\". Never fold several entries into one sentence. Each entry comes back once, on one result only: never repeat its line later.",
      +  "items": {
      +    "additionalProperties": false,
      +    "properties": {
      +      "name": {
      +        "type": "string"
      +      },
      +      "service_id": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "name",
      +      "service_id"
      +    ],
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
  5. Changed1 schema field changed
    • addedOutput schema / properties / not_drawn
      Added value: +{
      +  "items": {
      +    "additionalProperties": false,
      +    "properties": {
      +      "reason": {
      +        "type": "string"
      +      },
      +      "source": {
      +        "type": "string"
      +      },
      +      "tile_id": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "tile_id",
      +      "source",
      +      "reason"
      +    ],
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
  6. Added

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the readOnly/non-destructive annotations: it discloses that every feed block needs an entry or the board is refused, that a stale version causes refusal, that partial readings still draw with disclosure, that undrawable readings land in `not_drawn` and are a data state never the reader's fault, and that on a person-triggered Refresh turn the readings are kept as a dated private snapshot. That snapshot note is a nuance the readOnlyHint annotation cannot express, but the description states it explicitly rather than contradicting the annotation.

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?

Front-loads the key distinction ('measures nothing'), then a clean numbered workflow, then exclusions. It is long, but nearly every sentence carries an operational constraint. Slight redundancy around the refusal/partial-reading cases, which are restated in several forms.

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?

An output schema exists, so return shape needn't be explained, and the description nonetheless covers failure modes, staleness, partial data and the not_drawn list. For a six-parameter orchestrator that chains seven sibling render tools, this is complete enough that an agent could execute it without further clarification.

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 100%, so the schema already documents canvas_view_id, version, blocks and workspace_id. The description still adds real semantics the schema can't: readings must be forwarded WHOLE and unmodified, one entry per feed block but none for notes, and version must match what well_show_canvas returned. Minor deduction because the guidance arrives embedded in prose rather than tied explicitly to each parameter.

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?

States a specific verb and resource ('Draw one of the workspace's saved canvases WITH its figures') and immediately clarifies the crucial scope boundary: 'This tool measures nothing.' It separates itself from well_show_canvas (reads the board) and well_upsert_canvas (saves/rearranges) by name.

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?

Gives a numbered when-to-use workflow (show_canvas → per-block render tool → render_canvas) and explicit exclusions at the end: not for a single figure (use the figure's own render tool), not for saving/rearranging (use well_upsert_canvas). Nothing is left to inference.

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

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources