proofboard-mcp 0.1.0 → 0.1.1

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.
@@ -160,5 +160,5 @@ ${n.code}`)}},{name:"get_board_image",mutates:!1,description:"Get a picture of t
160
160
  `+T_(e.D(),e.tr),!0):_e(`Unknown example "${Te(t.id)}".`):_e("Examples are not available here.")}},{name:"new_design",mutates:!0,description:"Start an empty design on a new board. Discards the current design (the user can undo). Ask the user first unless they asked for it.",input_schema:q.obj({rows:q.int("rows (default 78)"),cols:q.int("columns (default 39)"),section:q.int("strip section length in rows: 1 = dot grid, 3 = tripad, rows = full strips (default 3)")}),run(e,t){let r=Math.max(5,Math.min(200,lt(t.rows)||78)),n=Math.max(5,Math.min(120,lt(t.cols)||39)),o=Math.max(1,Math.min(r,lt(t.section)||3)),s=ma();return s.board={rows:r,cols:n,sec:o},e.setD(Rl(s)),e.commit(),Ye(`New empty design: ${r}\xD7${n}, section ${o}.`,!0)}}];async function j_(e,t,r){let n=Ll.find(s=>s.name===t);if(!n)return _e(`Unknown tool "${t}".`);let o=r&&typeof r=="object"?r:{};for(let s of n.input_schema.required||[])if(o[s]==null)return _e(`Missing required input "${s}".`);try{return await n.run(e,o)}catch(s){return _e(`Tool failed: ${s.message}`)}}function M_(e,t={}){let r={design:e};return{get design(){return r.design},D:()=>r.design,setD:n=>{r.design=n},commit(){let n=r.design;n.settings.autoRoute&&n.links.length&&Do(n)},...t}}var F_="https://nemalsomel.github.io/proofboard/",MN=["https://nemalsomel.github.io"],DN=/^https?:\/\/(localhost|127\.0\.0\.1|\[::1\])(:\d+)?$/,LN=`Proofboard is a perfboard/stripboard circuit designer and simulator. These tools build and check a design: place parts, connect pins (wiring is routed automatically), simulate, check problems, generate code.
161
161
  Call bridge_status first. If the Proofboard app is connected, every change appears live in the user's browser and the user can undo it; otherwise you work on an in-memory design (save it with save_design_file, or ask the user to open ${F_}, then File \u203A Claude connection\u2026 \u203A Connect).
162
162
  Refer to parts by label ("R1") and pins as "R1.2" or "PICO1.GP15". Place parts next to the pin they connect to (place_part "near"); keep layouts compact (plan_compaction / compact_layout). Ask before large changes or replacing the user's design.`,V_=[{name:"bridge_status",description:"Whether the Proofboard app is connected (live editing in the user's browser) or the server works on an in-memory design. Call this first.",input_schema:{type:"object",properties:{},additionalProperties:!1}},{name:"open_design_file",description:"Load a design from a Proofboard JSON file (as saved by the app's File \u203A Save to file). Replaces the current design (in the app it can be undone).",input_schema:{type:"object",properties:{path:{type:"string",description:"file path"}},required:["path"],additionalProperties:!1}},{name:"save_design_file",description:"Save the current design to a Proofboard JSON file (it can be opened in the app with File \u203A Open).",input_schema:{type:"object",properties:{path:{type:"string",description:"file path, e.g. my-board.proofboard.json"}},required:["path"],additionalProperties:!1}}];function Z_(e){let t=e.log||(()=>{}),r=M_(ma()),n=null,o="",s=0,a=new Map,i=new cl({name:"proofboard",version:e.version},{capabilities:{tools:{}},instructions:LN}),c=()=>{let p=i.getClientVersion();return p?p.name:void 0},l=(p,m)=>{p.readyState===1&&p.send(JSON.stringify(m))};function u(p,m){let g=n;if(!g)return Promise.resolve({text:"The Proofboard app is not connected.",isError:!0});let v=++s;return new Promise(_=>{let w=setTimeout(()=>{a.delete(v),_({text:"The Proofboard app did not answer in time (it may be busy or the tab is in the background).",isError:!0})},e.callTimeout??12e4);a.set(v,{res:_,timer:w}),l(g,{t:"call",id:v,name:p,input:m})})}let d=p=>{for(let[m,g]of a)clearTimeout(g.timer),g.res({text:p,isError:!0}),a.delete(m)};async function f(p,m){if(p==="bridge_status")return{text:n?`Connected to the Proofboard app (${o}). Changes appear live in the user's browser; the user sees a "Claude is working" bar and can undo each burst of changes with Ctrl+Z.`:`The Proofboard app is not connected: you are working on an in-memory design (${r.D().parts.length} parts). To edit live, ask the user to open ${F_} and choose File \u203A Claude connection\u2026 \u203A Connect${e.port===47615||e.port==null?"":` (port ${e.port})`}. Save your work with save_design_file.`};if(p==="open_design_file"){let g=L_(String(m.path||"")),v;try{v=Rl(JSON.parse(await RN(g,"utf8")))}catch(_){return{text:`Cannot open ${g}: ${_.message}`,isError:!0}}return n?u(ry,{design:v}):($l(v.customParts),r.setD(v),r.commit(),{text:`Opened ${g} (${v.parts.length} parts).`,changed:!0})}if(p==="save_design_file"){let g=L_(String(m.path||"")),v;if(n){let _=await u(ty,{});if(_.isError)return _;v=_.text}else v=JSON.stringify(r.D(),null,1);try{await jN(g,v)}catch(_){return{text:`Cannot write ${g}: ${_.message}`,isError:!0}}return{text:`Saved to ${g}. Open it in the app with File \u203A Open.`}}return n?u(p,m):j_(r,p,m)}i.setRequestHandler(su,async()=>({tools:[...V_,...Ll].map(p=>({name:p.name,description:p.description,inputSchema:p.input_schema}))})),i.setRequestHandler(hs,async p=>{let m=await f(p.params.name,p.params.arguments||{});return{content:[{type:"text",text:m.text},...m.image?[{type:"image",data:m.image,mimeType:"image/png"}]:[]],isError:!!m.isError}}),i.oninitialized=()=>{let p=c();p&&n&&l(n,{t:"client",client:p})};let h=null;function b(){if(e.port===null)return Promise.resolve(null);let p=m=>!m||DN.test(m)||[...MN,...e.origins||[]].includes(m);return new Promise(m=>{let g=new Op.default({host:"127.0.0.1",port:e.port??47615,verifyClient:v=>p(v.origin)});g.on("error",v=>{t(`Bridge not started (${v.message}); working without the app.`),h=null,m(null)}),g.on("listening",()=>{h=g;let v=g.address(),_=typeof v=="object"&&v?v.port:null;t(`Bridge listening on ws://127.0.0.1:${_}`),m(_)}),g.on("connection",v=>{let _=!1;v.on("message",w=>{let k=ny(w);if(k){if(k.t==="hello"){if(k.proto!==1||k.app!=="proofboard"){l(v,{t:"bye",reason:`Unsupported protocol ${k.proto}; update proofboard-mcp.`}),v.close();return}n&&n!==v&&(l(n,{t:"bye",reason:"Another Proofboard tab connected."}),n.close()),_=!0,n=v,o=`Proofboard ${k.version}, design: ${k.design}`,t(`App connected: ${o}`),l(v,{t:"welcome",proto:1,server:"proofboard-mcp",version:e.version,client:c()})}else if(k.t==="result"&&_){let P=a.get(k.id);P&&(clearTimeout(P.timer),a.delete(k.id),P.res(k.result))}}}),v.on("close",()=>{n===v&&(n=null,t("App disconnected."),d("The Proofboard app disconnected before answering."))})})})}return{mcp:i,call:f,listen:b,tools:()=>[...V_,...Ll].map(p=>p.name),connected:()=>!!n,headless:r,async close(){d("Server closing.");for(let p of h?[...h.clients]:[])p.terminate();await new Promise(p=>h?h.close(()=>p()):p()),h=null,await i.close().catch(()=>{})}}}var bn=process.argv.slice(2),VN=e=>{let t=bn.indexOf(e);return t>=0?bn[t+1]:void 0};(bn.includes("--help")||bn.includes("-h"))&&(process.stderr.write(`proofboard-mcp [--port 47615] [--no-bridge] [--allow-origin https://example.org]
163
- `),process.exit(0));var FN=bn.includes("--no-bridge")?null:Number(VN("--port")||process.env.PROOFBOARD_PORT||47615),ZN=bn.flatMap((e,t)=>e==="--allow-origin"&&bn[t+1]?[bn[t+1]]:[]),cf=Z_({version:"0.1.0",port:FN,origins:ZN,log:e=>process.stderr.write(`[proofboard] ${e}
163
+ `),process.exit(0));var FN=bn.includes("--no-bridge")?null:Number(VN("--port")||process.env.PROOFBOARD_PORT||47615),ZN=bn.flatMap((e,t)=>e==="--allow-origin"&&bn[t+1]?[bn[t+1]]:[]),cf=Z_({version:"0.1.1",port:FN,origins:ZN,log:e=>process.stderr.write(`[proofboard] ${e}
164
164
  `)});await cf.listen();await cf.mcp.connect(new yi);var uf=()=>{cf.close().finally(()=>process.exit(0))};process.on("SIGINT",uf);process.on("SIGTERM",uf);process.stdin.on("close",uf);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "proofboard-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "MCP server for Proofboard (perfboard/stripboard designer & simulator): lets Claude design and check circuits, live in the browser app or headless.",
5
5
  "type": "module",
6
6
  "bin": { "proofboard-mcp": "dist/proofboard-mcp.mjs" },