Skip to content

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 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 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.

  • Linux, macOS or Windows.
  • Node.js 22.18.0 or later, with npm, whose npx starts the server. On an older release, cora-mcp stops: node.unsupported-version says 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.

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:

Terminal window
gh auth login
gh auth setup-git

Each 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.

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:

.claude/settings.json
{
"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:

Terminal window
claude plugin install cora --marketplace Vispero/cora-plugins --scope project

The 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.

Add the marketplace, then the plugin:

Terminal window
codex plugin marketplace add Vispero/cora-plugins
codex plugin add cora@vispero

Trust 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:

.codex/config.toml
[agents.a11y_fixer]
config_file = "../node_modules/@vispero/cora/dist/harness/.codex/agents/a11y-fixer.toml"
  • cora init appends the declaration to your config. It names the @vispero/cora Node 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/cora is installed in a project with a package.json, and outside Plug’n’Play, cora init sets 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, or yarn cora init --agent codex in a Yarn project.
  • A declaration of the fixer whose config_file names a file that is not there, which Codex ignores, gets its config_file replaced by the next cora init, with the rest of the config as it is. A declaration at a file that is there is yours, and cora init leaves it as it is, saying why where Codex does not load it as the fixer.
  • Where the project’s @vispero/cora is a release too old to hold the agent file, cora init says to upgrade it.
  • To add it by hand, add those two lines, with the path from .codex/ to the agent file in the @vispero/cora your project installed. Commit the config, so everyone who works on the project has the fixer.
  • A project without a package.json runs CORA through npx, with no node_modules to point into, and Yarn’s Plug’n’Play keeps @vispero/cora in a zip archive, which Codex cannot read. There, cora init writes a copy of the agent file to .codex/agents/a11y-fixer.toml. A later cora init replaces a copy nobody has changed with the one its release holds. Once you edit the copy, cora init leaves 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 later cora init, so run cora init again after you upgrade cora.
  • To give every project the fixer, keep the agent file in the agents/ of your Codex home: ~/.codex, unless CODEX_HOME moves 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.

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:

Terminal window
npx cora init --project-agent

It sets the plugin up, as Installing the plugin describes, and:

  • writes .claude/agents/cora-a11y-fixer.md, a project agent Claude Code names cora-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.json lacks it.

To make the settings change by hand, add these keys:

.claude/settings.json
{
"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’s mcp__plugin_cora_cora__<tool>. A run that allows tools by name, such as claude -p --allowedTools, allows mcp__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 or NODE_EXTRA_CA_CERTS you set there reaches it.
  • The file starts the cora-mcp release of the @vispero/cora that wrote it, which then runs at the project’s version (Version alignment).
  • A later npx cora init replaces 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, and cora init leaves 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 init leaves 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.

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.

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:

Terminal window
npx @vispero/cora auth login

The 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:

.mcp.json
{
"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:-}" }
}
}
}

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.