basanos
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@basanosaudit the event handlers in the component I just wrote"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Basanos
English · Português
The dead-button detector. Does this control call a handler that actually exists — and does something?
Βάσανος (basanos) was the touchstone the ancient Greeks rubbed gold against to tell real from fake. This one rubs your UI controls against your handlers: for every onClick, onSubmit, onClicked, it resolves the target and tells you whether that target is WIRED (exists, has a real body), DEAD (called but doesn't exist — a broken button), or STUB (exists but does nothing).
It's static, deterministic, and $0 — it reads the code, it doesn't run your app. No LLM. No network. No hallucination.
Built for the loop, not the post-mortem
A dead button is rare in finished code — QA catches it. But it's common in the half-built UI an agent is writing right now: it drops the <button> before the handler exists, or forgets to wire it. Basanos isn't a linter for shipped apps — it's the check an agent runs while it builds the screen, on the diff it just wrote, so it fixes the dead control before it says "the UI is done."
Primary use — the agent's self-check (MCP). Run Basanos as an MCP server inside your coding agent's loop: after writing a component the agent asks "is every control I just added wired to something real?" and can't declare the UI finished while a control is DEAD. (Also a CLI, pre-commit hook, and CI gate.)
Who it helps most: weaker, cheaper, autonomous models. A frontier model usually remembers to wire its handlers. Basanos catches the moments a model thinks the button works but left it dead — widest on cheap models running long, on their own, with nobody clicking through the UI. It's deterministic and free, so it's viable on every step. On a top model it's a seatbelt; on a cheap autonomous one it's what stops shipped-but-dead controls.
One of three, standalone or an eye for Platão. Basanos is its own product — the touchstone for UI wiring. Its siblings: Platão ("did you actually finish?" — completeness & placebo detection) and Socrates ("do your tests catch bugs, and is your API proven?"). Install Basanos alongside Platão and Platão's
ui_wiredquestion delegates to Basanos automatically.
Related MCP server: UIZZE
30 seconds
Requires Node.js 20+ and git. Basanos is not on the npm registry yet, so install it from GitHub (it builds itself on install):
npm install -g github:Owxessus/basanos
basanos audit src/This is the real output on the seeded example in this repo (examples/agent/demo/Cart.tsx):
$ basanos audit examples/agent/demo
Basanos - examples/agent/demo/Cart.tsx
[x] DEAD examples/agent/demo/Cart.tsx:13 onClick={checkout} — handler 'checkout' does not resolve to a real function (dead control)
[!] STUB examples/agent/demo/Cart.tsx:15 onClick={() => {}} — handler has an empty body (the control does nothing)
1 dead . 1 stubA clean tree prints OK Basanos: every control resolves to a real handler. The exit code is what CI and hooks read:
Exit | Meaning |
| nothing at or above |
| at least one finding at or above |
| usage error: unknown flag value, or a path that doesn't exist |
--json prints the findings as a JSON array (handler, target, verdict, severity, file, line, message); --fail-on high|medium|low|never sets the threshold (DEAD is high, STUB and PLACEBO are medium, an unparseable file is low).
git clone https://github.com/Owxessus/basanos && cd basanos
npm ci # also builds dist/
npm test # the proof suite
node dist/cli.js audit examples/agent/demoA dead button is the most common "looks done, does nothing" defect in a UI. Basanos checks every control in one deterministic pass, and where it cannot resolve a handler it says UNKNOWN instead of guessing (see Known limits below).
Why this exists
Every developer has shipped a button that does nothing. An agent generating UI does it constantly — it writes onClick={handleSave} and forgets to write handleSave, or writes it as an empty arrow, or wires two different buttons to the same action. The app compiles. The button renders. Nothing happens when you click it, and you find out from a user.
The concept is framework-agnostic: control → handler → does the target exist and do real work? Basanos implements it for the frameworks where dead handlers hurt most.
Supported today: React / JSX / TSX (.js, .jsx, .tsx), Vue SFCs (.vue, <script setup> and Options-API methods), Svelte (.svelte, on:click and Svelte-5 onclick), and Qt/QML (.qml — empty and do-nothing interaction handlers). Cross-file handler resolution (follow the import, check the real function in the other file) applies to React, Vue and Svelte.
What it reports
For every event handler it finds:
Verdict | Meaning |
WIRED | The handler resolves to a function that exists and has a real body. |
DEAD | The handler is called but the target does not exist — a broken control. |
STUB | The target exists but its body is empty ( |
PLACEBO | Two distinct controls fire the same concrete action ( |
UNKNOWN | The receiver isn't a resolvable local/imported symbol — reported honestly rather than claimed "wired." We never assert what we can't prove. |
It also flags placebo wiring: two distinct controls firing the same action with the same argument — often a copy-paste where "Approve" and "Discard" both call dispatch('open').
Everything is deterministic and static — it parses source, resolves handlers by scope and imports, and classifies bodies by AST. It never launches your app, so it's safe to run on every commit and in CI.
Usage modes
Mode | Command / setup |
CLI |
|
Pre-commit hook |
|
CI gate | GitHub Action: |
MCP server |
|
As a Platão eye | Install both; Platão calls Basanos for |
CI — the action builds Basanos from the ref you pin, so it needs nothing from npm:
# .github/workflows/ui-wiring.yml
on: [push, pull_request]
jobs:
basanos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: Owxessus/basanos@main # pin a tag or commit for reproducible runs
with:
paths: src # space-separated files/directories (default ".")
fail-on: high # high | medium | low | neverMCP — register the stdio server with your client. In Claude Code: claude mcp add basanos -- basanos mcp; in any client that reads an mcpServers config:
{ "mcpServers": { "basanos": { "command": "basanos", "args": ["mcp"] } } }The tool basanos_audit takes path (file or directory) and optional failOn, and returns { ok, failOn, summary, findings }. It reads the same .basanos.json as the CLI, from the server's working directory.
There's a complete, runnable UI-auditing agent in examples/agent/: a Claude Code project that wires Basanos as an MCP server and gives an agent one rule — build the UI, then audit it, then fix, and only then say "done". It ships with a seeded-broken component so you watch the tool fire on the first run.
Cost
$0. Basanos is purely deterministic — AST + static resolution, no LLM anywhere. There is no judgment layer and no API bill. That's the whole design: wiring is a structural fact, not a matter of opinion.
Configuration
Basanos auto-detects the framework per file and needs no config to run. Optionally, a .basanos.json in the directory you run it from (your repo root, typically) extends the audited handler set and ignores files by glob:
{
"handlers": ["onSwipe"],
"ignore": ["**/*.stories.tsx", "**/*.test.tsx"]
}handlers adds JSX attributes to the React set (onClick, onSubmit, onChange, onInput, onKeyDown, onKeyUp, onMouseDown, onMouseUp, onDoubleClick, onFocus, onBlur, onToggle, onDrop, onDragStart); Vue, Svelte and QML use their own fixed event lists. node_modules, .git, dist, build, .next, coverage and .turbo are always skipped.
Known limits — it is static, so it says UNKNOWN (silent) rather than guess: member handlers (onClick={this.save}, onClick={store.save}), handlers built by calls, path aliases (@/…) and packages are not followed; in Nuxt / unplugin-auto-import projects an unresolved Vue name is UNKNOWN, not DEAD. A bare browser global used as a handler (onClick={alert}) is reported DEAD.
Contributing — the same rigid gate
See CONTRIBUTING.md. Every framework adapter or classifier must ship with:
Deterministic — no LLM, no network.
prove_effect— a fixture with a DEAD/STUB control, and an assertion Basanos catches it.negative_control— a fixture where every control is WIRED, and an assertion Basanos stays silent.Declared severity.
CI runs both proofs on every PR. No proof, no merge. A wiring auditor that mis-reports wiring is worse than none.
Proven, not asserted
Anti-placebo is a rule this project holds itself to. Every check ships with a prove_effect + negative_control pair — it must catch the real defect and stay silent on the honest twin, or it doesn't ship (CI enforces it). And CI runs the built CLI on the seeded example and requires it to catch the dead button there.
It was then tuned by running it over a range of open-source front-ends. Where it flagged a real dead control, good; where it flagged a wired one, that pattern became a fix with a regression test. False positives still happen — when you hit one, an issue with the snippet is the most useful contribution there is.
Where this came from
Basanos was extracted from Athena, a personal AI agent project, where it checks the interface the agent builds for itself: "is this button wired to something real?" is asked before a screen is called done. It is released on its own because dead controls show up wherever agents write UI. It has no dependency on the rest of Athena.
License
MIT, plus the CONTRIBUTING.md proof gate.
This server cannot be deployed
Maintenance
Related MCP Connectors
Change-impact intelligence for tracing dependencies, stale references and required updates.
Live React design-system APIs, patterns, and code validation so AI agents build real UI, not slop.
Codebase intelligence for AI agents — dead code, blast radius, ownership.
Statically audits MCP tool surfaces for token cost, schema quality, and design issues.
Related MCP Servers
AlicenseAqualityAmaintenanceEnables agents to validate UI designs for missing states, accessibility issues, and journey completeness using state machine modeling.171MIT- AlicenseNot gradedqualityAmaintenanceSTOP UI SLOP. Gives coding agents searchable evidence from 800,000+ real web and iOS screens, design contracts, and a hard UI finish gate.1MIT
- AlicenseAqualityAmaintenanceProvides read-only MCP tools for discovering UI libraries, recommending minimal compatible UI stacks, and auditing plans to enforce deterministic UI policy and quality gates for coding agents.7112 npm1MIT
- FlicenseNot gradedqualityBmaintenanceEnables whole-program callgraph reachability analysis to locate dead functions and stale flags, and integrates with MCP-compliant clients for automated code review, breaking-change audits, and CI/CD workflow pruning.8-