Skip to main content

← Crash log Run 012

How to Cut QA Busywork with Rules Files, Agent Skills and MCP Servers

A rules file for house style, skills for routine workflows, an MCP server for Jira: copy-paste setup for Claude Code and Cursor, with writes on a leash.

Run 012 6 zones 7 samples (md 4, json 2, sh 1) 2 diagrams 1 compare

The real gains I get from an AI coding agent in QA work aren't in the clever bits. They're in the menial crap around the tests: pasting a ticket's acceptance criteria into a prompt, explaining again that we don't use waitForTimeout, naming the branch, writing the PR description in the team format. Three pieces of configuration take most of that off the table: a rules file, a few skills, and an MCP server for the issue tracker. Below are copy-pasteable files for a TypeScript + Playwright suite (plus a short pytest version), checked against the Claude Code, Cursor and Atlassian docs in October 2026.

Rules, skills and reach do different jobs

They're easy to conflate:

  • A rules file is house style. The agent reads it at the start of every session: how tests are written here, what's banned, which commands to run.
  • A skill extends the agent's reasoning for one task. It's a named workflow that tells the agent how to do something, step by step.
  • An MCP server extends the agent's reach. It's a connector that gives the agent API access to an external service, like Jira or Confluence.

Skills are the "how." MCP servers are the "what it can touch." The useful combinations use both: a shipping skill that calls the issue-tracker MCP server to pull a ticket's summary, then uses it to name the branch and seed the commit message.

How (context) Reach AGENTS.md house style, every run Scoped rules load for matching files Skills named workflows Coding agent Claude Code or Cursor tool calls MCP server OAuth, your perms Read ticket text Confluence pages ask before every write Write create issue transition, comment edit page The left side changes what the agent knows. The right side changes what it can do.
Rules and skills only shape the agent's context. The MCP server is where it touches shared systems, so the write path goes through a permission prompt and the read path doesn't.

Write one rules file and point every tool at it

The file I care about most is AGENTS.md in the repo root, because both tools read it:

Tool Project instructions Rules for some files
Claude Code CLAUDE.md, or AGENTS.md when there's no CLAUDE.md .claude/rules/*.md with paths:
Cursor AGENTS.md, plain markdown with no metadata .cursor/rules/*.mdc with globs:

Claude Code reads AGENTS.md directly only when there's no CLAUDE.md in or above the working directory (v2.1.277 or later). If you already have a CLAUDE.md, put @AGENTS.md at the top of it to import the shared file, and keep any Claude-only notes below.

A starting point for a Playwright suite (the paths are illustrative):

AGENTS.md
# Test suite rules
Stack: TypeScript, Playwright Test. Run `npx playwright test` before saying a change works.
## Writing tests
Locate elements with `getByRole`, `getByLabel` or `getByTestId`. No CSS or XPath tied to DOM structure.
Use web-first assertions: `await expect(locator).toBeVisible()`, never `expect(await locator.isVisible()).toBe(true)`.
No `page.waitForTimeout()`. Wait for the state you need with an assertion.
Each test creates its own data through fixtures or the API. No test relies on another test's leftovers.
Mock third-party services at the network boundary with `page.route()`. Don't mock our own modules.
Page objects live in `tests/pages/` and come from the fixtures in `tests/fixtures.ts`.
## Never
Never print, log or commit secrets, tokens or `.env` values.
Never point a test at production. Base URLs come from `playwright.config.ts`.
Never use real card numbers or customer data. Use the factories in `tests/data/`.
Never add a retry or a longer timeout to make a flaky test pass. Report it instead.

The locator, assertion, isolation and page.route() rules come straight from Playwright's best practices. The "Never" list is mine, and on a payments codebase it's the part that matters.

If you also run a Python suite, say pytest against a GraphQL API backed by Neo4j, the same file takes a short section:

AGENTS.md
## pytest suite (api-tests/)
Setup and teardown go in pytest fixtures in `conftest.py`, not `setUp`/`tearDown` methods.
No `time.sleep()`. Poll for the condition with a timeout.
Mock HTTP at the client boundary, not our own functions.
Each test cleans up the graph data it creates. Never run against a shared database.

For rules that only apply to some files, both tools support scoping. In Cursor a project rule must use the .mdc extension (a plain .md file in .cursor/rules is ignored):

.cursor/rules/playwright-specs.mdc
--
globs: tests/**/*.spec.ts
alwaysApply: false
--
One `test.describe` per user journey.
Tag smoke tests with `@smoke` in the title.

The Claude Code equivalent is .claude/rules/playwright-specs.md, with a paths: list of quoted globs in place of globs:. For a bigger worked example, see this site's own AGENTS.md: working rules, content rules and a repo map.

One caveat from the Claude Code docs: these files are context, not enforced configuration. To actually block an action you need permission rules or a sandbox, which is what How to Sandbox Your AI Coding Agent with a Dev Container covers.

Package the routine workflows as skills

A good prompt helps you once. A skill captures it as a workflow anyone can invoke without remembering the details. In Claude Code a skill is a folder with a SKILL.md in it: .claude/skills/<name>/ in the repo (commit it to share), or ~/.claude/skills/<name>/ for just you. You run it as /<name>, and Claude can also load it on its own when a request matches the description. Custom slash commands have been merged into skills, so an old .claude/commands/ship.md file still works.

The one I use most is shipping a change. Done ad hoc, every engineer prompts differently and you get different branch names, commit styles and PR descriptions. As a skill it runs the same steps every time:

.claude/skills/commit-and-mr/SKILL.md
--
name: commit-and-mr
description: Branch, commit, push and open a merge request for the current change using team conventions.
disable-model-invocation: true
allowed-tools: Bash(git diff *)
--
1. Use the ticket key from $ARGUMENTS, or ask for one. Fetch the ticket summary.
2. Run `git status` and `git diff` to see what's staged and unstaged.
3. Create a branch named `<type>/<TICKET-KEY>-<short-summary>`.
4. Commit with `<TICKET-KEY>: <imperative summary>`.
5. Push to origin.
6. Open the merge request with three sections: What, Files Changed, How to Test.
7. Report the link and a one-line summary.

disable-model-invocation: true means only a person can start it, which is what I want for anything that pushes. allowed-tools lets the read-only git diff run without a prompt while the skill is active. Nobody has to remember the branch-naming rule, because the skill is the rule.

1 Invoke /commit-and-mr 2 Load SKILL.md text 3 Research git diff, ticket 4 Execute branch, push, MR 5 Report link + summary read only: allow rules, no prompt impact zone: ask read only Only stage 4 changes anything other people can see. Keep the prompts there.
Every skill run has the same shape. Auto-approve the read-only stages and leave the permission prompt on the one stage that writes.

Other QA chores with a "correct" shape that's tedious to re-explain: cutting a hotfix (cherry-pick a merged PR's commits onto a release branch), scaffolding a spec from a ticket's acceptance criteria, and writing tests for the current diff in house style. A wiki page relies on people remembering it under deadline pressure. Write the convention down once, as a skill, and it stops being something you enforce.

Give the agent reach with an MCP server

An agent is good at code but blind to everything around it: the ticket, the design doc, the wiki page. An MCP server fixes that. For Jira and Confluence, Atlassian hosts one, the Rovo MCP Server, at https://mcp.atlassian.com/v2/mcp. You sign in with OAuth 2.1, and it acts with your existing Atlassian permissions and no more. It reads, and it can also create, edit, transition and comment on Jira work items and create or update Confluence pages.

In Claude Code:

claude mcp add --transport http --scope project atlassian https://mcp.atlassian.com/v2/mcp
# then, inside a session, run /mcp and follow the browser login

--scope project writes .mcp.json in the repo root so the team shares one definition. The default local scope keeps it private to you in ~/.claude.json, and user makes it live in every project. Cursor reads the same idea from .cursor/mcp.json:

.cursor/mcp.json
{
"mcpServers": {
"atlassian": {
"url": "https://mcp.atlassian.com/v2/mcp"
}
}
}

Now "look up ABC-1234 and start a branch for it" works without copy-paste, and the commit-and-mr skill can fetch the ticket summary itself.

Keep write access on a leash

Reach cuts both ways. A connector that can read your issue tracker is low risk. A connector that can write is acting on shared systems your teammates rely on, with your name on every change. The MCP spec makes authorization optional, uses OAuth 2.1 for HTTP servers, and has local stdio servers take credentials from the environment, so check which kind you're installing.

Wire it like this
  • Project scope, so it's only live in repos that need it
  • OAuth sign-in, scoped and revocable
  • Reads allowed, writes behind a prompt (the rules below)
  • Read the ticket, not the attachments with customer data
Not like this
  • User scope, live in every directory you open
  • A long-lived API token pasted into a config file
  • Every tool auto-approved so the prompts go away
  • One connector shared between a test site and production

In Claude Code, a bare "ask": ["mcp__atlassian"] matches every tool on that server, so it prompts on reads too. Tool-name globs scope the rules instead:

.claude/settings.json
{
"permissions": {
"allow": [
"mcp__atlassian__get*",
"mcp__atlassian__list*",
"mcp__atlassian__search*"
],
"ask": [
"mcp__atlassian__create*",
"mcp__atlassian__edit*",
"mcp__atlassian__update*",
"mcp__atlassian__transition*",
"mcp__atlassian__addOrEdit*"
]
}
}

Ask rules are checked before allow rules, and only an allow rule skips the prompt, so a tool on neither list (a delete, an attachment download) still asks. Cursor asks for approval before MCP tools by default. Either way the agent's output still needs review, and the AI code smells checklist is what I run over its diffs.

Tip

Put each convention where the agent reads it every run: house style in AGENTS.md, routine workflows in skills, and ticket access behind an MCP server that asks before it writes.

Why it matters for your team

On a team shipping payment code, this setup lets the agent read the ticket and follow the house rules while it stays away from customer data, PCI scope and production credentials.

The enforcement side is in How to Sandbox Your AI Coding Agent with a Dev Container. For the page objects the rules file refers to, see Vanilla Playwright with Page Object Model.