{"ok":true,"site":{"host":"labmcp.vercel.app","url":"https://labmcp.vercel.app/","type":"live","desc":"A virtual chemistry lab shared by a human and an AI agent. The human has hands, the agent has WebMCP tools, both act on the same experiment.","tools":[{"name":"get_lab_state","kind":"answer","impl":"imperative","description":"Returns the full current bench state: every container and instrument, the shelf, indicators, and the last few observations. This never reports pH, moles, or the concentration of a container's contents; use measure_ph, calculate_moles, or inspect_contents for those. Call this first when planning, and call it again after any OBJECT_NOT_FOUND error, because a human may have changed the bench since your last call.","title":"Get lab state","annotations":{"readOnlyHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"list_reagents","kind":"answer","impl":"imperative","description":"Lists every reagent stocked on the shelf: id, kind (solution or solid), formula, role (acid, base, salt, weak_acid, weak_base, carbonate), default and max concentration for liquids, and molar mass plus a solubility note for solids. Also lists the available indicators and their color ranges. Unknown samples from a challenge scenario are never listed here; use get_lab_state to see what is on the bench.","title":"List reagents","annotations":{"readOnlyHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"list_equipment","kind":"answer","impl":"imperative","description":"Lists every equipment type LabMCP supports (with capacity and purpose) and every object currently on the bench (id, type, and, for instruments, what it's attached to). Use add_container to place more.","title":"List equipment","annotations":{"readOnlyHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"check_objective","kind":"answer","impl":"imperative","description":"Returns the active scenario's goal: which steps are done, whether it is complete, and a short status line (e.g. current pH vs target, or current molarity vs target). Call this before planning so you know what is left; get_lab_state's `objective` field carries the same information.","title":"Check objective","annotations":{"readOnlyHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"measure_ph","kind":"act","impl":"imperative","description":"Measures the pH of a container's contents using a pH meter. If a pH meter is already on the bench but attached elsewhere, it is moved to this container automatically. If no pH meter exists on the bench at all, this fails with INSTRUMENT_MISSING; call add_container with type 'ph_meter' first. Prefer this over inspect_contents, which is blocked on unidentified samples until a challenge is revealed. The reading is logged to the notebook.","title":"Measure pH","annotations":{"readOnlyHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"container_id":{"type":"string","minLength":1,"maxLength":64,"description":"Container id exactly as returned by get_lab_state, e.g. \"c_1\". Type names are not ids."}},"required":["container_id"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"measure_temperature","kind":"act","impl":"imperative","description":"Measures the temperature (°C) of a container's contents using a thermometer. A thermometer already on the bench is moved to this container automatically; if none exists, this fails with INSTRUMENT_MISSING, so call add_container with type 'thermometer' first. Also reports whether the container is currently heating, cooling, or idle.","title":"Measure temperature","annotations":{"readOnlyHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"container_id":{"type":"string","minLength":1,"maxLength":64,"description":"Container id exactly as returned by get_lab_state, e.g. \"c_1\". Type names are not ids."}},"required":["container_id"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"measure_volume","kind":"answer","impl":"imperative","description":"Reads the current liquid volume (mL) in a container, its capacity, and the fraction of capacity filled (0 to 1).","title":"Measure volume","annotations":{"readOnlyHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"container_id":{"type":"string","minLength":1,"maxLength":64,"description":"Container id exactly as returned by get_lab_state, e.g. \"c_1\". Type names are not ids."}},"required":["container_id"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"get_titration_data","kind":"answer","impl":"imperative","description":"Returns the recorded titration curve: burette and flask ids, the titrant, the analyte's initial volume, and every dispensed point (titrant mL, pH if a meter was attached at the time, clock time). Empty outside the titration scenario. pH points are null until a pH meter has been attached to the flask via measure_ph.","title":"Get titration data","annotations":{"readOnlyHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"get_notebook","kind":"answer","impl":"imperative","description":"Returns the lab notebook: an append-only, timestamped log with one entry per action, each tagged with the actor (human or agent) that caused it, containers named by label rather than id. Undo does not remove entries, it adds one. Optionally limit to the last N entries (1 to 200, default all).","title":"Get notebook","annotations":{"readOnlyHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"last_n":{"description":"Only return the most recent N entries.","type":"integer","minimum":1,"maximum":200}},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"inspect_contents","kind":"act","impl":"imperative","description":"Reads the exact dissolved species, moles, and molarity in a container, plus any solids and which reactions have occurred there. Blocked with PERMISSION_DENIED on a container tainted by an unidentified sample until the challenge is revealed (via submit_conclusion); use measure_ph or add a test reagent and watch for a precipitate or color change instead. Always allowed on ordinary sandbox containers.","title":"Inspect contents","annotations":{"readOnlyHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"container_id":{"type":"string","minLength":1,"maxLength":64,"description":"Container id exactly as returned by get_lab_state, e.g. \"c_1\". Type names are not ids."}},"required":["container_id"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"predict_supported_reactions","kind":"act","impl":"imperative","description":"Predicts which reactions could fire in a container right now (based on which ions are already present) and which new reactions would become possible if each shelf reagent were added. On a hidden challenge container this returns only the general reaction registry (evaluated: false), since the actual ions can't be read.","title":"Predict supported reactions","annotations":{"readOnlyHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"container_id":{"type":"string","minLength":1,"maxLength":64,"description":"Container id exactly as returned by get_lab_state, e.g. \"c_1\". Type names are not ids."}},"required":["container_id"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"calculate_moles","kind":"act","impl":"imperative","description":"Computes moles, millimoles, and molarity of one species in a container. Blocked with PERMISSION_DENIED on a hidden challenge container, except in the titration scenario where the titrant species OH- and Na+ remain computable (needed to work the titration by material balance). Use get_titration_data plus this tool instead of guessing.","title":"Calculate moles","annotations":{"readOnlyHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"container_id":{"type":"string","minLength":1,"maxLength":64,"description":"Container id exactly as returned by get_lab_state, e.g. \"c_1\". Type names are not ids."},"species_id":{"type":"string","minLength":1,"maxLength":20,"pattern":"^[A-Za-z0-9()+\\-^]+$","description":"Species formula id, e.g. \"H+\", \"OH-\", \"AgCl(s)\"."}},"required":["container_id","species_id"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"add_container","kind":"act","impl":"imperative","description":"Places a new piece of equipment on the bench: glassware (beaker, flask, test_tube, graduated_cylinder, burette) or an instrument (ph_meter, thermometer, hotplate). Uses the free bench slot nearest the existing objects when position is omitted. Fails with OUT_OF_RANGE if the bench is full or the slot is occupied.","title":"Add container","annotations":{"readOnlyHint":false},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"type":{"type":"string","enum":["beaker","flask","test_tube","graduated_cylinder","burette","ph_meter","thermometer","hotplate"],"description":"Equipment type. beaker 250 mL, flask 250 mL (Erlenmeyer), test_tube 20 mL, graduated_cylinder 100 mL, burette 50 mL, ph_meter, thermometer, hotplate."},"position":{"type":"object","properties":{"col":{"type":"integer","minimum":0,"maximum":8,"description":"Bench column, 0 (left) to 8 (right)."},"row":{"type":"integer","minimum":0,"maximum":3,"description":"Bench row, 0 (back) to 3 (front)."}},"required":["col","row"],"additionalProperties":false,"description":"Bench grid slot. Omit to use the next free slot."},"label":{"description":"Optional custom label, shown instead of the default type name.","type":"string","maxLength":24}},"required":["type"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"add_reagent","kind":"act","impl":"imperative","description":"Adds a stock reagent (or water) to a container. Water dilutes without changing species. Overflow fails with nothing added; check capacity with measure_volume first if unsure. Mixing may trigger a supported reaction, reported in the observation and in `reaction`. Liquids use volume_ml; solid reagents (see list_reagents) use mass_g instead and dissolve up to their solubility limit. Use add_indicator for indicators and transfer to move existing liquid between containers instead of re-adding stock.","title":"Add reagent","annotations":{"readOnlyHint":false},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"container_id":{"type":"string","minLength":1,"maxLength":64,"description":"Container id exactly as returned by get_lab_state, e.g. \"c_1\". Type names are not ids."},"reagent_id":{"type":"string","enum":["water","hcl","naoh","nacl","bacl2","na2so4","acetic_acid","ammonia","kno3","agno3","cacl2","na2co3","nahco3","cuso4","unknown_acid","unknown_a","unknown_b","unknown_c","unknown_d"],"description":"Shelf reagent id from get_lab_state.shelf, e.g. \"hcl\", \"naoh\", \"water\", or a challenge's \"unknown_acid\". See list_reagents."},"volume_ml":{"description":"Volume in mL for a liquid reagent, greater than 0. Omit for a solid reagent; use mass_g instead.","type":"number","exclusiveMinimum":0,"maximum":1000},"mass_g":{"description":"Mass in grams for a solid reagent (see list_reagents for which ids are solid). Omit volume_ml when using mass_g.","type":"number","exclusiveMinimum":0,"maximum":500},"concentration_m":{"description":"Concentration in mol/L (0 < x ≤ 2) for a liquid reagent. Omit to use the reagent's default concentration.","type":"number","exclusiveMinimum":0,"maximum":2}},"required":["container_id","reagent_id"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"transfer","kind":"act","impl":"imperative","description":"Pours liquid from one container to another, moving species proportionally to the fraction of the source's volume moved. Solids stay behind in the source. Use dispense instead when the source is a burette (for controlled, incremental titration additions).","title":"Transfer liquid","annotations":{"readOnlyHint":false},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"source_id":{"type":"string","minLength":1,"maxLength":64,"description":"Container id exactly as returned by get_lab_state, e.g. \"c_1\". Type names are not ids."},"destination_id":{"type":"string","minLength":1,"maxLength":64,"description":"Container id exactly as returned by get_lab_state, e.g. \"c_1\". Type names are not ids."},"volume_ml":{"type":"number","exclusiveMinimum":0,"maximum":1000,"description":"Volume in millilitres (mL), greater than 0."}},"required":["source_id","destination_id","volume_ml"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"dispense","kind":"act","impl":"imperative","description":"Dispenses liquid from a burette into a destination container; the titration action. Use coarse increments (1 to 5 mL) far from the endpoint and fine increments (0.1 to 0.5 mL) once color or pH starts moving quickly. pH is recorded on the titration curve only when a pH meter is attached to the destination; call measure_ph on it first if you need pH tracked.","title":"Dispense from burette","annotations":{"readOnlyHint":false},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"burette_id":{"type":"string","minLength":1,"maxLength":64,"description":"Container id exactly as returned by get_lab_state, e.g. \"c_1\". Type names are not ids."},"destination_id":{"type":"string","minLength":1,"maxLength":64,"description":"Container id exactly as returned by get_lab_state, e.g. \"c_1\". Type names are not ids."},"volume_ml":{"type":"number","exclusiveMinimum":0,"maximum":50,"description":"Volume to dispense in mL, greater than 0, up to 50."}},"required":["burette_id","destination_id","volume_ml"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"stir","kind":"act","impl":"imperative","description":"Stirs a container for the given duration (0.5 to 30 s, default 3 s), keeping any solids suspended while stirring and speeding settling once it stops.","title":"Stir","annotations":{"readOnlyHint":false},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"container_id":{"type":"string","minLength":1,"maxLength":64,"description":"Container id exactly as returned by get_lab_state, e.g. \"c_1\". Type names are not ids."},"duration_s":{"default":3,"description":"Stir duration in seconds, 0.5 to 30.","type":"number","minimum":0.5,"maximum":30}},"required":["container_id","duration_s"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"heat","kind":"act","impl":"imperative","description":"Sets a container heating toward a target temperature (0 to 100 °C). Requires a hotplate on the bench; fails with INSTRUMENT_MISSING otherwise.","title":"Heat","annotations":{"readOnlyHint":false},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"container_id":{"type":"string","minLength":1,"maxLength":64,"description":"Container id exactly as returned by get_lab_state, e.g. \"c_1\". Type names are not ids."},"target_c":{"type":"number","minimum":0,"maximum":100,"description":"Target temperature in °C, 0 to 100 (sandbox range)."}},"required":["container_id","target_c"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"cool","kind":"act","impl":"imperative","description":"Sets a container cooling toward a target temperature (0 to 100 °C), or toward ambient if omitted. Requires a hotplate on the bench; fails with INSTRUMENT_MISSING otherwise.","title":"Cool","annotations":{"readOnlyHint":false},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"container_id":{"type":"string","minLength":1,"maxLength":64,"description":"Container id exactly as returned by get_lab_state, e.g. \"c_1\". Type names are not ids."},"target_c":{"type":"number","minimum":0,"maximum":100,"description":"Target temperature in °C, 0 to 100 (sandbox range)."}},"required":["container_id"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"add_indicator","kind":"act","impl":"imperative","description":"Adds drops of a color indicator to a container. phenolphthalein: colorless below pH ~8.2, pink above. universal: red/orange/yellow/green/blue/purple across pH 1 to 14. litmus: red below pH 7, blue at or above. 1 to 10 drops, default 2.","title":"Add indicator","annotations":{"readOnlyHint":false},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"container_id":{"type":"string","minLength":1,"maxLength":64,"description":"Container id exactly as returned by get_lab_state, e.g. \"c_1\". Type names are not ids."},"indicator_id":{"type":"string","enum":["phenolphthalein","universal","litmus"],"description":"Which indicator to add."},"drops":{"default":2,"description":"Number of drops, 1 to 10.","type":"integer","minimum":1,"maximum":10}},"required":["container_id","indicator_id","drops"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"remove_container","kind":"act","impl":"imperative","description":"Removes a container or instrument from the bench entirely, disposing of any liquid it held. This cannot be undone.","title":"Remove object","annotations":{"readOnlyHint":false},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"object_id":{"type":"string","minLength":1,"maxLength":64,"description":"Object id exactly as returned by get_lab_state, e.g. \"c_1\" for glassware or \"i_3\" for an instrument."}},"required":["object_id"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"undo_last_action","kind":"act","impl":"imperative","description":"Reverts the single most recent action in the history (yours or the human's) and restores the lab to its exact prior state. There is no redo, so use this only when you are sure. Fails with NOTHING_TO_UNDO if history is empty.","title":"Undo last action","annotations":{"readOnlyHint":false},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"reset_experiment","kind":"act","impl":"imperative","description":"Reloads the current scenario with its original seed, discarding every change. Destructive: requires confirm: true.","title":"Reset experiment","annotations":{"readOnlyHint":false},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"confirm":{"type":"boolean","const":true,"description":"Must be exactly true to acknowledge this discards all changes."}},"required":["confirm"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"load_scenario","kind":"act","impl":"imperative","description":"Loads a scenario onto a fresh bench. 'sandbox': Sandbox. 'titration': Titration. 'unknown_id': Reaction mystery. 'precipitation': Precipitation. 'neutralize': Neutralize to pH 7. 'dilution': Dilution. 'solubility': Solubility. Uses the fixed demo seed 42 unless a different seed is given.","title":"Load scenario","annotations":{"readOnlyHint":false},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"scenario_id":{"type":"string","enum":["sandbox","titration","unknown_id","precipitation","neutralize","dilution","solubility"],"description":"Which scenario to load."},"seed":{"description":"RNG seed. Defaults to the fixed demo seed 42.","type":"integer","minimum":0,"maximum":9007199254740991}},"required":["scenario_id"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"submit_conclusion","kind":"transact","impl":"imperative","description":"Ends the active challenge and grades your answer. For 'unknown_id' (up to four unknown samples), one { container_id, claim } per sample, claim being a reagent id guess. For 'titration', one { container_id, concentration_m } with the claimed analyte molarity. For 'neutralize' and 'dilution', omit answers entirely: there is nothing to guess, this just reveals the container and reports whether the objective was met. This is the only path that reveals hidden identities, and it ends the challenge, so call it once you're confident. Fails with PERMISSION_DENIED outside an active challenge scenario.","title":"Submit conclusion","annotations":{"readOnlyHint":false},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"answers":{"description":"One answer per unknown sample, or a single answer for the titration analyte. Omit for neutralize and dilution.","maxItems":10,"type":"array","items":{"type":"object","properties":{"container_id":{"type":"string","minLength":1,"maxLength":64,"description":"Container id exactly as returned by get_lab_state, e.g. \"c_1\". Type names are not ids."},"claim":{"description":"Reagent id guess for unknown_id (e.g. \"agno3\"). Omit for titration.","type":"string","enum":["water","hcl","naoh","nacl","bacl2","na2so4","acetic_acid","ammonia","kno3","agno3","cacl2","na2co3","nahco3","cuso4"]},"concentration_m":{"description":"Claimed molarity, used for the titration scenario's analyte.","type":"number","exclusiveMinimum":0,"maximum":5}},"required":["container_id"],"additionalProperties":false}}},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"}],"category":"Health & Education","favicon":"/favicons/labmcp.vercel.app.ico","apiSurface":"spec","toolCount":25}}