← WebMCP Directory

Tool tree

ghqsandbox.net

Health & EducationTools verified
Website
Interactive math and physics tools an AI agent can drive directly through WebMCP.
Explore capabilities

    page: Home

    list_learning_toolsAnswer

    List the learning tools this site offers, which of them are built yet, and the page each one lives on. Call this before open_tool if you are not sure which tool fits what the student is asking about.

    View tool JSON
    {
      "name": "list_learning_tools",
      "kind": "answer",
      "impl": "imperative",
      "description": "List the learning tools this site offers, which of them are built yet, and the page each one lives on. Call this before open_tool if you are not sure which tool fits what the student is asking about.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/"
    }
    open_toolAction

    Navigate to one of this site's learning tools. Use "2d" for curves in the plane, "3d" for surfaces, "circuits" for circuit building and measurement, "physics" for mechanics simulations, and "chemistry" for atoms and molecules. The page that opens registers its complete WebMCP toolset, so call this first and continue the lesson there.

    View tool JSON
    {
      "name": "open_tool",
      "kind": "act",
      "impl": "imperative",
      "description": "Navigate to one of this site's learning tools. Use \"2d\" for curves in the plane, \"3d\" for surfaces, \"circuits\" for circuit building and measurement, \"physics\" for mechanics simulations, and \"chemistry\" for atoms and molecules. The page that opens registers its complete WebMCP toolset, so call this first and continue the lesson there.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "tool": {
            "type": "string",
            "enum": [
              "2d",
              "3d",
              "circuits",
              "physics",
              "chemistry"
            ],
            "description": "Which tool to open."
          }
        },
        "required": [
          "tool"
        ]
      },
      "page": "/"
    }

    page: Graph

    list_expressionsAnswer

    Read the current state of the graph: every expression with its id, the sliders with their live values, the visible viewport, and whether the board is in 2D or 3D mode. Call this first whenever you are not certain what is already on screen, and after any change you did not make yourself. Expression ids returned here are what update_expression, remove_expression and find_features expect.

    View tool JSON
    {
      "name": "list_expressions",
      "kind": "answer",
      "impl": "imperative",
      "description": "Read the current state of the graph: every expression with its id, the sliders with their live values, the visible viewport, and whether the board is in 2D or 3D mode. Call this first whenever you are not certain what is already on screen, and after any change you did not make yourself. Expression ids returned here are what update_expression, remove_expression and find_features expect.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/graph.html?mode=2d"
    }
    add_expressionAction

    Plot a new expression and render it immediately. Accepts plain math ("y = a*x^2 + b"), LaTeX ("y = \\frac{x}{2}"), or a 2D point written as "(2, 3)". Point coordinates may contain expressions or slider parameters, such as "(a, sin(a))". In 2D it supports y = f(x), x = g(y), polar and implicit equations. In 3D it supports surfaces oriented as z = f(x,y), y = f(x,z), or x = f(y,z); a bare expression is treated as z = expression. Free variables automatically become sliders while x, y, z and the polar coordinate theta do not. A slider is removed when its parameter no longer appears in any expression. If the expression does not parse, this returns ok:false with the parser error -- read it, fix the syntax, and call again rather than reporting failure to the student.

    View tool JSON
    {
      "name": "add_expression",
      "kind": "act",
      "impl": "imperative",
      "description": "Plot a new expression and render it immediately. Accepts plain math (\"y = a*x^2 + b\"), LaTeX (\"y = \\\\frac{x}{2}\"), or a 2D point written as \"(2, 3)\". Point coordinates may contain expressions or slider parameters, such as \"(a, sin(a))\". In 2D it supports y = f(x), x = g(y), polar and implicit equations. In 3D it supports surfaces oriented as z = f(x,y), y = f(x,z), or x = f(y,z); a bare expression is treated as z = expression. Free variables automatically become sliders while x, y, z and the polar coordinate theta do not. A slider is removed when its parameter no longer appears in any expression. If the expression does not parse, this returns ok:false with the parser error -- read it, fix the syntax, and call again rather than reporting failure to the student.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "latex": {
            "type": "string",
            "description": "The expression, equation, or 2D point to plot, e.g. \"y = a*x^2\", \"(2, 3)\", or \"z = x^2 - y^2\"."
          },
          "id": {
            "type": "string",
            "description": "Optional stable id. Supply one if you intend to update or remove this expression later."
          },
          "color": {
            "type": "string",
            "description": "Optional CSS colour, e.g. \"#c74440\". Defaults to the next colour in the palette."
          }
        },
        "required": [
          "latex"
        ]
      },
      "page": "/graph.html?mode=2d"
    }
    plot_pointAction

    Plot one exact coordinate as a visible point on the 2D graph. Use this when teaching ordered pairs, marking a measured value, or placing a point before drawing a curve through it. The point is also added to the expression list in "(x, y)" form, so the student can edit or delete it normally. Add a short label when its meaning matters.

    View tool JSON
    {
      "name": "plot_point",
      "kind": "act",
      "impl": "imperative",
      "description": "Plot one exact coordinate as a visible point on the 2D graph. Use this when teaching ordered pairs, marking a measured value, or placing a point before drawing a curve through it. The point is also added to the expression list in \"(x, y)\" form, so the student can edit or delete it normally. Add a short label when its meaning matters.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "x": {
            "type": "number",
            "description": "Horizontal coordinate."
          },
          "y": {
            "type": "number",
            "description": "Vertical coordinate."
          },
          "label": {
            "type": "string",
            "description": "Optional short label shown beside the point."
          },
          "id": {
            "type": "string",
            "description": "Optional stable expression id for later updates or removal."
          },
          "color": {
            "type": "string",
            "description": "Optional CSS colour, e.g. \"#c74440\"."
          }
        },
        "required": [
          "x",
          "y"
        ]
      },
      "page": "/graph.html?mode=2d"
    }
    update_expressionAction

    Replace the formula of an expression already on the graph, keeping its id and colour so the student sees the same curve change rather than a new one appear. Use this when refining an example ("now make it a cubic"). Returns a parser error without modifying anything if the new formula is invalid.

    View tool JSON
    {
      "name": "update_expression",
      "kind": "act",
      "impl": "imperative",
      "description": "Replace the formula of an expression already on the graph, keeping its id and colour so the student sees the same curve change rather than a new one appear. Use this when refining an example (\"now make it a cubic\"). Returns a parser error without modifying anything if the new formula is invalid.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id from list_expressions."
          },
          "latex": {
            "type": "string",
            "description": "The replacement expression."
          },
          "color": {
            "type": "string"
          },
          "visible": {
            "type": "boolean",
            "description": "Set false to hide the curve without deleting it."
          }
        },
        "required": [
          "id"
        ]
      },
      "page": "/graph.html?mode=2d"
    }
    remove_expressionAction

    Delete one expression from the graph by id. Prefer setting visible:false through update_expression when you may want the curve back shortly, so the student keeps their place.

    View tool JSON
    {
      "name": "remove_expression",
      "kind": "act",
      "impl": "imperative",
      "description": "Delete one expression from the graph by id. Prefer setting visible:false through update_expression when you may want the curve back shortly, so the student keeps their place.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id from list_expressions."
          }
        },
        "required": [
          "id"
        ]
      },
      "page": "/graph.html?mode=2d"
    }
    define_sliderAction

    Change the range, step or value of a parameter slider already used by an expression. Add the parameter to an expression first; sliders exist only while their parameter appears on the board. Use this to widen a range when a demonstration needs values the current range does not reach (animate_slider widens automatically, but a manual set_slider will clamp).

    View tool JSON
    {
      "name": "define_slider",
      "kind": "act",
      "impl": "imperative",
      "description": "Change the range, step or value of a parameter slider already used by an expression. Add the parameter to an expression first; sliders exist only while their parameter appears on the board. Use this to widen a range when a demonstration needs values the current range does not reach (animate_slider widens automatically, but a manual set_slider will clamp).",
      "inputSchema": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Parameter name already present in an expression. Coordinates and reserved math values cannot be sliders."
          },
          "min": {
            "type": "number"
          },
          "max": {
            "type": "number"
          },
          "step": {
            "type": "number",
            "description": "Drag increment. Use 0.01 for smooth parameters, 1 for integer-valued ones."
          },
          "value": {
            "type": "number",
            "description": "Starting value. Clamped into [min, max]."
          }
        },
        "required": [
          "name"
        ]
      },
      "page": "/graph.html?mode=2d"
    }
    set_sliderAction

    Jump a parameter straight to a value. The on-screen slider moves and every curve using that parameter re-renders. Use this to set up a starting condition before an explanation. When the point of the moment is to let the student SEE the effect of a change, use animate_slider instead -- a jump cut teaches much less than a sweep.

    View tool JSON
    {
      "name": "set_slider",
      "kind": "act",
      "impl": "imperative",
      "description": "Jump a parameter straight to a value. The on-screen slider moves and every curve using that parameter re-renders. Use this to set up a starting condition before an explanation. When the point of the moment is to let the student SEE the effect of a change, use animate_slider instead -- a jump cut teaches much less than a sweep.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "value": {
            "type": "number"
          }
        },
        "required": [
          "name",
          "value"
        ]
      },
      "page": "/graph.html?mode=2d"
    }
    animate_sliderAction

    Sweep a parameter from one value to another over time, dragging the real on-screen slider so the student watches the curve deform continuously. This is the core teaching move of this app: when a student asks why a parameter has some effect, answer by animating it and narrating what they are about to see, rather than describing it in prose. Awaits the full sweep and resolves when the animation has finished, so any narration you send afterwards lands once the motion is complete. Widens the slider range automatically if from/to fall outside it. Animate one parameter at a time -- two at once is unreadable.

    View tool JSON
    {
      "name": "animate_slider",
      "kind": "act",
      "impl": "imperative",
      "description": "Sweep a parameter from one value to another over time, dragging the real on-screen slider so the student watches the curve deform continuously. This is the core teaching move of this app: when a student asks why a parameter has some effect, answer by animating it and narrating what they are about to see, rather than describing it in prose. Awaits the full sweep and resolves when the animation has finished, so any narration you send afterwards lands once the motion is complete. Widens the slider range automatically if from/to fall outside it. Animate one parameter at a time -- two at once is unreadable.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Slider to sweep."
          },
          "from": {
            "type": "number",
            "description": "Starting value. Defaults to the slider's current value."
          },
          "to": {
            "type": "number",
            "description": "Ending value."
          },
          "duration": {
            "type": "number",
            "description": "Milliseconds, 200 to 10000. Around 2000 reads well for a single idea; use 3000+ when the student needs to track a subtle change."
          }
        },
        "required": [
          "name",
          "to"
        ]
      },
      "page": "/graph.html?mode=2d"
    }
    set_viewportAction

    Set the visible x and y range (and z range in 3D). Use it to zoom onto the region that actually matters -- a root near the origin, the tail of a decay curve -- so the student is looking at the thing being discussed. Also widens the search window for find_features, which only reports features inside the current viewport. Pass only the bounds you want to change.

    View tool JSON
    {
      "name": "set_viewport",
      "kind": "act",
      "impl": "imperative",
      "description": "Set the visible x and y range (and z range in 3D). Use it to zoom onto the region that actually matters -- a root near the origin, the tail of a decay curve -- so the student is looking at the thing being discussed. Also widens the search window for find_features, which only reports features inside the current viewport. Pass only the bounds you want to change.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "xmin": {
            "type": "number"
          },
          "xmax": {
            "type": "number"
          },
          "ymin": {
            "type": "number"
          },
          "ymax": {
            "type": "number"
          },
          "zmin": {
            "type": "number",
            "description": "3D only: clips the surface height."
          },
          "zmax": {
            "type": "number",
            "description": "3D only: clips the surface height."
          }
        }
      },
      "page": "/graph.html?mode=2d"
    }
    set_cameraAction

    3D only. Orbit the camera around the surface. theta is the azimuth in degrees (rotates around the vertical axis), phi is the polar angle in degrees where 0 looks straight down from above and 90 is edge-on at the horizon, and distance is how far back the camera sits. This matters more than it sounds: a saddle point is invisible from directly overhead, so orbit to roughly phi 80 before explaining one, and use phi near 5 to show contour-like structure from above.

    View tool JSON
    {
      "name": "set_camera",
      "kind": "act",
      "impl": "imperative",
      "description": "3D only. Orbit the camera around the surface. theta is the azimuth in degrees (rotates around the vertical axis), phi is the polar angle in degrees where 0 looks straight down from above and 90 is edge-on at the horizon, and distance is how far back the camera sits. This matters more than it sounds: a saddle point is invisible from directly overhead, so orbit to roughly phi 80 before explaining one, and use phi near 5 to show contour-like structure from above.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "theta": {
            "type": "number",
            "description": "Azimuth in degrees."
          },
          "phi": {
            "type": "number",
            "description": "Polar angle in degrees, 1 (top-down) to 179."
          },
          "distance": {
            "type": "number",
            "description": "Camera distance, 2 to 200. Around 34 frames a typical surface."
          }
        }
      },
      "page": "/graph.html?mode=2d"
    }
    evaluateAction

    Compute a numeric value without plotting anything. Current slider values are in scope, so "a*x^2" evaluates with the slider a the student is looking at. Use this to check an answer, to get a coordinate before calling annotate, or to state a specific number in your explanation instead of estimating from the picture.

    View tool JSON
    {
      "name": "evaluate",
      "kind": "act",
      "impl": "imperative",
      "description": "Compute a numeric value without plotting anything. Current slider values are in scope, so \"a*x^2\" evaluates with the slider a the student is looking at. Use this to check an answer, to get a coordinate before calling annotate, or to state a specific number in your explanation instead of estimating from the picture.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "latex": {
            "type": "string",
            "description": "Expression to evaluate, e.g. \"a*x^2 + 1\" or \"sin(pi/4)\"."
          },
          "at": {
            "type": "object",
            "description": "Variable values, e.g. {\"x\": 2} or {\"x\": 1, \"y\": -1}. Overrides slider values of the same name.",
            "properties": {
              "x": {
                "type": "number"
              },
              "y": {
                "type": "number"
              },
              "z": {
                "type": "number"
              },
              "t": {
                "type": "number"
              }
            }
          }
        },
        "required": [
          "latex"
        ]
      },
      "page": "/graph.html?mode=2d"
    }
    read_changesAnswer

    What has changed on the board, and who changed it. Every readback carries a `revision`; pass the last one you saw as `since` and this returns only what happened after it, oldest first. Each entry says whether the actor was the "user" or the "agent" (you), what the action was, which expression or slider it touched, and the values before and after. This is how you answer "what did I just do?" or notice that the student edited something while you were talking: poll a cheap readback, compare `revision`, and call this when it has moved. Continuous gestures such as dragging a slider or panning arrive as one entry spanning the whole drag rather than hundreds. Hovering and selecting are not changes and are not recorded.

    View tool JSON
    {
      "name": "read_changes",
      "kind": "answer",
      "impl": "imperative",
      "description": "What has changed on the board, and who changed it. Every readback carries a `revision`; pass the last one you saw as `since` and this returns only what happened after it, oldest first. Each entry says whether the actor was the \"user\" or the \"agent\" (you), what the action was, which expression or slider it touched, and the values before and after. This is how you answer \"what did I just do?\" or notice that the student edited something while you were talking: poll a cheap readback, compare `revision`, and call this when it has moved. Continuous gestures such as dragging a slider or panning arrive as one entry spanning the whole drag rather than hundreds. Hovering and selecting are not changes and are not recorded.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "since": {
            "type": "number",
            "description": "Return only changes after this revision. Omit for everything still held (the last 200)."
          }
        }
      },
      "page": "/graph.html?mode=2d"
    }
    set_snappingAction

    Turn the board's two kinds of coordinate snapping on or off. to_curve makes the cursor ride a plotted curve and lock onto crossings, so the readout gives a point that is genuinely on the graph; to_grid rounds a free reading to the nearest grid line. The student has the "Snap to curve" checkbox for the first of these, so read `snapping` back before explaining a coordinate — a reading of exactly (5, 5) means something different when snapping is on than when it is off. Turn to_curve off when you want the student to read a raw position, and on when you are pointing at an intersection.

    View tool JSON
    {
      "name": "set_snapping",
      "kind": "act",
      "impl": "imperative",
      "description": "Turn the board's two kinds of coordinate snapping on or off. to_curve makes the cursor ride a plotted curve and lock onto crossings, so the readout gives a point that is genuinely on the graph; to_grid rounds a free reading to the nearest grid line. The student has the \"Snap to curve\" checkbox for the first of these, so read `snapping` back before explaining a coordinate — a reading of exactly (5, 5) means something different when snapping is on than when it is off. Turn to_curve off when you want the student to read a raw position, and on when you are pointing at an intersection.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "to_curve": {
            "type": "boolean",
            "description": "Snap the cursor onto plotted curves and their crossings."
          },
          "to_grid": {
            "type": "boolean",
            "description": "Round a free reading to the nearest grid line."
          }
        }
      },
      "page": "/graph.html?mode=2d"
    }
    find_featuresAnswer

    Numerically locate the interesting points of a curve or surface inside the current viewport, so you can reason from real coordinates instead of guessing from the algebra. For y = f(x) it returns roots, maxima, minima, vertical asymptotes, the y-intercept, and intersections with the other visible curves. For a 3D surface z = f(x,y) it returns critical points classified as maximum, minimum or saddle from the sign of the Hessian determinant. Call this before you make a claim about where something crosses zero or turns around, and before annotate so the label lands exactly on the feature. Features outside the viewport are not found -- widen it with set_viewport if you expect more.

    View tool JSON
    {
      "name": "find_features",
      "kind": "answer",
      "impl": "imperative",
      "description": "Numerically locate the interesting points of a curve or surface inside the current viewport, so you can reason from real coordinates instead of guessing from the algebra. For y = f(x) it returns roots, maxima, minima, vertical asymptotes, the y-intercept, and intersections with the other visible curves. For a 3D surface z = f(x,y) it returns critical points classified as maximum, minimum or saddle from the sign of the Hessian determinant. Call this before you make a claim about where something crosses zero or turns around, and before annotate so the label lands exactly on the feature. Features outside the viewport are not found -- widen it with set_viewport if you expect more.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Expression id from list_expressions."
          }
        },
        "required": [
          "id"
        ]
      },
      "page": "/graph.html?mode=2d"
    }
    annotateAction

    Pin a short text label to a specific point on the graph, with a leader line to the exact coordinate. Use it to make your reasoning persist on the artifact after you stop talking: mark the vertex, label the asymptote, name the saddle point. Get the coordinates from find_features or evaluate rather than eyeballing them. Keep the text to a few words -- long labels cover the curve.

    View tool JSON
    {
      "name": "annotate",
      "kind": "act",
      "impl": "imperative",
      "description": "Pin a short text label to a specific point on the graph, with a leader line to the exact coordinate. Use it to make your reasoning persist on the artifact after you stop talking: mark the vertex, label the asymptote, name the saddle point. Get the coordinates from find_features or evaluate rather than eyeballing them. Keep the text to a few words -- long labels cover the curve.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "x": {
            "type": "number"
          },
          "y": {
            "type": "number"
          },
          "z": {
            "type": "number",
            "description": "3D only: the surface height at (x, y)."
          },
          "text": {
            "type": "string",
            "description": "A few words, e.g. \"vertex (-1, -4)\" or \"saddle point\"."
          }
        },
        "required": [
          "x",
          "y",
          "text"
        ]
      },
      "page": "/graph.html?mode=2d"
    }
    clearAction

    Wipe part of the board. what:"annotations" removes the pinned labels but keeps the curves -- use this between explanations so labels do not pile up. what:"all" resets expressions, sliders and annotations for a fresh topic. Ask the student before clearing work they created themselves.

    View tool JSON
    {
      "name": "clear",
      "kind": "act",
      "impl": "imperative",
      "description": "Wipe part of the board. what:\"annotations\" removes the pinned labels but keeps the curves -- use this between explanations so labels do not pile up. what:\"all\" resets expressions, sliders and annotations for a fresh topic. Ask the student before clearing work they created themselves.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "what": {
            "type": "string",
            "enum": [
              "all",
              "annotations"
            ],
            "description": "Defaults to \"annotations\"."
          }
        }
      },
      "page": "/graph.html?mode=2d"
    }
    set_modeAction

    Switch the board between the 2D grapher and the 3D surface plotter. In 3D, equations beginning with x, y, or z render as surfaces solved for that axis. Switching does not delete anything, so you can move back and forth. Call this before plotting a surface, otherwise the student sees a blank 2D board.

    View tool JSON
    {
      "name": "set_mode",
      "kind": "act",
      "impl": "imperative",
      "description": "Switch the board between the 2D grapher and the 3D surface plotter. In 3D, equations beginning with x, y, or z render as surfaces solved for that axis. Switching does not delete anything, so you can move back and forth. Call this before plotting a surface, otherwise the student sees a blank 2D board.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "2d",
              "3d"
            ]
          }
        },
        "required": [
          "mode"
        ]
      },
      "page": "/graph.html?mode=2d"
    }
    undoAction

    Undo the last change to the graph -- an added or edited expression, a colour change, a slider adjustment, an annotation. Safe to call repeatedly to step further back. Does not change the viewport, camera or mode.

    View tool JSON
    {
      "name": "undo",
      "kind": "act",
      "impl": "imperative",
      "description": "Undo the last change to the graph -- an added or edited expression, a colour change, a slider adjustment, an annotation. Safe to call repeatedly to step further back. Does not change the viewport, camera or mode.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/graph.html?mode=2d"
    }
    redoAction

    Redo the change that was just undone. Returns an error if there is nothing to redo (making a new edit clears the redo history).

    View tool JSON
    {
      "name": "redo",
      "kind": "act",
      "impl": "imperative",
      "description": "Redo the change that was just undone. Returns an error if there is nothing to redo (making a new edit clears the redo history).",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/graph.html?mode=2d"
    }
    load_presetAction

    Load a ready-made lesson set-up: expressions, slider ranges, viewport, camera and a teaching note, all in one call. Faster and less error-prone than building a standard scenario expression by expression. Call with no arguments to list what is available. The returned teaching_note tells you which parameter is worth animating and what the student should notice -- read it before you start explaining.

    View tool JSON
    {
      "name": "load_preset",
      "kind": "act",
      "impl": "imperative",
      "description": "Load a ready-made lesson set-up: expressions, slider ranges, viewport, camera and a teaching note, all in one call. Faster and less error-prone than building a standard scenario expression by expression. Call with no arguments to list what is available. The returned teaching_note tells you which parameter is worth animating and what the student should notice -- read it before you start explaining.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Preset key. Omit to list the available presets.",
            "enum": [
              "sine_family",
              "parabola_family",
              "projectile",
              "damped_oscillator",
              "wave_beats",
              "rc_charging",
              "unit_circle",
              "saddle",
              "gaussian_hill",
              "ripple"
            ]
          }
        }
      },
      "page": "/graph.html?mode=2d"
    }

    page: Circuit

    list_componentsAnswer

    Read the whole board: every component with its id, type, position, value, orientation and live reading (current in mA, voltage across it, and whether an LED or lamp is lit), plus every wire, whether a ground is present, and any warnings from the last solve. Call this first whenever you are unsure what is on the board, and after any change you did not make yourself. The ids returned here are what every other tool expects. Pin names are listed per component; wire them with connect using "id.pin" refs such as "r1.a" or "bat1.pos".

    View tool JSON
    {
      "name": "list_components",
      "kind": "answer",
      "impl": "imperative",
      "description": "Read the whole board: every component with its id, type, position, value, orientation and live reading (current in mA, voltage across it, and whether an LED or lamp is lit), plus every wire, whether a ground is present, and any warnings from the last solve. Call this first whenever you are unsure what is on the board, and after any change you did not make yourself. The ids returned here are what every other tool expects. Pin names are listed per component; wire them with connect using \"id.pin\" refs such as \"r1.a\" or \"bat1.pos\".",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/circuit.html"
    }
    list_component_typesAnswer

    List the kinds of part you can add, with the pins each one has, the unit of its value, and a one-line description of what it does. Use this when deciding which component to place, or to remind yourself of a part's pin names before wiring it.

    View tool JSON
    {
      "name": "list_component_types",
      "kind": "answer",
      "impl": "imperative",
      "description": "List the kinds of part you can add, with the pins each one has, the unit of its value, and a one-line description of what it does. Use this when deciding which component to place, or to remind yourself of a part's pin names before wiring it.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/circuit.html"
    }
    add_componentAction

    Place a new part on the board and wire nothing yet. type is one of battery, resistor, led, lamp, switch, ground. Give value in the part's unit (volts for a battery, ohms for a resistor or lamp; ignored for the others). Optionally set position x,y on the integer grid (parts are two units long, so space them at least two apart), a rotation of 0, 90, 180 or 270 degrees, and for an LED a color. Returns the new id and its pin names — wire them up with connect. If you omit x and y the part is auto-placed on a free grid slot.

    View tool JSON
    {
      "name": "add_component",
      "kind": "act",
      "impl": "imperative",
      "description": "Place a new part on the board and wire nothing yet. type is one of battery, resistor, led, lamp, switch, ground. Give value in the part's unit (volts for a battery, ohms for a resistor or lamp; ignored for the others). Optionally set position x,y on the integer grid (parts are two units long, so space them at least two apart), a rotation of 0, 90, 180 or 270 degrees, and for an LED a color. Returns the new id and its pin names — wire them up with connect. If you omit x and y the part is auto-placed on a free grid slot.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "battery",
              "resistor",
              "led",
              "lamp",
              "switch",
              "capacitor",
              "inductor",
              "diode",
              "potentiometer",
              "currentsource",
              "acsource",
              "fuse",
              "voltmeter",
              "ammeter",
              "motor",
              "buzzer",
              "ground"
            ],
            "description": "Which part to add."
          },
          "value": {
            "type": "number",
            "description": "Volts for a battery, ohms for a resistor or lamp."
          },
          "x": {
            "type": "number",
            "description": "Grid x (integer). Omit to auto-place."
          },
          "y": {
            "type": "number",
            "description": "Grid y (integer). Omit to auto-place."
          },
          "rotation": {
            "type": "number",
            "enum": [
              "0",
              "90",
              "180",
              "270"
            ],
            "description": "Orientation in degrees."
          },
          "color": {
            "type": "string",
            "enum": [
              "red",
              "green",
              "blue",
              "yellow",
              "white"
            ],
            "description": "LED colour (LED only)."
          }
        },
        "required": [
          "type"
        ]
      },
      "page": "/circuit.html"
    }
    connectAction

    Run a wire between two pins so current can flow through them. Each pin is written "componentId.pin", e.g. connect("bat1.pos", "r1.a"). Wiring two pins puts them on the same electrical node — chain parts pin-to-pin to build a loop, and remember a circuit only works when it forms a complete loop back to the battery. Returns a parse-style error if a pin name is wrong, so read it and retry rather than reporting failure.

    View tool JSON
    {
      "name": "connect",
      "kind": "act",
      "impl": "imperative",
      "description": "Run a wire between two pins so current can flow through them. Each pin is written \"componentId.pin\", e.g. connect(\"bat1.pos\", \"r1.a\"). Wiring two pins puts them on the same electrical node — chain parts pin-to-pin to build a loop, and remember a circuit only works when it forms a complete loop back to the battery. Returns a parse-style error if a pin name is wrong, so read it and retry rather than reporting failure.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string",
            "description": "First pin, e.g. \"bat1.pos\"."
          },
          "to": {
            "type": "string",
            "description": "Second pin, e.g. \"r1.a\"."
          }
        },
        "required": [
          "from",
          "to"
        ]
      },
      "page": "/circuit.html"
    }
    disconnectAction

    Remove the wire between two pins, written the same way as connect: disconnect("r1.b", "led1.anode"). Use this to break a connection without deleting either part.

    View tool JSON
    {
      "name": "disconnect",
      "kind": "act",
      "impl": "imperative",
      "description": "Remove the wire between two pins, written the same way as connect: disconnect(\"r1.b\", \"led1.anode\"). Use this to break a connection without deleting either part.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string"
          },
          "to": {
            "type": "string"
          }
        },
        "required": [
          "from",
          "to"
        ]
      },
      "page": "/circuit.html"
    }
    set_valueAction

    Change a component's value: resistance in ohms for a resistor or lamp, voltage in volts for a battery. The board re-solves immediately, so follow with read_measurements (or read the reading in the return) to see the effect. This is the main teaching lever — e.g. lower a series resistor and watch the LED current climb toward the overcurrent warning.

    View tool JSON
    {
      "name": "set_value",
      "kind": "act",
      "impl": "imperative",
      "description": "Change a component's value: resistance in ohms for a resistor or lamp, voltage in volts for a battery. The board re-solves immediately, so follow with read_measurements (or read the reading in the return) to see the effect. This is the main teaching lever — e.g. lower a series resistor and watch the LED current climb toward the overcurrent warning.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Component id from list_components."
          },
          "value": {
            "type": "number",
            "description": "New value in the component's unit."
          }
        },
        "required": [
          "id",
          "value"
        ]
      },
      "page": "/circuit.html"
    }
    toggle_switchAction

    Open or close a switch. Pass closed:true or closed:false to set it directly, or omit closed to flip it. A closed switch behaves like a wire; an open one breaks the circuit. Great for showing cause and effect — close it and narrate the LED coming on.

    View tool JSON
    {
      "name": "toggle_switch",
      "kind": "act",
      "impl": "imperative",
      "description": "Open or close a switch. Pass closed:true or closed:false to set it directly, or omit closed to flip it. A closed switch behaves like a wire; an open one breaks the circuit. Great for showing cause and effect — close it and narrate the LED coming on.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Switch id."
          },
          "closed": {
            "type": "boolean",
            "description": "Desired state. Omit to toggle."
          }
        },
        "required": [
          "id"
        ]
      },
      "page": "/circuit.html"
    }
    set_led_colorAction

    Change an LED's colour, which also changes its forward voltage (red ≈ 1.8 V up to blue/white ≈ 3 V). Handy when a demonstration needs a specific colour, or to show why a blue LED needs more supply voltage than a red one to light.

    View tool JSON
    {
      "name": "set_led_color",
      "kind": "act",
      "impl": "imperative",
      "description": "Change an LED's colour, which also changes its forward voltage (red ≈ 1.8 V up to blue/white ≈ 3 V). Handy when a demonstration needs a specific colour, or to show why a blue LED needs more supply voltage than a red one to light.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "LED id."
          },
          "color": {
            "type": "string",
            "enum": [
              "red",
              "green",
              "blue",
              "yellow",
              "white"
            ]
          }
        },
        "required": [
          "id",
          "color"
        ]
      },
      "page": "/circuit.html"
    }
    configureAction

    Set a component-specific setting that is not its main value: the wiper position of a potentiometer (0 = fully toward pin a, 1 = fully toward pin b) or the frequency of an AC source in hertz. For example configure({id:"pot1", wiper:0.25}) taps off a quarter of the way, and configure({id:"ac1", frequency:2}) makes the AC source oscillate at 2 Hz. Pass only the field that applies to the part.

    View tool JSON
    {
      "name": "configure",
      "kind": "act",
      "impl": "imperative",
      "description": "Set a component-specific setting that is not its main value: the wiper position of a potentiometer (0 = fully toward pin a, 1 = fully toward pin b) or the frequency of an AC source in hertz. For example configure({id:\"pot1\", wiper:0.25}) taps off a quarter of the way, and configure({id:\"ac1\", frequency:2}) makes the AC source oscillate at 2 Hz. Pass only the field that applies to the part.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Component id."
          },
          "wiper": {
            "type": "number",
            "description": "Potentiometer wiper, 0 to 1."
          },
          "frequency": {
            "type": "number",
            "description": "AC source frequency in Hz."
          }
        },
        "required": [
          "id"
        ]
      },
      "page": "/circuit.html"
    }
    rotate_componentAction

    Rotate a part in 90-degree steps. Pass rotation as 0, 90, 180 or 270 to set it, or omit to turn it a quarter-turn clockwise. Rotating never changes the electrics, only how the part sits on the grid and where its pins land — useful for laying out a tidy loop.

    View tool JSON
    {
      "name": "rotate_component",
      "kind": "act",
      "impl": "imperative",
      "description": "Rotate a part in 90-degree steps. Pass rotation as 0, 90, 180 or 270 to set it, or omit to turn it a quarter-turn clockwise. Rotating never changes the electrics, only how the part sits on the grid and where its pins land — useful for laying out a tidy loop.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "rotation": {
            "type": "number",
            "enum": [
              "0",
              "90",
              "180",
              "270"
            ]
          }
        },
        "required": [
          "id"
        ]
      },
      "page": "/circuit.html"
    }
    move_componentAction

    Move a part to grid position x,y. Wires stay attached and follow the pins, so this only tidies the layout — it never changes the circuit. Parts are two grid units long; keep them at least two apart so their pins do not overlap.

    View tool JSON
    {
      "name": "move_component",
      "kind": "act",
      "impl": "imperative",
      "description": "Move a part to grid position x,y. Wires stay attached and follow the pins, so this only tidies the layout — it never changes the circuit. Parts are two grid units long; keep them at least two apart so their pins do not overlap.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "x": {
            "type": "number"
          },
          "y": {
            "type": "number"
          }
        },
        "required": [
          "id",
          "x",
          "y"
        ]
      },
      "page": "/circuit.html"
    }
    remove_componentAction

    Delete a part and any wires attached to it. Ask the student before removing work they built themselves.

    View tool JSON
    {
      "name": "remove_component",
      "kind": "act",
      "impl": "imperative",
      "description": "Delete a part and any wires attached to it. Ask the student before removing work they built themselves.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          }
        },
        "required": [
          "id"
        ]
      },
      "page": "/circuit.html"
    }
    read_measurementsAnswer

    Return the full solved state: the voltage at every electrical node, and for every component its current (mA), the voltage across it, its power, and whether an LED or lamp is lit — plus any warnings such as overcurrent or an open circuit. Call this before making a numeric claim, so you state measured values instead of estimating them from the schematic.

    View tool JSON
    {
      "name": "read_measurements",
      "kind": "answer",
      "impl": "imperative",
      "description": "Return the full solved state: the voltage at every electrical node, and for every component its current (mA), the voltage across it, its power, and whether an LED or lamp is lit — plus any warnings such as overcurrent or an open circuit. Call this before making a numeric claim, so you state measured values instead of estimating them from the schematic.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/circuit.html"
    }
    measureAnswer

    Act like a multimeter: return the voltage between two pins, from minus to. measure("r1.a", "r1.b") gives the voltage across r1; measure against a ground pin gives a node voltage. Both pins must be on parts that exist. Use this to answer "what is the voltage here" precisely rather than reading it off the picture.

    View tool JSON
    {
      "name": "measure",
      "kind": "answer",
      "impl": "imperative",
      "description": "Act like a multimeter: return the voltage between two pins, from minus to. measure(\"r1.a\", \"r1.b\") gives the voltage across r1; measure against a ground pin gives a node voltage. Both pins must be on parts that exist. Use this to answer \"what is the voltage here\" precisely rather than reading it off the picture.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string",
            "description": "Pin to measure at, e.g. \"r1.a\"."
          },
          "to": {
            "type": "string",
            "description": "Reference pin, e.g. \"bat1.neg\" or a ground pin."
          }
        },
        "required": [
          "from",
          "to"
        ]
      },
      "page": "/circuit.html"
    }
    clearAction

    Wipe the board. what:"wires" removes every wire but keeps the parts where they are, so you can rewire without re-placing. what:"all" removes everything for a fresh start. Ask the student before clearing work they made themselves.

    View tool JSON
    {
      "name": "clear",
      "kind": "act",
      "impl": "imperative",
      "description": "Wipe the board. what:\"wires\" removes every wire but keeps the parts where they are, so you can rewire without re-placing. what:\"all\" removes everything for a fresh start. Ask the student before clearing work they made themselves.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "what": {
            "type": "string",
            "enum": [
              "all",
              "wires"
            ],
            "description": "Defaults to \"all\"."
          }
        }
      },
      "page": "/circuit.html"
    }
    undoAction

    Undo the last change to the circuit (add, delete, wire, value change, and so on). Safe to call repeatedly to step further back. Does not affect the running simulation clock, only the circuit itself.

    View tool JSON
    {
      "name": "undo",
      "kind": "act",
      "impl": "imperative",
      "description": "Undo the last change to the circuit (add, delete, wire, value change, and so on). Safe to call repeatedly to step further back. Does not affect the running simulation clock, only the circuit itself.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/circuit.html"
    }
    redoAction

    Redo the change that was just undone. Returns an error if there is nothing to redo (for example after you make a new edit, which clears the redo history).

    View tool JSON
    {
      "name": "redo",
      "kind": "act",
      "impl": "imperative",
      "description": "Redo the change that was just undone. Returns an error if there is nothing to redo (for example after you make a new edit, which clears the redo history).",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/circuit.html"
    }
    add_probeAction

    Add a trace to the oscilloscope so a signal is plotted over time. Give a component id and whether to plot its "voltage" or "current". The scope opens automatically. Combined with the transient parts (capacitor, inductor, AC source) this is how you show a waveform -- e.g. add_probe on a charging capacitor's voltage to draw the RC curve, or on an AC source to draw the sine. Keep the simulation running so the trace fills in; call reset_simulation to replay from the start.

    View tool JSON
    {
      "name": "add_probe",
      "kind": "act",
      "impl": "imperative",
      "description": "Add a trace to the oscilloscope so a signal is plotted over time. Give a component id and whether to plot its \"voltage\" or \"current\". The scope opens automatically. Combined with the transient parts (capacitor, inductor, AC source) this is how you show a waveform -- e.g. add_probe on a charging capacitor's voltage to draw the RC curve, or on an AC source to draw the sine. Keep the simulation running so the trace fills in; call reset_simulation to replay from the start.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Component id to probe."
          },
          "quantity": {
            "type": "string",
            "enum": [
              "voltage",
              "current"
            ],
            "description": "Which signal to plot."
          }
        },
        "required": [
          "id",
          "quantity"
        ]
      },
      "page": "/circuit.html"
    }
    read_scopeAnswer

    Read what the oscilloscope has captured: each trace with its most recent value and a downsampled series of (time, value) points over the visible window. Use this to state a measured waveform fact precisely -- for example that a capacitor reached 63% of its final voltage after one time constant -- instead of estimating from the picture.

    View tool JSON
    {
      "name": "read_scope",
      "kind": "answer",
      "impl": "imperative",
      "description": "Read what the oscilloscope has captured: each trace with its most recent value and a downsampled series of (time, value) points over the visible window. Use this to state a measured waveform fact precisely -- for example that a capacitor reached 63% of its final voltage after one time constant -- instead of estimating from the picture.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/circuit.html"
    }
    show_scopeAction

    Show or hide the oscilloscope panel, and optionally clear its traces. Pass visible:false to hide it, or clear:true to remove every trace.

    View tool JSON
    {
      "name": "show_scope",
      "kind": "act",
      "impl": "imperative",
      "description": "Show or hide the oscilloscope panel, and optionally clear its traces. Pass visible:false to hide it, or clear:true to remove every trace.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "visible": {
            "type": "boolean",
            "description": "Show (true) or hide (false) the scope."
          },
          "clear": {
            "type": "boolean",
            "description": "Remove all traces when true."
          }
        }
      },
      "page": "/circuit.html"
    }
    read_changesAnswer

    What has changed on the board, and who changed it. Every readback carries a `revision`; pass the last one you saw as `since` and this returns only what happened after it, oldest first. Each entry names the actor -- "user" for the student working the board directly, "agent" for a tool call -- along with the action, the part or wire it touched, and the values either side. This is how you notice that the student rewired something or turned a knob while you were explaining: watch `revision` on a cheap readback and call this when it moves. A continuous gesture such as dragging a value slider arrives as one entry spanning the drag.

    View tool JSON
    {
      "name": "read_changes",
      "kind": "answer",
      "impl": "imperative",
      "description": "What has changed on the board, and who changed it. Every readback carries a `revision`; pass the last one you saw as `since` and this returns only what happened after it, oldest first. Each entry names the actor -- \"user\" for the student working the board directly, \"agent\" for a tool call -- along with the action, the part or wire it touched, and the values either side. This is how you notice that the student rewired something or turned a knob while you were explaining: watch `revision` on a cheap readback and call this when it moves. A continuous gesture such as dragging a value slider arrives as one entry spanning the drag.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "since": {
            "type": "number",
            "description": "Return only changes after this revision. Omit for everything still held."
          }
        }
      },
      "page": "/circuit.html"
    }
    pause_simulationAction

    Freeze the live simulation where it is. Voltages, currents and capacitor charges hold their present values instead of advancing, which is what you want before talking about a particular moment of a transient — the point where an RC curve reaches 63%, say. The board can still be edited while paused, but the readings will not update until you resume. The student has the same control as the Simulating/Paused button above the board, so check `running` in a readback before assuming the circuit is live.

    View tool JSON
    {
      "name": "pause_simulation",
      "kind": "act",
      "impl": "imperative",
      "description": "Freeze the live simulation where it is. Voltages, currents and capacitor charges hold their present values instead of advancing, which is what you want before talking about a particular moment of a transient — the point where an RC curve reaches 63%, say. The board can still be edited while paused, but the readings will not update until you resume. The student has the same control as the Simulating/Paused button above the board, so check `running` in a readback before assuming the circuit is live.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/circuit.html"
    }
    resume_simulationAction

    Let a paused simulation run again from where it stopped, re-solving the circuit immediately so the readings catch up. Use this after pause_simulation, or when a readback shows running:false and you need live values.

    View tool JSON
    {
      "name": "resume_simulation",
      "kind": "act",
      "impl": "imperative",
      "description": "Let a paused simulation run again from where it stopped, re-solving the circuit immediately so the readings catch up. Use this after pause_simulation, or when a readback shows running:false and you need live values.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/circuit.html"
    }
    reset_simulationAction

    Discharge every capacitor and restart the simulation from time zero. Use this before demonstrating an RC charging curve so the capacitor starts empty, or to replay a transient from the beginning. Has no effect on a purely resistive circuit.

    View tool JSON
    {
      "name": "reset_simulation",
      "kind": "act",
      "impl": "imperative",
      "description": "Discharge every capacitor and restart the simulation from time zero. Use this before demonstrating an RC charging curve so the capacitor starts empty, or to replay a transient from the beginning. Has no effect on a purely resistive circuit.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/circuit.html"
    }
    load_presetAction

    Load a ready-made starter circuit — parts, values and wiring in one call — instead of building a standard scenario piece by piece. Call with no arguments to list what is available. The returned note explains what the circuit shows and which value is worth changing, so read it before you start teaching.

    View tool JSON
    {
      "name": "load_preset",
      "kind": "act",
      "impl": "imperative",
      "description": "Load a ready-made starter circuit — parts, values and wiring in one call — instead of building a standard scenario piece by piece. Call with no arguments to list what is available. The returned note explains what the circuit shows and which value is worth changing, so read it before you start teaching.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "enum": [
              "led_basic",
              "led_no_resistor",
              "switch_led",
              "series_resistors",
              "parallel_resistors",
              "voltage_divider",
              "traffic_lights",
              "rc_charging",
              "diode_protection",
              "pot_dimmer",
              "rl_circuit",
              "ac_demo"
            ],
            "description": "Preset key. Omit to list them."
          }
        }
      },
      "page": "/circuit.html"
    }

    page: Physics

    describe_sandboxAnswer

    Read the rules and the current situation before you do anything else: which of the three stages the sandbox is in, the size of the box and where its origin is, the units every other tool uses, gravity, how many of the 15 object slots are used, and the physics that is and is not modelled (no friction, no air resistance). Call this first in a new conversation, and whenever you are unsure whether you are allowed to edit right now.

    View tool JSON
    {
      "name": "describe_sandbox",
      "kind": "answer",
      "impl": "imperative",
      "description": "Read the rules and the current situation before you do anything else: which of the three stages the sandbox is in, the size of the box and where its origin is, the units every other tool uses, gravity, how many of the 15 object slots are used, and the physics that is and is not modelled (no friction, no air resistance). Call this first in a new conversation, and whenever you are unsure whether you are allowed to edit right now.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/physics.html"
    }
    list_libraryAnswer

    List everything that can be placed or used, in the three sections the student sees: design tools (select, draw line, erase, force, velocity), blocks (fixed scenery that bears weight — ramps, platforms, walls, quarter-pipes, coaster dips, loops) and objects (things that move once the run starts — ball, cube, cart, plank). Each entry gives its default size, default mass and a one-line note on what it is for. Read this before add_object so you pick a shape that actually matches the problem.

    View tool JSON
    {
      "name": "list_library",
      "kind": "answer",
      "impl": "imperative",
      "description": "List everything that can be placed or used, in the three sections the student sees: design tools (select, draw line, erase, force, velocity), blocks (fixed scenery that bears weight — ramps, platforms, walls, quarter-pipes, coaster dips, loops) and objects (things that move once the run starts — ball, cube, cart, plank). Each entry gives its default size, default mass and a one-line note on what it is for. Read this before add_object so you pick a shape that actually matches the problem.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/physics.html"
    }
    list_objectsAnswer

    Read everything in the box: every object with its id, type, position, angle, size, mass, bounciness, and — while a run is going — its velocity, speed, kinetic and potential energy, momentum, the forces acting on it right now, and how long it has been at rest. Call this whenever you are unsure what is on screen, and after any change you did not make yourself. The ids returned here are what every other tool expects.

    View tool JSON
    {
      "name": "list_objects",
      "kind": "answer",
      "impl": "imperative",
      "description": "Read everything in the box: every object with its id, type, position, angle, size, mass, bounciness, and — while a run is going — its velocity, speed, kinetic and potential energy, momentum, the forces acting on it right now, and how long it has been at rest. Call this whenever you are unsure what is on screen, and after any change you did not make yourself. The ids returned here are what every other tool expects.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/physics.html"
    }
    get_objectAnswer

    Everything about one object, including its full motion history so far (time, position and velocity sampled 20 times a second while the run is going). Use this to make a precise claim about one thing — "it was doing 12.4 m/s when it hit the floor" — instead of estimating from the picture.

    View tool JSON
    {
      "name": "get_object",
      "kind": "answer",
      "impl": "imperative",
      "description": "Everything about one object, including its full motion history so far (time, position and velocity sampled 20 times a second while the run is going). Use this to make a precise claim about one thing — \"it was doing 12.4 m/s when it hit the floor\" — instead of estimating from the picture.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Object id from list_objects."
          }
        },
        "required": [
          "id"
        ]
      },
      "page": "/physics.html"
    }
    add_objectAction

    Place something in the box during the pre-simulation stage. type is one of the block or object types from list_library — blocks (ramp, platform, wall, step, curve, coaster, loop) are fixed scenery that bears weight, objects (ball, cube, cart, plank) are what actually moves. Give x and y in metres from the bottom-left of the box; anything you place simply floats there until the run starts, so you can build the scene in any order. Optionally set the size, mass and restitution (bounciness, 0 = a dead stop, 1 = bounces back to the same height). Returns the new id, which every other tool expects.

    View tool JSON
    {
      "name": "add_object",
      "kind": "act",
      "impl": "imperative",
      "description": "Place something in the box during the pre-simulation stage. type is one of the block or object types from list_library — blocks (ramp, platform, wall, step, curve, coaster, loop) are fixed scenery that bears weight, objects (ball, cube, cart, plank) are what actually moves. Give x and y in metres from the bottom-left of the box; anything you place simply floats there until the run starts, so you can build the scene in any order. Optionally set the size, mass and restitution (bounciness, 0 = a dead stop, 1 = bounces back to the same height). Returns the new id, which every other tool expects.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "ramp",
              "platform",
              "wall",
              "step",
              "curve",
              "coaster",
              "loop",
              "ball",
              "cube",
              "cart",
              "plank"
            ],
            "description": "What to place."
          },
          "x": {
            "type": "number",
            "description": "Centre x in metres, 0 at the left wall."
          },
          "y": {
            "type": "number",
            "description": "Centre y in metres, 0 at the floor."
          },
          "angle": {
            "type": "number",
            "description": "Rotation in degrees counter-clockwise. Handy for tilting a ramp or a platform."
          },
          "width": {
            "type": "number",
            "description": "Width in metres (rectangular and triangular shapes)."
          },
          "height": {
            "type": "number",
            "description": "Height in metres (rectangular and triangular shapes)."
          },
          "radius": {
            "type": "number",
            "description": "Radius in metres (ball only)."
          },
          "mass": {
            "type": "number",
            "description": "Mass in kilograms. Ignored for blocks, which never move."
          },
          "restitution": {
            "type": "number",
            "description": "Bounciness from 0 to 0.98. 0.2 settles quickly, 0.85 bounces for a long time."
          },
          "material": {
            "type": "string",
            "enum": [
              "rubber",
              "wood",
              "steel",
              "concrete",
              "ice"
            ],
            "description": "Surface material, which sets friction against whatever it touches. Only matters when friction is on. Defaults per type — see list_library."
          }
        },
        "required": [
          "type"
        ]
      },
      "page": "/physics.html"
    }
    draw_lineAction

    Draw a solid line or curve from a list of points, the same thing the student gets from the draw tool. The result is one fixed object that bears weight exactly like the floor, so this is how you build a shape the block library does not have — an arbitrary slope, a funnel, a bowl, a stepped path. Give at least two points in metres; give many points to draw a curve. Counts as one object against the 15-object cap however many points you use.

    View tool JSON
    {
      "name": "draw_line",
      "kind": "act",
      "impl": "imperative",
      "description": "Draw a solid line or curve from a list of points, the same thing the student gets from the draw tool. The result is one fixed object that bears weight exactly like the floor, so this is how you build a shape the block library does not have — an arbitrary slope, a funnel, a bowl, a stepped path. Give at least two points in metres; give many points to draw a curve. Counts as one object against the 15-object cap however many points you use.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "points": {
            "type": "array",
            "description": "The path, as [{x, y}, {x, y}, …] in metres. Two points make a straight line.",
            "items": {
              "type": "object",
              "properties": {
                "x": {
                  "type": "number"
                },
                "y": {
                  "type": "number"
                }
              },
              "required": [
                "x",
                "y"
              ]
            }
          }
        },
        "required": [
          "points"
        ]
      },
      "page": "/physics.html"
    }
    move_objectAction

    Move an object to a new centre position, in metres. Only works in the pre-simulation stage, where objects float wherever you put them regardless of what is underneath. Use this to line a ball up at the top of a ramp, or to drop something from a specific height.

    View tool JSON
    {
      "name": "move_object",
      "kind": "act",
      "impl": "imperative",
      "description": "Move an object to a new centre position, in metres. Only works in the pre-simulation stage, where objects float wherever you put them regardless of what is underneath. Use this to line a ball up at the top of a ramp, or to drop something from a specific height.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "x": {
            "type": "number",
            "description": "New centre x in metres."
          },
          "y": {
            "type": "number",
            "description": "New centre y in metres."
          }
        },
        "required": [
          "id",
          "x",
          "y"
        ]
      },
      "page": "/physics.html"
    }
    rotate_objectAction

    Set an object's angle in degrees counter-clockwise. Rotating a ramp turns the slope the other way; rotating a platform makes an incline out of it; rotating a plank stands it on end. Pre-simulation stage only.

    View tool JSON
    {
      "name": "rotate_object",
      "kind": "act",
      "impl": "imperative",
      "description": "Set an object's angle in degrees counter-clockwise. Rotating a ramp turns the slope the other way; rotating a platform makes an incline out of it; rotating a plank stands it on end. Pre-simulation stage only.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "angle": {
            "type": "number",
            "description": "Angle in degrees, counter-clockwise from horizontal."
          }
        },
        "required": [
          "id",
          "angle"
        ]
      },
      "page": "/physics.html"
    }
    set_propertyAction

    Change an object's mass, bounciness, size or surface material. This is the main teaching lever: double the mass and show that a frictionless slide is unaffected, raise restitution from 0.2 to 0.85 and watch the ball keep bouncing, or change the material from rubber to ice and watch the same block stop skidding. Material only matters while friction is switched on, and it is one half of a pair — see list_friction_pairs. Mass only applies to movable objects; blocks are immovable whatever you set. Pre-simulation stage only.

    View tool JSON
    {
      "name": "set_property",
      "kind": "act",
      "impl": "imperative",
      "description": "Change an object's mass, bounciness, size or surface material. This is the main teaching lever: double the mass and show that a frictionless slide is unaffected, raise restitution from 0.2 to 0.85 and watch the ball keep bouncing, or change the material from rubber to ice and watch the same block stop skidding. Material only matters while friction is switched on, and it is one half of a pair — see list_friction_pairs. Mass only applies to movable objects; blocks are immovable whatever you set. Pre-simulation stage only.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "mass": {
            "type": "number",
            "description": "Mass in kilograms (movable objects only)."
          },
          "restitution": {
            "type": "number",
            "description": "Bounciness, 0 to 0.98."
          },
          "material": {
            "type": "string",
            "enum": [
              "rubber",
              "wood",
              "steel",
              "concrete",
              "ice"
            ],
            "description": "Surface material, one half of the friction pair."
          },
          "width": {
            "type": "number",
            "description": "New width in metres."
          },
          "height": {
            "type": "number",
            "description": "New height in metres."
          },
          "radius": {
            "type": "number",
            "description": "New radius in metres (ball only)."
          }
        },
        "required": [
          "id"
        ]
      },
      "page": "/physics.html"
    }
    set_frictionAction

    Switch Coulomb friction on or off for the whole sandbox. Off (the default) is the idealised frictionless case: a block on a slope always slides, and nothing sliding on the flat ever stops. On, every contact also resists sliding, with the coefficient taken from the materials of the two surfaces touching — so a rubber ball on concrete (mu 0.90) stops almost at once while steel on ice (mu 0.03) barely slows. The natural lesson is to run a scene both ways: start frictionless, then retry_simulation, turn friction on, and run the identical layout again. Pre-simulation stage only, so that a run stays reproducible.

    View tool JSON
    {
      "name": "set_friction",
      "kind": "act",
      "impl": "imperative",
      "description": "Switch Coulomb friction on or off for the whole sandbox. Off (the default) is the idealised frictionless case: a block on a slope always slides, and nothing sliding on the flat ever stops. On, every contact also resists sliding, with the coefficient taken from the materials of the two surfaces touching — so a rubber ball on concrete (mu 0.90) stops almost at once while steel on ice (mu 0.03) barely slows. The natural lesson is to run a scene both ways: start frictionless, then retry_simulation, turn friction on, and run the identical layout again. Pre-simulation stage only, so that a run stays reproducible.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "true to switch friction on, false for the frictionless case."
          }
        },
        "required": [
          "enabled"
        ]
      },
      "page": "/physics.html"
    }
    list_friction_pairsAnswer

    The friction table: the coefficient for every pair of the five materials (rubber, wood, steel, concrete, ice), what each material is like, and which material each object in the box currently has. Friction is a property of the pair in contact, not of one object, so this is what you quote when explaining why one block skids further than another — and what you read before choosing a material with set_property.

    View tool JSON
    {
      "name": "list_friction_pairs",
      "kind": "answer",
      "impl": "imperative",
      "description": "The friction table: the coefficient for every pair of the five materials (rubber, wood, steel, concrete, ice), what each material is like, and which material each object in the box currently has. Friction is a property of the pair in contact, not of one object, so this is what you quote when explaining why one block skids further than another — and what you read before choosing a material with set_property.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/physics.html"
    }
    apply_forceAction

    Attach a force to a movable object, to be applied when the run starts. mode:"start" is a single kick at t=0 and fx/fy are an impulse in newton-seconds, so the object leaves with velocity impulse/mass. mode:"continuous" holds a steady push of fx/fy newtons from t=0 for duration seconds — at most 10 — and the object accelerates at F/m the whole time, then coasts (there is no friction to slow it afterwards). +x is right, +y is up. You can attach several forces to the same object; they add. Pre-simulation stage only.

    View tool JSON
    {
      "name": "apply_force",
      "kind": "act",
      "impl": "imperative",
      "description": "Attach a force to a movable object, to be applied when the run starts. mode:\"start\" is a single kick at t=0 and fx/fy are an impulse in newton-seconds, so the object leaves with velocity impulse/mass. mode:\"continuous\" holds a steady push of fx/fy newtons from t=0 for duration seconds — at most 10 — and the object accelerates at F/m the whole time, then coasts (there is no friction to slow it afterwards). +x is right, +y is up. You can attach several forces to the same object; they add. Pre-simulation stage only.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Movable object id."
          },
          "fx": {
            "type": "number",
            "description": "Horizontal component: newtons if continuous, newton-seconds if a start kick."
          },
          "fy": {
            "type": "number",
            "description": "Vertical component, positive upward."
          },
          "mode": {
            "type": "string",
            "enum": [
              "start",
              "continuous"
            ],
            "description": "A single kick at t=0, or a force held for up to 10 seconds. Defaults to \"start\"."
          },
          "duration": {
            "type": "number",
            "description": "Seconds to hold a continuous force, 0.05 to 10."
          }
        },
        "required": [
          "id",
          "fx",
          "fy"
        ]
      },
      "page": "/physics.html"
    }
    set_velocityAction

    Give a movable object a velocity for the start of the run. mode:"start" means it is already travelling at vx, vy the instant the run begins and gravity takes over immediately — this is how you set up a projectile. mode:"continuous" holds it at exactly that velocity for duration seconds (at most 10), ignoring gravity while the hold lasts, then releases it — use it to drive something onto a ramp at a known speed. Replaces any velocity already set on that object. Pre-simulation stage only.

    View tool JSON
    {
      "name": "set_velocity",
      "kind": "act",
      "impl": "imperative",
      "description": "Give a movable object a velocity for the start of the run. mode:\"start\" means it is already travelling at vx, vy the instant the run begins and gravity takes over immediately — this is how you set up a projectile. mode:\"continuous\" holds it at exactly that velocity for duration seconds (at most 10), ignoring gravity while the hold lasts, then releases it — use it to drive something onto a ramp at a known speed. Replaces any velocity already set on that object. Pre-simulation stage only.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Movable object id."
          },
          "vx": {
            "type": "number",
            "description": "Horizontal velocity in m/s, positive to the right."
          },
          "vy": {
            "type": "number",
            "description": "Vertical velocity in m/s, positive upward."
          },
          "mode": {
            "type": "string",
            "enum": [
              "start",
              "continuous"
            ],
            "description": "Starting velocity, or a velocity held for up to 10 seconds. Defaults to \"start\"."
          },
          "duration": {
            "type": "number",
            "description": "Seconds to hold a continuous velocity, 0.05 to 10."
          }
        },
        "required": [
          "id",
          "vx",
          "vy"
        ]
      },
      "page": "/physics.html"
    }
    clear_motionAction

    Remove the forces and/or the starting velocity you attached to an object, leaving the object itself in place. Use what:"forces" to drop every force, what:"velocity" to drop the starting velocity, or what:"all" for both — for example to show the same scene falling under gravity alone.

    View tool JSON
    {
      "name": "clear_motion",
      "kind": "act",
      "impl": "imperative",
      "description": "Remove the forces and/or the starting velocity you attached to an object, leaving the object itself in place. Use what:\"forces\" to drop every force, what:\"velocity\" to drop the starting velocity, or what:\"all\" for both — for example to show the same scene falling under gravity alone.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "what": {
            "type": "string",
            "enum": [
              "all",
              "forces",
              "velocity"
            ],
            "description": "Defaults to \"all\"."
          }
        },
        "required": [
          "id"
        ]
      },
      "page": "/physics.html"
    }
    remove_objectAction

    Delete one object from the box, freeing a slot against the 15-object cap. The floor and the side walls are part of the box and cannot be removed. Ask the student before deleting something they built themselves.

    View tool JSON
    {
      "name": "remove_object",
      "kind": "act",
      "impl": "imperative",
      "description": "Delete one object from the box, freeing a slot against the 15-object cap. The floor and the side walls are part of the box and cannot be removed. Ask the student before deleting something they built themselves.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          }
        },
        "required": [
          "id"
        ]
      },
      "page": "/physics.html"
    }
    set_gravityAction

    Change gravity in m/s². Earth is 9.81, the Moon 1.62, Jupiter 24.8, and 0 turns it off entirely so objects drift in straight lines. A memorable way to show that in the absence of air resistance the acceleration is the same for every mass. Pre-simulation stage only.

    View tool JSON
    {
      "name": "set_gravity",
      "kind": "act",
      "impl": "imperative",
      "description": "Change gravity in m/s². Earth is 9.81, the Moon 1.62, Jupiter 24.8, and 0 turns it off entirely so objects drift in straight lines. A memorable way to show that in the absence of air resistance the acceleration is the same for every mass. Pre-simulation stage only.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "gravity": {
            "type": "number",
            "description": "Downward acceleration in m/s², 0 to 50."
          }
        },
        "required": [
          "gravity"
        ]
      },
      "page": "/physics.html"
    }
    select_design_toolAction

    Switch the tool the student's pointer is holding: "select" to move things, "draw" to draw lines by hand, "erase" to click objects away, "force" and "velocity" to drag arrows onto an object. You mostly do not need this — add_object, draw_line, apply_force and set_velocity all work regardless — but switching the visible tool makes what you are doing legible on screen while you narrate it.

    View tool JSON
    {
      "name": "select_design_tool",
      "kind": "act",
      "impl": "imperative",
      "description": "Switch the tool the student's pointer is holding: \"select\" to move things, \"draw\" to draw lines by hand, \"erase\" to click objects away, \"force\" and \"velocity\" to drag arrows onto an object. You mostly do not need this — add_object, draw_line, apply_force and set_velocity all work regardless — but switching the visible tool makes what you are doing legible on screen while you narrate it.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "tool": {
            "type": "string",
            "enum": [
              "select",
              "draw",
              "erase",
              "force",
              "velocity"
            ],
            "description": "Which design tool to hold."
          }
        },
        "required": [
          "tool"
        ]
      },
      "page": "/physics.html"
    }
    start_simulationAction

    Leave the pre-simulation stage and start the run: gravity switches on, the forces and velocities you attached are applied, and everything floating falls. Needs at least one movable object. The run ends by itself once every object has been at rest for 5 seconds, or after 30 seconds, whichever comes first. Poll list_objects or read_simulation while it runs to narrate what is happening.

    View tool JSON
    {
      "name": "start_simulation",
      "kind": "act",
      "impl": "imperative",
      "description": "Leave the pre-simulation stage and start the run: gravity switches on, the forces and velocities you attached are applied, and everything floating falls. Needs at least one movable object. The run ends by itself once every object has been at rest for 5 seconds, or after 30 seconds, whichever comes first. Poll list_objects or read_simulation while it runs to narrate what is happening.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/physics.html"
    }
    pause_simulationAction

    Freeze the run where it is without ending it, so you can talk about a specific moment — the instant of a collision, or the top of an arc. Everything keeps its velocity; call resume_simulation to continue from exactly there.

    View tool JSON
    {
      "name": "pause_simulation",
      "kind": "act",
      "impl": "imperative",
      "description": "Freeze the run where it is without ending it, so you can talk about a specific moment — the instant of a collision, or the top of an arc. Everything keeps its velocity; call resume_simulation to continue from exactly there.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/physics.html"
    }
    resume_simulationAction

    Continue a paused run from where it stopped, with every velocity intact.

    View tool JSON
    {
      "name": "resume_simulation",
      "kind": "act",
      "impl": "imperative",
      "description": "Continue a paused run from where it stopped, with every velocity intact.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/physics.html"
    }
    end_simulationAction

    End the run now and move to the post-simulation stage: everything freezes exactly where it is, and the readings stay available so you can talk about the final state. This is the first of the three ways a run ends; the other two (everything at rest for 5 seconds, and the 30-second limit) happen on their own.

    View tool JSON
    {
      "name": "end_simulation",
      "kind": "act",
      "impl": "imperative",
      "description": "End the run now and move to the post-simulation stage: everything freezes exactly where it is, and the readings stay available so you can talk about the final state. This is the first of the three ways a run ends; the other two (everything at rest for 5 seconds, and the 30-second limit) happen on their own.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/physics.html"
    }
    retry_simulationAction

    Put every object back exactly where it was just before the last run started, including the forces and velocities that were attached, and return to the pre-simulation stage. This is the tool for "now change one thing and run it again" — retry, adjust a single value with set_property or set_velocity, then start_simulation, so the comparison is honest.

    View tool JSON
    {
      "name": "retry_simulation",
      "kind": "act",
      "impl": "imperative",
      "description": "Put every object back exactly where it was just before the last run started, including the forces and velocities that were attached, and return to the pre-simulation stage. This is the tool for \"now change one thing and run it again\" — retry, adjust a single value with set_property or set_velocity, then start_simulation, so the comparison is honest.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/physics.html"
    }
    reset_simulationAction

    Empty the box completely and return to the pre-simulation stage: every object, line, force and velocity is gone. Use retry_simulation instead if you only want to run the same scene again. Ask the student before wiping work they built themselves.

    View tool JSON
    {
      "name": "reset_simulation",
      "kind": "act",
      "impl": "imperative",
      "description": "Empty the box completely and return to the pre-simulation stage: every object, line, force and velocity is gone. Use retry_simulation instead if you only want to run the same scene again. Ask the student before wiping work they built themselves.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/physics.html"
    }
    read_simulationAnswer

    The full live readout in one call: the stage and elapsed time, gravity, every object's position, velocity, speed, acceleration-producing forces, energies and momentum, plus the totals for kinetic energy, potential energy and momentum across the whole box. Call this before stating any number, so you quote a measurement instead of estimating one from the picture — and call it twice, a moment apart, when you want to describe how something is changing.

    View tool JSON
    {
      "name": "read_simulation",
      "kind": "answer",
      "impl": "imperative",
      "description": "The full live readout in one call: the stage and elapsed time, gravity, every object's position, velocity, speed, acceleration-producing forces, energies and momentum, plus the totals for kinetic energy, potential energy and momentum across the whole box. Call this before stating any number, so you quote a measurement instead of estimating one from the picture — and call it twice, a moment apart, when you want to describe how something is changing.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/physics.html"
    }
    read_changesAnswer

    What has changed in the sandbox, and who changed it. Every readback carries a `revision`; pass the last one you saw as `since` and this returns only what happened after it, oldest first. Each entry names the actor -- "user" for the student building the scene directly, "agent" for a tool call -- along with the action and the object it touched. This is the edit history, and is different from read_events: that one reports what the *physics* did during a run (collisions, coming to rest), while this reports what a *person* did to the scene. Dragging an object into place arrives as one entry spanning the drag.

    View tool JSON
    {
      "name": "read_changes",
      "kind": "answer",
      "impl": "imperative",
      "description": "What has changed in the sandbox, and who changed it. Every readback carries a `revision`; pass the last one you saw as `since` and this returns only what happened after it, oldest first. Each entry names the actor -- \"user\" for the student building the scene directly, \"agent\" for a tool call -- along with the action and the object it touched. This is the edit history, and is different from read_events: that one reports what the *physics* did during a run (collisions, coming to rest), while this reports what a *person* did to the scene. Dragging an object into place arrives as one entry spanning the drag.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "since": {
            "type": "number",
            "description": "Return only changes after this revision. Omit for everything still held."
          }
        }
      },
      "page": "/physics.html"
    }
    read_telemetryAnswer

    The recorded motion of one object through the run: time, position and velocity sampled every 0.05 s. Use it to answer questions the live state cannot — when it was fastest, how high it actually got, how long it took to fall, whether the speed really went flat once a timed force stopped. Pass max_points to thin the series out before you read it.

    View tool JSON
    {
      "name": "read_telemetry",
      "kind": "answer",
      "impl": "imperative",
      "description": "The recorded motion of one object through the run: time, position and velocity sampled every 0.05 s. Use it to answer questions the live state cannot — when it was fastest, how high it actually got, how long it took to fall, whether the speed really went flat once a timed force stopped. Pass max_points to thin the series out before you read it.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Object id."
          },
          "max_points": {
            "type": "number",
            "description": "Downsample to at most this many points. Defaults to 80."
          }
        },
        "required": [
          "id"
        ]
      },
      "page": "/physics.html"
    }
    read_eventsAnswer

    The run's event log: when the run started, every collision with the two objects involved and the closing speed at impact, the moment everything came to rest, and why the run ended. This is what you narrate from — it turns "the ball bounced a few times" into "it hit the floor at 12.4 m/s, then at 6.1, then at 3.0".

    View tool JSON
    {
      "name": "read_events",
      "kind": "answer",
      "impl": "imperative",
      "description": "The run's event log: when the run started, every collision with the two objects involved and the closing speed at impact, the moment everything came to rest, and why the run ended. This is what you narrate from — it turns \"the ball bounced a few times\" into \"it hit the floor at 12.4 m/s, then at 6.1, then at 3.0\".",
      "inputSchema": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "all",
              "collision"
            ],
            "description": "Filter the log. Defaults to \"all\"."
          }
        }
      },
      "page": "/physics.html"
    }
    load_presetAction

    Load a ready-made scene — objects, sizes, forces and velocities in one call — instead of building a standard problem piece by piece. Call with no arguments to list what is available. Each preset returns a note saying what it demonstrates and which number is worth changing, so read that before you start teaching. Loading a preset clears the box and returns to the pre-simulation stage, so call start_simulation afterwards.

    View tool JSON
    {
      "name": "load_preset",
      "kind": "act",
      "impl": "imperative",
      "description": "Load a ready-made scene — objects, sizes, forces and velocities in one call — instead of building a standard problem piece by piece. Call with no arguments to list what is available. Each preset returns a note saying what it demonstrates and which number is worth changing, so read that before you start teaching. Loading a preset clears the box and returns to the pre-simulation stage, so call start_simulation afterwards.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "enum": [
              "free_fall",
              "ramp_slide",
              "projectile",
              "coaster_dip",
              "loop",
              "collision",
              "constant_push",
              "friction_ramp",
              "bounce_test"
            ],
            "description": "Preset key. Omit to list them."
          }
        }
      },
      "page": "/physics.html"
    }

    page: Chemistry

    list_atomsAnswer

    Read the whole board: every atom with its element, particle counts, valence, bond-aware shell count and formal charge; every bond; molecular formulas; and any invalid-structure warnings. Call this first to orient, and after any change you did not make. The ids returned here are what the other tools expect.

    View tool JSON
    {
      "name": "list_atoms",
      "kind": "answer",
      "impl": "imperative",
      "description": "Read the whole board: every atom with its element, particle counts, valence, bond-aware shell count and formal charge; every bond; molecular formulas; and any invalid-structure warnings. Call this first to orient, and after any change you did not make. The ids returned here are what the other tools expect.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/chemistry.html"
    }
    list_elementsAnswer

    List all 118 elements in the periodic table (hydrogen through oganesson), with symbol, name, category and typical mass. Use it to pick an element or recall an atomic number before adding an atom.

    View tool JSON
    {
      "name": "list_elements",
      "kind": "answer",
      "impl": "imperative",
      "description": "List all 118 elements in the periodic table (hydrogen through oganesson), with symbol, name, category and typical mass. Use it to pick an element or recall an atomic number before adding an atom.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/chemistry.html"
    }
    add_atomAction

    Add an atom to the board. Identify the element by symbol (element:"O") or atomic number (z:8). It starts neutral (electrons = protons) with the most common isotope's neutron count, unless you override neutrons or electrons. Optionally set grid position x,y. Returns the new atom id. To make an ion, set electrons; to make an isotope, set neutrons.

    View tool JSON
    {
      "name": "add_atom",
      "kind": "act",
      "impl": "imperative",
      "description": "Add an atom to the board. Identify the element by symbol (element:\"O\") or atomic number (z:8). It starts neutral (electrons = protons) with the most common isotope's neutron count, unless you override neutrons or electrons. Optionally set grid position x,y. Returns the new atom id. To make an ion, set electrons; to make an isotope, set neutrons.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "element": {
            "type": "string",
            "description": "Element symbol, e.g. \"O\", \"Na\", \"Cl\"."
          },
          "z": {
            "type": "number",
            "description": "Atomic number instead of a symbol."
          },
          "x": {
            "type": "number"
          },
          "y": {
            "type": "number"
          },
          "neutrons": {
            "type": "number",
            "description": "Override neutron count (isotope)."
          },
          "electrons": {
            "type": "number",
            "description": "Override electron count (ion)."
          }
        }
      },
      "page": "/chemistry.html"
    }
    set_protonsAction

    Change an atom's proton count. This changes which element it is (protons ARE the element). Range 1 to 118. A powerful teaching move: add a proton to lithium and watch it become beryllium. The electron and neutron counts stay put, so the charge may change until you adjust them.

    View tool JSON
    {
      "name": "set_protons",
      "kind": "act",
      "impl": "imperative",
      "description": "Change an atom's proton count. This changes which element it is (protons ARE the element). Range 1 to 118. A powerful teaching move: add a proton to lithium and watch it become beryllium. The electron and neutron counts stay put, so the charge may change until you adjust them.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "count": {
            "type": "number"
          }
        },
        "required": [
          "id",
          "count"
        ]
      },
      "page": "/chemistry.html"
    }
    set_neutronsAction

    Change an atom's neutron count. Neutrons change the mass number and make isotopes but not the element or the charge. For example, carbon with 6 neutrons is carbon-12; with 8 it is carbon-14.

    View tool JSON
    {
      "name": "set_neutrons",
      "kind": "act",
      "impl": "imperative",
      "description": "Change an atom's neutron count. Neutrons change the mass number and make isotopes but not the element or the charge. For example, carbon with 6 neutrons is carbon-12; with 8 it is carbon-14.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "count": {
            "type": "number"
          }
        },
        "required": [
          "id",
          "count"
        ]
      },
      "page": "/chemistry.html"
    }
    set_electronsAction

    Change an atom's electron count, which sets its charge (charge = protons − electrons). Fewer electrons than protons makes a positive cation; more makes a negative anion. E.g. sodium with 10 electrons is Na+, chlorine with 18 is Cl−. Use this to show how atoms reach a full octet by gaining or losing electrons.

    View tool JSON
    {
      "name": "set_electrons",
      "kind": "act",
      "impl": "imperative",
      "description": "Change an atom's electron count, which sets its charge (charge = protons − electrons). Fewer electrons than protons makes a positive cation; more makes a negative anion. E.g. sodium with 10 electrons is Na+, chlorine with 18 is Cl−. Use this to show how atoms reach a full octet by gaining or losing electrons.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "count": {
            "type": "number"
          }
        },
        "required": [
          "id",
          "count"
        ]
      },
      "page": "/chemistry.html"
    }
    bondAction

    Bond two atoms into a molecule. Give two atom ids. kind is "covalent" (shared electrons, between non-metals) or "ionic" (transferred electrons, between a metal and a non-metal); omit it to let the board choose based on the elements. order is 1, 2 or 3 for single/double/triple covalent bonds. Bonding updates the molecular formula, which you can read from list_atoms.

    View tool JSON
    {
      "name": "bond",
      "kind": "act",
      "impl": "imperative",
      "description": "Bond two atoms into a molecule. Give two atom ids. kind is \"covalent\" (shared electrons, between non-metals) or \"ionic\" (transferred electrons, between a metal and a non-metal); omit it to let the board choose based on the elements. order is 1, 2 or 3 for single/double/triple covalent bonds. Bonding updates the molecular formula, which you can read from list_atoms.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "a": {
            "type": "string",
            "description": "First atom id."
          },
          "b": {
            "type": "string",
            "description": "Second atom id."
          },
          "kind": {
            "type": "string",
            "enum": [
              "covalent",
              "ionic"
            ]
          },
          "order": {
            "type": "number",
            "enum": [
              1,
              2,
              3
            ],
            "description": "Covalent bond order."
          }
        },
        "required": [
          "a",
          "b"
        ]
      },
      "page": "/chemistry.html"
    }
    set_bondAction

    Change an existing bond: its kind ("covalent"/"ionic") or its order (1/2/3 for single/double/triple). Ionic bonds are always order 1.

    View tool JSON
    {
      "name": "set_bond",
      "kind": "act",
      "impl": "imperative",
      "description": "Change an existing bond: its kind (\"covalent\"/\"ionic\") or its order (1/2/3 for single/double/triple). Ionic bonds are always order 1.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "covalent",
              "ionic"
            ]
          },
          "order": {
            "type": "number",
            "enum": [
              1,
              2,
              3
            ]
          }
        },
        "required": [
          "id"
        ]
      },
      "page": "/chemistry.html"
    }
    remove_bondAction

    Delete a bond by id, breaking the two atoms apart.

    View tool JSON
    {
      "name": "remove_bond",
      "kind": "act",
      "impl": "imperative",
      "description": "Delete a bond by id, breaking the two atoms apart.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          }
        },
        "required": [
          "id"
        ]
      },
      "page": "/chemistry.html"
    }
    remove_atomAction

    Delete an atom and any bonds attached to it.

    View tool JSON
    {
      "name": "remove_atom",
      "kind": "act",
      "impl": "imperative",
      "description": "Delete an atom and any bonds attached to it.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          }
        },
        "required": [
          "id"
        ]
      },
      "page": "/chemistry.html"
    }
    move_atomAction

    Move an atom to grid position x,y. Bonds follow; this only tidies the layout.

    View tool JSON
    {
      "name": "move_atom",
      "kind": "act",
      "impl": "imperative",
      "description": "Move an atom to grid position x,y. Bonds follow; this only tidies the layout.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "x": {
            "type": "number"
          },
          "y": {
            "type": "number"
          }
        },
        "required": [
          "id",
          "x",
          "y"
        ]
      },
      "page": "/chemistry.html"
    }
    clearAction

    Remove every atom and bond for a fresh start. Ask the student before clearing their work.

    View tool JSON
    {
      "name": "clear",
      "kind": "act",
      "impl": "imperative",
      "description": "Remove every atom and bond for a fresh start. Ask the student before clearing their work.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/chemistry.html"
    }
    read_changesAnswer

    What has changed on the board, and who changed it. Every readback carries a `revision`; pass the last one you saw as `since` and this returns only what happened after it, oldest first. Each entry names the actor -- "user" for the student building the structure directly, "agent" for a tool call -- along with the action and the atom or bond it touched. This is how you notice that the student added an electron or broke a bond while you were explaining: watch `revision` on a cheap readback and call this when it moves.

    View tool JSON
    {
      "name": "read_changes",
      "kind": "answer",
      "impl": "imperative",
      "description": "What has changed on the board, and who changed it. Every readback carries a `revision`; pass the last one you saw as `since` and this returns only what happened after it, oldest first. Each entry names the actor -- \"user\" for the student building the structure directly, \"agent\" for a tool call -- along with the action and the atom or bond it touched. This is how you notice that the student added an electron or broke a bond while you were explaining: watch `revision` on a cheap readback and call this when it moves.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "since": {
            "type": "number",
            "description": "Return only changes after this revision. Omit for everything still held."
          }
        }
      },
      "page": "/chemistry.html"
    }
    undoAction

    Undo the last change to the board. Safe to call repeatedly to step further back.

    View tool JSON
    {
      "name": "undo",
      "kind": "act",
      "impl": "imperative",
      "description": "Undo the last change to the board. Safe to call repeatedly to step further back.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/chemistry.html"
    }
    redoAction

    Redo the change that was just undone.

    View tool JSON
    {
      "name": "redo",
      "kind": "act",
      "impl": "imperative",
      "description": "Redo the change that was just undone.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "page": "/chemistry.html"
    }
    load_presetAction

    Load a ready-made structure — a molecule or an atomic-structure demo — with a teaching note, in one call. Call with no arguments to list what is available. The note explains the bonding and what the student should notice.

    View tool JSON
    {
      "name": "load_preset",
      "kind": "act",
      "impl": "imperative",
      "description": "Load a ready-made structure — a molecule or an atomic-structure demo — with a teaching note, in one call. Call with no arguments to list what is available. The note explains the bonding and what the student should notice.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "enum": [
              "water",
              "salt",
              "carbon_dioxide",
              "methane",
              "oxygen_gas",
              "isotopes"
            ]
          }
        }
      },
      "page": "/chemistry.html"
    }