Coding agents
@vispero/cora-mcp is an MCP server for coding agents. It gives an agent a browser, through playwright-mcp’s tools, and CORA’s tools to scan the page that browser shows. Its scans use the rules and config of the project’s cora scan. An agent can open a page, sign in, open a menu or fill a form, scan the result, change the code, and scan again.
cora-mcp scans the app you run while the agent works. It never starts the server the config’s server names: only cora scan does. It is never installed in a project. Agents start it with npx, which keeps it in npm’s cache.
CORA’s plugin
Section titled “CORA’s plugin”CORA’s plugin for Claude Code and Codex starts cora-mcp for you, and adds:
- a fixer agent, which runs CORA’s scans and fixes apart from your main conversation;
- skills, such as
/cora:fix, which has the fixer carry out a request; - hooks, which ask you before an edit that changes what CORA reports, and start the fixer after UI edits;
- in Claude Code, a status line that shows the fixer’s progress.
Installing the plugin sets it up, and Using CORA in an agent shows what to ask it.
The skills
Section titled “The skills”The skills are instructions an agent loads when its task calls for them. You invoke three: cora:fix, cora:pause and cora:resume. The fixer and the main agent load the rest. The skills lists each, with who uses it.
Requirements
Section titled “Requirements”- Linux, macOS or Windows.
- Node.js 22.18.0 or later, with npm, whose
npxstarts the server. On an older release, cora-mcp stops:node.unsupported-versionsays why, and how to install a later one. - Google Chrome, which cora-mcp’s browser runs. Without it, run
npx playwright install chrome. - An MCP client, such as Claude Code or Codex. CORA’s plugin needs Claude Code 2.1.295 or Codex 0.145.0 or later. Before each release, it is checked on the newest Claude Code and Codex releases.
- On Windows, Git for Windows, for the plugin’s hooks in Claude Code.
- A CORA account: see Your account.
cora-mcp brings its own Playwright, so the project needs none for it.
Installing the plugin
Section titled “Installing the plugin”npx cora init sets the plugin up for the coding agents the project uses, and prints the commands that finish the install. This section shows each change, so you can make it by hand where cora init cannot.
Both agents install the plugin from CORA’s plugin marketplace, vispero, in the GitHub repository Vispero/cora-plugins. You need read access to it, and, where git asks for one, a credential git stores. Each release of CORA publishes the plugin there at its own version, once npm serves that release.
Claude Code and Codex clone the marketplace with git, and never ask for a password. Where git needs a credential to reach it, sign in to GitHub with GitHub CLI, then let git use that sign-in:
gh auth logingh auth setup-gitEach coding agent then reaches the marketplace through the credential git stores.
cora init never writes an account token into an agent’s settings: see Your account.
In Claude Code
Section titled “In Claude Code”cora init adds the marketplace, set to update itself, and enables the plugin in the project’s .claude/settings.json, which everyone who works on the project shares. It adds each key only where the file lacks it. To make the change by hand, add these keys:
{ "extraKnownMarketplaces": { "vispero": { "source": { "source": "github", "repo": "Vispero/cora-plugins" }, "autoUpdate": true } }, "enabledPlugins": { "cora@vispero": true }}Then install the plugin for the project, as cora init’s next steps show:
claude plugin install cora --marketplace Vispero/cora-plugins --scope projectThe command adds the marketplace to your own settings where it is missing, and records the install, so every session in the project loads the plugin, interactive or with claude -p. Each person who works on the project runs it once, in their copy of it. Claude Code rewrites .claude/settings.json in its own layout as it records the install. The settings it holds stay the same, so a formatter can put the project’s layout back.
Trusting the project without the install command is not enough: Claude Code loads the plugin from the project’s settings only in the session that first adds the marketplace, and records no install. Later sessions start without CORA’s server and skills, and /plugin’s Errors tab shows Plugin "cora" not cached. Run the install command to fix it.
With autoUpdate, Claude Code refreshes the marketplace after it starts, and updates the plugin in the background. A running session keeps the version it loaded until you run /reload-plugins.
In Codex
Section titled “In Codex”Add the marketplace, then the plugin:
codex plugin marketplace add Vispero/cora-pluginscodex plugin add cora@visperoTrust CORA’s hooks when Codex asks. Codex keeps that trust in your own Codex config, for each hook while its command stays the same, so a release that changes a hook asks again. From Codex 0.164.0, the trust holds only while the plugin’s files stay the same, so each release of the plugin asks again.
Codex upgrades a marketplace added from a Git repository each time it starts, and the plugin with it.
Codex’s plugins carry no agents. So in a project that uses Codex, cora init sets CORA’s fixer agent for Codex, a11y_fixer, up in the project’s .codex/. Where the project has @vispero/cora installed, it declares the agent in .codex/config.toml, which Codex reads once you trust the project. The declaration names the agent file in the project’s installed @vispero/cora, by a path relative to the config, so the fixer moves with each upgrade:
[agents.a11y_fixer]config_file = "../node_modules/@vispero/cora/dist/harness/.codex/agents/a11y-fixer.toml"cora initappends the declaration to your config. It names the@vispero/coraNode finds installed from the project’s folder up, so in a workspace whose package manager installs packages at its root, the path leads there. It leaves the config as it is where it already declares an agent of that name at a file that is there, or where the project’s.codex/agents/holds one.- Before
@vispero/corais installed in a project with apackage.json, and outside Plug’n’Play,cora initsets no fixer up, since where the install puts it is the package manager’s to decide. It prints the step that does once cora is installed:npx cora init --agent codex, oryarn cora init --agent codexin a Yarn project. - A declaration of the fixer whose
config_filenames a file that is not there, which Codex ignores, gets itsconfig_filereplaced by the nextcora init, with the rest of the config as it is. A declaration at a file that is there is yours, andcora initleaves it as it is, saying why where Codex does not load it as the fixer. - Where the project’s
@vispero/corais a release too old to hold the agent file,cora initsays to upgrade it. - To add it by hand, add those two lines, with the path from
.codex/to the agent file in the@vispero/corayour project installed. Commit the config, so everyone who works on the project has the fixer. - A project without a
package.jsonruns CORA throughnpx, with nonode_modulesto point into, and Yarn’s Plug’n’Play keeps@vispero/corain a zip archive, which Codex cannot read. There,cora initwrites a copy of the agent file to.codex/agents/a11y-fixer.toml. A latercora initreplaces a copy nobody has changed with the one its release holds. Once you edit the copy,cora initleaves it as it is, and it says why where it leaves a file at that path that Codex does not load as the fixer. The copy follows an upgrade of cora only through a latercora init, so runcora initagain after you upgrade cora. - To give every project the fixer, keep the agent file in the
agents/of your Codex home:~/.codex, unlessCODEX_HOMEmoves it.
Without the fixer, the plugin’s hooks leave CORA’s tools to whichever agent calls them, ask for no fixer, and say so once a session (Scans run in the fixer).
Codex asks you before the plugin’s scan_url, and before each browser_* tool that acts on the page: Approving tools says how to have it run them without asking.
The project agent
Section titled “The project agent”In Claude Code, the plugin’s cora-mcp gives every agent of a session CORA’s tools, and their definitions load into your main conversation. CORA’s hooks turn back a call of them from any agent but the fixer. For full isolation, set the fixer up as a project agent, so that only the fixer has CORA’s tools:
npx cora init --project-agentIt sets the plugin up, as Installing the plugin describes, and:
- writes
.claude/agents/cora-a11y-fixer.md, a project agent Claude Code namescora-a11y-fixer. It is the plugin’s fixer, with an MCP server of its own, which Claude Code starts when the agent starts and stops when it finishes. - turns the plugin’s cora-mcp off for the project, and keeps the main agent from dispatching the plugin’s fixer. It adds each setting only where
.claude/settings.jsonlacks it.
To make the settings change by hand, add these keys:
{ "deniedMcpServers": [{ "serverName": "plugin:cora:cora" }], "permissions": { "deny": ["Agent(cora:a11y-fixer)"] }}Deny the plugin’s server by the name Claude Code gives it, plugin:cora:cora: a bare cora also turns off the project agent’s server. Commit the agent file and the settings, so everyone who works on the project has them.
- The agent’s skills come from the plugin, so keep the plugin installed. Without it, the agent edits nothing, and says that CORA’s plugin is not enabled.
- The plugin’s hooks, skills and status line still run. The hooks tell the main agent to dispatch
cora-a11y-fixer. - Claude Code starts the agent’s server only in a folder you trusted. Elsewhere, the agent says CORA’s MCP server is not connected.
- Claude Code names the server’s tools
mcp__cora__<tool>, rather than the plugin’smcp__plugin_cora_cora__<tool>. A run that allows tools by name, such asclaude -p --allowedTools, allowsmcp__cora__*. - Each dispatch starts its own cora-mcp, with its own browser. The server takes the environment Claude Code runs in, so
CORA_TOKEN, a proxy orNODE_EXTRA_CA_CERTSyou set there reaches it. - The file starts the cora-mcp release of the
@vispero/corathat wrote it, which then runs at the project’s version (Version alignment). - A later
npx cora initreplaces a file nobody has changed with the one its release holds, so run it after you upgrade@vispero/cora. Once you edit the file, it is yours, andcora initleaves it as it is. - Where a file at the agent’s path is not CORA’s project agent, such as one whose frontmatter names another agent,
cora initleaves the file as it is, turns nothing off, and says why. - To go back to the plugin’s fixer, delete the file and the two settings.
Adding cora-mcp to an agent
Section titled “Adding cora-mcp to an agent”Without the plugin, add cora-mcp to your agent as an MCP entry named cora. Setting up cora-mcp gives each agent’s command, the version to give, and the settings a Codex entry needs.
Your account
Section titled “Your account”cora-mcp checks your CORA account on each tool call, as the Needs an account line of cora mcp says. Without one, it still starts and lists its tools, and a call answers with account.missing. What needs an account lists the other errors a call can answer with.
Sign in once on the machine, in a terminal:
npx @vispero/cora auth loginThe package’s full name keeps npx from fetching an unrelated npm package named cora, where the project has no @vispero/cora installed. cora-mcp reads the credential cora auth login saves in your per-user config directory, as cora does. The agent’s next call finds it, without a restart, in every project.
Where no one can sign in, such as a cloud agent, give the agent’s environment a CI token in CORA_TOKEN. The plugin’s entry passes it on to cora-mcp. An entry of your own passes it on like this:
{ "mcpServers": { "cora": { "type": "stdio", "command": "npx", "args": [ "--yes", "--prefer-offline=false", "--fetch-retries=0", "--fetch-timeout=3000", "@vispero/cora-mcp@<version>" ], "env": { "CORA_TOKEN": "${CORA_TOKEN:-}" } } }}[mcp_servers.cora]command = "npx"args = [ "--yes", "--prefer-offline=false", "--fetch-retries=0", "--fetch-timeout=3000", "@vispero/cora-mcp@<version>"]env_vars = ["CORA_TOKEN"]cora-mcp gives an agent the browser_* tools, playwright-mcp’s core tools for navigating, clicking, typing and filling forms, tabs, dialogs, snapshots, screenshots, console messages and network requests, and tools of its own, from scan_page and scan_url to summarize_fixes, which the MCP tools reference lists. cora-mcp withholds browser_run_code_unsafe, which would run any code the agent writes in cora-mcp’s own process, outside the browser, and browser_tabs’ isolatedContext option, which would open a tab in a browser context of its own that CORA’s scans do not read. The tool sequence gives the order an agent calls them in.