Perfboard Designer
v0.1.0Design perfboard and stripboard layouts, check the wiring and get a link to open them in the editor.
Endpoint health
online- Last checked
- 11/3/2026, 4:18:51 PM
- Latency
- 340 ms
- Consecutive failures
- 0
- Endpoint reachable: pass
- Protocol valid: pass
- MCP handshake valid: pass
Machine-readable profile
mcp-server · schema v1 · callable
mcp-server:com.perfboarddesigner%2Fmcp
Connect this server
Generated config contains no credentials. Keep any required secrets in your client's local environment.
{
"mcpServers": {
"com.perfboarddesigner/mcp": {
"url": "https://mcp.perfboarddesigner.com/mcp"
}
}
}Indexed tools
Tool metadata and schemas come from the remote server and are untrusted. Indexing only lists tools; it never calls them.
12 shown · authentication unknown
Edit project
edit_project
Changes a project kept here, by a list of ops applied in order: add_part, update_part (move, turn, change value or ref), remove_parts, add_wire (from/to, or a `path` of holes made into one wire per leg), update_wire, remove_wires, set_board, add_cuts / remove_cuts (stripboard), set_pad (a note on a hole). If any op can't be done, none are. Returns what changed, where the pins of added or moved parts landed, and the board checked as validate_project does (with any expectNets remembered for it); when it has no errors, also its nets. get_project returns the whole file. The goal is a board that's right, looks neat and is easy to solder, not just the shortest wires: parts in tidy rows near what they connect to, connectors at the edges, straight wires with few corners and no crossing you can avoid, none over a part's body. Before giving the user the link, look at it (render_board with format "png"), rework what's messy and check it again.
Schema valid · SHA-256 213c7be8b329…
View input schema
{ "type": "object", "properties": { "projectId": { "description": "The id of a project kept here (from new_project, validate_project, edit_project or read_share_link), instead of sending the whole project.", "type": "string" }, "project": { "description": "A whole project file to start from instead of a projectId: it's kept here and the reply gives its new projectId.", "anyOf": [ { "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": {} }, { "type": "string" } ] }, "ops": { "minItems": 1, "type": "array", "items": { "oneOf": [ { "type": "object", "properties": { "op": { "type": "string", "const": "add_part" }, "part": { "description": "The part's id from list_parts (\"resistor\", \"ne555\", \"header-pins\"), or the name of a custom part or chip the project already has. Give this, `custom` or `chip`.", "type": "string" }, "custom": { "description": "Instead of `part`, a new part of your own for one the catalog doesn't have, placed by `at` and `rotation`.", "type": "object", "properties": { "name": { "type": "string", "description": "What it is, e.g. \"HC-SR04\", \"BC337\". Later add_part ops can place more of it by this name." }, "shape": { "description": "The id of a fixed-pin part from list_parts whose package this part comes in, e.g. \"transistor\" (TO-92) for a BC337 or \"mosfet\" (TO-220) for an IRLZ44N: its pins, outline and look, with this part's name and pin names. Any fixed-pin part but header-pins. Leave out for a body of your own with pins anywhere.", "type": "string" }, "pins": { "description": "In pin order, pin 1 first; pin 1 is the hole add_part's `at` places it by. Without shape: every pin with its `at`. With shape: just their names, in the package's pin order (get_part gives it), as many as it has pins.", "type": "array", "items": { "type": "object", "properties": { "at": { "description": "Without shape: the pin's hole [column, row] within the body, from its top-left hole. With shape: leave out, the package decides.", "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "label": { "description": "Its name, e.g. \"VCC\", \"TRIG\", \"B\". Wire to it by name (\"M1.VCC\").", "type": "string" } } } }, "cols": { "description": "Without shape: the body's width in holes. Default: just wide enough for its pins.", "type": "integer", "minimum": 1, "maximum": 9007199254740991 }, "rows": { "description": "Without shape: the body's height in holes. Default: just tall enough for its pins.", "type": "integer", "minimum": 1, "maximum": 9007199254740991 }, "refPrefix": { "description": "Letters of its refs: \"Q\" makes Q1, Q2. Default: the package's, else \"M\".", "type": "string" }, "color": { "description": "Without shape: the body's colour, \"#rrggbb\", e.g. a blue module board \"#1d4ed8\".", "type": "string" } }, "required": [ "name" ] }, "chip": { "description": "Instead of `part`, a new dual-row chip or dev board of your own, placed by `at` (its top-left pin) and `rotation`.", "type": "object", "properties": { "name": { "type": "string", "description": "What it is, e.g. \"LM358\", \"ESP32 DevKit\". Later add_part ops can place more of it by this name." }, "pinsPerSide": { "type": "integer", "minimum": 2, "maximum": 9007199254740991, "description": "Pins in each of its two rows." }, "width": { "type": "integer", "minimum": 2, "maximum": 9007199254740991, "description": "Columns from one pin row to the other, both included: 4 for a narrow (0.3\") DIP, 7 for a wide (0.6\") one; a dev board's row spacing in holes plus one." }, "pins": { "description": "Pin names in DIP order: pin 1 at the top of the left row (at rotation 0), down the left row, then up the right row, so the last pin is at the top right. \"\" for a pin without a name.", "type": "array", "items": { "type": "string" } }, "board": { "description": "true: a dev board or module (drawn as a board, listed under Modules); else a DIP chip.", "type": "boolean" } }, "required": [ "name", "pinsPerSide", "width" ] }, "ref": { "description": "Its reference designator. The next free one (R1, R2...) when left out.", "type": "string" }, "from": { "description": "Two-lead parts: the hole of lead 1. On a polarized part get_part's leadLabels say which that is: K for a diode or LED, - for an electrolytic.", "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "to": { "description": "Two-lead parts: the hole of lead 2.", "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "at": { "description": "Other parts: pin 1's hole (a chip's or pin block's top-left pin).", "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "rotation": { "description": "Other parts: 0 (default), 90, 180 or 270, clockwise.", "anyOf": [ { "type": "number", "const": 0 }, { "type": "number", "const": 90 }, { "type": "number", "const": 180 }, { "type": "number", "const": 270 } ] }, "rows": { "description": "Pin blocks (header-pins): rows of pins.", "type": "integer", "minimum": 1, "maximum": 9007199254740991 }, "cols": { "description": "Pin blocks (header-pins): pins per row.", "type": "integer", "minimum": 1, "maximum": 9007199254740991 }, "value": { "description": "E.g. \"10k\", \"100n\".", "type": "string" }, "color": { "description": "Two-lead parts, e.g. an LED's \"#ef4444\".", "type": "string" } }, "required": [ "op" ] }, { "type": "object", "properties": { "op": { "type": "string", "const": "update_part" }, "ref": { "type": "string" }, "by": { "description": "Move it by [columns, rows].", "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "from": { "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "description": "A hole, [column, row]." }, "to": { "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "description": "A hole, [column, row]." }, "at": { "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "description": "A hole, [column, row]." }, "rotation": { "description": "Turns about `at`; a chip's widthPin and heightPin are swapped to match.", "anyOf": [ { "type": "number", "const": 0 }, { "type": "number", "const": 90 }, { "type": "number", "const": 180 }, { "type": "number", "const": 270 } ] }, "rows": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 }, "cols": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 }, "value": { "description": "\"\" removes it.", "type": "string" }, "color": { "description": "\"\" removes it.", "type": "string" }, "newRef": { "type": "string" } }, "required": [ "op", "ref" ] }, { "type": "object", "properties": { "op": { "type": "string", "const": "remove_parts" }, "refs": { "minItems": 1, "type": "array", "items": { "type": "string" } } }, "required": [ "op", "refs" ] }, { "type": "object", "properties": { "op": { "type": "string", "const": "add_wire" }, "from": { "anyOf": [ { "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, { "type": "string" } ], "description": "A hole [column, row], or a pin by name: \"U1.7\", \"U1.DISCH\", \"D1.K\", \"R2.1\" (the hole that pin is in)." }, "to": { "anyOf": [ { "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, { "type": "string" } ], "description": "A hole [column, row], or a pin by name: \"U1.7\", \"U1.DISCH\", \"D1.K\", \"R2.1\" (the hole that pin is in)." }, "via": { "description": "With from/to: the holes the wire bends at on its way, in order, to route it around things as one wire. A bend joins nothing, like a hole the wire passes over, so it's soldered only at its two ends.", "type": "array", "items": { "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "description": "A hole, [column, row]." } }, "path": { "description": "Instead of from/to: holes (or pins) to run through in order, made into one wire per leg. Each corner is a wire end, so it joins whatever is in that hole. For corners that only bend, use from/to with via.", "minItems": 2, "type": "array", "items": { "anyOf": [ { "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, { "type": "string" } ], "description": "A hole [column, row], or a pin by name: \"U1.7\", \"U1.DISCH\", \"D1.K\", \"R2.1\" (the hole that pin is in)." } }, "solder": { "description": "true: a solder path instead of a jumper wire, a trail of solder on the copper side, made as one path (a `path`'s corners and any `via` are its bends). Bare solder joins every hole it runs over, ends, bends and all, and every solder path it touches: run it only over holes it should connect. No color or width.", "type": "boolean" }, "color": { "type": "string" }, "width": { "type": "number", "exclusiveMinimum": 0 }, "side": { "description": "Which face a jumper lies on: \"top\" (default) or \"bottom\", the solder side (format version 9). A bottom wire joins its end holes like any jumper but never crosses a top wire, so use it to take a long wire out of the way; it is shown faded in the top view. Not for solder paths.", "type": "string", "enum": [ "top", "bottom" ] } }, "required": [ "op" ] }, { "type": "object", "properties": { "op": { "type": "string", "const": "update_wire" }, "wire": { "anyOf": [ { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, { "minItems": 2, "maxItems": 2, "type": "array", "items": { "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "description": "A hole, [column, row]." } } ], "description": "A wire by its index in wires, or by its two end holes [[c, r], [c, r]] (either way round)." }, "from": { "anyOf": [ { "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, { "type": "string" } ], "description": "A hole [column, row], or a pin by name: \"U1.7\", \"U1.DISCH\", \"D1.K\", \"R2.1\" (the hole that pin is in)." }, "to": { "anyOf": [ { "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, { "type": "string" } ], "description": "A hole [column, row], or a pin by name: \"U1.7\", \"U1.DISCH\", \"D1.K\", \"R2.1\" (the hole that pin is in)." }, "via": { "description": "Its bends, replacing the ones it has; [] makes it straight. Left out, its bends stay.", "type": "array", "items": { "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "description": "A hole, [column, row]." } }, "color": { "type": "string" }, "width": { "type": "number", "exclusiveMinimum": 0 }, "side": { "description": "Which face a jumper lies on: \"top\" (default) or \"bottom\", the solder side (format version 9). A bottom wire joins its end holes like any jumper but never crosses a top wire, so use it to take a long wire out of the way; it is shown faded in the top view. Not for solder paths.", "type": "string", "enum": [ "top", "bottom" ] } }, "required": [ "op", "wire" ] }, { "type": "object", "properties": { "op": { "type": "string", "const": "remove_wires" }, "wires": { "minItems": 1, "type": "array", "items": { "anyOf": [ { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, { "minItems": 2, "maxItems": 2, "type": "array", "items": { "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "description": "A hole, [column, row]." } } ], "description": "A wire by its index in wires, or by its two end holes [[c, r], [c, r]] (either way round)." }, "description": "Indexes are as they stand before this op, so list several in one op." } }, "required": [ "op", "wires" ] }, { "type": "object", "properties": { "op": { "type": "string", "const": "set_board" }, "cols": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 }, "rows": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 }, "type": { "type": "string", "enum": [ "perfboard", "stripboard" ] }, "strips": { "description": "A stripboard's strips.", "type": "string", "enum": [ "horizontal", "vertical" ] } }, "required": [ "op" ] }, { "type": "object", "properties": { "op": { "type": "string", "const": "add_cuts" }, "at": { "minItems": 1, "type": "array", "items": { "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "description": "A hole, [column, row]." }, "description": "Stripboard holes to cut the strip at." } }, "required": [ "op", "at" ] }, { "type": "object", "properties": { "op": { "type": "string", "const": "remove_cuts" }, "at": { "minItems": 1, "type": "array", "items": { "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "description": "A hole, [column, row]." } } }, "required": [ "op", "at" ] }, { "type": "object", "properties": { "op": { "type": "string", "const": "set_pad" }, "at": { "minItems": 2, "maxItems": 2, "type": "array", "items": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "description": "A hole, [column, row]." }, "note": { "description": "A label on the hole, such as \"+9V\", \"GND\" or \"to battery\". Left out (or \"\") removes it.", "type": "string" } }, "required": [ "op", "at" ] } ] } }, "expectNets": { "description": "The nets the board should make: each a list of pins written REF.pin, e.g. [[\"U1.8\", \"U1.4\", \"R1.1\", \"J1.1\"], [\"U1.2\", \"U1.6\", \"C1.+\"]]. A pin can also be named by its label (\"U1.VCC\", \"D1.K\") or as get_nets writes it (\"U1.8 VCC\"). List every net of two or more pins: a pin in none of them should connect to nothing. With a projectId it's remembered for that project, so later checks use it too; [] forgets it.", "type": "array", "items": { "type": "array", "items": { "type": "string" } } } }, "required": [ "ops" ], "$schema": "http://json-schema.org/draft-07/schema#" }
Connections
streamable-http
https://mcp.perfboarddesigner.com/mcp