ec-sim-mcp 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -48,17 +48,17 @@ in the dialog. A badge in the menu bar shows the link and has a Disconnect butto
48
48
  | Tool | Purpose |
49
49
  |---|---|
50
50
  | `open_editor` | Link to a tab (opens the pairing link, waits for Connect) |
51
- | `list_component_types` | Part types, pins, parameters with SI units and defaults |
51
+ | `list_component_types` | Part types, pins, pin positions and outline for each rotation, spacing rule, parameters with SI units and defaults |
52
52
  | `list_parts` | The editor's parts library (diodes, BJTs, MOSFETs, JFETs, op-amps): categories, or one category's table in SI, with limits on any column (`where`), sorting, word search and paging |
53
53
  | `list_examples`, `load_example` | Built-in circuits |
54
54
  | `get_circuit` | The circuit as a netlist (works for hand-drawn circuits), ERC result, revision |
55
- | `place_part` | Place one part without wires, at a cell, rotated or mirrored; the reply gives its pin positions |
55
+ | `place_part` | Place one part without wires, at a grid point or next to a pin of another part, rotated or mirrored; the reply gives its pin positions |
56
56
  | `draw_wire`, `remove_wire` | Draw one wire along a stated path (pins, points, corners); remove a wire or one of its segments |
57
57
  | `place_symbol`, `remove_symbol` | Ground symbols and net labels at a stated point |
58
58
  | `move_label` | Move a part's designator and value text, a probe's badge, or a net label / ground symbol |
59
- | `get_drawing` | The drawing with positions: parts, pin coordinates and nets, symbols, wires with ids |
59
+ | `get_drawing` | The drawing with positions: parts, pin coordinates and nets, symbols, wires with ids, and the layout numbers |
60
60
  | `set_circuit` | Replace the circuit from a structured netlist or a SPICE deck; drawn and wired automatically |
61
- | `edit_circuit` | Change values and parameters, swap in a library part, add and remove parts, move, turn or mirror a part, move a pin to another net (all or nothing) |
61
+ | `edit_circuit` | Change values and parameters, swap in a library part, add and remove parts, move, turn or mirror a part, move a pin to another net, rename a net and choose how its name is shown (all or nothing) |
62
62
  | `set_probes` | Choose the signals in the waveform viewer |
63
63
  | `set_design_notes` | Write the project's design notes (Edit > Design Notes in the editor); `get_circuit` returns them |
64
64
  | `set_math_waveforms` | Named expressions drawn as traces in the viewer (`avg`, `rms`, `d/dt`, `integ`, arithmetic); the tool description carries the syntax |
@@ -109,10 +109,31 @@ time: `move_part` for a part in the wrong place or out of line, `remove_wire` an
109
109
  a wire with a detour or a needless crossing, `move_label` for text in the way. You see the
110
110
  circuit appear and then being cleaned up.
111
111
 
112
+ **Placement is by pins, on the 20 px grid.** The first part of a block gets `at: {x, y}` (its
113
+ centre in grid steps); every part around it gets `next_to: {pin, to, dx, dy}`, which puts one of
114
+ its pins a stated number of steps from a pin of another part. The editor works out the centre from
115
+ the rotation and the pin offsets, so the agent does no coordinate arithmetic and connected pins
116
+ end up exactly in line. `line_up` only straightens a part that already has a place.
117
+
118
+ - `list_component_types` gives, for every type and rotation, the pin offsets and the outline (the
119
+ body with its pins), where the text is written, and the spacing rule.
120
+ - A part that is too close is refused with the reason and the room both parts take. The check is
121
+ on what will be drawn: bodies need 1 free step between them; every connected pin gets a wire
122
+ 2 steps straight out (a ground pin also its symbol), which needs 2 free steps to other parts and
123
+ 1 to the lead-out wires of other nets. A part placed bare with `place_part` only needs the step
124
+ between bodies, since the agent draws its wires itself.
125
+ - Every reply that draws (`set_circuit`, `edit_circuit`, `get_drawing`) carries `layout`: size of
126
+ the drawing in grid steps, area and wire length per part, crossings, net labels, texts that lie
127
+ on another part, and the values to aim for (about 80 to 150 square steps per part).
128
+ - A whole-drawing screenshot that would be too small to read says so in `note` and points to
129
+ close-ups by block.
130
+ - `at: {col, row}` still names a cell of a coarse grid (16 x 12 steps), and a part given no place
131
+ goes to the next free cell. That is for a quick sketch: it is about three times too wide for a
132
+ finished drawing, which is why the instructions steer agents to pins and blocks.
133
+
112
134
  The hand tools work like this:
113
135
 
114
- - Positions are in grid steps of 20 px. A layout cell is 16 steps wide and 12 high; the part in
115
- cell (col, row) is centred at (10 + 16 col, 10 + 12 row).
136
+ - Positions are in grid steps of 20 px, x to the right and y downwards.
116
137
  - A wire goes exactly where the agent says. Nothing is snapped: an off-grid point or a wire
117
138
  through a part is refused. Two points that are not in line get one corner between them.
118
139
  - Nothing is assumed about what a wire connects. The reply to `draw_wire` reports what the
@@ -129,8 +150,8 @@ The wording that steers the agent (workflow, layout and wire style) is `INSTRUCT
129
150
 
130
151
  ## How the automatic drawing works
131
152
 
132
- Parts go on a layout grid (320 × 240 px cells): where the agent says with `at: {col, row}` and
133
- `rotation`, otherwise row by row in the order listed. Every connected pin is led out of its part
153
+ Parts go where the agent says (`at`, `next_to`, `rotation`); parts given no place go into free
154
+ cells of a 320 × 240 px grid below the placed ones, row by row in the order listed. Every connected pin is led out of its part
134
155
  on a short stub. Then, per net (`web/src/agent/wireRouter.ts`):
135
156
 
136
157
  - Pins are joined by orthogonal wires on the 20 px grid, nearest first, as short as the placement
@@ -143,8 +164,13 @@ on a short stub. Then, per net (`web/src/agent/wireRouter.ts`):
143
164
  - A pin gets a **net label instead of a wire** only when no clear path exists within about
144
165
  1800 px of wire (bends and crossings count extra). The reply to `set_circuit` / `edit_circuit`
145
166
  then carries a `drawing` note naming those nets, so the agent can move parts closer.
146
- - Each net gets one label that carries its name (what `V(name)` refers to). Ground pins get a
147
- ground symbol each.
167
+ - Each net carries its name on its wires (what `V(name)` refers to), without showing it. The
168
+ agent chooses the names to show: `labels` in `set_circuit`, or the `name_net` operation of
169
+ `edit_circuit`, as text on a wire or as a net label. Ground pins get a ground symbol each.
170
+ - **Only names that are shown connect.** Nets that show the same name are one net; a name that
171
+ is not shown connects nothing. So where a pin has to be joined by a label, the rest of its net
172
+ gets a label too. Two separate nets that carry the same unshown name stay separate, and the
173
+ check warns about it.
148
174
  - `add_part` puts the new part in the free cell nearest to the pins it connects to and wires it to
149
175
  the existing net, including nets of a hand-drawn circuit that have no label.
150
176
  - `remove_part` also removes the wires that were left leading nowhere.
@@ -164,13 +190,11 @@ instead and the reply says so.
164
190
  ## Limits
165
191
 
166
192
  - Placement is the agent's job: there is no automatic placement. The server's instructions
167
- (`LAYOUT` in `src/tools.mjs`) tell it how to choose cells: signal flow left to right, series
168
- parts in one row, shunt parts upright in the row below their node, tidy rows and columns, then
169
- check the screenshot and fix with `move_part`.
170
- - Parts sit in the middle of a cell unless the agent says otherwise. Pins of three-pin parts are
171
- not on the centre line, so `list_component_types` gives every pin's offset from the centre for
172
- each rotation, and `line_up` (in `place_part`, `add_part` and `move_part`) shifts a part so one
173
- of its pins is exactly in line with a pin of another part.
193
+ (`LAYOUT` in `src/tools.mjs`) tell it to work in functional blocks, place by pins, keep the
194
+ drawing about one and a half to two times as wide as it is high, check the layout numbers and
195
+ the screenshot, and fix with `move_part`.
196
+ - Text is not placed for the agent: a value can lie on a wire of its own part (a horizontal
197
+ capacitor), and `layout.text_overlaps` only reports text on other parts.
174
198
  - `move_part` on a hand-wired part redraws that part's wires automatically.
175
199
  - `list_parts` covers the built-in library only. Passives have no library entries.
176
200
  - Design notes are not part of undo, and writing them does not change the circuit revision.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ec-sim-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "MCP server that lets an AI agent drive the Ec-Sim circuit simulator in an open browser tab",
5
5
  "keywords": [
6
6
  "mcp",
package/src/server.mjs CHANGED
@@ -141,7 +141,7 @@ async function main() {
141
141
  });
142
142
  }
143
143
 
144
- const server = new Server({ name: 'ec-sim', version: '0.1.0' }, { capabilities: { tools: {} }, instructions: INSTRUCTIONS });
144
+ const server = new Server({ name: 'ec-sim', version: '0.2.0' }, { capabilities: { tools: {} }, instructions: INSTRUCTIONS });
145
145
  server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [...LOCAL_TOOLS, ...EDITOR_TOOLS] }));
146
146
  server.setRequestHandler(CallToolRequestSchema, async (req) => {
147
147
  const name = req.params.name;
package/src/tools.mjs CHANGED
@@ -29,21 +29,27 @@ const PART = {
29
29
  at: {
30
30
  type: 'object',
31
31
  description:
32
- 'Where to draw the part, in cells of the layout grid: col grows to the right, row downwards. Every part sits in the middle of its cell, so parts in one row or one column line up exactly. Left out: the next free cell, in the order the parts are listed. See LAYOUT in the server instructions for how to choose cells.',
33
- properties: { col: { type: 'integer', minimum: 0 }, row: { type: 'integer', minimum: 0 } },
34
- required: ['col', 'row'],
32
+ 'Where the centre of the part goes, in grid steps of 20 px: {"x": 30, "y": 20} (x to the right, y downwards). Use it for the first part of a block and place the parts around it with "next_to". {"col": 2, "row": 1} names a cell of a coarse layout grid instead (16 x 12 steps per cell): quick, but far too spread out for a finished drawing. Left out, with no "next_to": the next free cell.',
33
+ properties: { x: { type: 'integer', minimum: 0 }, y: { type: 'integer', minimum: 0 }, col: { type: 'integer', minimum: 0 }, row: { type: 'integer', minimum: 0 } },
34
+ },
35
+ next_to: {
36
+ type: 'object',
37
+ description:
38
+ 'Place the part by one of its pins, relative to a pin of another part: {"pin": "p2", "to": "Q1.collector", "dx": 0, "dy": -4} puts this part so that its pin p2 is 4 grid steps above Q1\'s collector (dx steps to the right, dy steps down; left out = 0). The editor works out the centre from the rotation and the pin offsets, so no coordinate arithmetic is needed. The other part needs a place of its own ("at" or its own "next_to"); in set_circuit the order of the list does not matter. This is the normal way to place every part except the first of a block.',
39
+ properties: { pin: { type: 'string' }, to: { type: 'string' }, dx: { type: 'integer' }, dy: { type: 'integer' } },
40
+ required: ['pin', 'to'],
35
41
  },
36
42
  rotation: { type: 'number', enum: [0, 90, 180, 270], description: 'Rotation in degrees (default 0). Two-pin passives lie horizontal at 0; sources stand vertical.' },
37
43
  line_up: {
38
44
  type: 'object',
39
45
  description:
40
- 'Shift the part a few grid steps so that one of its pins is exactly in line with a pin of a part already placed: {"pin": "p1", "with": "M1.drain"}. The shift is along one axis, the one that needs the smaller move: a part beside the other lines up in height, a part above or below lines up sideways (at most 4 steps). Use it whenever two connected pins are not on the same line, e.g. a resistor above a transistor\'s drain, so the wire between them is straight. Not available in set_circuit.',
46
+ 'Shift a part that has a place ("at") a few grid steps so that one of its pins is exactly in line with a pin of another part: {"pin": "p1", "with": "M1.drain"}. The shift is along one axis, the one that needs the smaller move (at most 4 steps). "next_to" does the same and sets the distance too; use line_up to straighten a part that is already about right.',
41
47
  properties: { pin: { type: 'string' }, with: { type: 'string' } },
42
48
  required: ['pin', 'with'],
43
49
  },
44
50
  centre: {
45
51
  type: 'object',
46
- description: 'Exact centre of the part in grid steps, instead of a cell ("at"), for the rare case where a cell plus line_up does not fit.',
52
+ description: 'The same as "at": {"x", "y"} (kept for older calls).',
47
53
  properties: { x: { type: 'integer' }, y: { type: 'integer' } },
48
54
  required: ['x', 'y'],
49
55
  },
@@ -56,7 +62,7 @@ const PART = {
56
62
  /** A part for place_part: as PART, placed without wires */
57
63
  const PART_BARE = {
58
64
  type: 'object',
59
- description: 'The part to place: id, type, value or model, params, and where ("at", "rotation"). No "nets".',
65
+ description: 'The part to place: id, type, value or model, params, and where ("at" or "next_to", "rotation"). No "nets".',
60
66
  properties: Object.fromEntries(Object.entries(PART.properties).filter(([key]) => key !== 'nets')),
61
67
  required: ['id', 'type'],
62
68
  };
@@ -138,7 +144,7 @@ export const INTERNAL_METHODS = ['waveform_rows'];
138
144
  export const EDITOR_TOOLS = [
139
145
  {
140
146
  name: 'list_component_types',
141
- description: 'Part types the simulator has, with their pin names, where each pin is relative to the part\'s centre for every rotation (pin_offsets, in grid steps), the unit of their main value, and their parameters (SI units and defaults).',
147
+ description: 'Part types the simulator has, with their pin names, where each pin is relative to the part\'s centre for every rotation (pin_offsets, in grid steps), the room the part takes (outline), where its text is written, how much space parts need between them (spacing), the unit of their main value, and their parameters (SI units and defaults).',
142
148
  inputSchema: { type: 'object', properties: {} },
143
149
  },
144
150
  {
@@ -180,7 +186,7 @@ export const EDITOR_TOOLS = [
180
186
  {
181
187
  name: 'set_circuit',
182
188
  description:
183
- 'Replace the circuit in the editor. Give either "parts" (structured netlist) or "spice" (a SPICE deck). The editor draws it as a schematic: you choose where each part goes ("at", "rotation") and the editor draws the wires, falling back to a net label only where parts are far apart or the way is blocked. Plan the placement before you call this (LAYOUT in the server instructions): a good placement gives short straight wires, a careless one gives detours and crossings. This is the first step of every new circuit; afterwards look at a screenshot and tidy single parts and wires by hand (move_part, remove_wire, draw_wire, move_label). Returns the electrical rule check, the new revision, and "drawing" notes naming any nets that needed labels. The user can undo it.',
189
+ 'Replace the circuit in the editor. Give either "parts" (structured netlist) or "spice" (a SPICE deck). The editor draws it as a schematic: you choose where each part goes ("at" for the first part of a block, "next_to" for the parts around it, "rotation") and the editor draws the wires, falling back to net labels only where parts are far apart or the way is blocked. Every net keeps the name you give it (V(name) refers to it), carried on its wires without being shown; "labels" chooses the few names to show. Parts placed too close for their wires are refused with the reason and the room each one takes. Plan the placement before you call this (LAYOUT in the server instructions): a compact placement by blocks gives short straight wires, a spread-out one gives a drawing nobody can read on one screen. This is the first step of every new circuit; afterwards read "layout" in the reply, look at a screenshot and tidy single parts and wires by hand (move_part, remove_wire, draw_wire, move_label). Returns the electrical rule check, the new revision, "layout" (size of the drawing, area and wire length per part, crossings, text overlaps, with the values to aim for) and "drawing" notes naming any nets that needed labels. The user can undo it.',
184
190
  inputSchema: {
185
191
  type: 'object',
186
192
  properties: {
@@ -189,13 +195,19 @@ export const EDITOR_TOOLS = [
189
195
  spice: { type: 'string', description: 'SPICE deck text (R, C, L, V, I, D, Q, M, J, E, G, F, H, K, .model, .subckt, .tran/.ac/.op).' },
190
196
  simulation: SIMULATION,
191
197
  probes: PROBES,
198
+ labels: {
199
+ type: 'object',
200
+ description:
201
+ 'Nets whose name is shown in the drawing, for example {"in": "flag", "out": "flag", "vcc": "text"}. "text" writes the name on a wire of the net; "flag" draws a net label on it. Every net keeps its name either way; nets not listed do not show it.',
202
+ additionalProperties: { type: 'string', enum: ['flag', 'text'] },
203
+ },
192
204
  },
193
205
  },
194
206
  },
195
207
  {
196
208
  name: 'edit_circuit',
197
209
  description:
198
- 'Change the circuit in place; all operations succeed or none is applied. Operations: {"op":"set_value","part":"R1","value":"2.2k"}, {"op":"set_params","part":"M1","params":{"rdsOn":0.02}} (null removes a parameter), {"op":"set_model","part":"M1","model":"IRLZ44N"} (a library part from list_parts), {"op":"add_part","part":{...}} (wired to its nets where a clear path exists), {"op":"remove_part","part":"C3"}, {"op":"move_part","part":"R2","at":{"col":3,"row":1},"rotation":90} (any of at, centre, line_up, rotation, mirrorX, mirrorY; its wires are drawn again. move_part and connect operations that follow each other are made together: places are checked as they end up, so parts can swap places or take a cell another part of the same call leaves, with no need to park anything first), {"op":"connect","part":"R2","nets":{"p2":"out"}} (moves the listed pins to other nets; null disconnects a pin; other pins keep their nets). Use move_part to tidy a layout after looking at the screenshot.',
210
+ 'Change the circuit in place; all operations succeed or none is applied. Operations: {"op":"set_value","part":"R1","value":"2.2k"}, {"op":"set_params","part":"M1","params":{"rdsOn":0.02}} (null removes a parameter), {"op":"set_model","part":"M1","model":"IRLZ44N"} (a library part from list_parts), {"op":"add_part","part":{...}} (wired to its nets where a clear path exists), {"op":"remove_part","part":"C3"}, {"op":"move_part","part":"R2","next_to":{"pin":"p1","to":"Q1.collector","dy":-4},"rotation":90} (any of at, next_to, line_up, rotation, mirrorX, mirrorY; its wires are drawn again. move_part and connect operations that follow each other are made together: places are checked as they end up, so parts can swap places or take the place another part of the same call leaves, with no need to park anything first; a "next_to" sees the parts moved earlier in the list at their new place), {"op":"connect","part":"R2","nets":{"p2":"out"}} (moves the listed pins to other nets; null disconnects a pin; other pins keep their nets), {"op":"name_net","net":"N3","name":"out","show":"text"} (renames a net and/or chooses how its name is shown: "text" on a wire, "flag" as a net label, "hidden" not at all; probes follow a new name; connections never change, so a name another net has is refused). Use move_part to tidy a layout after looking at the screenshot. When parts were drawn again the reply carries "layout" (see set_circuit).',
199
211
  inputSchema: {
200
212
  type: 'object',
201
213
  properties: {
@@ -205,12 +217,16 @@ export const EDITOR_TOOLS = [
205
217
  items: {
206
218
  type: 'object',
207
219
  properties: {
208
- op: { type: 'string', enum: ['set_value', 'set_params', 'set_model', 'add_part', 'remove_part', 'move_part', 'connect'] },
209
- part: { description: 'Part id (set_value, set_params, remove_part) or a part object (add_part).' },
220
+ op: { type: 'string', enum: ['set_value', 'set_params', 'set_model', 'add_part', 'remove_part', 'move_part', 'connect', 'name_net'] },
221
+ part: { description: 'Part id (all operations on a part) or a part object (add_part). Not used by name_net.' },
222
+ net: { type: 'string', description: 'name_net: the net, by its current name (get_circuit).' },
223
+ name: { type: 'string', description: 'name_net: the new name.' },
224
+ show: { type: 'string', enum: ['text', 'flag', 'hidden'], description: 'name_net: how the name is shown.' },
210
225
  value: { type: ['number', 'string'] },
211
226
  params: { type: 'object' },
212
227
  model: { type: 'string' },
213
- at: { type: 'object', properties: { col: { type: 'integer' }, row: { type: 'integer' } } },
228
+ at: { type: 'object', properties: { x: { type: 'integer' }, y: { type: 'integer' }, col: { type: 'integer' }, row: { type: 'integer' } } },
229
+ next_to: { type: 'object', properties: { pin: { type: 'string' }, to: { type: 'string' }, dx: { type: 'integer' }, dy: { type: 'integer' } } },
214
230
  rotation: { type: 'number', enum: [0, 90, 180, 270] },
215
231
  mirrorX: { type: 'boolean' },
216
232
  mirrorY: { type: 'boolean' },
@@ -218,7 +234,7 @@ export const EDITOR_TOOLS = [
218
234
  centre: { type: 'object', properties: { x: { type: 'integer' }, y: { type: 'integer' } } },
219
235
  nets: { type: 'object', description: 'connect: pin name -> net name, or null to disconnect the pin.' },
220
236
  },
221
- required: ['op', 'part'],
237
+ required: ['op'],
222
238
  },
223
239
  },
224
240
  },
@@ -233,13 +249,13 @@ export const EDITOR_TOOLS = [
233
249
  {
234
250
  name: 'get_drawing',
235
251
  description:
236
- 'The drawing with positions, for wiring by hand: every part with its cell, rotation, and each pin\'s position and net (null = nothing drawn on the pin yet); every ground symbol and net label; every probe; every wire with its id, its corner points and its net. Positions are in grid steps of 20 px: a cell is 16 steps wide and 12 high, and the part in cell (col, row) is centred at (10 + 16 col, 10 + 12 row); x grows to the right, y downwards. Use get_circuit for the netlist itself.',
252
+ 'The drawing with positions, for wiring by hand and for judging the layout: every part with its centre, rotation, and each pin\'s position and net (null = nothing drawn on the pin yet); every ground symbol and net label; every probe; every wire with its id, its corner points, its net and "name_shown" when the net name is written on it; and "layout": the size of the drawing, area and wire length per part, crossings and text overlaps, with the values to aim for. Positions are in grid steps of 20 px; x grows to the right, y downwards. Use get_circuit for the netlist itself.',
237
253
  inputSchema: { type: 'object', properties: {} },
238
254
  },
239
255
  {
240
256
  name: 'place_part',
241
257
  description:
242
- 'Place one part on the schematic without wires, to wire it yourself with draw_wire (for adding to a circuit; a new circuit starts with set_circuit). Give "at" (cell) and, where needed, "rotation", "mirrorX" or "mirrorY"; the reply lists where its pins are, in grid steps, ready for draw_wire. "part" is as in set_circuit but without "nets". Left-out "at": the next free cell.',
258
+ 'Place one part on the schematic without wires, to wire it yourself with draw_wire (for adding to a circuit; a new circuit starts with set_circuit). Give "at" (centre in grid steps) or "next_to" (a pin of it a stated number of steps from a pin of another part) and, where needed, "rotation", "mirrorX" or "mirrorY"; the reply lists where its pins are, in grid steps, ready for draw_wire. "part" is as in set_circuit but without "nets". A part placed bare only needs 1 free grid step to the bodies around it, so parts you wire yourself can sit closer than parts the editor wires. Left-out place: the next free cell beside the drawing.',
243
259
  inputSchema: { type: 'object', properties: { part: PART_BARE, revision: REVISION_OPTIONAL }, required: ['part'] },
244
260
  },
245
261
  {
@@ -271,7 +287,7 @@ export const EDITOR_TOOLS = [
271
287
  {
272
288
  name: 'place_symbol',
273
289
  description:
274
- 'Place a ground symbol or a net label with its connection point exactly at "at" (a pin, or a point in grid steps; usually the end of a wire). A ground symbol hangs down from the point and makes the net ground ("0"). A net label names the net ("name"); labels with the same name are one net without a wire between them, and V(name) refers to it. "side" puts a label\'s text left or right of the point.',
290
+ 'Place a ground symbol or a net label with its connection point exactly at "at" (a pin, or a point in grid steps; usually the end of a wire). A ground symbol hangs down from the point and makes the net ground ("0"). A net label shows the name of the net ("name") and V(name) refers to it; nets that show the same name (by a label, or by a name written on a wire) are one net without a wire between them. "side" puts a label\'s text left or right of the point.',
275
291
  inputSchema: {
276
292
  type: 'object',
277
293
  properties: {
@@ -391,7 +407,7 @@ export const EDITOR_TOOLS = [
391
407
  {
392
408
  name: 'screenshot',
393
409
  description:
394
- 'A PNG picture of what the user sees. view "schematic" (default): the whole drawing with its probes, always fitted to the picture whatever the user\'s zoom; give "parts" to get a close-up of the area around those parts instead (useful when a large circuit is too small to read). view "waveforms": the plot panes of the waveform viewer as shown now, with their zoom and cursors (needs a simulation result). Look at the schematic after every set_circuit and after adding parts: check that parts and labels do not overlap, that wires are short and readable, and that the signal flows left to right; if not, place the parts again with "at" and "rotation". For numbers use measure or get_waveform.',
410
+ 'A PNG picture of what the user sees. view "schematic" (default): the whole drawing with its probes, always fitted to the picture whatever the user\'s zoom; give "parts" to get a close-up of the area around those parts instead. When the whole drawing would be too small to read, the reply says so in "note": then take close-ups, one block at a time. view "waveforms": the plot panes of the waveform viewer as shown now, with their zoom and cursors (needs a simulation result). Look at the schematic after every set_circuit and after adding parts: check that parts and labels do not overlap, that wires are short and readable, and that the signal flows left to right; if not, move the parts (move_part with "next_to"). For numbers use measure or get_waveform.',
395
411
  inputSchema: {
396
412
  type: 'object',
397
413
  properties: {
@@ -413,33 +429,36 @@ export const INSTRUCTIONS = [
413
429
  'Start with open_editor, then list_component_types (once). All numbers are SI. Ground is net "0". The user can edit the circuit too: if a call reports that the revision changed, call get_circuit and continue from what is there.',
414
430
  '',
415
431
  'HOW TO BUILD A CIRCUIT. Draw the whole circuit in one go, then tidy it by hand like an engineer cleaning up a sketch:',
416
- '1. Plan the placement first (LAYOUT below): decide the cell and rotation of every part.',
417
- '2. Draw it with one set_circuit call: every part with its "at", "rotation" and nets. The editor places the parts and wires them. Check "check" for errors.',
418
- '3. Call screenshot and look at the result critically, as the user will.',
419
- '4. Fix what does not read cleanly, one change at a time, so the user can follow: move_part for a part in the wrong place or out of line ("line_up" puts a pin exactly in line with the pin it connects to); remove_wire and draw_wire for a wire with a detour, a jog or a needless crossing; move_label for text or a net label that sits on something. After a few fixes take another screenshot. Stop when the drawing reads cleanly from left to right; do not redraw what is already good. To rearrange a whole block, put all its move_part operations in one edit_circuit call: they are applied together and the wires are drawn once.',
432
+ '1. Plan the placement first (LAYOUT below): split the circuit into blocks, and decide for every part its rotation and which pin of which neighbour it sits next to.',
433
+ '2. Draw it with one set_circuit call: every part with "at" or "next_to", its "rotation" and its nets. The editor places the parts and wires them. Check "check" for errors. A part refused as too close comes with the room both parts take: move it out by the missing steps and send the call again.',
434
+ '3. Read "layout" in the reply: it gives the size of the drawing, the area and wire length per part, crossings and text overlaps, and the values to aim for. Then call screenshot and look at the result critically, as the user will. If the screenshot reply says the drawing is too small to read, take close-ups of one block at a time ("parts").',
435
+ '4. Fix what does not read cleanly, one change at a time, so the user can follow: move_part for a part in the wrong place or out of line ("next_to" puts a pin a stated distance from the pin it connects to, "line_up" puts it exactly in line); remove_wire and draw_wire for a wire with a detour, a jog or a needless crossing; move_label for text or a net label that sits on something. After a few fixes take another screenshot. Stop when the drawing reads cleanly from left to right; do not redraw what is already good. To rearrange a whole block, put all its move_part operations in one edit_circuit call: they are applied together and the wires are drawn once.',
420
436
  '5. After changing wires by hand, call get_circuit and compare the net on every pin with the circuit you intended: read every draw_wire reply too, it lists the pins the wire connected.',
421
437
  '6. Then set_probes -> run_simulation -> measure. Change values with edit_circuit and run again.',
422
438
  'To extend a circuit later, add parts one at a time: edit_circuit add_part wires the part for you, place_part places it bare for wiring with draw_wire. Building a whole circuit with place_part and draw_wire is possible and fine for a handful of parts, but slower; a SPICE deck or a large circuit always starts with set_circuit.',
423
439
  '',
424
- 'LAYOUT. You place the parts, in cells: col grows to the right, row downwards, and a part sits in the middle of its cell, so a row or a column lines up exactly.',
425
- '- Signal flow runs left to right: sources and inputs in column 0, each following stage one column to the right, the load or output last.',
426
- '- One main row carries the signal path. Parts in series with the signal (series resistor, inductor, switch, coupling capacitor) lie in that row at rotation 0, one per column, in the order the signal meets them.',
427
- '- Parts from a node to ground or to a rail (shunt capacitor, load resistor, divider leg, freewheel diode) stand upright (rotation 90 or 270) in the row directly below the node they hang from, in the same column or the next, so the wire to them is one short line. Parts up to a supply rail go in the row directly above.',
428
- '- Feedback parts go in the row above the stage they bridge, in the column of that stage.',
429
- '- Keep rows and columns tidy: use consecutive cells, do not leave gaps or stagger parts. Two parts that share a net belong in neighbouring cells; if a net joins many parts, line them up along one row or column.',
430
- '- Rotate or mirror a part so its pins face what they connect to ("rotation"; "mirrorX" swaps left and right, "mirrorY" top and bottom). list_component_types gives the pins; place_part and get_drawing give their positions.',
431
- '- A source stands in column 0 with its negative pin to ground; a second supply gets its own cell in column 0, above or below.',
432
- '- Pins are not all on the centre line of a part: a resistor\'s pins are, a transistor\'s drain and source are one grid step to the side (list_component_types gives pin_offsets for every rotation). Two parts in the same row or column can therefore have connected pins one or two steps out of line, which forces an ugly jog in the wire. Prevent it when placing: give "line_up": {"pin": "p2", "with": "M1.drain"} and the part is shifted so those two pins are exactly in line. Place the part with the most pins first (the transistor, the op-amp), then line the two-pin parts up with it.',
440
+ 'LAYOUT. You place the parts. Positions are in grid steps of 20 px, x to the right, y downwards. A schematic is read on one screen: the aim is a compact drawing in which every block is recognisable, not parts spread evenly over a large sheet.',
441
+ '- Work in blocks. A block is one function with its helpers: a transistor or op-amp stage with its bias, feedback and decoupling parts; a rectifier with its reservoir capacitor; a reference; an output filter. Lay each block out compactly around its main part, with 4 to 6 grid steps between neighbouring parts.',
442
+ '- Arrange the blocks in signal order from left to right, sources and inputs at the left, the load or output at the right, with 6 to 10 steps between blocks. When the drawing gets wider than about 120 steps, start a second row of blocks below instead of going on to the right: aim for a drawing about one and a half to two times as wide as it is high. A circuit drawn as one long row is wrong.',
443
+ '- Place by pins, not by centres. Give the main part of a block "at": {"x", "y"}, and every part around it "next_to": {"pin": "<its pin>", "to": "Q1.collector", "dx": ..., "dy": ...}, which puts that pin dx steps right and dy steps down of the named pin. Connected pins then sit exactly in line and the wire between them is one straight piece. The editor does the arithmetic from the rotation and the pin offsets (list_component_types gives pin_offsets and the outline of every part for each rotation).',
444
+ '- Distances that work: a part in series with a pin goes 4 steps from it in the direction the pin points (dx: 4 or -4 for a pin pointing sideways, dy: 4 or -4 for one pointing up or down). A part hanging from a node to ground stands upright (rotation 90 or 270) with its top pin 4 steps below that pin. A ground pin needs 6 free steps below it for its wire and symbol. Two parts side by side need 1 free step between their bodies. A pin that faces the side of another part needs 4 steps to it, and a pin of another net opposite a pin needs 5.',
445
+ '- The editor refuses a part that is too close for the wires it has to draw and says which part is in the way and what room each takes; widen that one distance, do not spread everything out.',
446
+ '- Inside a block: parts in series with the signal lie in the signal row at rotation 0, in the order the signal meets them. Parts from a node to ground or to a rail stand upright below (to ground) or above (to a supply) the node. Feedback parts go above the stage they bridge. Decoupling capacitors stand next to the supply pin they serve.',
447
+ '- Rotate or mirror a part so its pins face what they connect to ("rotation"; "mirrorX" swaps left and right, "mirrorY" top and bottom).',
448
+ '- A source stands at the left with its negative pin to ground; a second supply stands above or below it.',
449
+ '- Check the numbers: after every drawing, "layout.area_per_part" should be about 80 to 150 square grid steps and "layout.wire_per_part" about 6 to 18 steps. Much more means the parts are too far apart: move them closer block by block. "layout.text_overlaps" lists texts that lie on another part: move the text with move_label or the part by a step or two.',
450
+ '- {"col", "row"} in "at" names a cell of a coarse grid (16 x 12 steps per cell) and a part given no place at all goes to the next free cell. That is for a quick sketch only: a drawing on that grid is three times too wide.',
433
451
  '',
434
- 'WIRES. Positions are in grid steps of 20 px; a cell is 16 steps wide and 12 high, and the part in cell (col, row) is centred at (10 + 16 col, 10 + 12 row).',
435
- '- Wires are horizontal and vertical only, as short as the placement allows, with as few corners as possible. Two pins on one line are joined by one straight wire. If a wire between neighbouring parts needs a jog of one or two steps, the parts are out of line: fix the part with move_part and "line_up" instead of drawing the jog.',
452
+ 'WIRES. Positions are in grid steps of 20 px.',
453
+ '- Wires are horizontal and vertical only, as short as the placement allows, with as few corners as possible. Two pins on one line are joined by one straight wire. If a wire between neighbouring parts needs a jog of one or two steps, the parts are out of line: fix the part with move_part and "next_to" or "line_up" instead of drawing the jog.',
436
454
  '- Lead a wire two grid steps straight out of a pin before it turns or meets another wire. Extend that only when other connections need the room.',
437
455
  '- Avoid crossings. Where one cannot be avoided, both wires run straight for at least two grid steps on each side of it.',
438
456
  '- Join a third pin to a net by ending its wire on the existing wire (a junction), two grid steps or more from the nearest pin or corner, not by a second wire into the same pin.',
439
457
  '- Never run a wire over a part or along another wire. Go around, with "via" points.',
440
458
  '- A ground symbol goes two grid steps below a pin that points down (draw the short wire, then place the symbol at its end). Prefer one ground symbol per ground pin over long ground wires.',
441
- '- Name the nets you will measure (in, out, sw ...) with one net label each, at a wire end or on a short stub, where its text is clear of parts.',
442
- 'These are the rules to judge the automatic wiring by and to follow when you redraw a wire. In a screenshot: parts and text must not overlap, wires must be short and straight, rows and columns must line up, and the drawing must read from left to right. Tidy what does not (remove_wire and redraw; move_part for a part; move_label for text or a net label that sits on a wire, a part or other text).',
459
+ '- Net names: every net has the name you gave it, on its wires, and V(name) works whether the name is shown or not. Show only the names a reader looks for: inputs, outputs, supply rails and the nodes you measure. "labels" in set_circuit or the name_net operation does it: "text" writes the name on a wire (good for a rail or a node inside a block), "flag" draws a net label (good for an input or output at the edge). Leave the names of internal nodes unshown.',
460
+ '- A name connects only where it is shown: two nets that show the same name are one net, and a name that is not shown connects nothing. Two separate nets must not carry the same name; the check reports it as a warning.',
461
+ 'These are the rules to judge the automatic wiring by and to follow when you redraw a wire. In a screenshot: parts and text must not overlap, wires must be short and straight, blocks must be recognisable, and the drawing must read from left to right. Tidy what does not (remove_wire and redraw; move_part for a part; move_label for text or a net label that sits on a wire, a part or other text).',
443
462
  '',
444
463
  'For a real semiconductor or op-amp, select it with list_parts ("where" and "sort" pick by voltage, current, resistance and so on) and name it with "model" instead of typing datasheet values.',
445
464
  'Keep the design notes current with set_design_notes (requirements, decisions, measured results). For data too large to read, export_waveforms writes a CSV file.',