<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: metataro</title>
    <description>The latest articles on DEV Community by metataro (@metataro).</description>
    <link>https://dev.to/metataro</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4181017%2Fc5a2c2a5-dc6b-4fc5-a789-941233530f6f.jpg</url>
      <title>DEV Community: metataro</title>
      <link>https://dev.to/metataro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/metataro"/>
    <language>en</language>
    <item>
      <title>Let Claude Code read your production database — even a write disguised as SELECT gets refused</title>
      <dc:creator>metataro</dc:creator>
      <pubDate>Mon, 02 Nov 2026 05:03:14 +0000</pubDate>
      <link>https://dev.to/metataro/let-claude-code-read-your-production-database-and-make-update-fail-at-the-server-g75</link>
      <guid>https://dev.to/metataro/let-claude-code-read-your-production-database-and-make-update-fail-at-the-server-g75</guid>
      <description>&lt;p&gt;Pointing an AI agent at a database is easy. Pointing it at &lt;strong&gt;production&lt;/strong&gt; is where people stop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;dbboard is an open-source desktop database client with an MCP server that gives Claude Code read-only access to Postgres, MySQL, Turso / libSQL, Cloudflare D1, MongoDB and Firestore — enforced by the database, not by a keyword filter.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The usual guard is a keyword filter: reject anything that starts with &lt;code&gt;UPDATE&lt;/code&gt; or &lt;code&gt;DROP&lt;/code&gt;. It holds until the agent writes a CTE that deletes, a &lt;code&gt;SELECT … FOR UPDATE&lt;/code&gt;, or two statements in one string. And the agent still needs the password, which now sits in its config and its transcript.&lt;/p&gt;

&lt;p&gt;dbboard is built so that this question has a boring answer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc1b5xcjnwr4a0wectbi4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc1b5xcjnwr4a0wectbi4.png" alt="dbboard with a Postgres server open: databases, schemas and tables in the sidebar, and a JOIN with 180 rows in the grid" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The one idea: the database says no, not only a filter
&lt;/h2&gt;

&lt;p&gt;dbboard ships a separate MCP server, &lt;code&gt;dbboard-mcp&lt;/code&gt;, that hands the connections you already have to Claude Code or Claude Desktop. Every read passes two checks.&lt;/p&gt;

&lt;p&gt;First, the statement is &lt;strong&gt;parsed&lt;/strong&gt; — a real SQL parser, not string matching — and anything that is not a single read-only statement is refused. Then it &lt;strong&gt;runs inside the database's own read-only mode&lt;/strong&gt;, or through an adapter that cannot write at all:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Engine&lt;/th&gt;
&lt;th&gt;How a read is run&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Postgres, Neon, Supabase, CockroachDB, Aurora DSQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BEGIN TRANSACTION READ ONLY&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL / MariaDB&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SET TRANSACTION READ ONLY&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Turso / libSQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PRAGMA query_only&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloudflare D1&lt;/td&gt;
&lt;td&gt;no read-only mode exists, so the parser is the enforcement and fails closed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud Firestore&lt;/td&gt;
&lt;td&gt;the adapter has no write path to reach: it implements the read endpoints and nothing else&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MongoDB&lt;/td&gt;
&lt;td&gt;only six commands (&lt;code&gt;find&lt;/code&gt;, &lt;code&gt;aggregate&lt;/code&gt;, &lt;code&gt;count&lt;/code&gt;, &lt;code&gt;distinct&lt;/code&gt;, &lt;code&gt;listCollections&lt;/code&gt;, &lt;code&gt;listIndexes&lt;/code&gt;) and only their known options; the pipeline is walked for a &lt;code&gt;$out&lt;/code&gt; or &lt;code&gt;$merge&lt;/code&gt; hidden inside a &lt;code&gt;$facet&lt;/code&gt;, &lt;code&gt;$lookup&lt;/code&gt; or &lt;code&gt;$unionWith&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The second check is the one that matters. Here is &lt;code&gt;dbboard-mcp&lt;/code&gt; against a local Postgres with fictional data, called exactly as an agent calls it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;run_read_query  DELETE FROM orders WHERE quantity = 1
→ query failed: not a single read-only statement: only read-only SELECT / WITH / EXPLAIN statements are allowed

run_read_query  WITH gone AS (DELETE FROM orders RETURNING id) SELECT count(*) FROM gone
→ query failed: not a single read-only statement: a data-modifying statement (in a CTE or set expression) is not read-only

run_read_query  SELECT nextval('orders_id_seq')
→ query failed: cannot execute nextval() in a read-only transaction

run_read_query  SELECT count(*) FROM orders
→ 180
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first two are caught by the parser. The third is a plain &lt;code&gt;SELECT&lt;/code&gt; that still writes — it advances a sequence — so no parser flags it. &lt;strong&gt;Postgres refuses it&lt;/strong&gt;, because the transaction itself is read-only. The table still has all 180 rows, and the sequence did not move.&lt;/p&gt;

&lt;p&gt;Three more rules sit around that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Passwords never reach the agent.&lt;/strong&gt; They stay in the OS keychain. The agent sees &lt;code&gt;{ id, name, kind }&lt;/code&gt; for each connection, and no error message embeds a URL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Writing is off until a person turns it on, per connection.&lt;/strong&gt; Even then only an allowlist runs (&lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, &lt;code&gt;CREATE TABLE&lt;/code&gt;, …). &lt;code&gt;DROP&lt;/code&gt; of anything, &lt;code&gt;TRUNCATE&lt;/code&gt;, &lt;code&gt;GRANT&lt;/code&gt; / &lt;code&gt;REVOKE&lt;/code&gt; and user or role changes are refused whatever the setting says.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Results are bounded,&lt;/strong&gt; so one careless query cannot pour a whole table into the agent's context.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to give Claude Code read-only access to your database
&lt;/h2&gt;

&lt;p&gt;Download &lt;code&gt;dbboard-mcp&lt;/code&gt; for your platform from the &lt;a href="https://github.com/meta-taro/dbboard/releases/latest" rel="noopener noreferrer"&gt;latest release&lt;/a&gt; (one self-contained executable), then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# macOS&lt;/span&gt;
claude mcp add dbboard &lt;span class="nt"&gt;--scope&lt;/span&gt; user &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.local/bin/dbboard-mcp"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Windows (PowerShell)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;claude&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mcp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dbboard&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--scope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;LOCALAPPDATA&lt;/span&gt;&lt;span class="s2"&gt;/dbboard/dbboard-mcp.exe"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent can then list connections and tables, describe a table, search a connection's schema by column name, follow foreign keys, and run read queries. Ask it "which orders in the last week have no shipment row?" and it finds the tables itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  And for the person: one window
&lt;/h2&gt;

&lt;p&gt;The desktop app is the same connections, for you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Every database you look after in one sidebar&lt;/strong&gt; — Postgres (Neon, Supabase, CockroachDB, Aurora DSQL), MySQL / MariaDB, Turso / libSQL, Cloudflare D1, Firestore and MongoDB. An SSH tunnel reaches the one behind a bastion; a colour and a tag on each connection show which one you are about to run against.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leave the database name blank&lt;/strong&gt; and one login lists every database it can open, with schemas and tables beneath.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix one value without writing an &lt;code&gt;UPDATE&lt;/code&gt;:&lt;/strong&gt; double-click the cell, type, save. Edits are keyed on the primary key, so a table without one stays read-only instead of guessing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What it is not
&lt;/h2&gt;

&lt;p&gt;Not a hosted service: nothing leaves your machine except your own queries to your own databases. Not code-signed either — a certificate is a recurring purchase this project does not make, so every release ships &lt;code&gt;SHA256SUMS.txt&lt;/code&gt; to check instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Download from the &lt;a href="https://meta-taro.github.io/dbboard/" rel="noopener noreferrer"&gt;download page&lt;/a&gt; (Windows x64 / ARM64 &lt;code&gt;.exe&lt;/code&gt;, macOS &lt;code&gt;.dmg&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manage → Add connection&lt;/strong&gt;, pick the database type, fill in the server&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;dbboard-mcp&lt;/code&gt; to your agent as above&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;MIT-licensed. Bug reports and requests: &lt;a href="https://github.com/meta-taro/dbboard/issues" rel="noopener noreferrer"&gt;Issues&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>database</category>
      <category>postgres</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Human-in-the-loop QA: let an AI agent run your test sheet on Android, iPhone, web and desktop — and sign off each case yourself</title>
      <dc:creator>metataro</dc:creator>
      <pubDate>Tue, 13 Oct 2026 11:51:34 +0000</pubDate>
      <link>https://dev.to/metataro/human-in-the-loop-qa-let-an-ai-agent-run-your-test-sheet-on-android-iphone-web-and-desktop-and-2dp0</link>
      <guid>https://dev.to/metataro/human-in-the-loop-qa-let-an-ai-agent-run-your-test-sheet-on-android-iphone-web-and-desktop-and-2dp0</guid>
      <description>&lt;p&gt;Most "AI testing" tools aim for the same promise: the AI runs everything and tells you it passed.&lt;br&gt;
When something ships broken anyway, "the AI said it passed" is not an answer anyone accepts. A person is accountable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/meta-taro/git-qa" rel="noopener noreferrer"&gt;git-qa&lt;/a&gt; is an open-source &lt;strong&gt;human-in-the-loop QA runner&lt;/strong&gt; built around that fact.&lt;br&gt;
An AI agent works through your test sheet; you watch the live screen and sign off each case with one key.&lt;br&gt;
The evidence records who verified what.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpoe53329up9xgyyj2hzh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpoe53329up9xgyyj2hzh.png" alt="git-qa running a test sheet" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reads a test sheet&lt;/strong&gt; — a TSV you can write in any spreadsheet: step, expected result&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drives the target&lt;/strong&gt; — Android devices and emulators (adb), web pages (Chrome, Edge, Firefox, Safari),
desktop apps on macOS and Windows, and iPhone / iPad (USB mirroring; tapping through WebDriverAgent)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shows you the live screen&lt;/strong&gt; while it works, with a marker where it found the expected text&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lets you place the verdict with one key&lt;/strong&gt; — &lt;code&gt;D&lt;/code&gt; pass, &lt;code&gt;F&lt;/code&gt; fail&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keeps the evidence&lt;/strong&gt; — &lt;code&gt;run.json&lt;/code&gt; plus the screen at the moment of the verdict, per case&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The vocabulary is the point
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VERIFIED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A person put their name on it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AUTO_PASS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The AI passed it. Nobody looked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FAIL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;It failed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BLOCKED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Could not run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SKIP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Not run&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A plain &lt;code&gt;PASS&lt;/code&gt; would merge the first two. That merge is exactly what git-qa exists to prevent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Letting Claude Code test on a real device
&lt;/h2&gt;

&lt;p&gt;git-qa ships an MCP server. An agent such as Claude Code can take screenshots, read the screen,&lt;br&gt;
list what can be pressed and tap it — on Android, web, desktop or iPhone.&lt;br&gt;
You can run a sheet unattended overnight (&lt;code&gt;--no-ui&lt;/code&gt;) and review only the failures in the morning.&lt;br&gt;
&lt;strong&gt;The pass is still yours to give.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It also refuses sheets that verify nothing: if the expected text was already on screen before the step,&lt;br&gt;
the case is stopped as &lt;code&gt;BLOCKED&lt;/code&gt; with a note on how to fix the sheet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Download from the &lt;a href="https://meta-taro.github.io/git-qa/en/" rel="noopener noreferrer"&gt;distribution page&lt;/a&gt; (macOS signed and notarized; Windows x64 / ARM64, unsigned) and install Node 22+&lt;/li&gt;
&lt;li&gt;Enter a handle, pick a device / URL / desktop app, pick a test sheet&lt;/li&gt;
&lt;li&gt;Start, watch, press &lt;code&gt;D&lt;/code&gt; / &lt;code&gt;F&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It is beta and MIT-licensed. Bug reports and requests: &lt;a href="https://github.com/meta-taro/git-qa/issues" rel="noopener noreferrer"&gt;Issues&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>opensource</category>
      <category>testing</category>
    </item>
    <item>
      <title>Let Claude Code draw your architecture diagram — and keep the box you moved</title>
      <dc:creator>metataro</dc:creator>
      <pubDate>Tue, 13 Oct 2026 05:14:27 +0000</pubDate>
      <link>https://dev.to/metataro/let-claude-code-draw-your-architecture-diagram-and-keep-the-box-you-moved-1n9b</link>
      <guid>https://dev.to/metataro/let-claude-code-draw-your-architecture-diagram-and-keep-the-box-you-moved-1n9b</guid>
      <description>&lt;p&gt;Asking an AI to draw an architecture diagram works fine — the first time.&lt;/p&gt;

&lt;p&gt;The trouble starts with the second one. The architecture changes, you ask the AI to update the diagram, and the layout you spent ten minutes tidying is gone. So you stop asking. A month later the diagram is fiction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;zumen&lt;/strong&gt; is an open-source diagram editor built around that second drawing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffo5h0j65athkzqikiuhg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffo5h0j65athkzqikiuhg.gif" alt="The AI draws; a person moves one box; the AI is asked again and adds a node; the moved box stays put" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The one idea: the AI may not write where you wrote
&lt;/h2&gt;

&lt;p&gt;A zumen diagram is one YAML file. The AI writes &lt;code&gt;nodes:&lt;/code&gt; and &lt;code&gt;edges:&lt;/code&gt;. When &lt;em&gt;you&lt;/em&gt; drag a box, the position goes into a separate &lt;code&gt;pins:&lt;/code&gt; block — and &lt;strong&gt;the AI is not allowed to write &lt;code&gt;pins:&lt;/code&gt;&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;nodes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Order API&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Order DB&lt;/span&gt;
&lt;span class="na"&gt;edges&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api&lt;/span&gt;
    &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
&lt;span class="na"&gt;pins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;40&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;170&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;   &lt;span class="c1"&gt;# you put it here; regeneration cannot move it&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Regenerate as often as you like: the AI rewrites the nodes and edges, your pins stay. Across 10 real AI round trips, every human edit survived; a pin survives regeneration in all 389 bundled example drawings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set it up in Claude Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add zumen &lt;span class="nt"&gt;--&lt;/span&gt; npx &lt;span class="nt"&gt;-y&lt;/span&gt; @metataro/zumen-mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other MCP clients: run &lt;code&gt;npx -y @metataro/zumen-mcp&lt;/code&gt; as a stdio server.&lt;/p&gt;

&lt;p&gt;The agent gets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;zumen_spec&lt;/code&gt; — the format, read first&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;zumen_examples&lt;/code&gt; — 389 worked examples to learn the notation from&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;zumen_propose&lt;/code&gt; — a &lt;strong&gt;proposal&lt;/strong&gt;, shown to you as a diff. There is no tool that writes the file directly; a person presses apply&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;zumen_inspect&lt;/code&gt; — checks for crossings, overlapping boxes, labels that did not fit, lines of different colours running on top of each other&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;zumen_export&lt;/code&gt; — SVG, PNG, Mermaid, draw.io&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj1lno06z9guvt9u53h9c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj1lno06z9guvt9u53h9c.png" alt="The desktop app showing an AI proposal as a diff, with Apply and Cancel" width="800" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Show what changed, to people who will never read YAML
&lt;/h2&gt;

&lt;p&gt;From a clone of the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm compare before.zumen.yaml after.zumen.yaml out.svg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before and after, side by side: new nodes marked, removed ones dotted, and the nodes a person placed marked on both sides — so a reviewer can see at a glance that the hand edits survived.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not only boxes and arrows
&lt;/h2&gt;

&lt;p&gt;The same small set of primitives draws things most diagram tools never attempt: stage lighting plots in USITT RP-2 notation, pallet loading patterns in isometric (checked against the Japanese Industrial Standards), floor plans with door swings, periodontal charts, transit maps with parallel lines that don't hide each other. All 389 examples are in the &lt;a href="https://meta-taro.github.io/zumen/" rel="noopener noreferrer"&gt;gallery&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it is not
&lt;/h2&gt;

&lt;p&gt;Not a Figma replacement, not a general drawing program, not a web app or real-time collaboration tool. Editing features exist only as far as a person needs them to approve what the AI changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/meta-taro/zumen" rel="noopener noreferrer"&gt;https://github.com/meta-taro/zumen&lt;/a&gt; (MIT)&lt;/li&gt;
&lt;li&gt;Gallery and docs: &lt;a href="https://meta-taro.github.io/zumen/" rel="noopener noreferrer"&gt;https://meta-taro.github.io/zumen/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm: &lt;code&gt;@metataro/zumen&lt;/code&gt; (library: YAML → SVG), &lt;code&gt;@metataro/zumen-mcp&lt;/code&gt; (MCP server)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>mcp</category>
      <category>claudecode</category>
      <category>diagrams</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
