Ask your AI to write in your Wren notebook: notes, graphs, flowcharts and formulas as real ink.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Add a NEW handwritten page at the end of an existing note (same drawing inputs as create_handwritten_note). Returns which note and page the ink went to and the note's outline; use it whenever the user stays on the note's topic.
The note's id, from list_notes or a previous Wren result.
The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. "claude-opus-4-8", "gpt-5.2"). Used for analytics only. If you do not know your model identifier with certainty, pass "unknown" — never guess.
Older way to name the kind; omit it when you send `figure` (figure.kind is the kind). Call get_drawing_guide for every kind, its exact arguments and an example.
PREFERRED way to say what to draw: one figure of a registered kind. graph_2d: content {prompt:'y = sin(x)'} (ONE equation, no words) or {xRange, yRange, curves:[{equation}]}. annotation: content {text} or {lines}. Every other kind: its fields. Do not combine with kind / prompt / sourceSpec / graphSpec for the drawing itself.
Not a drawing by itself: a call with only `prompt` is refused. Send `figure` (graph: figure.content.prompt = "y = x^2 - 4"; text: figure.content.text = the exact words). Here `prompt` is only the note's title and search text; Wren never reads words in it for what to draw (exactly one equation, e.g. "y = x^2 - 4", is read as that curve).
Study context (subject, course, level, goal, language, coveredSoFar, userRequest).
Explicit graph_2d spec: one `equation` per curve, the window in xRange / yRange. A prompt that is exactly one equation (e.g. "y = x^2 - 4") still decides the curve; words in the prompt are never read. Prefer `figure`.
Structured content for a non-graph drawing; `kind` selects the shape. annotation: {text} or {lines}; formula: {latex} or {expr}; table: {headers, rows}; bar_chart: {bars}; timeline: {events}; flowchart / concept_map: {nodes, edges} (a concept map reads best with at most 10 nodes per page; bigger ones split over pages automatically); mindmap: {root, branches}. Write the user's content into it faithfully — Wren draws exactly this, it never invents content.
Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request.
This tool isn't marked read-only, so it may change something on the server or in an account you connected.
Pick your client. Values in capitals, like YOUR_TOKEN, are yours to fill in.
Run in a terminal
claude mcp add --transport http wren https://mcp.wrenapp.ai/mcp
Add a NEW handwritten page at the end of an existing note (same drawing inputs as create_handwritten_note). Returns which note and page the ink went to and the note's outline; use it whenever the user stays on the note's topic.
| Argument | Type | Description |
|---|---|---|
| kind | string | Older way to name the kind; omit it when you send `figure` (figure.kind is the kind). Call get_drawing_guide for every kind, its exact arguments and an example. |
| figure | object | PREFERRED way to say what to draw: one figure of a registered kind. graph_2d: content {prompt:'y = sin(x)'} (ONE equation, no words) or {xRange, yRange, curves:[{equation}]}. annotation: content {text} or {lines}. Every other kind: its fields. Do not combine with kind / prompt / sourceSpec / graphSpec for the drawing itself. |
| noteId* | string | The note's id, from list_notes or a previous Wren result. |
| prompt | string | Not a drawing by itself: a call with only `prompt` is refused. Send `figure` (graph: figure.content.prompt = "y = x^2 - 4"; text: figure.content.text = the exact words). Here `prompt` is only the note's title and search text; Wren never reads words in it for what to draw (exactly one equation, e.g. "y = x^2 - 4", is read as that curve). |
| context | object | Study context (subject, course, level, goal, language, coveredSoFar, userRequest). |
| graphSpec | object | Explicit graph_2d spec: one `equation` per curve, the window in xRange / yRange. A prompt that is exactly one equation (e.g. "y = x^2 - 4") still decides the curve; words in the prompt are never read. Prefer `figure`. |
| llm_model* | string | The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. "claude-opus-4-8", "gpt-5.2"). Used for analytics only. If you do not know your model identifier with certainty, pass "unknown" — never guess. |
| sourceSpec | any | Structured content for a non-graph drawing; `kind` selects the shape. annotation: {text} or {lines}; formula: {latex} or {expr}; table: {headers, rows}; bar_chart: {bars}; timeline: {events}; flowchart / concept_map: {nodes, edges} (a concept map reads best with at most 10 nodes per page; bigger ones split over pages automatically); mindmap: {root, branches}. Write the user's content into it faithfully — Wren draws exactly this, it never invents content. |
| conversation_id | string | Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"noteId",
"llm_model"
],
"properties": {
"kind": {
"enum": [
"graph_2d",
"annotation",
"handwritten_text",
"formula",
"table",
"bar_chart",
"timeline",
"flowchart",
"mindmap",
"concept_map",
"checklist",
"labelled_diagram",
"study_page",
"worked_solution",
"shapes",
"number_line"
],
"type": "string",
"description": "Older way to name the kind; omit it when you send `figure` (figure.kind is the kind). Call get_drawing_guide for every kind, its exact arguments and an example."
},
"figure": {
"type": "object",
"required": [
"kind"
],
"properties": {
"kind": {
"enum": [
"graph_2d",
"annotation",
"table",
"bar_chart",
"timeline",
"flowchart",
"mindmap",
"concept_map",
"formula",
"study_page",
"worked_solution",
"checklist",
"labelled_diagram",
"shapes",
"number_line"
],
"type": "string"
},
"content": {
"anyOf": [
{
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
{
"type": "string"
}
],
"description": "That kind's fields, the same as sourceSpec without `kind`. get_drawing_guide lists them per kind."
}
},
"description": "PREFERRED way to say what to draw: one figure of a registered kind. graph_2d: content {prompt:'y = sin(x)'} (ONE equation, no words) or {xRange, yRange, curves:[{equation}]}. annotation: content {text} or {lines}. Every other kind: its fields. Do not combine with kind / prompt / sourceSpec / graphSpec for the drawing itself.",
"additionalProperties": {}
},
"noteId": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The note's id, from list_notes or a previous Wren result."
},
"prompt": {
"type": "string",
"maxLength": 2000,
"minLength": 1,
"description": "Not a drawing by itself: a call with only `prompt` is refused. Send `figure` (graph: figure.content.prompt = \"y = x^2 - 4\"; text: figure.content.text = the exact words). Here `prompt` is only the note's title and search text; Wren never reads words in it for what to draw (exactly one equation, e.g. \"y = x^2 - 4\", is read as that curve)."
},
"context": {
"type": "object",
"description": "Study context (subject, course, level, goal, language, coveredSoFar, userRequest).",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
}
},
"graphSpec": {
"type": "object",
"required": [
"kind",
"xRange",
"yRange",
"curves"
],
"properties": {
"kind": {
"enum": [
"graph_2d"
],
"type": "string"
},
"marks": {
"type": "array",
"items": {
"type": "object",
"required": [
"at"
],
"properties": {
"at": {
"type": "array",
"items": {
"type": "number"
},
"maxItems": 2,
"minItems": 2,
"description": "[x, y]"
},
"label": {
"type": "string"
}
}
}
},
"shade": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "number"
},
"from": {
"type": "number"
}
},
"description": "The area between the curve and the x-axis to hatch, from x = from to x = to (a definite integral). Wren also draws dashed bound lines and widens the window to show it."
},
"curves": {
"type": "array",
"items": {
"type": "object",
"properties": {
"expr": {
"type": "string",
"maxLength": 200,
"description": "The curve as a bare function of x (no \"y =\"), e.g. \"sin(x)\", \"e^x\", \"1/x\", \"abs(x)\". Alternative to `equation`."
},
"poly": {
"type": "array",
"items": {
"type": "number"
},
"minItems": 1,
"description": "Fallback for polynomials only — prefer `equation`. Coefficients in ASCENDING power order: y = x^2 - 4 is [-4, 0, 1]; y = 2x + 3 is [3, 2]."
},
"label": {
"type": "string"
},
"equation": {
"type": "string",
"maxLength": 200,
"description": "PREFERRED. The curve as an equation in x, e.g. \"y = x^2 - 4\", \"y = sin(x)\", \"y = e^x\", \"y = 1/x\". Wren parses it exactly."
}
}
},
"minItems": 1
},
"xRange": {
"type": "array",
"items": {
"type": "number"
},
"maxItems": 2,
"minItems": 2,
"description": "[xMin, xMax]"
},
"yRange": {
"type": "array",
"items": {
"type": "number"
},
"maxItems": 2,
"minItems": 2,
"description": "[yMin, yMax]"
}
},
"description": "Explicit graph_2d spec: one `equation` per curve, the window in xRange / yRange. A prompt that is exactly one equation (e.g. \"y = x^2 - 4\") still decides the curve; words in the prompt are never read. Prefer `figure`."
},
"llm_model": {
"type": "string",
"description": "The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess."
},
"sourceSpec": {
"anyOf": [
{
"type": "object",
"required": [
"kind"
],
"properties": {
"kind": {
"enum": [
"annotation"
],
"type": "string"
},
"text": {
"type": "string",
"maxLength": 4000,
"minLength": 1,
"description": "Words to handwrite; wrapped to the page."
},
"lines": {
"type": "array",
"items": {
"type": "string",
"maxLength": 200
},
"maxItems": 60,
"minItems": 1,
"description": "Explicit lines, used verbatim."
}
}
},
{
"type": "object",
"required": [
"kind"
],
"properties": {
"expr": {
"type": "string",
"maxLength": 400,
"description": "The formula as plain text, e.g. a^2 + b^2 = c^2."
},
"kind": {
"enum": [
"formula"
],
"type": "string"
},
"latex": {
"type": "string",
"maxLength": 400,
"description": "The formula as LaTeX."
}
}
},
{
"type": "object",
"required": [
"kind",
"headers",
"rows"
],
"properties": {
"kind": {
"enum": [
"table"
],
"type": "string"
},
"rows": {
"type": "array",
"items": {
"type": "array",
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
},
"maxItems": 12
},
"maxItems": 40,
"minItems": 1,
"description": "Each row is a list of cells, one per header."
},
"headers": {
"type": "array",
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
},
"maxItems": 12,
"minItems": 1
}
}
},
{
"type": "object",
"required": [
"kind",
"bars"
],
"properties": {
"bars": {
"type": "array",
"items": {
"type": "object",
"required": [
"value"
],
"properties": {
"label": {
"type": "string",
"maxLength": 200
},
"value": {
"type": "number",
"minimum": 0
}
}
},
"maxItems": 24,
"minItems": 1
},
"kind": {
"enum": [
"bar_chart"
],
"type": "string"
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The chart title, drawn above the bars."
}
}
},
{
"type": "object",
"required": [
"kind",
"events"
],
"properties": {
"kind": {
"enum": [
"timeline"
],
"type": "string"
},
"events": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"at": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"maxLength": 200
}
]
},
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 24,
"minItems": 2,
"description": "At least 2 events, unique ids, in order."
}
}
},
{
"type": "object",
"required": [
"kind",
"nodes",
"edges"
],
"properties": {
"kind": {
"enum": [
"flowchart"
],
"type": "string"
},
"edges": {
"type": "array",
"items": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "string",
"minLength": 1
},
"from": {
"type": "string",
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 60,
"minItems": 1,
"description": "from/to must be node ids."
},
"nodes": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 30,
"minItems": 2,
"description": "Unique ids."
}
}
},
{
"type": "object",
"required": [
"kind",
"nodes",
"edges"
],
"properties": {
"kind": {
"enum": [
"concept_map"
],
"type": "string"
},
"edges": {
"type": "array",
"items": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "string",
"minLength": 1
},
"from": {
"type": "string",
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 60,
"minItems": 1,
"description": "from/to must be node ids; label the relation. At most 11 per page read best."
},
"nodes": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 30,
"minItems": 2,
"description": "Unique ids. Reads best with at most 10 nodes per page; a bigger map is split over pages automatically."
}
}
},
{
"type": "object",
"required": [
"kind",
"root",
"branches"
],
"properties": {
"kind": {
"enum": [
"mindmap"
],
"type": "string"
},
"root": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"branches": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
},
"children": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 30
}
}
},
"maxItems": 30,
"minItems": 1,
"description": "All ids (root, branches, children) unique."
}
}
},
{
"type": "object",
"required": [
"kind",
"steps"
],
"properties": {
"kind": {
"enum": [
"worked_solution"
],
"type": "string"
},
"steps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"explain": {
"type": "string",
"maxLength": 600,
"minLength": 1,
"description": "A sentence explaining this step."
},
"equation": {
"type": "object",
"properties": {
"expr": {
"type": "string",
"maxLength": 1024,
"description": "This step's equation as plain text."
},
"latex": {
"type": "string",
"maxLength": 1024,
"description": "This step's equation as LaTeX."
}
},
"description": "ONE equation for this step (latex OR expr)."
}
}
},
"maxItems": 12,
"minItems": 1,
"description": "1..12 ordered steps; each has an explanation and/or an equation. Drawn top-to-bottom, no overlap."
},
"problem": {
"type": "string",
"maxLength": 400,
"minLength": 1,
"description": "The problem statement, drawn at the top."
}
}
},
{
"type": "object",
"required": [
"kind",
"items"
],
"properties": {
"kind": {
"enum": [
"checklist"
],
"type": "string"
},
"items": {
"type": "array",
"items": {
"type": "object",
"required": [
"text"
],
"properties": {
"done": {
"type": "boolean",
"description": "True draws a tick inside the checkbox (text stays legible)."
},
"text": {
"type": "string",
"maxLength": 300,
"minLength": 1,
"description": "The thing to do."
}
}
},
"maxItems": 16,
"minItems": 1,
"description": "1..16 items; each a checkbox + text, done:true adds a tick. Drawn top-to-bottom, no overlap."
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The checklist title, drawn at the top."
}
}
},
{
"type": "object",
"required": [
"kind",
"center",
"labels"
],
"properties": {
"kind": {
"enum": [
"labelled_diagram"
],
"type": "string"
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The diagram title, drawn at the top."
},
"center": {
"type": "string",
"maxLength": 120,
"minLength": 1,
"description": "The name of the whole being labelled (written under its outline)."
},
"labels": {
"type": "array",
"items": {
"type": "object",
"required": [
"text"
],
"properties": {
"at": {
"type": "object",
"required": [
"x",
"y"
],
"properties": {
"x": {
"type": "number"
},
"y": {
"type": "number"
}
},
"description": "Optional: where the part sits inside the whole, x and y from 0 to 1 (0,0 = top-left)."
},
"size": {
"type": "string",
"maxLength": 20,
"description": "Optional: small, medium or large."
},
"text": {
"type": "string",
"maxLength": 120,
"minLength": 1,
"description": "The callout label text (a part name)."
},
"shape": {
"type": "string",
"maxLength": 40,
"description": "Optional: how the part is drawn inside the whole: circle, ellipse, blob, rect, dot, line, outline (the part IS the whole's outline, e.g. a cell wall) or ring (an inner layer, e.g. a membrane)."
}
}
},
"maxItems": 8,
"minItems": 2,
"description": "2..8 callout labels; each part is drawn inside the whole, its label in a margin with a leader line to it."
},
"outline": {
"type": "string",
"maxLength": 40,
"description": "Optional: the whole's outline: rounded (default), rect, circle, ellipse or cell."
}
}
},
{
"type": "object",
"required": [
"kind",
"shapes"
],
"properties": {
"kind": {
"enum": [
"shapes"
],
"type": "string"
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The title, drawn at the top."
},
"shapes": {
"type": "array",
"items": {
"type": "object",
"required": [
"shape",
"x",
"y",
"w",
"h"
],
"properties": {
"h": {
"type": "number",
"description": "Box height, 0..1 of the area. For line/arrow, the y delta (may be negative)."
},
"w": {
"type": "number",
"description": "Box width, 0..1 of the area. For line/arrow, the x delta (may be negative)."
},
"x": {
"type": "number",
"description": "Box left, 0..1 across the area (0 = left). For line/arrow, the start x."
},
"y": {
"type": "number",
"description": "Box top, 0..1 down the area (0 = top). For line/arrow, the start y."
},
"label": {
"type": "string",
"maxLength": 80,
"minLength": 1,
"description": "Optional short label, centred in / beside the shape."
},
"shape": {
"type": "string",
"maxLength": 20,
"minLength": 1,
"description": "rect, ellipse (or circle), diamond, triangle, line or arrow."
}
}
},
"maxItems": 12,
"minItems": 1,
"description": "1..12 plain shapes in a normalised box layout (x/y/w/h in 0..1)."
},
"connectors": {
"type": "array",
"items": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "Index into shapes of the target shape."
},
"from": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "Index into shapes of the source shape."
},
"label": {
"type": "string",
"maxLength": 80,
"minLength": 1,
"description": "Optional short label beside the connector."
},
"connector": {
"type": "string",
"maxLength": 20,
"description": "'arrow' (default) or 'line'."
}
}
},
"maxItems": 24,
"description": "Optional connectors joining shapes by their index in `shapes`."
}
}
},
{
"type": "object",
"required": [
"kind",
"min",
"max"
],
"properties": {
"max": {
"type": "number",
"description": "Maximum value on the number line (min < max)."
},
"min": {
"type": "number",
"description": "Minimum value on the number line (min < max)."
},
"kind": {
"enum": [
"number_line"
],
"type": "string"
},
"step": {
"type": "number",
"description": "Optional tick step (> 0)."
},
"marks": {
"type": "array",
"items": {
"type": "object",
"required": [
"value"
],
"properties": {
"label": {
"type": "string",
"maxLength": 120,
"description": "Optional label for the mark."
},
"style": {
"type": "string",
"maxLength": 20,
"description": "'dot' (default), 'open', or 'tick'."
},
"value": {
"type": "number",
"description": "Value on the number line within [min, max]."
}
}
},
"maxItems": 12,
"description": "Up to 12 point marks."
},
"title": {
"type": "string",
"maxLength": 120,
"minLength": 1,
"description": "Optional title drawn above the number line."
},
"intervals": {
"type": "array",
"items": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "number",
"description": "End of the interval (from < to)."
},
"from": {
"type": "number",
"description": "Start of the interval (from < to)."
},
"label": {
"type": "string",
"maxLength": 120,
"description": "Optional interval label."
},
"toClosed": {
"type": "boolean",
"description": "True for a closed dot at `to`, false/omitted for an open circle."
},
"fromClosed": {
"type": "boolean",
"description": "True for a closed dot at `from`, false/omitted for an open circle."
}
}
},
"maxItems": 3,
"description": "Up to 3 shaded/thickened intervals."
}
}
},
{
"type": "object",
"required": [
"kind",
"sections"
],
"properties": {
"kind": {
"enum": [
"study_page"
],
"type": "string"
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The page title, drawn large at the top."
},
"sections": {
"type": "array",
"items": {
"type": "object",
"properties": {
"text": {
"type": "string",
"maxLength": 2000,
"minLength": 1,
"description": "A paragraph of handwritten prose."
},
"figure": {
"anyOf": [
{
"type": "object",
"required": [
"kind",
"xRange",
"yRange",
"curves"
],
"properties": {
"kind": {
"enum": [
"graph_2d"
],
"type": "string"
},
"curves": {
"type": "array",
"items": {
"type": "object",
"properties": {
"expr": {
"type": "string",
"maxLength": 200,
"description": "A bare function of x, e.g. \"sin(x)\", \"e^x\", \"1/x\"."
},
"poly": {
"type": "array",
"items": {
"type": "number"
},
"minItems": 1,
"description": "Polynomial coefficients, ascending power order."
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"minItems": 1
},
"xRange": {
"type": "array",
"items": {
"type": "number"
},
"maxItems": 2,
"minItems": 2,
"description": "[xMin, xMax]"
},
"yRange": {
"type": "array",
"items": {
"type": "number"
},
"maxItems": 2,
"minItems": 2,
"description": "[yMin, yMax]"
}
}
},
{
"type": "object",
"required": [
"kind"
],
"properties": {
"kind": {
"enum": [
"annotation"
],
"type": "string"
},
"text": {
"type": "string",
"maxLength": 4000,
"minLength": 1,
"description": "Words to handwrite; wrapped to the page."
},
"lines": {
"type": "array",
"items": {
"type": "string",
"maxLength": 200
},
"maxItems": 60,
"minItems": 1,
"description": "Explicit lines, used verbatim."
}
}
},
{
"type": "object",
"required": [
"kind"
],
"properties": {
"expr": {
"type": "string",
"maxLength": 400,
"description": "The formula as plain text, e.g. a^2 + b^2 = c^2."
},
"kind": {
"enum": [
"formula"
],
"type": "string"
},
"latex": {
"type": "string",
"maxLength": 400,
"description": "The formula as LaTeX."
}
}
},
{
"type": "object",
"required": [
"kind",
"headers",
"rows"
],
"properties": {
"kind": {
"enum": [
"table"
],
"type": "string"
},
"rows": {
"type": "array",
"items": {
"type": "array",
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
},
"maxItems": 12
},
"maxItems": 40,
"minItems": 1,
"description": "Each row is a list of cells, one per header."
},
"headers": {
"type": "array",
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
},
"maxItems": 12,
"minItems": 1
}
}
},
{
"type": "object",
"required": [
"kind",
"bars"
],
"properties": {
"bars": {
"type": "array",
"items": {
"type": "object",
"required": [
"value"
],
"properties": {
"label": {
"type": "string",
"maxLength": 200
},
"value": {
"type": "number",
"minimum": 0
}
}
},
"maxItems": 24,
"minItems": 1
},
"kind": {
"enum": [
"bar_chart"
],
"type": "string"
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The chart title, drawn above the bars."
}
}
},
{
"type": "object",
"required": [
"kind",
"events"
],
"properties": {
"kind": {
"enum": [
"timeline"
],
"type": "string"
},
"events": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"at": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"maxLength": 200
}
]
},
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 24,
"minItems": 2,
"description": "At least 2 events, unique ids, in order."
}
}
},
{
"type": "object",
"required": [
"kind",
"nodes",
"edges"
],
"properties": {
"kind": {
"enum": [
"flowchart"
],
"type": "string"
},
"edges": {
"type": "array",
"items": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "string",
"minLength": 1
},
"from": {
"type": "string",
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 60,
"minItems": 1,
"description": "from/to must be node ids."
},
"nodes": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 30,
"minItems": 2,
"description": "Unique ids."
}
}
},
{
"type": "object",
"required": [
"kind",
"nodes",
"edges"
],
"properties": {
"kind": {
"enum": [
"concept_map"
],
"type": "string"
},
"edges": {
"type": "array",
"items": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "string",
"minLength": 1
},
"from": {
"type": "string",
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 60,
"minItems": 1,
"description": "from/to must be node ids; label the relation. At most 11 per page read best."
},
"nodes": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 30,
"minItems": 2,
"description": "Unique ids. Reads best with at most 10 nodes per page; a bigger map is split over pages automatically."
}
}
},
{
"type": "object",
"required": [
"kind",
"root",
"branches"
],
"properties": {
"kind": {
"enum": [
"mindmap"
],
"type": "string"
},
"root": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"branches": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
},
"children": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 30
}
}
},
"maxItems": 30,
"minItems": 1,
"description": "All ids (root, branches, children) unique."
}
}
}
],
"description": "ONE embedded figure of any OTHER kind."
},
"heading": {
"type": "string",
"maxLength": 160,
"minLength": 1,
"description": "Optional section heading."
}
}
},
"maxItems": 48,
"minItems": 1,
"description": "Ordered sections; each is a text paragraph OR one figure. Drawn top-to-bottom, no overlap; a page holds up to 8 and more continue on the next pages automatically."
}
}
}
],
"description": "Structured content for a non-graph drawing; `kind` selects the shape. annotation: {text} or {lines}; formula: {latex} or {expr}; table: {headers, rows}; bar_chart: {bars}; timeline: {events}; flowchart / concept_map: {nodes, edges} (a concept map reads best with at most 10 nodes per page; bigger ones split over pages automatically); mindmap: {root, branches}. Write the user's content into it faithfully — Wren draws exactly this, it never invents content."
},
"conversation_id": {
"type": "string",
"description": "Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request."
}
}
}Link the user's Wren account so authored notes land in their notebook. Returns the linked uid when a credential is present; otherwise explains that account linking must be completed first.
| Argument | Type | Description |
|---|---|---|
| context* | string | Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as "a user", "the customer", or "an account". Example: "Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution." |
| llm_model* | string | The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. "claude-opus-4-8", "gpt-5.2"). Used for analytics only. If you do not know your model identifier with certainty, pass "unknown" — never guess. |
| conversation_id | string | Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request. |
Create a NEW note in the user's Wren notebook with a handwritten page (pressure-faithful PencilKit strokes) drawn from a `figure{kind, content}`. The result has a link (noteUrl) that opens the note on any device — always give it to the user — and the note also syncs to the Wren iPad app. To add to a note that already exists use add_page (new page) or edit_page (same page) with its noteId — find it with list_notes or list_recent_notebooks. Same topic as an earlier note → add a page to it, not a new note. A figure is required: graphs (graph_2d, content.prompt = the equation), handwritten text (annotation, content.text = the exact words), formulas, tables, bar charts, timelines, flowcharts, mind maps, concept maps and more — call get_drawing_guide for every kind. Write in the user's language: Wren handwrites Hindi, Arabic, Bengali, Tamil, Telugu, Hebrew, Russian and most other scripts (not yet Chinese / Japanese / Korean) — never transliterate. A concept map reads best with at most 10 concepts and 11 links per page; a bigger one is split over pages automatically (links between pages are drawn on both with a page reference).
| Argument | Type | Description |
|---|---|---|
| kind | string | Older way to name the kind; omit it when you send `figure` (figure.kind is the kind). Call get_drawing_guide for every kind, its exact arguments and an example. |
| title | string | Note title shown in the notebook. |
| figure | object | PREFERRED way to say what to draw: one figure of a registered kind. graph_2d: content {prompt:'y = sin(x)'} (ONE equation, no words) or {xRange, yRange, curves:[{equation}]}. annotation: content {text} or {lines}. Every other kind: its fields. Do not combine with kind / prompt / sourceSpec / graphSpec for the drawing itself. |
| prompt | string |
Write handwriting onto an EXISTING page that Wren wrote. mode 'append' (default) keeps the page's ink and adds the new ink below it; mode 'replace' erases the page's ink and draws the new ink. Only pages get_note reports as editable (Wren-written and not changed on the iPad since) can be edited — the user's own handwriting is never modified; use add_page for those notes. Pick the page by pageNumber (1-based) or pageId; omitted = last page. If an append has no room on the page, Wren puts it on a new page at the end of the note and says so.
| Argument | Type | Description |
|---|---|---|
| kind | string | Older way to name the kind; omit it when you send `figure` (figure.kind is the kind). Call get_drawing_guide for every kind, its exact arguments and an example. |
| mode | string | append keeps existing ink; replace erases it. |
| figure | object | PREFERRED way to say what to draw: one figure of a registered kind. graph_2d: content {prompt:'y = sin(x)'} (ONE equation, no words) or {xRange, yRange, curves:[{equation}]}. annotation: content {text} or {lines}. Every other kind: its fields. Do not combine with kind / prompt / sourceSpec / graphSpec for the drawing itself. |
| noteId* | string | The note's id, from list_notes or a previous Wren result. |
| pageId | string | Page id from get_note (wins over pageNumber). |
| prompt | string |
Returns what Wren can draw (every kind), the exact arguments for each with a working example, and the rules for writing into notes. Call it before your first drawing in a conversation, or whenever unsure which kind or fields to use. Read-only; needs no account.
| Argument | Type | Description |
|---|---|---|
| context* | string | Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as "a user", "the customer", or "an account". Example: "Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution." |
| llm_model* | string | The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. "claude-opus-4-8", "gpt-5.2"). Used for analytics only. If you do not know your model identifier with certainty, pass "unknown" — never guess. |
| conversation_id | string | Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request. |
Show one note's title, subject, page size and its pages (number, id, ink stroke count, whether Wren can add ink to it). Use before edit_page to pick the page.
| Argument | Type | Description |
|---|---|---|
| noteId* | string | The note's id, from list_notes or a previous Wren result. |
| context* | string | Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as "a user", "the customer", or "an account". Example: "Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution." |
| llm_model* | string | The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. "claude-opus-4-8", "gpt-5.2"). Used for analytics only. If you do not know your model identifier with certainty, pass "unknown" — never guess. |
| conversation_id | string | Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request. |
Return the stable Wren account id (Firebase uid) and basic profile of the currently linked account. Call this after connect_account to confirm linkage.
| Argument | Type | Description |
|---|---|---|
| context* | string | Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as "a user", "the customer", or "an account". Example: "Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution." |
| llm_model* | string | The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. "claude-opus-4-8", "gpt-5.2"). Used for analytics only. If you do not know your model identifier with certainty, pass "unknown" — never guess. |
| conversation_id | string | Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"context",
"llm_model"
],
"properties": {
"context": {
"type": "string",
"description": "Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\""
},
"llm_model": {
"type": "string",
"description": "The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess."
},
"conversation_id": {
"type": "string",
"description": "Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request."
}
}
}List the user's Wren notes, most recently edited first, with id, title, subject and page count. Use it to find the noteId before add_page, edit_page, get_note or update_note.
| Argument | Type | Description |
|---|---|---|
| limit | integer | Max notes to return (default 20). |
| query | string | Case-insensitive match on title or subject. |
| context* | string | Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as "a user", "the customer", or "an account". Example: "Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution." |
| llm_model* | string | The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. "claude-opus-4-8", "gpt-5.2"). Used for analytics only. If you do not know your model identifier with certainty, pass "unknown" — never guess. |
| conversation_id | string | Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request. |
The user's latest notes, newest first: id, title, subject, pages, last edit, the outline of pages Wren wrote, and the note this chat has been writing to. Use it when the user returns to a topic, then add pages to that noteId.
| Argument | Type | Description |
|---|---|---|
| limit | integer | Notes per call (default 5). |
| cursor | string | nextCursor from the previous call. |
| context* | string | Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as "a user", "the customer", or "an account". Example: "Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution." |
| llm_model* | string | The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. "claude-opus-4-8", "gpt-5.2"). Used for analytics only. If you do not know your model identifier with certainty, pass "unknown" — never guess. |
| conversation_id | string | Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request. |
Plan a 1-30 page handwritten Wren notebook and write its cover and contents (page 1; a plan over 8 pages gets a cover page and contents pages). Pass noteId to extend a plan and update the contents. Then write each page via write_notebook_page with target.noteId and planSlot, one call per slot. Same topic as an earlier note → pass its noteId, not a new note. Optional: part (topic group, e.g. "Lecture 3") per page; coverFigure = one small sketch of the topic.
| Argument | Type | Description |
|---|---|---|
| pages* | array | |
| title* | string | |
| accent | string | |
| noteId | string | |
| context | object | |
| subject | string | |
| llm_model* | string | The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. "claude-opus-4-8", "gpt-5.2"). Used for analytics only. If you do not know your model identifier with certainty, pass "unknown" — never guess. |
| coverFigure |
Render a pressure-faithful SVG preview of a handwritten note's strokes (the same VectorStroke array create_handwritten_note produces), framed at the page size, with an Open in Wren button (pass the note link as openInNoteNestUrl). Returns an inline widget.
| Argument | Type | Description |
|---|---|---|
| title | string | |
| noteId | string | |
| context* | string | Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as "a user", "the customer", or "an account". Example: "Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution." |
| strokes | array | The strokes to preview. Omit in stub mode to preview the sample strokes. |
| llm_model* | string | The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. "claude-opus-4-8", "gpt-5.2"). Used for analytics only. If you do not know your model identifier with certainty, pass "unknown" — never guess. |
Change a note's title and/or subject. Does not touch its pages.
| Argument | Type | Description |
|---|---|---|
| title | string | New title. |
| noteId* | string | The note's id, from list_notes or a previous Wren result. |
| context* | string | Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as "a user", "the customer", or "an account". Example: "Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution." |
| subject | string | New subject/class tag (empty string clears it). |
| llm_model* | string | The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. "claude-opus-4-8", "gpt-5.2"). Used for analytics only. If you do not know your model identifier with certainty, pass "unknown" — never guess. |
| conversation_id | string |
Write a handwritten study page in the user's Wren notebook (same topic -> add/replace via target.noteId, not a new note). Write a FULL page (not for verbatim): 110-140 words in 6-8 blocks (a table or figure = ~40 words), a heading per part, 3-6 key terms marked (underline or highlight, never a whole callout; a mark copies exact words of its own block), every number+unit and formula marked as math, one accent, at most 1 short marginNote, and the takeaway in a callout (key_answer, key_idea) or summary. Pair two <=12-word callouts with layoutHint:"side_by_side". Editable ink in Wren's handwriting font, not the user's own handwriting. Recipes: worked_example = problem callout (box) + 3-4 steps (1-2 sentences + equation each) + Check heading+paragraph + common_mistake & key_answer callouts. concept_summary = definition callout + boxed equation or figure (table/flowchart, required) + 2 headings with 2-sentence paragraphs + key_idea callout. comparison_sheet = 1-sentence intro + 5-6 row table or bar_chart + heading+paragraph on why it matters + common_mistake & key_idea callouts (side_by_side). process_walkthrough = overview + 4-6 steps, each with label (stage name) + 1-2 sentences, a flowchart of the stages (cycle: last edge back to the first) + key_idea or summary. revision_sheet = 2-3 headings with concise paragraphs/equations + common_mistake & remember callouts (side_by_side) + 4-item checklist. verbatim = text the user gave you to copy: their words unchanged and once, in paragraph/heading blocks, nothing added.
| Argument | Type | Description |
|---|---|---|
| title* | string | Page title. |
| accent | string | |
| blocks* | array | |
We read every tool's name, description and inputs the way the model will, looking for instructions hidden from you. A clean result means no warning signs, not a guarantee. Scanned Nov 14, 2026.
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"context",
"llm_model"
],
"properties": {
"context": {
"type": "string",
"description": "Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\""
},
"llm_model": {
"type": "string",
"description": "The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess."
},
"conversation_id": {
"type": "string",
"description": "Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request."
}
}
}| Not a drawing by itself: a call with only `prompt` is refused. Send `figure` (graph: figure.content.prompt = "y = x^2 - 4"; text: figure.content.text = the exact words). Here `prompt` is only the note's title and search text; Wren never reads words in it for what to draw (exactly one equation, e.g. "y = x^2 - 4", is read as that curve). |
| context | object | Study context (subject, course, level, goal, language, coveredSoFar, userRequest). |
| subject | string | Optional class/subject tag. |
| graphSpec | object | Explicit graph_2d spec: one `equation` per curve, the window in xRange / yRange. A prompt that is exactly one equation (e.g. "y = x^2 - 4") still decides the curve; words in the prompt are never read. Prefer `figure`. |
| llm_model* | string | The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. "claude-opus-4-8", "gpt-5.2"). Used for analytics only. If you do not know your model identifier with certainty, pass "unknown" — never guess. |
| pageWidth | number | Target page width in points (default A4 595). |
| pageHeight | number | Target page height in points (default A4 842). |
| sourceSpec | any | Structured content for a non-graph drawing; `kind` selects the shape. annotation: {text} or {lines}; formula: {latex} or {expr}; table: {headers, rows}; bar_chart: {bars}; timeline: {events}; flowchart / concept_map: {nodes, edges} (a concept map reads best with at most 10 nodes per page; bigger ones split over pages automatically); mindmap: {root, branches}. Write the user's content into it faithfully — Wren draws exactly this, it never invents content. |
| conversation_id | string | Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"llm_model"
],
"properties": {
"kind": {
"enum": [
"graph_2d",
"annotation",
"handwritten_text",
"formula",
"table",
"bar_chart",
"timeline",
"flowchart",
"mindmap",
"concept_map",
"checklist",
"labelled_diagram",
"study_page",
"worked_solution",
"shapes",
"number_line"
],
"type": "string",
"description": "Older way to name the kind; omit it when you send `figure` (figure.kind is the kind). Call get_drawing_guide for every kind, its exact arguments and an example."
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "Note title shown in the notebook."
},
"figure": {
"type": "object",
"required": [
"kind"
],
"properties": {
"kind": {
"enum": [
"graph_2d",
"annotation",
"table",
"bar_chart",
"timeline",
"flowchart",
"mindmap",
"concept_map",
"formula",
"study_page",
"worked_solution",
"checklist",
"labelled_diagram",
"shapes",
"number_line"
],
"type": "string"
},
"content": {
"anyOf": [
{
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
{
"type": "string"
}
],
"description": "That kind's fields, the same as sourceSpec without `kind`. get_drawing_guide lists them per kind."
}
},
"description": "PREFERRED way to say what to draw: one figure of a registered kind. graph_2d: content {prompt:'y = sin(x)'} (ONE equation, no words) or {xRange, yRange, curves:[{equation}]}. annotation: content {text} or {lines}. Every other kind: its fields. Do not combine with kind / prompt / sourceSpec / graphSpec for the drawing itself.",
"additionalProperties": {}
},
"prompt": {
"type": "string",
"maxLength": 2000,
"minLength": 1,
"description": "Not a drawing by itself: a call with only `prompt` is refused. Send `figure` (graph: figure.content.prompt = \"y = x^2 - 4\"; text: figure.content.text = the exact words). Here `prompt` is only the note's title and search text; Wren never reads words in it for what to draw (exactly one equation, e.g. \"y = x^2 - 4\", is read as that curve)."
},
"context": {
"type": "object",
"description": "Study context (subject, course, level, goal, language, coveredSoFar, userRequest).",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
}
},
"subject": {
"type": "string",
"maxLength": 120,
"description": "Optional class/subject tag."
},
"graphSpec": {
"type": "object",
"required": [
"kind",
"xRange",
"yRange",
"curves"
],
"properties": {
"kind": {
"enum": [
"graph_2d"
],
"type": "string"
},
"marks": {
"type": "array",
"items": {
"type": "object",
"required": [
"at"
],
"properties": {
"at": {
"type": "array",
"items": {
"type": "number"
},
"maxItems": 2,
"minItems": 2,
"description": "[x, y]"
},
"label": {
"type": "string"
}
}
}
},
"shade": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "number"
},
"from": {
"type": "number"
}
},
"description": "The area between the curve and the x-axis to hatch, from x = from to x = to (a definite integral). Wren also draws dashed bound lines and widens the window to show it."
},
"curves": {
"type": "array",
"items": {
"type": "object",
"properties": {
"expr": {
"type": "string",
"maxLength": 200,
"description": "The curve as a bare function of x (no \"y =\"), e.g. \"sin(x)\", \"e^x\", \"1/x\", \"abs(x)\". Alternative to `equation`."
},
"poly": {
"type": "array",
"items": {
"type": "number"
},
"minItems": 1,
"description": "Fallback for polynomials only — prefer `equation`. Coefficients in ASCENDING power order: y = x^2 - 4 is [-4, 0, 1]; y = 2x + 3 is [3, 2]."
},
"label": {
"type": "string"
},
"equation": {
"type": "string",
"maxLength": 200,
"description": "PREFERRED. The curve as an equation in x, e.g. \"y = x^2 - 4\", \"y = sin(x)\", \"y = e^x\", \"y = 1/x\". Wren parses it exactly."
}
}
},
"minItems": 1
},
"xRange": {
"type": "array",
"items": {
"type": "number"
},
"maxItems": 2,
"minItems": 2,
"description": "[xMin, xMax]"
},
"yRange": {
"type": "array",
"items": {
"type": "number"
},
"maxItems": 2,
"minItems": 2,
"description": "[yMin, yMax]"
}
},
"description": "Explicit graph_2d spec: one `equation` per curve, the window in xRange / yRange. A prompt that is exactly one equation (e.g. \"y = x^2 - 4\") still decides the curve; words in the prompt are never read. Prefer `figure`."
},
"llm_model": {
"type": "string",
"description": "The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess."
},
"pageWidth": {
"type": "number",
"maximum": 5000,
"minimum": 200,
"description": "Target page width in points (default A4 595)."
},
"pageHeight": {
"type": "number",
"maximum": 5000,
"minimum": 200,
"description": "Target page height in points (default A4 842)."
},
"sourceSpec": {
"anyOf": [
{
"type": "object",
"required": [
"kind"
],
"properties": {
"kind": {
"enum": [
"annotation"
],
"type": "string"
},
"text": {
"type": "string",
"maxLength": 4000,
"minLength": 1,
"description": "Words to handwrite; wrapped to the page."
},
"lines": {
"type": "array",
"items": {
"type": "string",
"maxLength": 200
},
"maxItems": 60,
"minItems": 1,
"description": "Explicit lines, used verbatim."
}
}
},
{
"type": "object",
"required": [
"kind"
],
"properties": {
"expr": {
"type": "string",
"maxLength": 400,
"description": "The formula as plain text, e.g. a^2 + b^2 = c^2."
},
"kind": {
"enum": [
"formula"
],
"type": "string"
},
"latex": {
"type": "string",
"maxLength": 400,
"description": "The formula as LaTeX."
}
}
},
{
"type": "object",
"required": [
"kind",
"headers",
"rows"
],
"properties": {
"kind": {
"enum": [
"table"
],
"type": "string"
},
"rows": {
"type": "array",
"items": {
"type": "array",
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
},
"maxItems": 12
},
"maxItems": 40,
"minItems": 1,
"description": "Each row is a list of cells, one per header."
},
"headers": {
"type": "array",
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
},
"maxItems": 12,
"minItems": 1
}
}
},
{
"type": "object",
"required": [
"kind",
"bars"
],
"properties": {
"bars": {
"type": "array",
"items": {
"type": "object",
"required": [
"value"
],
"properties": {
"label": {
"type": "string",
"maxLength": 200
},
"value": {
"type": "number",
"minimum": 0
}
}
},
"maxItems": 24,
"minItems": 1
},
"kind": {
"enum": [
"bar_chart"
],
"type": "string"
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The chart title, drawn above the bars."
}
}
},
{
"type": "object",
"required": [
"kind",
"events"
],
"properties": {
"kind": {
"enum": [
"timeline"
],
"type": "string"
},
"events": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"at": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"maxLength": 200
}
]
},
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 24,
"minItems": 2,
"description": "At least 2 events, unique ids, in order."
}
}
},
{
"type": "object",
"required": [
"kind",
"nodes",
"edges"
],
"properties": {
"kind": {
"enum": [
"flowchart"
],
"type": "string"
},
"edges": {
"type": "array",
"items": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "string",
"minLength": 1
},
"from": {
"type": "string",
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 60,
"minItems": 1,
"description": "from/to must be node ids."
},
"nodes": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 30,
"minItems": 2,
"description": "Unique ids."
}
}
},
{
"type": "object",
"required": [
"kind",
"nodes",
"edges"
],
"properties": {
"kind": {
"enum": [
"concept_map"
],
"type": "string"
},
"edges": {
"type": "array",
"items": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "string",
"minLength": 1
},
"from": {
"type": "string",
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 60,
"minItems": 1,
"description": "from/to must be node ids; label the relation. At most 11 per page read best."
},
"nodes": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 30,
"minItems": 2,
"description": "Unique ids. Reads best with at most 10 nodes per page; a bigger map is split over pages automatically."
}
}
},
{
"type": "object",
"required": [
"kind",
"root",
"branches"
],
"properties": {
"kind": {
"enum": [
"mindmap"
],
"type": "string"
},
"root": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"branches": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
},
"children": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 30
}
}
},
"maxItems": 30,
"minItems": 1,
"description": "All ids (root, branches, children) unique."
}
}
},
{
"type": "object",
"required": [
"kind",
"steps"
],
"properties": {
"kind": {
"enum": [
"worked_solution"
],
"type": "string"
},
"steps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"explain": {
"type": "string",
"maxLength": 600,
"minLength": 1,
"description": "A sentence explaining this step."
},
"equation": {
"type": "object",
"properties": {
"expr": {
"type": "string",
"maxLength": 1024,
"description": "This step's equation as plain text."
},
"latex": {
"type": "string",
"maxLength": 1024,
"description": "This step's equation as LaTeX."
}
},
"description": "ONE equation for this step (latex OR expr)."
}
}
},
"maxItems": 12,
"minItems": 1,
"description": "1..12 ordered steps; each has an explanation and/or an equation. Drawn top-to-bottom, no overlap."
},
"problem": {
"type": "string",
"maxLength": 400,
"minLength": 1,
"description": "The problem statement, drawn at the top."
}
}
},
{
"type": "object",
"required": [
"kind",
"items"
],
"properties": {
"kind": {
"enum": [
"checklist"
],
"type": "string"
},
"items": {
"type": "array",
"items": {
"type": "object",
"required": [
"text"
],
"properties": {
"done": {
"type": "boolean",
"description": "True draws a tick inside the checkbox (text stays legible)."
},
"text": {
"type": "string",
"maxLength": 300,
"minLength": 1,
"description": "The thing to do."
}
}
},
"maxItems": 16,
"minItems": 1,
"description": "1..16 items; each a checkbox + text, done:true adds a tick. Drawn top-to-bottom, no overlap."
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The checklist title, drawn at the top."
}
}
},
{
"type": "object",
"required": [
"kind",
"center",
"labels"
],
"properties": {
"kind": {
"enum": [
"labelled_diagram"
],
"type": "string"
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The diagram title, drawn at the top."
},
"center": {
"type": "string",
"maxLength": 120,
"minLength": 1,
"description": "The name of the whole being labelled (written under its outline)."
},
"labels": {
"type": "array",
"items": {
"type": "object",
"required": [
"text"
],
"properties": {
"at": {
"type": "object",
"required": [
"x",
"y"
],
"properties": {
"x": {
"type": "number"
},
"y": {
"type": "number"
}
},
"description": "Optional: where the part sits inside the whole, x and y from 0 to 1 (0,0 = top-left)."
},
"size": {
"type": "string",
"maxLength": 20,
"description": "Optional: small, medium or large."
},
"text": {
"type": "string",
"maxLength": 120,
"minLength": 1,
"description": "The callout label text (a part name)."
},
"shape": {
"type": "string",
"maxLength": 40,
"description": "Optional: how the part is drawn inside the whole: circle, ellipse, blob, rect, dot, line, outline (the part IS the whole's outline, e.g. a cell wall) or ring (an inner layer, e.g. a membrane)."
}
}
},
"maxItems": 8,
"minItems": 2,
"description": "2..8 callout labels; each part is drawn inside the whole, its label in a margin with a leader line to it."
},
"outline": {
"type": "string",
"maxLength": 40,
"description": "Optional: the whole's outline: rounded (default), rect, circle, ellipse or cell."
}
}
},
{
"type": "object",
"required": [
"kind",
"shapes"
],
"properties": {
"kind": {
"enum": [
"shapes"
],
"type": "string"
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The title, drawn at the top."
},
"shapes": {
"type": "array",
"items": {
"type": "object",
"required": [
"shape",
"x",
"y",
"w",
"h"
],
"properties": {
"h": {
"type": "number",
"description": "Box height, 0..1 of the area. For line/arrow, the y delta (may be negative)."
},
"w": {
"type": "number",
"description": "Box width, 0..1 of the area. For line/arrow, the x delta (may be negative)."
},
"x": {
"type": "number",
"description": "Box left, 0..1 across the area (0 = left). For line/arrow, the start x."
},
"y": {
"type": "number",
"description": "Box top, 0..1 down the area (0 = top). For line/arrow, the start y."
},
"label": {
"type": "string",
"maxLength": 80,
"minLength": 1,
"description": "Optional short label, centred in / beside the shape."
},
"shape": {
"type": "string",
"maxLength": 20,
"minLength": 1,
"description": "rect, ellipse (or circle), diamond, triangle, line or arrow."
}
}
},
"maxItems": 12,
"minItems": 1,
"description": "1..12 plain shapes in a normalised box layout (x/y/w/h in 0..1)."
},
"connectors": {
"type": "array",
"items": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "Index into shapes of the target shape."
},
"from": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "Index into shapes of the source shape."
},
"label": {
"type": "string",
"maxLength": 80,
"minLength": 1,
"description": "Optional short label beside the connector."
},
"connector": {
"type": "string",
"maxLength": 20,
"description": "'arrow' (default) or 'line'."
}
}
},
"maxItems": 24,
"description": "Optional connectors joining shapes by their index in `shapes`."
}
}
},
{
"type": "object",
"required": [
"kind",
"min",
"max"
],
"properties": {
"max": {
"type": "number",
"description": "Maximum value on the number line (min < max)."
},
"min": {
"type": "number",
"description": "Minimum value on the number line (min < max)."
},
"kind": {
"enum": [
"number_line"
],
"type": "string"
},
"step": {
"type": "number",
"description": "Optional tick step (> 0)."
},
"marks": {
"type": "array",
"items": {
"type": "object",
"required": [
"value"
],
"properties": {
"label": {
"type": "string",
"maxLength": 120,
"description": "Optional label for the mark."
},
"style": {
"type": "string",
"maxLength": 20,
"description": "'dot' (default), 'open', or 'tick'."
},
"value": {
"type": "number",
"description": "Value on the number line within [min, max]."
}
}
},
"maxItems": 12,
"description": "Up to 12 point marks."
},
"title": {
"type": "string",
"maxLength": 120,
"minLength": 1,
"description": "Optional title drawn above the number line."
},
"intervals": {
"type": "array",
"items": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "number",
"description": "End of the interval (from < to)."
},
"from": {
"type": "number",
"description": "Start of the interval (from < to)."
},
"label": {
"type": "string",
"maxLength": 120,
"description": "Optional interval label."
},
"toClosed": {
"type": "boolean",
"description": "True for a closed dot at `to`, false/omitted for an open circle."
},
"fromClosed": {
"type": "boolean",
"description": "True for a closed dot at `from`, false/omitted for an open circle."
}
}
},
"maxItems": 3,
"description": "Up to 3 shaded/thickened intervals."
}
}
},
{
"type": "object",
"required": [
"kind",
"sections"
],
"properties": {
"kind": {
"enum": [
"study_page"
],
"type": "string"
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The page title, drawn large at the top."
},
"sections": {
"type": "array",
"items": {
"type": "object",
"properties": {
"text": {
"type": "string",
"maxLength": 2000,
"minLength": 1,
"description": "A paragraph of handwritten prose."
},
"figure": {
"anyOf": [
{
"type": "object",
"required": [
"kind",
"xRange",
"yRange",
"curves"
],
"properties": {
"kind": {
"enum": [
"graph_2d"
],
"type": "string"
},
"curves": {
"type": "array",
"items": {
"type": "object",
"properties": {
"expr": {
"type": "string",
"maxLength": 200,
"description": "A bare function of x, e.g. \"sin(x)\", \"e^x\", \"1/x\"."
},
"poly": {
"type": "array",
"items": {
"type": "number"
},
"minItems": 1,
"description": "Polynomial coefficients, ascending power order."
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"minItems": 1
},
"xRange": {
"type": "array",
"items": {
"type": "number"
},
"maxItems": 2,
"minItems": 2,
"description": "[xMin, xMax]"
},
"yRange": {
"type": "array",
"items": {
"type": "number"
},
"maxItems": 2,
"minItems": 2,
"description": "[yMin, yMax]"
}
}
},
{
"type": "object",
"required": [
"kind"
],
"properties": {
"kind": {
"enum": [
"annotation"
],
"type": "string"
},
"text": {
"type": "string",
"maxLength": 4000,
"minLength": 1,
"description": "Words to handwrite; wrapped to the page."
},
"lines": {
"type": "array",
"items": {
"type": "string",
"maxLength": 200
},
"maxItems": 60,
"minItems": 1,
"description": "Explicit lines, used verbatim."
}
}
},
{
"type": "object",
"required": [
"kind"
],
"properties": {
"expr": {
"type": "string",
"maxLength": 400,
"description": "The formula as plain text, e.g. a^2 + b^2 = c^2."
},
"kind": {
"enum": [
"formula"
],
"type": "string"
},
"latex": {
"type": "string",
"maxLength": 400,
"description": "The formula as LaTeX."
}
}
},
{
"type": "object",
"required": [
"kind",
"headers",
"rows"
],
"properties": {
"kind": {
"enum": [
"table"
],
"type": "string"
},
"rows": {
"type": "array",
"items": {
"type": "array",
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
},
"maxItems": 12
},
"maxItems": 40,
"minItems": 1,
"description": "Each row is a list of cells, one per header."
},
"headers": {
"type": "array",
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
},
"maxItems": 12,
"minItems": 1
}
}
},
{
"type": "object",
"required": [
"kind",
"bars"
],
"properties": {
"bars": {
"type": "array",
"items": {
"type": "object",
"required": [
"value"
],
"properties": {
"label": {
"type": "string",
"maxLength": 200
},
"value": {
"type": "number",
"minimum": 0
}
}
},
"maxItems": 24,
"minItems": 1
},
"kind": {
"enum": [
"bar_chart"
],
"type": "string"
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The chart title, drawn above the bars."
}
}
},
{
"type": "object",
"required": [
"kind",
"events"
],
"properties": {
"kind": {
"enum": [
"timeline"
],
"type": "string"
},
"events": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"at": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"maxLength": 200
}
]
},
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 24,
"minItems": 2,
"description": "At least 2 events, unique ids, in order."
}
}
},
{
"type": "object",
"required": [
"kind",
"nodes",
"edges"
],
"properties": {
"kind": {
"enum": [
"flowchart"
],
"type": "string"
},
"edges": {
"type": "array",
"items": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "string",
"minLength": 1
},
"from": {
"type": "string",
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 60,
"minItems": 1,
"description": "from/to must be node ids."
},
"nodes": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 30,
"minItems": 2,
"description": "Unique ids."
}
}
},
{
"type": "object",
"required": [
"kind",
"nodes",
"edges"
],
"properties": {
"kind": {
"enum": [
"concept_map"
],
"type": "string"
},
"edges": {
"type": "array",
"items": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "string",
"minLength": 1
},
"from": {
"type": "string",
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 60,
"minItems": 1,
"description": "from/to must be node ids; label the relation. At most 11 per page read best."
},
"nodes": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 30,
"minItems": 2,
"description": "Unique ids. Reads best with at most 10 nodes per page; a bigger map is split over pages automatically."
}
}
},
{
"type": "object",
"required": [
"kind",
"root",
"branches"
],
"properties": {
"kind": {
"enum": [
"mindmap"
],
"type": "string"
},
"root": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"branches": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
},
"children": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 30
}
}
},
"maxItems": 30,
"minItems": 1,
"description": "All ids (root, branches, children) unique."
}
}
}
],
"description": "ONE embedded figure of any OTHER kind."
},
"heading": {
"type": "string",
"maxLength": 160,
"minLength": 1,
"description": "Optional section heading."
}
}
},
"maxItems": 48,
"minItems": 1,
"description": "Ordered sections; each is a text paragraph OR one figure. Drawn top-to-bottom, no overlap; a page holds up to 8 and more continue on the next pages automatically."
}
}
}
],
"description": "Structured content for a non-graph drawing; `kind` selects the shape. annotation: {text} or {lines}; formula: {latex} or {expr}; table: {headers, rows}; bar_chart: {bars}; timeline: {events}; flowchart / concept_map: {nodes, edges} (a concept map reads best with at most 10 nodes per page; bigger ones split over pages automatically); mindmap: {root, branches}. Write the user's content into it faithfully — Wren draws exactly this, it never invents content."
},
"conversation_id": {
"type": "string",
"description": "Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request."
}
}
}| Not a drawing by itself: a call with only `prompt` is refused. Send `figure` (graph: figure.content.prompt = "y = x^2 - 4"; text: figure.content.text = the exact words). Here `prompt` is only the note's title and search text; Wren never reads words in it for what to draw (exactly one equation, e.g. "y = x^2 - 4", is read as that curve). |
| context | object | Study context (subject, course, level, goal, language, coveredSoFar, userRequest). |
| graphSpec | object | Explicit graph_2d spec: one `equation` per curve, the window in xRange / yRange. A prompt that is exactly one equation (e.g. "y = x^2 - 4") still decides the curve; words in the prompt are never read. Prefer `figure`. |
| llm_model* | string | The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. "claude-opus-4-8", "gpt-5.2"). Used for analytics only. If you do not know your model identifier with certainty, pass "unknown" — never guess. |
| pageNumber | integer | 1-based page number. |
| sourceSpec | any | Structured content for a non-graph drawing; `kind` selects the shape. annotation: {text} or {lines}; formula: {latex} or {expr}; table: {headers, rows}; bar_chart: {bars}; timeline: {events}; flowchart / concept_map: {nodes, edges} (a concept map reads best with at most 10 nodes per page; bigger ones split over pages automatically); mindmap: {root, branches}. Write the user's content into it faithfully — Wren draws exactly this, it never invents content. |
| conversation_id | string | Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"noteId",
"llm_model"
],
"properties": {
"kind": {
"enum": [
"graph_2d",
"annotation",
"handwritten_text",
"formula",
"table",
"bar_chart",
"timeline",
"flowchart",
"mindmap",
"concept_map",
"checklist",
"labelled_diagram",
"study_page",
"worked_solution",
"shapes",
"number_line"
],
"type": "string",
"description": "Older way to name the kind; omit it when you send `figure` (figure.kind is the kind). Call get_drawing_guide for every kind, its exact arguments and an example."
},
"mode": {
"enum": [
"append",
"replace"
],
"type": "string",
"default": "append",
"description": "append keeps existing ink; replace erases it."
},
"figure": {
"type": "object",
"required": [
"kind"
],
"properties": {
"kind": {
"enum": [
"graph_2d",
"annotation",
"table",
"bar_chart",
"timeline",
"flowchart",
"mindmap",
"concept_map",
"formula",
"study_page",
"worked_solution",
"checklist",
"labelled_diagram",
"shapes",
"number_line"
],
"type": "string"
},
"content": {
"anyOf": [
{
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
{
"type": "string"
}
],
"description": "That kind's fields, the same as sourceSpec without `kind`. get_drawing_guide lists them per kind."
}
},
"description": "PREFERRED way to say what to draw: one figure of a registered kind. graph_2d: content {prompt:'y = sin(x)'} (ONE equation, no words) or {xRange, yRange, curves:[{equation}]}. annotation: content {text} or {lines}. Every other kind: its fields. Do not combine with kind / prompt / sourceSpec / graphSpec for the drawing itself.",
"additionalProperties": {}
},
"noteId": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The note's id, from list_notes or a previous Wren result."
},
"pageId": {
"type": "string",
"maxLength": 200,
"description": "Page id from get_note (wins over pageNumber)."
},
"prompt": {
"type": "string",
"maxLength": 2000,
"minLength": 1,
"description": "Not a drawing by itself: a call with only `prompt` is refused. Send `figure` (graph: figure.content.prompt = \"y = x^2 - 4\"; text: figure.content.text = the exact words). Here `prompt` is only the note's title and search text; Wren never reads words in it for what to draw (exactly one equation, e.g. \"y = x^2 - 4\", is read as that curve)."
},
"context": {
"type": "object",
"description": "Study context (subject, course, level, goal, language, coveredSoFar, userRequest).",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
}
},
"graphSpec": {
"type": "object",
"required": [
"kind",
"xRange",
"yRange",
"curves"
],
"properties": {
"kind": {
"enum": [
"graph_2d"
],
"type": "string"
},
"marks": {
"type": "array",
"items": {
"type": "object",
"required": [
"at"
],
"properties": {
"at": {
"type": "array",
"items": {
"type": "number"
},
"maxItems": 2,
"minItems": 2,
"description": "[x, y]"
},
"label": {
"type": "string"
}
}
}
},
"shade": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "number"
},
"from": {
"type": "number"
}
},
"description": "The area between the curve and the x-axis to hatch, from x = from to x = to (a definite integral). Wren also draws dashed bound lines and widens the window to show it."
},
"curves": {
"type": "array",
"items": {
"type": "object",
"properties": {
"expr": {
"type": "string",
"maxLength": 200,
"description": "The curve as a bare function of x (no \"y =\"), e.g. \"sin(x)\", \"e^x\", \"1/x\", \"abs(x)\". Alternative to `equation`."
},
"poly": {
"type": "array",
"items": {
"type": "number"
},
"minItems": 1,
"description": "Fallback for polynomials only — prefer `equation`. Coefficients in ASCENDING power order: y = x^2 - 4 is [-4, 0, 1]; y = 2x + 3 is [3, 2]."
},
"label": {
"type": "string"
},
"equation": {
"type": "string",
"maxLength": 200,
"description": "PREFERRED. The curve as an equation in x, e.g. \"y = x^2 - 4\", \"y = sin(x)\", \"y = e^x\", \"y = 1/x\". Wren parses it exactly."
}
}
},
"minItems": 1
},
"xRange": {
"type": "array",
"items": {
"type": "number"
},
"maxItems": 2,
"minItems": 2,
"description": "[xMin, xMax]"
},
"yRange": {
"type": "array",
"items": {
"type": "number"
},
"maxItems": 2,
"minItems": 2,
"description": "[yMin, yMax]"
}
},
"description": "Explicit graph_2d spec: one `equation` per curve, the window in xRange / yRange. A prompt that is exactly one equation (e.g. \"y = x^2 - 4\") still decides the curve; words in the prompt are never read. Prefer `figure`."
},
"llm_model": {
"type": "string",
"description": "The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess."
},
"pageNumber": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 1,
"description": "1-based page number."
},
"sourceSpec": {
"anyOf": [
{
"type": "object",
"required": [
"kind"
],
"properties": {
"kind": {
"enum": [
"annotation"
],
"type": "string"
},
"text": {
"type": "string",
"maxLength": 4000,
"minLength": 1,
"description": "Words to handwrite; wrapped to the page."
},
"lines": {
"type": "array",
"items": {
"type": "string",
"maxLength": 200
},
"maxItems": 60,
"minItems": 1,
"description": "Explicit lines, used verbatim."
}
}
},
{
"type": "object",
"required": [
"kind"
],
"properties": {
"expr": {
"type": "string",
"maxLength": 400,
"description": "The formula as plain text, e.g. a^2 + b^2 = c^2."
},
"kind": {
"enum": [
"formula"
],
"type": "string"
},
"latex": {
"type": "string",
"maxLength": 400,
"description": "The formula as LaTeX."
}
}
},
{
"type": "object",
"required": [
"kind",
"headers",
"rows"
],
"properties": {
"kind": {
"enum": [
"table"
],
"type": "string"
},
"rows": {
"type": "array",
"items": {
"type": "array",
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
},
"maxItems": 12
},
"maxItems": 40,
"minItems": 1,
"description": "Each row is a list of cells, one per header."
},
"headers": {
"type": "array",
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
},
"maxItems": 12,
"minItems": 1
}
}
},
{
"type": "object",
"required": [
"kind",
"bars"
],
"properties": {
"bars": {
"type": "array",
"items": {
"type": "object",
"required": [
"value"
],
"properties": {
"label": {
"type": "string",
"maxLength": 200
},
"value": {
"type": "number",
"minimum": 0
}
}
},
"maxItems": 24,
"minItems": 1
},
"kind": {
"enum": [
"bar_chart"
],
"type": "string"
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The chart title, drawn above the bars."
}
}
},
{
"type": "object",
"required": [
"kind",
"events"
],
"properties": {
"kind": {
"enum": [
"timeline"
],
"type": "string"
},
"events": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"at": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"maxLength": 200
}
]
},
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 24,
"minItems": 2,
"description": "At least 2 events, unique ids, in order."
}
}
},
{
"type": "object",
"required": [
"kind",
"nodes",
"edges"
],
"properties": {
"kind": {
"enum": [
"flowchart"
],
"type": "string"
},
"edges": {
"type": "array",
"items": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "string",
"minLength": 1
},
"from": {
"type": "string",
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 60,
"minItems": 1,
"description": "from/to must be node ids."
},
"nodes": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 30,
"minItems": 2,
"description": "Unique ids."
}
}
},
{
"type": "object",
"required": [
"kind",
"nodes",
"edges"
],
"properties": {
"kind": {
"enum": [
"concept_map"
],
"type": "string"
},
"edges": {
"type": "array",
"items": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "string",
"minLength": 1
},
"from": {
"type": "string",
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 60,
"minItems": 1,
"description": "from/to must be node ids; label the relation. At most 11 per page read best."
},
"nodes": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 30,
"minItems": 2,
"description": "Unique ids. Reads best with at most 10 nodes per page; a bigger map is split over pages automatically."
}
}
},
{
"type": "object",
"required": [
"kind",
"root",
"branches"
],
"properties": {
"kind": {
"enum": [
"mindmap"
],
"type": "string"
},
"root": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"branches": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
},
"children": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 30
}
}
},
"maxItems": 30,
"minItems": 1,
"description": "All ids (root, branches, children) unique."
}
}
},
{
"type": "object",
"required": [
"kind",
"steps"
],
"properties": {
"kind": {
"enum": [
"worked_solution"
],
"type": "string"
},
"steps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"explain": {
"type": "string",
"maxLength": 600,
"minLength": 1,
"description": "A sentence explaining this step."
},
"equation": {
"type": "object",
"properties": {
"expr": {
"type": "string",
"maxLength": 1024,
"description": "This step's equation as plain text."
},
"latex": {
"type": "string",
"maxLength": 1024,
"description": "This step's equation as LaTeX."
}
},
"description": "ONE equation for this step (latex OR expr)."
}
}
},
"maxItems": 12,
"minItems": 1,
"description": "1..12 ordered steps; each has an explanation and/or an equation. Drawn top-to-bottom, no overlap."
},
"problem": {
"type": "string",
"maxLength": 400,
"minLength": 1,
"description": "The problem statement, drawn at the top."
}
}
},
{
"type": "object",
"required": [
"kind",
"items"
],
"properties": {
"kind": {
"enum": [
"checklist"
],
"type": "string"
},
"items": {
"type": "array",
"items": {
"type": "object",
"required": [
"text"
],
"properties": {
"done": {
"type": "boolean",
"description": "True draws a tick inside the checkbox (text stays legible)."
},
"text": {
"type": "string",
"maxLength": 300,
"minLength": 1,
"description": "The thing to do."
}
}
},
"maxItems": 16,
"minItems": 1,
"description": "1..16 items; each a checkbox + text, done:true adds a tick. Drawn top-to-bottom, no overlap."
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The checklist title, drawn at the top."
}
}
},
{
"type": "object",
"required": [
"kind",
"center",
"labels"
],
"properties": {
"kind": {
"enum": [
"labelled_diagram"
],
"type": "string"
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The diagram title, drawn at the top."
},
"center": {
"type": "string",
"maxLength": 120,
"minLength": 1,
"description": "The name of the whole being labelled (written under its outline)."
},
"labels": {
"type": "array",
"items": {
"type": "object",
"required": [
"text"
],
"properties": {
"at": {
"type": "object",
"required": [
"x",
"y"
],
"properties": {
"x": {
"type": "number"
},
"y": {
"type": "number"
}
},
"description": "Optional: where the part sits inside the whole, x and y from 0 to 1 (0,0 = top-left)."
},
"size": {
"type": "string",
"maxLength": 20,
"description": "Optional: small, medium or large."
},
"text": {
"type": "string",
"maxLength": 120,
"minLength": 1,
"description": "The callout label text (a part name)."
},
"shape": {
"type": "string",
"maxLength": 40,
"description": "Optional: how the part is drawn inside the whole: circle, ellipse, blob, rect, dot, line, outline (the part IS the whole's outline, e.g. a cell wall) or ring (an inner layer, e.g. a membrane)."
}
}
},
"maxItems": 8,
"minItems": 2,
"description": "2..8 callout labels; each part is drawn inside the whole, its label in a margin with a leader line to it."
},
"outline": {
"type": "string",
"maxLength": 40,
"description": "Optional: the whole's outline: rounded (default), rect, circle, ellipse or cell."
}
}
},
{
"type": "object",
"required": [
"kind",
"shapes"
],
"properties": {
"kind": {
"enum": [
"shapes"
],
"type": "string"
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The title, drawn at the top."
},
"shapes": {
"type": "array",
"items": {
"type": "object",
"required": [
"shape",
"x",
"y",
"w",
"h"
],
"properties": {
"h": {
"type": "number",
"description": "Box height, 0..1 of the area. For line/arrow, the y delta (may be negative)."
},
"w": {
"type": "number",
"description": "Box width, 0..1 of the area. For line/arrow, the x delta (may be negative)."
},
"x": {
"type": "number",
"description": "Box left, 0..1 across the area (0 = left). For line/arrow, the start x."
},
"y": {
"type": "number",
"description": "Box top, 0..1 down the area (0 = top). For line/arrow, the start y."
},
"label": {
"type": "string",
"maxLength": 80,
"minLength": 1,
"description": "Optional short label, centred in / beside the shape."
},
"shape": {
"type": "string",
"maxLength": 20,
"minLength": 1,
"description": "rect, ellipse (or circle), diamond, triangle, line or arrow."
}
}
},
"maxItems": 12,
"minItems": 1,
"description": "1..12 plain shapes in a normalised box layout (x/y/w/h in 0..1)."
},
"connectors": {
"type": "array",
"items": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "Index into shapes of the target shape."
},
"from": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "Index into shapes of the source shape."
},
"label": {
"type": "string",
"maxLength": 80,
"minLength": 1,
"description": "Optional short label beside the connector."
},
"connector": {
"type": "string",
"maxLength": 20,
"description": "'arrow' (default) or 'line'."
}
}
},
"maxItems": 24,
"description": "Optional connectors joining shapes by their index in `shapes`."
}
}
},
{
"type": "object",
"required": [
"kind",
"min",
"max"
],
"properties": {
"max": {
"type": "number",
"description": "Maximum value on the number line (min < max)."
},
"min": {
"type": "number",
"description": "Minimum value on the number line (min < max)."
},
"kind": {
"enum": [
"number_line"
],
"type": "string"
},
"step": {
"type": "number",
"description": "Optional tick step (> 0)."
},
"marks": {
"type": "array",
"items": {
"type": "object",
"required": [
"value"
],
"properties": {
"label": {
"type": "string",
"maxLength": 120,
"description": "Optional label for the mark."
},
"style": {
"type": "string",
"maxLength": 20,
"description": "'dot' (default), 'open', or 'tick'."
},
"value": {
"type": "number",
"description": "Value on the number line within [min, max]."
}
}
},
"maxItems": 12,
"description": "Up to 12 point marks."
},
"title": {
"type": "string",
"maxLength": 120,
"minLength": 1,
"description": "Optional title drawn above the number line."
},
"intervals": {
"type": "array",
"items": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "number",
"description": "End of the interval (from < to)."
},
"from": {
"type": "number",
"description": "Start of the interval (from < to)."
},
"label": {
"type": "string",
"maxLength": 120,
"description": "Optional interval label."
},
"toClosed": {
"type": "boolean",
"description": "True for a closed dot at `to`, false/omitted for an open circle."
},
"fromClosed": {
"type": "boolean",
"description": "True for a closed dot at `from`, false/omitted for an open circle."
}
}
},
"maxItems": 3,
"description": "Up to 3 shaded/thickened intervals."
}
}
},
{
"type": "object",
"required": [
"kind",
"sections"
],
"properties": {
"kind": {
"enum": [
"study_page"
],
"type": "string"
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The page title, drawn large at the top."
},
"sections": {
"type": "array",
"items": {
"type": "object",
"properties": {
"text": {
"type": "string",
"maxLength": 2000,
"minLength": 1,
"description": "A paragraph of handwritten prose."
},
"figure": {
"anyOf": [
{
"type": "object",
"required": [
"kind",
"xRange",
"yRange",
"curves"
],
"properties": {
"kind": {
"enum": [
"graph_2d"
],
"type": "string"
},
"curves": {
"type": "array",
"items": {
"type": "object",
"properties": {
"expr": {
"type": "string",
"maxLength": 200,
"description": "A bare function of x, e.g. \"sin(x)\", \"e^x\", \"1/x\"."
},
"poly": {
"type": "array",
"items": {
"type": "number"
},
"minItems": 1,
"description": "Polynomial coefficients, ascending power order."
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"minItems": 1
},
"xRange": {
"type": "array",
"items": {
"type": "number"
},
"maxItems": 2,
"minItems": 2,
"description": "[xMin, xMax]"
},
"yRange": {
"type": "array",
"items": {
"type": "number"
},
"maxItems": 2,
"minItems": 2,
"description": "[yMin, yMax]"
}
}
},
{
"type": "object",
"required": [
"kind"
],
"properties": {
"kind": {
"enum": [
"annotation"
],
"type": "string"
},
"text": {
"type": "string",
"maxLength": 4000,
"minLength": 1,
"description": "Words to handwrite; wrapped to the page."
},
"lines": {
"type": "array",
"items": {
"type": "string",
"maxLength": 200
},
"maxItems": 60,
"minItems": 1,
"description": "Explicit lines, used verbatim."
}
}
},
{
"type": "object",
"required": [
"kind"
],
"properties": {
"expr": {
"type": "string",
"maxLength": 400,
"description": "The formula as plain text, e.g. a^2 + b^2 = c^2."
},
"kind": {
"enum": [
"formula"
],
"type": "string"
},
"latex": {
"type": "string",
"maxLength": 400,
"description": "The formula as LaTeX."
}
}
},
{
"type": "object",
"required": [
"kind",
"headers",
"rows"
],
"properties": {
"kind": {
"enum": [
"table"
],
"type": "string"
},
"rows": {
"type": "array",
"items": {
"type": "array",
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
},
"maxItems": 12
},
"maxItems": 40,
"minItems": 1,
"description": "Each row is a list of cells, one per header."
},
"headers": {
"type": "array",
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
},
"maxItems": 12,
"minItems": 1
}
}
},
{
"type": "object",
"required": [
"kind",
"bars"
],
"properties": {
"bars": {
"type": "array",
"items": {
"type": "object",
"required": [
"value"
],
"properties": {
"label": {
"type": "string",
"maxLength": 200
},
"value": {
"type": "number",
"minimum": 0
}
}
},
"maxItems": 24,
"minItems": 1
},
"kind": {
"enum": [
"bar_chart"
],
"type": "string"
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The chart title, drawn above the bars."
}
}
},
{
"type": "object",
"required": [
"kind",
"events"
],
"properties": {
"kind": {
"enum": [
"timeline"
],
"type": "string"
},
"events": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"at": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"maxLength": 200
}
]
},
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 24,
"minItems": 2,
"description": "At least 2 events, unique ids, in order."
}
}
},
{
"type": "object",
"required": [
"kind",
"nodes",
"edges"
],
"properties": {
"kind": {
"enum": [
"flowchart"
],
"type": "string"
},
"edges": {
"type": "array",
"items": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "string",
"minLength": 1
},
"from": {
"type": "string",
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 60,
"minItems": 1,
"description": "from/to must be node ids."
},
"nodes": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 30,
"minItems": 2,
"description": "Unique ids."
}
}
},
{
"type": "object",
"required": [
"kind",
"nodes",
"edges"
],
"properties": {
"kind": {
"enum": [
"concept_map"
],
"type": "string"
},
"edges": {
"type": "array",
"items": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "string",
"minLength": 1
},
"from": {
"type": "string",
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 60,
"minItems": 1,
"description": "from/to must be node ids; label the relation. At most 11 per page read best."
},
"nodes": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 30,
"minItems": 2,
"description": "Unique ids. Reads best with at most 10 nodes per page; a bigger map is split over pages automatically."
}
}
},
{
"type": "object",
"required": [
"kind",
"root",
"branches"
],
"properties": {
"kind": {
"enum": [
"mindmap"
],
"type": "string"
},
"root": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"branches": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
},
"children": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"label": {
"type": "string",
"maxLength": 200
}
}
},
"maxItems": 30
}
}
},
"maxItems": 30,
"minItems": 1,
"description": "All ids (root, branches, children) unique."
}
}
}
],
"description": "ONE embedded figure of any OTHER kind."
},
"heading": {
"type": "string",
"maxLength": 160,
"minLength": 1,
"description": "Optional section heading."
}
}
},
"maxItems": 48,
"minItems": 1,
"description": "Ordered sections; each is a text paragraph OR one figure. Drawn top-to-bottom, no overlap; a page holds up to 8 and more continue on the next pages automatically."
}
}
}
],
"description": "Structured content for a non-graph drawing; `kind` selects the shape. annotation: {text} or {lines}; formula: {latex} or {expr}; table: {headers, rows}; bar_chart: {bars}; timeline: {events}; flowchart / concept_map: {nodes, edges} (a concept map reads best with at most 10 nodes per page; bigger ones split over pages automatically); mindmap: {root, branches}. Write the user's content into it faithfully — Wren draws exactly this, it never invents content."
},
"conversation_id": {
"type": "string",
"description": "Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request."
}
}
}{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"context",
"llm_model"
],
"properties": {
"context": {
"type": "string",
"description": "Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\""
},
"llm_model": {
"type": "string",
"description": "The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess."
},
"conversation_id": {
"type": "string",
"description": "Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request."
}
}
}{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"noteId",
"context",
"llm_model"
],
"properties": {
"noteId": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The note's id, from list_notes or a previous Wren result."
},
"context": {
"type": "string",
"description": "Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\""
},
"llm_model": {
"type": "string",
"description": "The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess."
},
"conversation_id": {
"type": "string",
"description": "Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request."
}
}
}{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"context",
"llm_model"
],
"properties": {
"limit": {
"type": "integer",
"maximum": 50,
"minimum": 1,
"description": "Max notes to return (default 20)."
},
"query": {
"type": "string",
"maxLength": 200,
"description": "Case-insensitive match on title or subject."
},
"context": {
"type": "string",
"description": "Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\""
},
"llm_model": {
"type": "string",
"description": "The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess."
},
"conversation_id": {
"type": "string",
"description": "Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request."
}
}
}{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"context",
"llm_model"
],
"properties": {
"limit": {
"type": "integer",
"maximum": 10,
"minimum": 1,
"description": "Notes per call (default 5)."
},
"cursor": {
"type": "string",
"maxLength": 20,
"description": "nextCursor from the previous call."
},
"context": {
"type": "string",
"description": "Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\""
},
"llm_model": {
"type": "string",
"description": "The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess."
},
"conversation_id": {
"type": "string",
"description": "Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request."
}
}
}| object |
| e.g. formula {latex}, graph_2d {curves:[{expr}]}, flowchart; drawn in one pen. |
| conversation_id | string | Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"title",
"pages",
"llm_model"
],
"properties": {
"pages": {
"type": "array",
"items": {
"type": "object",
"required": [
"pageKind",
"title",
"covers"
],
"properties": {
"part": {
"type": "string",
"maxLength": 60,
"minLength": 1
},
"title": {
"type": "string",
"maxLength": 160,
"minLength": 1
},
"covers": {
"type": "string",
"maxLength": 160,
"minLength": 1
},
"pageKind": {
"enum": [
"worked_example",
"concept_summary",
"comparison_sheet",
"process_walkthrough",
"revision_sheet",
"verbatim"
],
"type": "string"
}
}
},
"maxItems": 30,
"minItems": 1
},
"title": {
"type": "string",
"maxLength": 160,
"minLength": 1
},
"accent": {
"enum": [
"blue",
"green",
"purple"
],
"type": "string"
},
"noteId": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"context": {
"type": "object",
"properties": {
"goal": {
"enum": [
"exam_revision",
"homework",
"lecture_notes",
"self_study",
"teaching",
"copy_text",
"other"
],
"type": "string"
},
"level": {
"enum": [
"primary",
"middle",
"high_school",
"undergraduate",
"graduate",
"professional",
"other"
],
"type": "string"
},
"course": {
"type": "string"
},
"subject": {
"type": "string"
},
"language": {
"type": "string"
},
"userRequest": {
"type": "string",
"maxLength": 300
},
"coveredSoFar": {
"type": "string",
"maxLength": 300
}
}
},
"subject": {
"type": "string",
"maxLength": 120
},
"llm_model": {
"type": "string",
"description": "The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess."
},
"coverFigure": {
"type": "object",
"required": [
"figureKind",
"figureContent"
],
"properties": {
"title": {
"type": "string",
"maxLength": 80,
"minLength": 1
},
"figureKind": {
"enum": [
"graph_2d",
"formula",
"annotation",
"table",
"bar_chart",
"timeline",
"flowchart",
"mindmap",
"concept_map"
],
"type": "string"
},
"figureContent": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
}
},
"description": "e.g. formula {latex}, graph_2d {curves:[{expr}]}, flowchart; drawn in one pen."
},
"conversation_id": {
"type": "string",
"description": "Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request."
}
}
}| pageWidth |
| number |
| pageHeight | number |
| conversation_id | string | Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request. |
| openInNoteNestUrl | string | The note's link (noteUrl from a write result); the preview's Open in Wren button opens it. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"context",
"llm_model"
],
"properties": {
"title": {
"type": "string",
"maxLength": 200
},
"noteId": {
"type": "string",
"maxLength": 200
},
"context": {
"type": "string",
"description": "Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\""
},
"strokes": {
"type": "array",
"items": {
"type": "object",
"required": [
"points",
"color",
"opacity",
"inkType"
],
"properties": {
"color": {
"type": "string"
},
"points": {
"type": "array",
"items": {
"type": "object",
"required": [
"x",
"y"
],
"properties": {
"x": {
"type": "number"
},
"y": {
"type": "number"
},
"force": {
"type": "number"
},
"size_h": {
"type": "number"
},
"size_w": {
"type": "number"
},
"azimuth": {
"type": "number"
},
"opacity": {
"type": "number"
},
"altitude": {
"type": "number"
},
"timeOffset": {
"type": "number"
}
}
},
"minItems": 1
},
"inkType": {
"type": "string"
},
"opacity": {
"type": "number"
}
}
},
"minItems": 1,
"description": "The strokes to preview. Omit in stub mode to preview the sample strokes."
},
"llm_model": {
"type": "string",
"description": "The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess."
},
"pageWidth": {
"type": "number",
"maximum": 5000,
"minimum": 200
},
"pageHeight": {
"type": "number",
"maximum": 5000,
"minimum": 200
},
"conversation_id": {
"type": "string",
"description": "Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request."
},
"openInNoteNestUrl": {
"type": "string",
"format": "uri",
"description": "The note's link (noteUrl from a write result); the preview's Open in Wren button opens it."
}
}
}| Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"noteId",
"context",
"llm_model"
],
"properties": {
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "New title."
},
"noteId": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The note's id, from list_notes or a previous Wren result."
},
"context": {
"type": "string",
"description": "Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\""
},
"subject": {
"type": "string",
"maxLength": 120,
"description": "New subject/class tag (empty string clears it)."
},
"llm_model": {
"type": "string",
"description": "The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess."
},
"conversation_id": {
"type": "string",
"description": "Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request."
}
}
}| target |
| object |
| context | object |
| subject | string |
| pageKind* | string | worked_example problem + 3-4 steps + Check + key_answer; concept_summary definition + equation/figure + 2 headed parts + key_idea; comparison_sheet intro + 5-6 row table + paragraph + 2 callouts; process_walkthrough intro + 4-5 steps + paragraph + summary; revision_sheet 3 headed sections + 2 callouts + 4-item checklist; verbatim user's own text. |
| planSlot | integer | 1-based slot from plan_notebook. |
| llm_model* | string | The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. "claude-opus-4-8", "gpt-5.2"). Used for analytics only. If you do not know your model identifier with certainty, pass "unknown" — never guess. |
| conversation_id | string | Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"title",
"pageKind",
"blocks",
"llm_model"
],
"properties": {
"title": {
"type": "string",
"maxLength": 160,
"minLength": 1,
"description": "Page title."
},
"accent": {
"enum": [
"blue",
"green",
"purple"
],
"type": "string"
},
"blocks": {
"type": "array",
"items": {
"type": "object",
"required": [
"type"
],
"properties": {
"text": {
"type": "string",
"maxLength": 2000,
"minLength": 1,
"description": "heading, paragraph, callout, summary text."
},
"type": {
"enum": [
"heading",
"paragraph",
"steps",
"equation",
"callout",
"figure",
"checklist",
"summary"
],
"type": "string"
},
"boxed": {
"type": "boolean"
},
"items": {
"type": "array",
"items": {
"type": "object",
"required": [
"text"
],
"properties": {
"done": {
"type": "boolean"
},
"text": {
"type": "string",
"maxLength": 300,
"minLength": 1
}
}
},
"maxItems": 16,
"minItems": 1
},
"marks": {
"type": "array",
"items": {
"type": "object",
"required": [
"text",
"as"
],
"properties": {
"as": {
"enum": [
"underline",
"highlight",
"math"
],
"type": "string",
"description": "underline/highlight term; math draws latex."
},
"text": {
"type": "string",
"maxLength": 120,
"minLength": 1,
"description": "Exact words in block."
},
"latex": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "LaTeX drawn for math."
}
}
},
"maxItems": 12,
"description": "3-6 key terms per page; every formula, symbol and number+unit (20 m/s) as math."
},
"steps": {
"type": "array",
"items": {
"type": "object",
"required": [
"explain"
],
"properties": {
"label": {
"type": "string",
"maxLength": 40,
"minLength": 1
},
"explain": {
"type": "string",
"maxLength": 600,
"minLength": 1
},
"equation": {
"type": "string",
"maxLength": 400,
"minLength": 1
}
}
},
"maxItems": 12,
"minItems": 1,
"description": "Ordered steps (label = stage name, explain, optional equation)."
},
"style": {
"enum": [
"key_answer",
"box",
"highlight",
"remember",
"warning",
"common_mistake",
"definition",
"key_idea",
"example"
],
"type": "string",
"description": "box=problem, key_answer=answer, definition, key_idea, example, warning/common_mistake, remember, highlight."
},
"title": {
"type": "string",
"maxLength": 160,
"minLength": 1,
"description": "Caption above callout, summary, checklist or figure."
},
"equation": {
"type": "string",
"maxLength": 400,
"minLength": 1,
"description": "LaTeX or plain math."
},
"figureKind": {
"enum": [
"graph_2d",
"formula",
"annotation",
"table",
"bar_chart",
"timeline",
"flowchart",
"mindmap",
"concept_map"
],
"type": "string"
},
"layoutHint": {
"enum": [
"full_width",
"side_by_side"
],
"type": "string",
"description": "side_by_side pairs with next short block."
},
"marginNote": {
"type": "string",
"maxLength": 80,
"minLength": 1,
"description": "Short fact in right margin."
},
"figureContent": {
"type": "object",
"description": "table: {\"headers\":[\"Feature\",\"A\",\"B\"],\"rows\":[[\"x\",\"1\",\"2\"]]}; others: get_drawing_guide. title=caption.",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
}
}
},
"maxItems": 24,
"minItems": 1
},
"target": {
"type": "object",
"properties": {
"action": {
"enum": [
"new_note",
"add_page",
"replace_page"
],
"type": "string"
},
"noteId": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"pageNumber": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 1
}
}
},
"context": {
"type": "object",
"properties": {
"goal": {
"enum": [
"exam_revision",
"homework",
"lecture_notes",
"self_study",
"teaching",
"copy_text",
"other"
],
"type": "string"
},
"level": {
"enum": [
"primary",
"middle",
"high_school",
"undergraduate",
"graduate",
"professional",
"other"
],
"type": "string"
},
"course": {
"type": "string"
},
"subject": {
"type": "string"
},
"language": {
"type": "string"
},
"userRequest": {
"type": "string",
"maxLength": 300
},
"coveredSoFar": {
"type": "string",
"maxLength": 300
}
}
},
"subject": {
"type": "string",
"maxLength": 120
},
"pageKind": {
"enum": [
"worked_example",
"concept_summary",
"comparison_sheet",
"process_walkthrough",
"revision_sheet",
"verbatim"
],
"type": "string",
"description": "worked_example problem + 3-4 steps + Check + key_answer; concept_summary definition + equation/figure + 2 headed parts + key_idea; comparison_sheet intro + 5-6 row table + paragraph + 2 callouts; process_walkthrough intro + 4-5 steps + paragraph + summary; revision_sheet 3 headed sections + 2 callouts + 4-item checklist; verbatim user's own text."
},
"planSlot": {
"type": "integer",
"maximum": 30,
"minimum": 1,
"description": "1-based slot from plan_notebook."
},
"llm_model": {
"type": "string",
"description": "The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess."
},
"conversation_id": {
"type": "string",
"description": "Pass the exact conversation_id from the server's previous response, unchanged. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it. Keep passing the same conversation_id for the rest of the conversation, including after later user messages or on a different task; do not reset it when the user starts a new request."
}
}
}