Hi! I’m Oleg, a QA Engineering Manager focused on test strategy, automation, and AI-assisted QA workflows.

I lead one of the testing stacks at a large grocery retail company. We've been working through a series of experiments on where AI actually saves time in QA — not where it looks impressive in a demo, but where it removes hours of mechanical work that nobody wants to do.

An earlier piece covered AI review of manual test cases. This one is about a less typical use of Cursor: migrating test cases from a task tracker into Allure TestOps with essentially one command.

Two bits of context for readers outside our stack:

  • Yandex Trackeris an issue tracker, roughly analogous to Jira. Its issue descriptions support wiki-style tables, which matters here.
  • Allure TestOpsis a test management system (TMS) that connects manual test cases with automated tests, CI/CD runs, and reporting.

If you use Jira and TestRail, or Linear and Xray, the specific tools change but the pattern in this article doesn't.

The problem: test cases living in the wrong place

Historically, one large team kept all of its test cases and checklists inside the issue tracker. Each issue had a "Testing" section containing a table, and that table was the team's actual test documentation.

Then the inevitable happened — we needed all of it in Allure TestOps, because:

  • it's the correct place for it (a single TMS instead of test cases scattered across issues);
  • it gives us analytics, traceability, and artifacts;
  • it links properly to automated tests and CI/CD;
  • and the rest of the company already lives in TestOps.

The catch was volume. Years of accumulated cases and checklists. Migrating them by hand meant a large amount of pure copy-paste work for QA engineers, which is exactly the kind of task you don't want to hand to people if you can avoid it.

So the plan was to migrate everything quickly, with no manual copy-paste, using an AI agent.

The approach: Cursor + two MCP servers + two prompts

The plan looked suspiciously simple:

  • Take Cursor.
  • Connect the Yandex Tracker MCP server.
  • Connect the Allure TestOps MCP server (ours, built in-house).
  • Write a project rule that:

  • takes the "Testing" table from the issue description,

  • converts it into a "title + steps" structure,
  • collects links to the source issue and its related issues.

Run it as a single command.

For anyone new to it: MCP (Model Context Protocol) is a standard way to expose external systems as tools an AI agent can call. Instead of the agent guessing, it gets real API access to the tracker and to TestOps.

What the source data looked like

Inside each tracker issue, the test cases lived in a table under a "Testing" section, structured roughly like this:

  • an "Acceptance criterion" column,
  • a "Test" column,
  • other columns (outcome, result, comments, and so on) that exist but are irrelevant to the migration.

We agreed on a simple mapping:

  • Acceptance criterion → test case title in TestOps
  • Test → scenario steps in TestOps

Everything else gets dropped.

One wrinkle worth flagging early, because it's the kind of thing that quietly corrupts a migration: the tables contain visually merged cells. When several test rows belong to one acceptance criterion, only the first row has the criterion filled in and the rest are blank. A naive parser produces a pile of test cases with empty titles.

The project rule: parsing the tracker and producing JSON

The first artifact is a project rule describing exactly how to parse the table and what JSON to return. I called the file get-check-list-rule. Here it is, translated:

You are a QA Automation / test analyst assistant. Your task: given a tracker issue key {ISSUE_KEY}, extract the "Testing" section (a table) from its description and prepare structured data for migration into Allure TestOps. Input {ISSUE_KEY} — the issue key (for example, LKP-3260). What to do Fetch the issue via the issue_get tool with include_description=true. Fetch related issues via issue_get_links. In the description field, find the "Testing" section block. It starts with a line reading "Testing" and contains a table where the table opens with #|, followed by rows starting with || and the column headers, and closes with |#. Parse the table and extract all test rows: Relevant columns: "Acceptance criterion", "Test". All other columns and their values (for example "Acceptance outcome", "Result", "Comment") must be completely ignored and carried nowhere — not into titles, not into steps, not into links. Account for merged cells: If the "Acceptance criterion" column is empty in a row, inherit the last non-empty value above it. The same technique applies to other columns if needed, though in practice only the criterion requires it. Group results by acceptance criterion. For each criterion, build: title: the criterion text, which becomes the test case title in TestOps, without leading numbering. If the criterion starts with 1., 1), 1\. or similar, strip it. Normalize the same way as steps: plain text, no markdown or HTML, no redundant whitespace or blank lines. If the criterion spans several lines, join them into one with spaces. steps: the list of steps from the "Test" column for every row belonging to that criterion. Normalize steps: Remove redundant whitespace and repeated blank lines. Steps must arrive in Allure TestOps as plain text — no HTML tags, no markdown. Converting "Test" into plain text: Strip or unwrap markdown formatting: bold, italics, # headings, > quotes, inline code and backticks, fenced code blocks — keep the text only. Convert markdown links [text](url) into text (url), or just url when there is no text. Convert lists (-, *, numbered) into multi-line plain text, preserving order but dropping the markers and numbers. Important: if the "Test" column contains fragments that actually belong to other columns (PASS/FAIL/BLOCK markers, checkboxes, comments, references like screen/log from the "Comment" column) — do not migrate them. Migrate only what genuinely describes scenario steps. Strip explicit numbering from the start of step lines where present (1. ..., 2) ..., a) ...) — steps[] must contain unnumbered steps. Important: do not add a line like "Source: {ISSUE_KEY}" into the steps or description. Issue links belong in the Links (external links) section in Allure TestOps, not inline in the description. Important, on related issues: In the TestOps Links section, add a link to the source issue {ISSUE_KEY}, plus links to all issues related to it from issue_get_links. Order matters: {ISSUE_KEY} always comes first (the source issue the tests came from), followed by the related issues (their object.key), with no duplicates and without repeating {ISSUE_KEY}. Link naming rule, so the source is always first in the Links list: For the source issue {ISSUE_KEY}, use the link name Source: {ISSUE_KEY}. For related issues, use a link name equal to their key (for example LKP-4106) with no prefix. Return the result strictly as JSON in this shape, with no surrounding text: json { "issueKey": "{ISSUE_KEY}", "linkIssueToTestOps": { "field": "Links", "keys": ["{ISSUE_KEY}", "<LINKED_ISSUE_KEY_1>", "<LINKED_ISSUE_KEY_2>"] }, "testops": [ { "name": "<Acceptance criterion>", "steps": [ "<Step 1 from the Test column, unnumbered, plain text>", "<Step 2 from the Test column, unnumbered, plain text>" ] } ] } Quality checks testops[] must contain every criterion from the "Testing" table, not just the first one. If there is no "Testing" table, return JSON with an empty testops array and an error field explaining why. If there are several "Testing" tables (for example, one per environment), extract each and add an environment field (stage, test, and so on) when it can be determined from the surrounding text. A note on the literal strings: in our tracker the section and column headings are in Russian, so the rule matches on those exact names. If you adapt this, swap in whatever your own tables actually say. Two details in that rule carry most of the weight. The first is the merged-cell inheritance, which is the difference between a clean migration and a pile of untitled cases. The second is the instruction to return an error field rather than improvise — without it, an agent that can't find a table will happily invent a plausible one, and you won't notice until a QA engineer opens TestOps. The command: /move, one issue or a batch The second artifact is the command, which I called move. It: accepts one or more ISSUE_KEY values (links to the issues to migrate from), pulls each issue from the tracker, generates JSON according to get-check-list-rule, creates the test cases in TestOps and populates the Links section. The command prompt, translated: Migrate test cases from the tracker into Allure TestOps, strictly following the project rule @cursor/rules/get-check-list-rule.mdc. Input I will give you one of: a link to a tracker issue (or several links) issue key(s) (for example LKP-3260, ABC-123) plus attached screenshots, if wording or context needs clarifying What to do Support migrating several issues at once. Example invocation: /move LKP-3260 LKP-3261 LKP-3262 Extract the list of ISSUE_KEY values from the text (support both bare keys and URLs), remove duplicates, preserve order of appearance. For each ISSUE_KEY: fetch the issue through the Yandex Tracker MCP (issue_get, include_description=true) generate JSON strictly according to @.cursor/rules/get-check-list-rule.mdc (including issue_get_links, title/step normalization, and building linkIssueToTestOps.keys) push the result into Allure TestOps through the user-allure MCP: create test cases from testops[], then add the issue links from linkIssueToTestOps into the Links section, using a link_name of the form Source: <ISSUE_KEY> for the source issue If the Allure MCP is unavailable or errors out for some issue — do not invent anything. Add an error field to that issue's JSON and carry on processing the rest. Output (strict) If one issue was passed — a single JSON object per get-check-list-rule.mdc, with no surrounding text. If several issues were passed — a JSON array where each element follows the same format. If there were problems with the Allure MCP for an element, add an error field to that element with a short description. Otherwise, omit it.
A note on the literal strings: in our tracker the section and column headings are in Russian, so the rule matches on those exact names. If you adapt this, swap in whatever your own tables actually say.

Two details in that rule carry most of the weight. The first is the merged-cell inheritance, which is the difference between a clean migration and a pile of untitled cases. The second is the instruction to return an error field rather than improvise — without it, an agent that can't find a table will happily invent a plausible one, and you won't notice until a QA engineer opens TestOps.

The command: /move, one issue or a batch

The second artifact is the command, which I called move. It:

  • accepts one or more ISSUE_KEY values (links to the issues to migrate from),
  • pulls each issue from the tracker,
  • generates JSON according to get-check-list-rule,
  • creates the test cases in TestOps and populates the Links section.

The command prompt, translated:

Migrate test cases from the tracker into Allure TestOps, strictly following the project rule @cursor/rules/get-check-list-rule.mdc. Input I will give you one of: a link to a tracker issue (or several links) issue key(s) (for example LKP-3260, ABC-123) plus attached screenshots, if wording or context needs clarifying What to do Support migrating several issues at once. Example invocation: /move LKP-3260 LKP-3261 LKP-3262 Extract the list of ISSUE_KEY values from the text (support both bare keys and URLs), remove duplicates, preserve order of appearance. For each ISSUE_KEY: fetch the issue through the Yandex Tracker MCP (issue_get, include_description=true) generate JSON strictly according to @.cursor/rules/get-check-list-rule.mdc (including issue_get_links, title/step normalization, and building linkIssueToTestOps.keys) push the result into Allure TestOps through the user-allure MCP: create test cases from testops[], then add the issue links from linkIssueToTestOps into the Links section, using a link_name of the form Source: <ISSUE_KEY> for the source issue If the Allure MCP is unavailable or errors out for some issue — do not invent anything. Add an error field to that issue's JSON and carry on processing the rest. Output (strict) If one issue was passed — a single JSON object per get-check-list-rule.mdc, with no surrounding text. If several issues were passed — a JSON array where each element follows the same format. If there were problems with the Allure MCP for an element, add an error field to that element with a short description. Otherwise, omit it.
Point 4 is the one I'd copy into any similar workflow. Partial failure is the normal case in batch migrations, and an agent that stops on the first error is nearly as unhelpful as one that hallucinates through it.

MCP configuration

For any of this to run, both MCP servers need to be connected: Allure TestOps (ours) and Yandex Tracker.

Example config for the tracker MCP:

"yandex-tracker": {``"command": "uvx",``"args": [``"yandex-tracker-mcp@latest"``],``"env": {``"TRACKER_TOKEN": "your token",``"TRACKER_ORG_ID": "your org id"``}``}

What this looks like for a QA engineer

Exactly one thing: run the command with the issue keys.

/move LKP-3260

or, as a batch:

/move LKP-3260 LKP-3261 LKP-3262

LKP-* are the issues to migrate test cases from, and a project number identifies the TestOps project to migrate them into.

That's the whole interface. No spreadsheet, no intermediate export, no clipboard.

  • The result
  • After a run, the migration lands as expected: test cases appear in TestOps, with titles and steps; in the Links section, the source issue comes first, labeled Source: , followed by all related issues.

That last part turned out to matter more than we expected. Test cases that carry a link back to the issue they came from stay traceable — six months later you can still see why a case exists, which is usually the first thing lost in a bulk migration.

Where else this pattern fits

The specific tools here are incidental. The shape of the problem — structured content trapped in the wrong system, in a format that's regular enough to parse but irregular enough to defeat a simple script — is extremely common.

The same setup would move requirements documentation out of Jira issues into Confluence with a Jira MCP and a Confluence MCP, or the reverse. Anywhere you have two systems with API access and a mechanical transformation between them, two prompts and an agent will beat a weekend of copy-paste.

We also could have written a prompt that iterated across every issue in the tracker and migrated everything automatically, saving QA even more time. We decided we didn't need all of it migrated yet — which is its own small lesson. The value came from removing a specific painful backlog, not from automating the maximum possible surface area.

Let’s Work Together

I’m open to QA leadership opportunities and collaborations with teams looking to improve their testing strategy, build scalable test automation, introduce AI-assisted QA workflows, and establish meaningful quality metrics.

If this sounds relevant to your team, feel free to connect with me on LinkedIn.