# Referencia de herramientas MCP

> Cada herramienta y operación que responde el servidor MCP de Probara, generada desde el registro de herramientas.

Esta página se **genera** desde `apps/worker/src/mcp/registry.ts`, `apps/worker/src/mcp/operations.ts` y `apps/worker/src/mcp/tools/index.ts`. A continuación se listan todas las herramientas y operaciones que responde el servidor MCP de Probara, con su nombre exacto, descripción, campos de entrada y anotaciones. Las herramientas predeterminadas se listan para todos los clientes. Cada otra operación se ejecuta con la herramienta de ejecución que indica su línea de Listado, con su nombre como `operation` y sus datos como `arguments`. Las cinco herramientas que buscan y ejecutan operaciones son también predeterminadas: `search_operations` busca una operación por palabra clave, `describe_operation` lee su esquema de entrada, y `run_read_operation`, `run_write_operation` y `run_delete_operation` la ejecutan según su riesgo. Nunca puede desincronizarse del servidor: regenérala con `pnpm mcp-tools:generate` cada vez que se agregue, elimine o renombre una herramienta u operación.

La lista predeterminada tiene hasta 14 herramientas, menos para una credencial con permisos más acotados.

### `get_project_context`

Call this to read one project in one call: its suite tree (depth and parentUlid per suite), custom fields, configuration groups with their values, milestones, environments and members. Requires project_id, the project code such as ACME. Each section shows its first items with a total, and a cut section says which tool reads the rest. Pick it when a task starts in a project; use the per-resource list tools for one section in full.

**Listado**: herramienta predeterminada, listada para todos los clientes.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio

### `query`

Call this to run one PQL query and read one page of its rows, or of its groups when it aggregates. Requires query (1 to 4,000 characters); offset (0 to 10,000) pages through the result, and the SELECT clause names the columns. Call query_help for the fields, operators and examples of each entity. PQL (Probara Query Language) reads the organization’s test data: a query names one entity, filters it, and can group and aggregate it. For example, FROM results WHERE status = failed lists the failed results you can open. Syntax and examples: https://docs.probara.net/en/developers/api/pql/. Needs a plan that includes dashboards.

**Listado**: herramienta predeterminada, listada para todos los clientes.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `query` — obligatorio
- `offset` — opcional

### `query_help`

Call this to read what a PQL query can name: each entity’s fields with their types and enum values, the custom fields that apply to it, the operators, and worked examples to run unchanged. Takes no arguments. Pick it for the fields and operators; get_pql_schema lists the organization’s own values. Syntax and examples: https://docs.probara.net/en/developers/api/pql/. Needs a plan that includes dashboards.

**Listado**: herramienta predeterminada, listada para todos los clientes.

**Anotaciones**: solo lectura, idempotente

**Parámetros**: Esta herramienta no recibe parámetros.

### `get`

Call this to read one entity by display ID or ULID: a test case, suite, run, milestone, defect, test plan, shared step, test case review or project. A display ID names its kind; a bare ULID needs kind. Bare display IDs (R-n, M-n, D-n, PLAN-n, SS-n, RV-n) and shared step and review ULIDs need project_id. Returns displayId, ulid and kind first, then the entity’s fields: concise keeps the main ones, detailed keeps all, and fields names the ones to keep. Pick it for one entity of any kind; use the list tools for a set and analyze_run for why a run failed.

**Listado**: herramienta predeterminada, listada para todos los clientes.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `id` — obligatorio
- `kind` — opcional
- `project_id` — opcional
- `response_format` — opcional
- `fields` — opcional

### `analyze_run`

Call this to explain why a run failed, in one bounded result however large the run is. The failing cases (failed or blocked) are grouped by the defect linked to them, and the cases no defect explains by suite. Each group counts its cases and shows the first examples, with each case’s display ID, attempts, and flakyInRun, which is true when a pass and a failure both appear among the attempts of this run. Requires run_ulid, or the run display ID with project_id. Pick it before list_run_cases or list_results when the question is why a run failed.

**Listado**: herramienta predeterminada, listada para todos los clientes.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `run_ulid` — obligatorio
- `project_id` — opcional

### `upsert_test_cases`

Call this to create or update up to 50 test cases in one call. Requires project_id and cases; each item needs a displayId (SHOP-45) or an automationKey to match an existing case, and a title to create a case that matches none. An item may send parameters and excludedCombinations (a parameterized case expands into one run case per combination). Fields an item sends replace the current values, omitted fields stay, and steps replace the whole list when they differ. Returns \{ items, summary \}: one outcome per item in request order (created, updated, unchanged, or failed with its reason, and the case it names when one exists). Repeating a batch answers unchanged. Under mandatory review an item is not written and fails with the review request it opened.

**Listado**: herramienta predeterminada, listada para todos los clientes.

**Anotaciones**: escritura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `cases` — obligatorio

### `start_run`

Call this to start a test run in a project from exactly one source: caseUlids (cases by ULID or display ID), suiteUlid (every active case of a suite), planUlid (a test plan's selection) or pql (a PQL query over cases). Requires project_id and name. Up to 5000 cases. A parameterized case expands into one run case per combination of its parameters, minus its excluded combinations. Optionally description, environment or environmentId, milestoneId, configurations or configurationUlids (the run's configuration combination), defaultAssigneeUlid, and caseAssignees (\{ caseUlid, assigneeUlid \}, each case one the run takes). Returns \{ displayId, ulid, name, cases, assigned \}. The run, its cases and its assignments are created in one request, so a refused request creates no run.

**Listado**: herramienta predeterminada, listada para todos los clientes.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `name` — obligatorio
- `description` — opcional
- `environmentId` — opcional
- `environment` — opcional
- `milestoneId` — opcional
- `defaultAssigneeUlid` — opcional
- `configurationUlids` — opcional
- `configurations` — opcional
- `caseUlids` — opcional
- `suiteUlid` — opcional
- `planUlid` — opcional
- `pql` — opcional
- `caseAssignees` — opcional

### `record_results`

Call this to record results for test cases of a run in one call. Requires run_ulid and items; each item needs case (a test case display ID such as SHOP-45, or its run case ULID) and status (passed, failed, skipped or blocked). A case with several instances in the run (parameterized cases) takes its parameters, the exact combination of one instance, or its run case ULID; without either the item fails and names the instances. Optionally notes, totalDurationMs, steps (\{ position, status \}, with the positions get_run_case shows, marked before the case result), and attachments (up to 5 evidence files, base64, at most 32 MiB each). Each item appends one result, so a repeated batch records again. Up to 50 items. Returns \{ items, summary \}: one outcome per item in request order, recorded with its resultUlid, or failed with its reason.

**Listado**: herramienta predeterminada, listada para todos los clientes.

**Anotaciones**: escritura

**Parámetros**

- `run_ulid` — obligatorio
- `items` — obligatorio
- `project_id` — opcional

### `file_defect`

Call this to file a defect from a run result: it creates the defect, links the result, and links each test case. Requires project_id, title, resultUlid, and testCaseUlids (cases by ULID or display ID). Optionally description, github (\{ connectionUlid, repository, title, body, labels \}), and jira (the fields create_defect_jira_issue takes), which also open the external issue of the defect. The steps are separate calls: if a later step fails, the defect exists and the error names it and the step to finish. Returns \{ displayId, ulid, title, resultUlid, testCaseCount, githubIssue?, jiraIssue? \}.

**Listado**: herramienta predeterminada, listada para todos los clientes.

**Anotaciones**: escritura, sistemas externos

**Parámetros**

- `project_id` — obligatorio
- `title` — obligatorio
- `description` — opcional
- `resultUlid` — obligatorio
- `testCaseUlids` — obligatorio
- `github` — opcional
- `jira` — opcional

### `create_project`

Call this to create a new project in the caller organization. Requires name (string) and id (an uppercase project code starting with a letter, e.g. "ACME"). Requires the project management permission, so only an owner or admin role may create a project — a member or viewer token receives 403 forbidden. Returns the created project, including its ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `name` — obligatorio
- `id` — obligatorio
- `description` — opcional
- `idempotency_key` — opcional

### `list_projects`

Call this to list projects in the caller organization with offset pagination. Accepts page (1-based) and pageSize, plus optional q/include/filters. Returns items plus page, pageSize, and total so you can page through the full collection by incrementing page. By default a call returns at most 50 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `page` — opcional
- `pageSize` — opcional
- `q` — opcional
- `include` — opcional
- `memberUlid` — opcional
- `runs` — opcional
- `defects` — opcional
- `milestones` — opcional
- `cases` — opcional
- `status` — opcional

### `check_project_id_available`

Call this to check whether a candidate project id is free before creating a project. Requires id (the candidate uppercase project code). Returns \{ available: boolean \}.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `id` — obligatorio

### `get_project`

Call this to fetch a single project within the caller organization. Requires project_id (the project's human-readable code, e.g. "ACME" — the same value returned as `id` by create_project).

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio

### `update_project`

Call this to update a project's name, id, description, or defaultStepFormat (classic or gherkin: the format a new case takes when it names none). Requires project_id and at least one of name/id/description/defaultStepFormat to change.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `name` — opcional
- `id` — opcional
- `description` — opcional
- `defaultStepFormat` — opcional
- `idempotency_key` — opcional

### `delete_project`

Call this to permanently delete a project. Requires project_id. Returns \{ deleted: true \} on success. The first call returns a preview and a confirm_token; the call executes only when it is repeated with that confirm_token.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `project_id` — obligatorio
- `idempotency_key` — opcional
- `confirm_token` — opcional

### `archive_project`

Call this to archive an active project. Requires project_id. Reversible: hides the project from list_projects by default; does not gate reads/writes or release a project seat.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `idempotency_key` — opcional

### `unarchive_project`

Call this to restore an archived project to active. Requires project_id.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `idempotency_key` — opcional

### `get_project_review_settings`

Call this to read a project's test case review settings. Requires project_id. Returns enabled, mandatory, allowSelfMerge, approvalsRequired, autoMerge, onlyApprovedInRuns and defaultReviewerUserIds (member ULIDs). Test case review is a Pro or Enterprise plan feature: on other plans this returns 403 forbidden.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio

### `update_project_review_settings`

Call this to change a project's test case review settings. Requires project_id and at least one of enabled, mandatory, allowSelfMerge, approvalsRequired (0 or more; 0 means no threshold), autoMerge (needs approvalsRequired of at least 1), onlyApprovedInRuns (only approved test cases can be added to runs while review is enabled) and defaultReviewerUserIds (member ULIDs with access to the project; an empty list clears them). Omitted settings are left unchanged. Requires an owner or admin role, or ownership of the project. Pro or Enterprise plan only: on other plans this returns 403 forbidden.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `enabled` — opcional
- `mandatory` — opcional
- `allowSelfMerge` — opcional
- `approvalsRequired` — opcional
- `autoMerge` — opcional
- `onlyApprovedInRuns` — opcional
- `defaultReviewerUserIds` — opcional
- `idempotency_key` — opcional

### `create_suite`

Call this to create a new suite inside a project. Requires project_id (the project code) and name; optionally description and parentUlid to nest under an existing suite in the same project. Returns the created suite, including its ulid and displayId.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `name` — obligatorio
- `description` — opcional
- `parentUlid` — opcional
- `idempotency_key` — opcional

### `list_suites`

Call this to list the root-level suites of a project with cursor pagination. Requires project_id. Accepts limit and cursor; feed the returned nextCursor back as cursor to fetch the next page — the field is absent once you have reached the last page. By default a call returns at most 50 items; pass limit to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `limit` — opcional
- `cursor` — opcional

### `get_suite_by_display_id`

Call this to fetch a suite by its human-readable display id (e.g. "ACME-S3"). Requires project_id and display_id.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `display_id` — obligatorio

### `list_suite_children`

Call this to list the direct child suites of a parent suite with cursor pagination. Requires project_id and suite_ulid (the parent suite's ulid). Accepts limit and cursor. By default a call returns at most 50 items; pass limit to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `suite_ulid` — obligatorio
- `limit` — opcional
- `cursor` — opcional

### `clone_suite`

Call this to deep-clone a suite (and by default its cases) into a destination project/suite. Requires project_id (the source suite's project) and suite_ulid (the source suite), plus destinationProjectCode and destinationSuiteUlid (null clones into the destination project root); optionally prefix and strategy ("casesAndSuites" default, or "suitesOnly" to clone structure only, no cases).

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `suite_ulid` — obligatorio
- `destinationProjectCode` — obligatorio
- `destinationSuiteUlid` — obligatorio
- `prefix` — opcional
- `strategy` — opcional
- `idempotency_key` — opcional

### `get_suite`

Call this to fetch a single suite by its ulid. Requires suite_ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `suite_ulid` — obligatorio

### `update_suite`

Call this to update a suite's name, description, or parent. Requires suite_ulid and at least one of name/description/parentUlid to change.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `suite_ulid` — obligatorio
- `name` — opcional
- `description` — opcional
- `parentUlid` — opcional
- `idempotency_key` — opcional

### `delete_suite`

Call this to permanently delete a suite. Requires suite_ulid. If the suite (or its descendants) still has test cases, caseDisposition ("deleteCases" or "moveToParent") is required. When the project requires review, a suite that still holds test cases that are not archived is refused (409 review_required); archive them through review requests first. Returns \{ deleted: true \} on success. The first call returns a preview and a confirm_token; the call executes only when it is repeated with that confirm_token.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `suite_ulid` — obligatorio
- `caseDisposition` — opcional
- `idempotency_key` — opcional
- `confirm_token` — opcional

### `get_suite_case_counts`

Call this to read the active/archived/total test case counts for a suite and its descendants. Requires suite_ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `suite_ulid` — obligatorio

### `get_test_case_steps`

Call this to read the ordered list of steps (and their committed attachments) for a test case. Requires case_ulid. Each row has a kind: "step" is an inline step (\{ ulid, position, keyword, action, data, expected, attachments \}, keyword the Gherkin keyword of a gherkin case step and null on a classic one); "shared" is a reference to a shared step, expanded live with the set's current content (\{ ulid, position, sharedStep: \{ ulid, displayId, title, scope, items, itemFromUlid, itemToUlid \} \}). A reference may cover only a range of the set: items holds only the items from itemFromUlid through itemToUlid (null = from the first / through the last item).

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `case_ulid` — obligatorio

### `replace_test_case_steps`

Call this to replace ALL steps of a test case with a new ordered list in one write. Requires case_ulid and steps: each row is either an inline step \{ ulid?, position, keyword?, action, data?, expected?, attachments? \} or a shared step reference \{ ulid?, position, sharedStepUlid, itemFromUlid?, itemToUlid? \} (a set of the case's project or an organization-wide one; it occupies one position; the optional item ULIDs of that set limit it to a range of its items, omitted = from the first / through the last). Keep a row's ulid to keep it; a list read with get_test_case_steps can be sent back unchanged. A gherkin case needs a keyword (Given, When, Then, And, But, or the asterisk keyword) on each inline step and sends no expected; a classic case sends no keyword. Include a previously staged attachment reference (ulid + position) in an inline step to commit it. Optional expectedRevision (the case's currentRevision you last read) refuses the write with 409 conflict when the case changed since. Returns \{ steps, currentRevision \}: the full replacement list and the case's new revision.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `case_ulid` — obligatorio
- `steps` — obligatorio
- `expectedRevision` — opcional
- `idempotency_key` — opcional

### `create_test_case`

Call this to create a new test case inside a project. Requires project_id (the project code) and title; optionally description, suiteUlid, milestoneUlid, tags, automationKey (the stable key CI tooling uses to link an automated test, unique per project), stepFormat (classic or gherkin; omitted, the project's default format), parameters (singles, groups and shared parameter references; a parameterized case expands into one run case per combination), excludedCombinations (the combinations no run creates), and an initial ordered steps array (inline steps and shared step references, as in replace_test_case_steps), and customFieldValues, an array of \{ fieldUlid, value \} the case is created with (for example the preconditions and postconditions system fields; find field ULIDs with list_custom_fields), so no follow-up update is needed. Returns the created test case, including its ulid and displayId. When the project requires review (Pro or Enterprise plan, review enabled and mandatory), nothing is written: the call opens a review request instead and returns \{ sentToReview: true, message, review \} with its displayId (RV-n); a case with an open request answers 409 conflict.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `title` — obligatorio
- `description` — opcional
- `descriptionDocument` — opcional
- `suiteUlid` — opcional
- `milestoneUlid` — opcional
- `tags` — opcional
- `automationKey` — opcional
- `stepFormat` — opcional
- `steps` — opcional
- `customFieldValues` — opcional
- `parameters` — opcional
- `excludedCombinations` — opcional
- `idempotency_key` — opcional

### `list_test_cases`

Call this to list the test cases of a project with cursor pagination. Requires project_id. Accepts limit and cursor (feed the returned nextCursor back as cursor for the next page), plus optional suiteUlid, archived ("true"/"false"), and milestoneUlid filters. By default a call returns at most 50 items; pass limit to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `limit` — opcional
- `cursor` — opcional
- `suiteUlid` — opcional
- `archived` — opcional
- `milestoneUlid` — opcional

### `search_test_cases`

Call this to search test cases (and optionally suites) within a project by a text term. Requires project_id and q. Accepts scope ("all" default, "cases", or "suites"), limit, and cf (repeatable — pass the same argument more than once for several "\<fieldUlid>:\<value[,value…]>" filters at once, every entry ANDed together), and reviewStatus (comma-separated not_reviewed, in_review, approved: only cases with one of these review statuses). Returns items and total. By default a call returns at most 50 items; pass limit to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `q` — opcional
- `scope` — opcional
- `cf` — opcional
- `reviewStatus` — opcional
- `limit` — opcional

### `get_test_case_by_display_id`

Call this to fetch a test case by its human-readable display id (e.g. "ACME-42"). Requires project_id and display_id.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `display_id` — obligatorio

### `clone_test_case`

Call this to clone a single test case into a destination project/suite. Requires project_id (the source case's project) and case_ulid (the source case), plus destinationProjectCode and destinationSuiteUlid (null clones into the destination project root); optionally prefix, applied to the cloned title.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `case_ulid` — obligatorio
- `destinationProjectCode` — obligatorio
- `destinationSuiteUlid` — obligatorio
- `prefix` — opcional
- `idempotency_key` — opcional

### `bulk_archive_test_cases`

Call this to archive several test cases in one project in a single call. Requires project_id and caseUlids (1-200 ulids). Returns \{ affected, unchanged \} counts. When the project requires review (Pro or Enterprise plan, review enabled and mandatory), nothing is archived: the call opens one archive review request per case and returns \{ sentToReview: true, message, created, skipped \} (skipped: cases with an open request or already archived).

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `caseUlids` — obligatorio
- `idempotency_key` — opcional

### `bulk_clone_test_cases`

Call this to clone several suites and/or loose test cases from one project into a destination project in a single call. Requires project_id, destinationProjectCode, destinationSuiteUlid (null for the destination root), strategy ("casesAndSuites" or "suitesOnly"), and at least one non-empty array of caseUlids/suiteUlids. Returns \{ destinationProjectCode, suitesCreated, casesCloned \}.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `caseUlids` — obligatorio
- `suiteUlids` — obligatorio
- `destinationProjectCode` — obligatorio
- `destinationSuiteUlid` — obligatorio
- `strategy` — obligatorio
- `idempotency_key` — opcional

### `get_test_case`

Call this to fetch a single test case by its ulid. Requires case_ulid. The case carries parameters (with shared references expanded), excludedCombinations, and combinationCount, the combinations its parameters produce before exclusions.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `case_ulid` — obligatorio

### `list_test_case_revisions`

Call this to list a test case's version history, newest first. Requires case_ulid. Accepts limit and cursor (feed the returned nextCursor back as cursor for the next page). Each item has revision, source (edit, import or shared_step), author, createdAt and isCurrent; the response also carries the case's currentRevision. A case that has not been saved since it was created has an empty history. Use get_test_case_revision to read one version. Version history needs a plan that includes it (Pro or Enterprise); on other plans this answers forbidden, while every save still records a revision. By default a call returns at most 50 items; pass limit to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `case_ulid` — obligatorio
- `limit` — opcional
- `cursor` — opcional

### `get_test_case_revision`

Call this to read one version of a test case as it was saved. Requires case_ulid and revision (a revision number from list_test_case_revisions). Returns the revision with its snapshot: title, description, suite, milestone, automationKey, tags, custom field values (option fields carry selectedOptions labeled from the current options; label is null for a deleted option) and steps with their images (deleted: true for an image deleted since, from the workspace Attachments screen). Needs a plan that includes version history (Pro or Enterprise); otherwise answers forbidden.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `case_ulid` — obligatorio
- `revision` — obligatorio

### `restore_test_case_revision`

Call this to bring a test case back to an earlier version. Requires case_ulid and revision (a revision number from list_test_case_revisions, not the current one: that answers 422 already_current). The restore is a new save, so it creates a new revision; optional expectedRevision (the case's currentRevision you last read) refuses it with 409 conflict when the case changed since. Shared steps that still exist come back as references showing their current content. Returns the case with its new currentRevision and restore.skipped, listing what could not come back (deleted suite, milestone, custom field or option, a deleted shared step restored as inline steps, or a deleted image). Needs a plan that includes version history (Pro or Enterprise); otherwise answers forbidden.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `case_ulid` — obligatorio
- `revision` — obligatorio
- `expectedRevision` — opcional
- `idempotency_key` — opcional

### `update_test_case`

Call this to update a test case in one consolidated write. Requires case_ulid and at least one of patch (title/description/suiteUlid/milestoneUlid/tags/automationKey/stepFormat/parameters/excludedCombinations; a stepFormat change needs the steps after the save to fit the new format), steps (full replacement list), or customFieldValues (the full set: a value you leave out is cleared, so read the case first, or change one value with patch_test_case_custom_field_value). Optional expectedRevision (the case's currentRevision you last read) refuses the save with 409 conflict when the case changed since; omit it to save regardless. Returns the case with its new currentRevision. When the project requires review (Pro or Enterprise plan, review enabled and mandatory), nothing is written: the call opens a review request instead and returns \{ sentToReview: true, message, review \} with its displayId (RV-n); a case with an open request answers 409 conflict.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `case_ulid` — obligatorio
- `patch` — opcional
- `steps` — opcional
- `customFieldValues` — opcional
- `expectedRevision` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional

### `delete_test_case`

Call this to permanently delete a test case. Requires case_ulid. Returns \{ deleted: true \} on success. When the project requires review (Pro or Enterprise plan, review enabled and mandatory), nothing is deleted: the call opens a review request instead and returns \{ sentToReview: true, message, review \} with its displayId (RV-n); a case with an open request answers 409 conflict. The first call returns a preview and a confirm_token; the call executes only when it is repeated with that confirm_token.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `case_ulid` — obligatorio
- `idempotency_key` — opcional
- `confirm_token` — opcional

### `archive_test_case`

Call this to archive an active test case. Requires case_ulid. When the project requires review (Pro or Enterprise plan, review enabled and mandatory), nothing is archived: the call opens a review request instead and returns \{ sentToReview: true, message, review \} with its displayId (RV-n); a case with an open request answers 409 conflict.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `case_ulid` — obligatorio
- `idempotency_key` — opcional

### `unarchive_test_case`

Call this to restore an archived test case to active. Requires case_ulid. Not sent to review: it applies even when the project requires review.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `case_ulid` — obligatorio
- `idempotency_key` — opcional

### `create_test_case_review`

Call this to send a test case change for review instead of saving it. Requires project_id and kind. kind "create" proposes a new case: pass case (the same fields as create_test_case). kind "update" proposes a change to an existing case: pass case_ulid and changes (the same fields as update_test_case, expectedRevision included). Optional reviewerUserIds (member ULIDs) replaces the project's default reviewers; the author is not a reviewer. Nothing in the repository changes until the request is merged. A case can have one open request at a time (409 conflict, details.reason review_already_open). The project has to have review enabled (409 review_not_enabled). Pro or Enterprise plan only: on other plans this returns 403 forbidden. Returns the request with its displayId (RV-n), reviewers and proposal.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `kind` — obligatorio
- `case` — opcional
- `case_ulid` — opcional
- `changes` — opcional
- `reviewerUserIds` — opcional
- `idempotency_key` — opcional

### `get_test_case_review`

Call this to read one test case review request. Requires project_id and review_ulid. Returns its displayId (RV-n), kind (create or update), status (open, merged or declined), the case and the revision an update was proposed on, the proposal, the author and each reviewer with their decision. Pro or Enterprise plan only: on other plans this returns 403 forbidden.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `review_ulid` — obligatorio

### `get_test_case_review_by_display_id`

Call this to read one test case review request by its readable id (RV-n), as shown in the app and in its URL. Requires project_id and display_id. Returns the same body as get_test_case_review, the request ULID included. Pro or Enterprise plan only: on other plans this returns 403 forbidden.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `display_id` — obligatorio

### `list_test_case_reviews`

Call this to list a project's test case review requests, newest first, with offset pagination. Requires project_id. Optional status (open by default; merged, declined or all), kind (create or update), author and reviewer (member ULIDs), assignedToMe (true: only requests you review), needsMyReview (true: only open requests waiting for your decision, an outdated decision included), page and pageSize. Each item carries its displayId (RV-n), status, the case, the reviewers with their decisions, approvalCount, approvalsRequired and conflict; read one with get_test_case_review for its proposal. Pro or Enterprise plan only: on other plans this returns 403 forbidden. By default a call returns at most 50 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `page` — opcional
- `pageSize` — opcional
- `status` — opcional
- `kind` — opcional
- `author` — opcional
- `reviewer` — opcional
- `assignedToMe` — opcional
- `needsMyReview` — opcional

### `get_test_case_review_versions`

Call this to compare what a review request proposes with the case as it is now. Requires project_id and review_ulid. Returns live (the case now; null for a create request) and proposed (the case as the proposal leaves it), both in the shape of a stored revision, plus notApplicable with the reason when the proposal no longer applies (for example a suite or custom field it names was deleted). Closed requests return neither version. Pro or Enterprise plan only: on other plans this returns 403 forbidden.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `review_ulid` — obligatorio

### `approve_test_case_review`

Call this to approve an open test case review request. Requires project_id and review_ulid; optional note (Markdown). Open to members whose role holds the test-cases.review permission, not to the author (403 forbidden); acting adds you as a reviewer. A decision counts only on the proposal version it was made on: editing the proposal makes earlier decisions outdated. When the project auto-merges and this approval reaches the required approvals with no open request for changes, the request is merged at once (status merged). A merged or declined request answers 409 conflict. Pro or Enterprise plan only: on other plans this returns 403 forbidden.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `review_ulid` — obligatorio
- `note` — opcional
- `idempotency_key` — opcional

### `unapprove_test_case_review`

Call this to withdraw your own approval of an open test case review request, for example one given by mistake: you go back to pending and the approval stops counting toward the required approvals. Requires project_id and review_ulid. Open to members whose role holds the test-cases.review permission. Without an approval of yours on the current proposal version it answers 409 conflict (not_approved); a merged or declined request answers 409 conflict. Pro or Enterprise plan only: on other plans this returns 403 forbidden.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `review_ulid` — obligatorio
- `idempotency_key` — opcional

### `request_changes_test_case_review`

Call this to ask for changes on an open test case review request (the author or a reviewer then edits the proposal with update_test_case_review). Requires project_id and review_ulid; optional note (Markdown). Open to members whose role holds the test-cases.review permission, not to the author (403 forbidden); acting adds you as a reviewer. A decision counts only on the proposal version it was made on: editing the proposal makes earlier decisions outdated. An open request for changes blocks auto-merge, not a manual merge. A merged or declined request answers 409 conflict. Pro or Enterprise plan only: on other plans this returns 403 forbidden.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `review_ulid` — obligatorio
- `note` — opcional
- `idempotency_key` — opcional

### `update_test_case_review`

Call this to replace an open review request's proposal. Requires project_id, review_ulid and kind, the same kind as the request: "create" with case (the fields of create_test_case), "update" with changes (the fields of update_test_case; expectedRevision is ignored). Open to the author and to members with the test-cases.review permission. The proposal version goes up by one and is rebased onto the case as it is now, which clears a conflict; approvals and requests for changes made before stop counting. A merged or declined request answers 409 conflict. Pro or Enterprise plan only: on other plans this returns 403 forbidden.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `review_ulid` — obligatorio
- `kind` — obligatorio
- `case` — opcional
- `changes` — opcional
- `idempotency_key` — opcional

### `set_test_case_review_reviewers`

Call this to replace an open review request's reviewers. Requires project_id, review_ulid and reviewerUserIds (the whole list of member ULIDs; [] removes every reviewer). A reviewer kept keeps their decision. Every new reviewer has to be a current member with access to the project and the test-cases.review permission, other than the author (422 otherwise). Open to the author and to members with test-cases.review. A merged or declined request answers 409 conflict. Pro or Enterprise plan only: on other plans this returns 403 forbidden.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `project_id` — obligatorio
- `review_ulid` — obligatorio
- `reviewerUserIds` — obligatorio
- `idempotency_key` — opcional

### `decline_test_case_review`

Call this to decline an open review request: it closes as declined, the case stays as it is and the proposal is discarded. Requires project_id and review_ulid; optional note (why, shown on the request as declineNote). Open to the author and to members with the test-cases.review permission. A merged or declined request answers 409 conflict. Pro or Enterprise plan only: on other plans this returns 403 forbidden.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `review_ulid` — obligatorio
- `note` — opcional
- `idempotency_key` — opcional

### `merge_test_case_review`

Call this to merge an open review request: its proposal is written to the repository (a new case for create, the change for update) and the case becomes approved. Requires project_id and review_ulid. Open to members with the test-cases.review permission, and to the author only when the project allows self-merge (403 forbidden otherwise). Needs the approvals the project requires on the current proposal version, the author not counting (409 conflict, approvals_required; no threshold set: no approvals needed). An update whose case changed after the proposal was made answers 409 conflict (review_conflict): edit the proposal with update_test_case_review to rebase it, then merge. A merged or declined request answers 409 conflict. Pro or Enterprise plan only: on other plans this returns 403 forbidden.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `review_ulid` — obligatorio
- `idempotency_key` — opcional

### `bulk_create_test_case_reviews`

Call this to send existing test cases for review as they are (no change proposed), one review request per case: reviewers approve the current version, and merging a request marks its case approved without writing a new revision (409 conflict, review_conflict, if the case changed meanwhile). Requires project_id and caseUlids (1-200). Optional reviewerUserIds (member ULIDs) replaces the project's default reviewers for every request. Returns created (the requests, currentVersionOnly true) and skipped (caseUlid with reason review_already_open, archived or not_found). The project has to have review enabled (409 review_not_enabled). Pro or Enterprise plan only: on other plans this returns 403 forbidden.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `caseUlids` — obligatorio
- `reviewerUserIds` — opcional
- `idempotency_key` — opcional

### `bulk_decide_test_case_reviews`

Call this to approve, request changes on, or decline several open review requests at once. Requires project_id, reviewUlids (1-50) and decision (approve, request_changes or decline). note is required for request_changes and decline (422 without it) and optional for approve; it is left on every request (the decision note, or declineNote). Each request goes through the single action: an approval can auto-merge, and a request the single action refuses (your own request, a closed or unknown one) is skipped. Returns done (reviewUlid, displayId, status after) and skipped (reviewUlid, httpStatus, reason, message). Pro or Enterprise plan only: on other plans this returns 403 forbidden.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `reviewUlids` — obligatorio
- `decision` — obligatorio
- `note` — opcional
- `idempotency_key` — opcional

### `create_run`

Call this to create a new test run inside a project. Requires project_id (the project code) and name. Requires caseUlids (1-5000) UNLESS planUlid or plan is supplied, in which case an omitted caseUlids seeds from the plan's current selection, OR automated is true, which allows creating the run with no cases so CI results can be reported into it later (automated does not remove cases: caseUlids or planUlid cases are still added; it is request-only, not stored; caseUlids, when present, still holds at least one entry). Optionally description, environmentId, environment (a name: matched by slug, created when none matches), defaultAssigneeUlid, caseAssignees, startedAt, milestoneId, customFieldValues (an array of \{ fieldUlid, value \} to seed the run's custom fields in the same call), planUlid (link the run to a test plan), and configurationUlids (tag the run with one configuration combination — at most one value per group, rejected otherwise). milestone, plan and configurations are the by-name forms of milestoneId, planUlid and configurationUlids: milestone takes M-\<n> or an exact milestone name, plan takes PLAN-\<n> or an exact plan name, configurations takes [\{ group, name \}]; an unknown one is rejected, and giving both forms of one reference (environmentId and environment included) is rejected. This ONE call creates ONE run: linking a plan and naming a combination does not fan out into several runs, even when the plan has many combinations — call it once per combination you actually want a run for. Optionally tags (catalog tag names, created when unknown). Optionally source (\{ provider, repository, branch, commit, commitAuthor, pullRequestNumber, workflow, ciRunId, ciRunAttempt, ciJob, buildUrl \}, each optional; provider 'github_actions' or 'other'; repository owner/name; buildUrl http(s)) records where a CI run came from; it cannot be changed later. A GitHub Actions source with repository and ciRunId is refused with a conflict when another run of the project already holds that CI run and attempt (details.runUlid names it). When the project lets only approved test cases into runs, caseUlids with a case that is not approved is refused with 422 (details.reason cases_not_approved, details.notApprovedCaseUlids lists them), and a plan selection keeps only its approved cases (notApprovedSkippedCount in the answer says how many were left out). Returns the created run, including its ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `name` — obligatorio
- `description` — opcional
- `descriptionDocument` — opcional
- `environmentId` — opcional
- `environment` — opcional
- `defaultAssigneeUlid` — opcional
- `caseUlids` — opcional
- `caseAssignees` — opcional
- `startedAt` — opcional
- `milestoneId` — opcional
- `milestone` — opcional
- `customFieldValues` — opcional
- `planUlid` — opcional
- `plan` — opcional
- `tags` — opcional
- `configurationUlids` — opcional
- `configurations` — opcional
- `source` — opcional
- `automated` — opcional
- `idempotency_key` — opcional

### `list_runs`

Call this to list runs in a project with offset pagination. Requires project_id. Accepts page (1-based), pageSize, q, status (comma-separated projected statuses), env (repeatable — pass the same argument more than once to filter by several environment names at once), authorUlid, assigneeUlid ("empty" for unassigned), milestoneUlid, and cf (repeatable — pass the same argument more than once for several "\<fieldUlid>:\<value[,value…]>" run custom-field filters at once, every entry ANDed together). Returns items plus page, pageSize, and total. By default a call returns at most 50 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `page` — opcional
- `pageSize` — opcional
- `q` — opcional
- `status` — opcional
- `env` — opcional
- `authorUlid` — opcional
- `assigneeUlid` — opcional
- `milestoneUlid` — opcional
- `cf` — opcional

### `list_run_environments`

Call this to list the distinct environment names used by runs in a project (no paging — the full set is returned). Requires project_id.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio

### `clone_run`

Call this to "run again": clone a run into a new run in the same project ("Run again"). Requires project_id, run_ulid (the source run), and title. Optionally cloneAssignees (carry each case's assignee into the new run) and statusFilter (an array of outcome statuses — omitted/empty clones every case). When the project lets only approved test cases into runs, only the approved cases are cloned and notApprovedSkippedCount says how many were left out. Returns the created run.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `run_ulid` — obligatorio
- `title` — obligatorio
- `cloneAssignees` — opcional
- `statusFilter` — opcional
- `idempotency_key` — opcional

### `get_run`

Call this to fetch a single run by its ulid. Requires run_ulid. The run carries source: null, or where a CI run came from — \{ provider, repository, branch, commit, commitAuthor, pullRequestNumber, workflow, ciRunId, ciRunAttempt, ciJob, buildUrl \}, null when unknown.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `run_ulid` — obligatorio
- `project_id` — opcional

### `update_run`

Call this to update a run's name, description, defaultAssigneeUlid, environmentId, milestoneId, or configurationUlids (replace-set: an array replaces the whole combination, [] clears it, omitted leaves it untouched). Requires run_ulid and at least one field to change. Does NOT accept customFieldValues — call set_run_custom_field_values to reconcile a run's custom-field values instead. A configuration-only body is still rejected on an aborted run — aborted runs are terminal-frozen and take no exception.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `run_ulid` — obligatorio
- `name` — opcional
- `description` — opcional
- `descriptionDocument` — opcional
- `defaultAssigneeUlid` — opcional
- `environmentId` — opcional
- `milestoneId` — opcional
- `tags` — opcional
- `configurationUlids` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional

### `reconcile_run_cases`

Call this to REPLACE the full set of cases on a run with the given desired selection — this is DESTRUCTIVE: any case currently on the run whose ulid is not in caseUlids is removed. An empty caseUlids array removes every case from the run. Requires run_ulid and caseUlids (0-5000); optionally caseAssignees. When the project lets only approved test cases into runs, adding a case that is not approved is refused with 422 (details.notApprovedCaseUlids lists them). The first call returns a preview and a confirm_token; the call executes only when it is repeated with that confirm_token.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `run_ulid` — obligatorio
- `caseUlids` — obligatorio
- `caseAssignees` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional
- `confirm_token` — opcional

### `close_run`

Call this to close an open run, finalizing its aggregate outcome. Requires run_ulid. Returns the closed run (state="closed", finishedAt set).

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `run_ulid` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `abort_run`

Call this to abort an open run before it finishes, recomputing its aggregate outcome from cases executed so far. Requires run_ulid. Returns the aborted run (abortedAt set).

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `run_ulid` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `delete_run`

Call this to permanently delete a run. Requires run_ulid. Only the owner or admin role may delete a run — any other role is rejected as forbidden. Returns \{ deleted: true \} on success. The first call returns a preview and a confirm_token; the call executes only when it is repeated with that confirm_token.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `run_ulid` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional
- `confirm_token` — opcional

### `set_run_custom_field_values`

Call this to reconcile a run's custom-field values to an EXACT desired set: values for fields not present in the input are cleared (or rejected if the field is required). Requires run_ulid and values (an array of \{ fieldUlid, value \}). Returns \{ customFieldValues \} with the full resulting set. There is no single-value update tool for runs — call this with the full desired set.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `run_ulid` — obligatorio
- `values` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `bulk_mark_run_cases`

Call this to mark up to 500 run cases with the SAME status in one batch. Requires run_ulid, caseUlids (1-500), and status (passed/failed/skipped/blocked). Rejects if the run is closed. Returns \{ affected, unchanged \}.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura, idempotente

**Parámetros**

- `run_ulid` — obligatorio
- `caseUlids` — obligatorio
- `status` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `bulk_assign_run_cases`

Call this to assign (or unassign, with assigneeUlid = null) up to 500 run cases in one batch. Requires run_ulid, caseUlids (1-500), and assigneeUlid. Rejects if the run is closed. Returns \{ affected, unchanged \}.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `run_ulid` — obligatorio
- `caseUlids` — obligatorio
- `assigneeUlid` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `bulk_retry_run_cases`

Call this to reset up to 500 marked run cases back to retested in one batch. Requires run_ulid and caseUlids (1-500). Rejects if the run is closed. Returns \{ affected, unchanged \}.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura, idempotente

**Parámetros**

- `run_ulid` — obligatorio
- `caseUlids` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `bulk_apply_latest_case_versions`

Call this to bring every run case whose test case was edited after the run snapshotted it to the latest version of its case, in one call. Requires run_ulid. Applies only to pending run cases (untested, or retested after a retry); completed run cases are left alone. A pending case whose current attempt already has step results, or that is locked to another assignee, is left unchanged and reported. Rejects if the run is closed. Returns \{ updated: [runCaseUlid], skipped: [\{ runCaseUlid, reason \}] \} with reason run_case_has_results, assignee_locked or combination_removed (the latest version no longer produces the run case combination).

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `run_ulid` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `bulk_remove_run_cases`

Call this to remove up to 500 cases from a run in one batch. Requires run_ulid and caseUlids (1-500). Rejects if the run is closed. Returns \{ affected, unchanged \}. The first call returns a preview and a confirm_token; the call executes only when it is repeated with that confirm_token.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `run_ulid` — obligatorio
- `caseUlids` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional
- `confirm_token` — opcional

### `bulk_submit_run_result`

Call this to atomically submit the SAME result to up to 500 run cases in one batch, optionally creating or attaching ONE defect linked to every affected case. Requires run_ulid, caseUlids (1-500), and status. Optionally totalDurationMs, notes, attachments, and defect (only valid when status is failed or blocked): defect.mode="create" carries a full new defect (title required) or defect.mode="attach" carries an existing defectUlid with an optional reopen flag. Rejects if the run is closed. Returns \{ affected, defect? \}.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `run_ulid` — obligatorio
- `caseUlids` — obligatorio
- `status` — obligatorio
- `totalDurationMs` — opcional
- `notes` — opcional
- `notesDocument` — opcional
- `attachments` — opcional
- `defect` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional

### `mark_run_case_step`

Call this to set a single step's status within a run case. Requires run_ulid, run_case_ulid, step_ulid, and status (passed/failed/skipped/blocked). This only changes the STEP row — the parent case's own status is untouched (call mark_run_case to mark the case itself). Returns \{ caseStatus \} — the case's current status, read before this write. Pass amendResult=true only to correct a step of an already-recorded attempt: it skips the project's step-to-case derivation so no result row is appended, and it is rejected with 422 unless the run case is already in a terminal status.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `run_ulid` — obligatorio
- `run_case_ulid` — obligatorio
- `step_ulid` — obligatorio
- `parameters` — opcional
- `status` — obligatorio
- `amendResult` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional

### `save_run_case_step_result`

Call this to persist a step's actualResult text. Requires run_ulid, run_case_ulid, and step_ulid. Optionally actualResult (null clears the saved text, omitted leaves it unchanged). Rejects if the run is closed, or if the run case is already marked (no longer untested). Pass amendResult=true only to correct the note of an already-recorded attempt: it lifts that already-marked rejection for a run case in a terminal status, and is itself rejected with 422 on any other status. Returns \{ actualResult \}.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `run_ulid` — obligatorio
- `run_case_ulid` — obligatorio
- `step_ulid` — obligatorio
- `parameters` — opcional
- `actualResult` — opcional
- `amendResult` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional

### `list_run_cases`

Call this to list every case on a run in one response, in run order. Requires run_ulid, or the run display ID with project_id. The route has no paging, so nextCursor is not set: a long run can exceed the response budget. Name the fields you need with fields, or read one case, with its steps and linked defects, with get_run_case.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `run_ulid` — obligatorio
- `project_id` — opcional
- `fields` — opcional

### `get_run_case`

Call this to fetch full detail for one run case, including steps, linked defects, and result attachments. The result attachments list the committed files of the current attempt and the inline images its texts reference, each image tagged by field (description, precondition, postcondition, notes, or step_actual_result with its step position). Requires run_ulid and run_case_ulid, or the test case display ID with run_ulid. A test case with several instances in the run takes parameters, the exact combination of one instance. The detail carries parameters (the combination this instance was expanded with), siblings (its other instances in the run, each with its status and combination) and reportedParameters (the parameters its current attempt reported).

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `run_ulid` — obligatorio
- `run_case_ulid` — obligatorio
- `parameters` — opcional
- `project_id` — opcional

### `add_run_cases`

Call this to add up to 500 cases to a run (additive — existing cases are untouched). Requires run_ulid and caseUlids (1-500). When the project lets only approved test cases into runs, a case that is not approved is refused with 422 (details.reason cases_not_approved, details.notApprovedCaseUlids lists them).

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `run_ulid` — obligatorio
- `caseUlids` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `remove_run_case`

Call this to remove one case from a run. Requires run_ulid and run_case_ulid. Returns \{ deleted: true \} on success.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `run_ulid` — obligatorio
- `run_case_ulid` — obligatorio
- `parameters` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional

### `retry_run_case`

Call this to reset a marked run case back to retested, ready for re-execution. Requires run_ulid and run_case_ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `run_ulid` — obligatorio
- `run_case_ulid` — obligatorio
- `parameters` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional

### `apply_latest_case_version`

Call this to bring a run case to the latest version of its test case when the case was edited after the run snapshotted it (the run case has caseUpdated: true). Requires run_ulid and run_case_ulid. Allowed only in an open run, on a pending run case (untested, or retested after retry_run_case) whose current attempt has no step results yet (canApplyLatest: true): replaces its title, fields and steps; assignee, position and status stay, and earlier attempts keep the version they executed. Returns the run case with its steps. Rejects with 409: run is closed first; otherwise error.details.reason is the first that applies, in this order: case_deleted (source case deleted), case_not_outdated (source case unchanged since the run case froze it, whatever its status; independent of caseUpdated, which is false for every completed run case), run_case_has_results (completed, or the current attempt has step results), combination_removed (the latest version no longer produces this run case combination: remove the run case, or add the case to the run again, to get the current combinations). A completed run case whose source case changed answers run_case_has_results.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `run_ulid` — obligatorio
- `run_case_ulid` — obligatorio
- `parameters` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional

### `open_run_case`

Call this to reopen an individual run case (there is no run-level reopen — this is the per-case equivalent). Requires run_ulid and run_case_ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `run_ulid` — obligatorio
- `run_case_ulid` — obligatorio
- `parameters` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional

### `mark_run_case`

Call this to submit a result for a single run case — the single-case "submit result" gesture. Requires run_ulid and run_case_ulid, plus status (passed/failed/skipped/blocked). Optionally assigneeUlid and elapsedMs. Appends a result row and returns the case with the new resultUlid.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `run_ulid` — obligatorio
- `run_case_ulid` — obligatorio
- `parameters` — opcional
- `assigneeUlid` — opcional
- `status` — opcional
- `elapsedMs` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional

### `list_results`

Call this to list the append-only result log of a run, one row per marking, in the order recorded. Requires run_ulid, or the run display ID with project_id. Returns the whole log in one response (no paging), so a long run can exceed the response budget: name the fields you need with fields, or read one result with get_result.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `run_ulid` — obligatorio
- `project_id` — opcional
- `fields` — opcional

### `get_result`

Call this to fetch the full snapshot for one result: the result row, case header, frozen steps, and result attachments. Requires run_ulid and result_ulid. The result row names its run case (runCaseUlid), the instance of a parameterized case it was recorded on, and that instance’s parameters; the snapshot’s parameters are the ones the attempt reported.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `run_ulid` — obligatorio
- `result_ulid` — obligatorio
- `project_id` — opcional

### `patch_result`

Call this to enrich an EXISTING result with notes and/or durationMs — not its outcome, not a new result row. Requires run_ulid and result_ulid, plus at least one of notes or durationMs. Rejects with a conflict if the run is closed.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `run_ulid` — obligatorio
- `result_ulid` — obligatorio
- `notes` — opcional
- `notesDocument` — opcional
- `durationMs` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional

### `submit_report`

Call this to record a batch of automated test results in ONE call (a CI report). Requires project_id, run and results. run is either \{ ulid \} of an OPEN run of the project to append to, or a new run \{ name, description?, environmentId? or environment?, milestoneId? or milestone?, plan?, configurationUlids? or configurations?, tags? \} (milestone: M-\<n> or an exact name; plan: PLAN-\<n> or an exact name, seeding the run with its selected cases; configurations: [\{ group, name \}]; an unknown one is rejected with nothing written); either may carry source (\{ provider, repository, branch, commit, commitAuthor, pullRequestNumber, workflow, ciRunId, ciRunAttempt, ciJob, buildUrl \}) — a reused run takes it only while it has none. A new run whose source has provider 'github_actions', repository (owner/name) and ciRunId groups parallel CI jobs: every report of that repository, CI run and attempt (ciRunAttempt, 1 when omitted) appends to ONE run, created by the first; the final job closes it with options.close: true (results may then be empty). A job reporting after that run was closed is refused with ci_run_closed. A run launched from Probara keeps its ulid in every GitHub re-run: a \{ ulid \} report whose source names another attempt of its workflow run appends to that attempt's own run (named after the launched run), returned in run.ulid. results holds 1-500 entries (0 only with options.close), each \{ caseDisplayId?, automationKey?, title?, suitePath?, status, durationMs?, notes?, executedAt?, parameters?, steps?, links?, case? \} with at least a caseDisplayId or an automationKey; parameters (\{ name: value \}), steps (a tree of \{ action, status, durationMs?, error?, steps? \}) and links ([\{ url, name? \}], absolute http(s) urls only, at most 20) are stored with the result. Each entry matches a case by caseDisplayId when present (authoritative; it does not fall back), else by automationKey; an unknown key with a title creates the case under suitePath beneath options.suiteUlid (or the project root) unless options.createMissingCases is false (default true). A matched archived case is restored and records the result; a case matched by automationKey whose entry has a suitePath moves there when it sits elsewhere, unless options.createMissingCases is false (a case matched by caseDisplayId, or managed by a Git sync, keeps its suite). case (\{ description?, tags?, fields?, steps? \}; fields by name, e.g. \{ priority: 'High' \}) applies only when the entry creates the case, and not to an existing one; an unresolved field is skipped and listed in warnings. options.assignFailedTo (1-20 member emails) assigns every run case the report leaves failed and unassigned, round-robin in the order given and starting again with the first in every report, and not overwriting an assignee. An email resolves only to an active member whose role can execute runs and who can access the project (a custom role is skipped); the emails that do not resolve do not fail the report and are reported only by their count, in the first of the warnings. For more than 500 results, or more than 10000 result steps, 10000 case steps or 1000 case tags across the entries, send chunks: reuse the run.ulid the first call returned and pass options.close: true only on the last chunk — a report does not complete the run on its own. All writes are atomic; pass idempotency_key so a retried chunk is not recorded twice. Returns \{ run: \{ ulid, displayId, state \}, results, summary: \{ recorded, created, unmatched \}, warnings? \}, where results[i] answers entry i: \{ outcome: 'recorded', caseUlid, resultUlid, created? \} or \{ outcome: 'unmatched', reason \}.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `run` — obligatorio
- `results` — obligatorio
- `options` — opcional
- `idempotency_key` — opcional

### `sync_gherkin_features`

Call this to make a project’s synced cases match the scenarios of Gherkin feature files that a CI job parsed (a Git sync). Requires project_id, scope and features. scope lists the glob patterns the feature files were matched with; every feature path has to match one of them, and only synced cases whose file matches scope can be archived. features is a list of \{ path, name, tags, description?, scenarios \} where path is a repository-relative .feature path and each scenario is \{ automationKey, name, line, tags, description?, rule?, background?, steps, examples?, url? \}: automationKey is "\<path> > \<Feature name> > \<Scenario name>", steps are \{ keyword, text, argument? \}, examples are \{ name?, header, rows \} for a Scenario Outline, and a tag probara:\<case display ID> binds the scenario to that case whatever its automation key. Each scenario creates, updates, moves, restores or keeps its case, and a synced case whose scenario is gone is archived, not deleted. suiteUlid places the feature suites under a suite; dryRun answers the same outcome and writes nothing. Two scenarios with one automation key, two scenarios binding one case, or a binding to an unknown case are refused with 422 and nothing is written. Returns \{ dryRun, summary: \{ created, updated, moved, restored, archived, unchanged \}, cases: [\{ action, automationKey, previousAutomationKey, caseUlid, caseDisplayId, path, line \}] \}, one entry per scenario in order, then one per archived case.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `scope` — obligatorio
- `suiteUlid` — opcional
- `dryRun` — opcional
- `features` — obligatorio
- `idempotency_key` — opcional

### `stage_result_attachment`

Call this to upload ONE evidence file of ANY content type for a run result, before including its returned reference in commit_result_attachments. Requires run_ulid, result_ulid, filename, content_type, and data_base64 (the raw file bytes, base64-encoded). Decoded bytes stay within 32 MiB. Content type is not pre-checked here — an executable file type is rejected by the server after upload. Rejects if the run is closed. Returns the staged attachment reference.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `run_ulid` — obligatorio
- `result_ulid` — obligatorio
- `filename` — obligatorio
- `content_type` — obligatorio
- `data_base64` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `commit_result_attachments`

Call this to reconcile a result's committed attachments to an EXACT set: staged references are promoted, existing references are kept (position may change), and any committed attachment absent from the list is removed. Requires run_ulid, result_ulid, and attachments (the desired reference set, from stage_result_attachment). Rejects if the run is closed. Returns \{ attachments \}.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `run_ulid` — obligatorio
- `result_ulid` — obligatorio
- `attachments` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `delete_result_attachment`

Call this to permanently delete a committed result attachment. Requires run_ulid, result_ulid, and attachment_ulid. Rejects if the run is closed. Returns \{ deleted: true \}.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `run_ulid` — obligatorio
- `result_ulid` — obligatorio
- `attachment_ulid` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `create_test_plan`

Call this to create a new test plan inside a project. Requires project_id (the project code) and name. Optionally description and status (defaults to draft). Returns the created plan, including its ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `name` — obligatorio
- `description` — opcional
- `status` — opcional
- `idempotency_key` — opcional

### `list_test_plans`

Call this to list test plans in a project with offset pagination. Requires project_id. Accepts page (1-based) and pageSize. Returns items plus page, pageSize, and total. By default a call returns at most 50 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `page` — opcional
- `pageSize` — opcional

### `get_test_plan`

Call this to fetch a single test plan by its ulid. Requires plan_ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `plan_ulid` — obligatorio
- `project_id` — opcional

### `update_test_plan`

Call this to update a test plan's name, description, status, or milestoneId. Requires plan_ulid and at least one field to change.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `plan_ulid` — obligatorio
- `name` — opcional
- `description` — opcional
- `status` — opcional
- `milestoneId` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional

### `delete_test_plan`

Call this to permanently delete a test plan. Requires plan_ulid. Returns \{ deleted: true \}. The first call returns a preview and a confirm_token; the call executes only when it is repeated with that confirm_token.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `plan_ulid` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional
- `confirm_token` — opcional

### `replace_test_plan_cases`

Call this to REPLACE a plan's entire case selection with the given ordered list — this is DESTRUCTIVE: any case currently selected whose ulid is not in caseUlids is dropped. Duplicate ulids are rejected. Requires plan_ulid and caseUlids (0-5000; may be empty to clear the selection). Optionally caseAssignees. Returns the new selection as \{ items \}.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `plan_ulid` — obligatorio
- `caseUlids` — obligatorio
- `caseAssignees` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional

### `list_test_plan_cases`

Call this to list a plan's selected cases (no paging — the full selection is returned). Requires plan_ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `plan_ulid` — obligatorio
- `project_id` — opcional

### `patch_test_plan_case`

Call this to change one selected case's assignee within a plan (null clears it). Requires plan_ulid, plan_case_ulid, and assigneeUlid.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `plan_ulid` — obligatorio
- `plan_case_ulid` — obligatorio
- `assigneeUlid` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `list_test_plan_runs`

Call this to list the runs linked to a plan, most-recent first (no paging — the full set is returned). Each item is a full run plus comboKey/comboLabel identifying its configuration combination (empty for a plain, unconfigured run). Requires plan_ulid. Create a run for a plan with create_run (planUlid + configurationUlids).

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `plan_ulid` — obligatorio
- `project_id` — opcional

### `replace_test_plan_configurations`

Call this to REPLACE a plan's entire configuration-value selection (the axes of the generate-run matrix) with the given set — DESTRUCTIVE, duplicate ulids rejected. Requires plan_ulid and configurationUlids (may be empty to clear the matrix). Returns the new selection as \{ items \}.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `plan_ulid` — obligatorio
- `configurationUlids` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `list_test_plan_configurations`

Call this to list a plan's selected configuration values (no paging — the full selection is returned). Requires plan_ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `plan_ulid` — obligatorio
- `project_id` — opcional

### `set_test_plan_excluded_combinations`

Call this to REPLACE a plan's entire excluded-combination set, scoping which combinations the plan intends to cover — DESTRUCTIVE, duplicate keys rejected. Requires plan_ulid and comboKeys (may be empty to clear all exclusions; use get_test_plan_combinations to read each candidate combination's comboKey). To actually create a run for a combination, call create_run with the plan's planUlid and the combination's configurationUlids. Returns \{ comboKeys \}.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `plan_ulid` — obligatorio
- `comboKeys` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `get_test_plan_combinations`

Call this to preview every candidate configuration combination for a plan, each with its comboKey, included flag (false when excluded), and ordered configuration values — plus includedCount/totalCount. Requires plan_ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `plan_ulid` — obligatorio
- `project_id` — opcional

### `create_milestone`

Call this to create a new milestone inside a project. Requires project_id (the project code) and name. Optionally description, status (defaults to upcoming), startAt/dueAt/forecastAt (epoch milliseconds; dueAt has to be >= startAt when both are set), and parentId (an existing milestone in the same project — the hierarchy is strictly one level, a child cannot itself have children). Returns the created milestone, including its ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `name` — obligatorio
- `description` — opcional
- `descriptionDocument` — opcional
- `status` — opcional
- `startAt` — opcional
- `dueAt` — opcional
- `forecastAt` — opcional
- `parentId` — opcional
- `idempotency_key` — opcional

### `list_milestones`

Call this to list milestones in a project with offset pagination. Requires project_id. Accepts page (1-based), pageSize, q, status (comma-separated), and view ("flat" default, or "tree" — pages over parent milestones only, each carrying its live direct children nested one level deep). Returns items plus page, pageSize, and total either way. By default a call returns at most 50 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `page` — opcional
- `pageSize` — opcional
- `q` — opcional
- `status` — opcional
- `view` — opcional

### `get_milestone`

Call this to fetch a single milestone by its ulid. Requires milestone_ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `milestone_ulid` — obligatorio
- `project_id` — opcional

### `update_milestone`

Call this to update a milestone's name, description, status, startAt/dueAt/forecastAt, or parentId. Requires milestone_ulid and at least one field to change; dueAt has to remain >= startAt when both are set.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `milestone_ulid` — obligatorio
- `name` — opcional
- `description` — opcional
- `descriptionDocument` — opcional
- `status` — opcional
- `startAt` — opcional
- `dueAt` — opcional
- `forecastAt` — opcional
- `parentId` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional

### `delete_milestone`

Call this to permanently delete a milestone. Requires milestone_ulid. Returns \{ deleted: true \} on success. The first call returns a preview and a confirm_token; the call executes only when it is repeated with that confirm_token.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `milestone_ulid` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional
- `confirm_token` — opcional

### `create_environment`

Call this to create a new environment inside a project. Requires project_id (the project code), name, and slug (lowercase alphanumeric segments joined by single hyphens, e.g. "pre-prod"). Optionally description, url, and isDefault (defaults to false). Returns the created environment, including its ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `name` — obligatorio
- `slug` — obligatorio
- `description` — opcional
- `descriptionDocument` — opcional
- `url` — opcional
- `isDefault` — opcional
- `idempotency_key` — opcional

### `list_environments`

Call this to list environments in a project with offset pagination. Requires project_id. Accepts page (1-based) and pageSize. Returns items plus page, pageSize, and total. By default a call returns at most 50 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `page` — opcional
- `pageSize` — opcional

### `get_environment`

Call this to fetch a single environment by its ulid. Requires environment_ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `environment_ulid` — obligatorio

### `update_environment`

Call this to update an environment's name, slug, description, url, or isDefault. Requires environment_ulid and at least one field to change.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `environment_ulid` — obligatorio
- `name` — opcional
- `slug` — opcional
- `description` — opcional
- `descriptionDocument` — opcional
- `url` — opcional
- `isDefault` — opcional
- `idempotency_key` — opcional

### `delete_environment`

Call this to permanently delete an environment. Requires environment_ulid. Returns \{ deleted: true \} on success. The first call returns a preview and a confirm_token; the call executes only when it is repeated with that confirm_token.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `environment_ulid` — obligatorio
- `idempotency_key` — opcional
- `confirm_token` — opcional

### `list_webhooks`

Call this to list a project's outgoing webhooks. Requires project_id. Returns items. Each webhook shows its header names and the last four characters of its secret, not the secret or any header value.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio

### `get_webhook`

Call this to fetch one outgoing webhook by its ulid. Requires project_id and webhook_ulid. The secret is not returned.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `webhook_ulid` — obligatorio

### `create_webhook`

Call this to create an outgoing webhook in a project. Requires project_id, name, and url (a public https endpoint). Choose one or more events, or allEvents. Optional: enabled, method (POST, PUT or PATCH), headers (each with a name and a value, stored encrypted), payload (the standard event envelope when omitted, or a custom body template), and secret (generated when omitted). The full secret is returned only by this call.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `name` — obligatorio
- `url` — obligatorio
- `events` — opcional
- `allEvents` — opcional
- `enabled` — opcional
- `method` — opcional
- `headers` — opcional
- `payload` — opcional
- `secret` — opcional
- `idempotency_key` — opcional

### `update_webhook`

Call this to update an outgoing webhook's name, url, events, allEvents, enabled, method, headers, or payload. Requires project_id, webhook_ulid, and at least one field to change. A header sent without a value keeps the value already stored under its name; a header left out of the list is removed.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `webhook_ulid` — obligatorio
- `name` — opcional
- `url` — opcional
- `events` — opcional
- `allEvents` — opcional
- `enabled` — opcional
- `method` — opcional
- `headers` — opcional
- `payload` — opcional
- `idempotency_key` — opcional

### `delete_webhook`

Call this to permanently delete an outgoing webhook. Requires project_id and webhook_ulid. Returns \{ deleted: true \} on success.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `project_id` — obligatorio
- `webhook_ulid` — obligatorio
- `idempotency_key` — opcional

### `rotate_webhook_secret`

Call this to replace an outgoing webhook's signing secret. Requires project_id and webhook_ulid. Returns the new secret and its hint. The new secret is returned only by this call.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva

**Parámetros**

- `project_id` — obligatorio
- `webhook_ulid` — obligatorio
- `idempotency_key` — opcional

### `send_webhook_test`

Call this to send one sample event to an outgoing webhook, once and synchronously, with no retries. Requires project_id and webhook_ulid. Optional: eventType (the event to sample; defaults to the first subscribed event, or test_case.created for All events). The webhook has to be enabled. Returns the test delivery with its request and the response it got. A test does not count toward the webhook’s health.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura, sistemas externos

**Parámetros**

- `project_id` — obligatorio
- `webhook_ulid` — obligatorio
- `eventType` — opcional
- `idempotency_key` — opcional

### `list_webhook_deliveries`

Call this to list the delivery history of an outgoing webhook, newest first. Requires project_id and webhook_ulid. Optional: status (pending, succeeded or failed), page, and pageSize (default 50). Returns items with page, pageSize, and total. History is kept for 30 days. By default a call returns at most 50 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `webhook_ulid` — obligatorio
- `page` — opcional
- `pageSize` — opcional
- `status` — opcional

### `get_webhook_delivery`

Call this to fetch one delivery of an outgoing webhook. Requires project_id, webhook_ulid, and delivery_ulid. Returns the request as sent (custom header values are "[redacted]") and the latest attempt’s response, with a body excerpt of at most 4096 bytes.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `webhook_ulid` — obligatorio
- `delivery_ulid` — obligatorio

### `redeliver_webhook_delivery`

Call this to send a delivery again: a new delivery of the same event and body to the webhook’s current endpoint, attempted promptly. Requires project_id, webhook_ulid, and delivery_ulid. The webhook has to be enabled, and the delivery's body has to have been sent: a delivery refused as too large cannot be resent. Returns the new pending delivery, whose redeliveryOf names the original.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura, sistemas externos

**Parámetros**

- `project_id` — obligatorio
- `webhook_ulid` — obligatorio
- `delivery_ulid` — obligatorio
- `idempotency_key` — opcional

### `create_configuration_group`

Call this to create a new configuration group inside a project (e.g. "Browser", "OS"). Requires project_id (the project code) and name. Rejects with a conflict if a group with the same name already exists in the project. Returns the created group, including its ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `name` — obligatorio
- `idempotency_key` — opcional

### `list_configuration_groups`

Call this to list configuration groups in a project with offset pagination. Requires project_id. Accepts page (1-based), pageSize, and optional include="values" to embed each group's nested values in the response. Returns items plus page, pageSize, and total. By default a call returns at most 50 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `page` — opcional
- `pageSize` — opcional
- `include` — opcional

### `get_configuration_group`

Call this to fetch a single configuration group by its ulid. Requires group_ulid. Optional include="values" embeds the group's nested values in the response.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `group_ulid` — obligatorio
- `include` — opcional

### `update_configuration_group`

Call this to rename a configuration group. Requires group_ulid and name. Rejects with a conflict if another group in the same project already has that name.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `group_ulid` — obligatorio
- `name` — opcional
- `idempotency_key` — opcional

### `delete_configuration_group`

Call this to delete a configuration group. Requires group_ulid. The group disappears from group-scoped reads; its nested values are NOT deleted and stay individually addressable via update_configuration and delete_configuration by their own configuration_ulid. Returns \{ deleted: true \} on success.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `group_ulid` — obligatorio
- `idempotency_key` — opcional

### `create_configuration`

Call this to add a new configuration value under a group (e.g. "Chrome" under "Browser"). Requires group_ulid and name. Rejects with a conflict if a value with the same name already exists in the group. Returns the created value, including its ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `group_ulid` — obligatorio
- `name` — obligatorio
- `idempotency_key` — opcional

### `list_configurations`

Call this to list every configuration value nested under a group. Requires group_ulid. The underlying route has no independent pagination — every live value is returned in one page.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `group_ulid` — obligatorio

### `update_configuration`

Call this to rename a configuration value standalone by its own ulid (not nested under a group path). Requires configuration_ulid and name. Rejects with a conflict if another value in the same group already has that name.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `configuration_ulid` — obligatorio
- `name` — opcional
- `idempotency_key` — opcional

### `delete_configuration`

Call this to permanently delete a configuration value by its ulid. Requires configuration_ulid. Returns \{ deleted: true \} on success.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `configuration_ulid` — obligatorio
- `idempotency_key` — opcional

### `create_defect`

Call this to report a new defect inside a project. Requires project_id (the project code) and title. Optionally description, assigneeUlid, tags, milestoneUlid, and customFieldValues (array of \{fieldUlid, value\}). Returns the created defect (status defaults to "open"), including its ulid and defectNumber.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `title` — obligatorio
- `description` — opcional
- `descriptionDocument` — opcional
- `assigneeUlid` — opcional
- `tags` — opcional
- `milestoneUlid` — opcional
- `customFieldValues` — opcional
- `idempotency_key` — opcional

### `list_defects`

Call this to list defects in a project with offset pagination. Requires project_id. Accepts page (1-based), pageSize, q (matches "D-\<n>"/a bare number as the defect number, otherwise a title substring), status/severity/priority/assigneeUlid (each an array, ANDed as one CSV filter), aging (an age-bucket key), milestoneUlid, fields (custom-field ulids to embed on each item), and cf (repeatable — pass the same argument more than once for several "\<fieldUlid>:\<value[,value…]>" filters at once, every entry ANDed together). Returns items plus page, pageSize, and total. By default a call returns at most 50 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `page` — opcional
- `pageSize` — opcional
- `q` — opcional
- `status` — opcional
- `severity` — opcional
- `priority` — opcional
- `assigneeUlid` — opcional
- `aging` — opcional
- `fields` — opcional
- `milestoneUlid` — opcional
- `cf` — opcional

### `get_defect_metrics`

Call this to fetch project-scoped defect metrics: totalOpen, openBySeverity, aging buckets, reopenRate, mttrMs, byMilestone, and maxAgeDaysOpen. Requires project_id. The aggregate does not include another project’s defects.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio

### `get_defect`

Call this to fetch a single defect by its ulid, including occurrence/affected-case counts and embedded custom-field values. Requires defect_ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `defect_ulid` — obligatorio
- `project_id` — opcional

### `update_defect`

Call this to update a defect's title, description, status, resolution, assigneeUlid, tags, milestoneUlid, or duplicateOfUlid. Requires defect_ulid and at least one field to change. status is a plain enum (open|in_progress|resolved|closed) validated against the transition graph server-side — there is no separate "resolve"/"reopen" tool. resolution is required when status becomes resolved or closed, and duplicateOfUlid is only allowed alongside resolution="duplicate".

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `defect_ulid` — obligatorio
- `title` — opcional
- `description` — opcional
- `descriptionDocument` — opcional
- `status` — opcional
- `resolution` — opcional
- `assigneeUlid` — opcional
- `tags` — opcional
- `milestoneUlid` — opcional
- `duplicateOfUlid` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional

### `delete_defect`

Call this to permanently delete a defect. Requires defect_ulid. The route allow-lists owner, admin, AND member — only a viewer role is rejected as forbidden. Returns \{ deleted: true \} on success.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `defect_ulid` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `list_run_defects`

Call this to list the defects linked within a run (no paging — the full set is returned, each item carrying linkCountInRun, assigneeUlid and systemFieldValues). Requires run_ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `run_ulid` — obligatorio
- `project_id` — opcional

### `link_defect_case`

Call this to manually link a defect to a test case. Requires defect_ulid and testCaseUlid (has to belong to the same project as the defect). Idempotent: re-linking the same pair returns the existing link and also clears any prior dismissal for that pair. A case link does not link a run result: to record a failed result as an occurrence of the defect, use link_defect_result. Returns the created link, including its ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `defect_ulid` — obligatorio
- `testCaseUlid` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `unlink_defect_case`

Call this to remove a manual defect-case link. Requires defect_ulid and link_ulid (from link_defect_case). Returns \{ deleted: true \} on success.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `defect_ulid` — obligatorio
- `link_ulid` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `dismiss_defect_case`

Call this to dismiss a defect-case association (hides the case from the defect's affected-cases list without deleting any run history). Requires defect_ulid and testCaseUlid (has to belong to the same project as the defect). Idempotent: re-dismissing the same pair refreshes the dismissal timestamp. Returns the dismissal record.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura, idempotente

**Parámetros**

- `defect_ulid` — obligatorio
- `testCaseUlid` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `link_defect_result`

Call this to link a defect to a run result (one occurrence of the defect). Requires defect_ulid and resultUlid; optionally stepSnapshotUlid to pin the specific failing step. Idempotent: re-linking the same result returns the existing link. Returns the created link, including its ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `defect_ulid` — obligatorio
- `resultUlid` — obligatorio
- `stepSnapshotUlid` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional

### `list_defect_result_links`

Call this to list the result links (occurrences) recorded on a defect, with cursor pagination. Requires defect_ulid. Accepts limit and cursor; feed the returned nextCursor back as cursor to fetch the next page — the field is absent once you have reached the last page. By default a call returns at most 50 items; pass limit to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `defect_ulid` — obligatorio
- `limit` — opcional
- `cursor` — opcional
- `project_id` — opcional

### `unlink_defect_result`

Call this to remove a defect-result link. Requires defect_ulid and link_ulid (from link_defect_result or list_defect_result_links). Returns \{ deleted: true \} on success.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `defect_ulid` — obligatorio
- `link_ulid` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `list_defect_affected_cases`

Call this to list the test cases affected by a defect (aggregated from result links and manual case links), with cursor pagination. Requires defect_ulid. Accepts limit and cursor; feed the returned nextCursor back as cursor to fetch the next page — the field is absent once you have reached the last page. By default a call returns at most 50 items; pass limit to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `defect_ulid` — obligatorio
- `limit` — opcional
- `cursor` — opcional
- `project_id` — opcional

### `upload_defect_attachment`

Call this to upload ONE evidence file of ANY content type directly onto a defect (this is an immediate upload, not a stage-then-commit flow). Requires defect_ulid, filename, content_type, and data_base64 (the raw file bytes, base64-encoded). Decoded bytes stay within 33554432 bytes (32 MiB). Content type is not pre-checked here — an executable file type is rejected by the server after upload. Returns \{ attachments \} with one persisted attachment reference.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `defect_ulid` — obligatorio
- `filename` — obligatorio
- `content_type` — obligatorio
- `data_base64` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `list_defect_attachments`

Call this to list every attachment of a defect: its evidence files and the inline images of its description and paragraph values, each tagged kind "file" or "image". Requires defect_ulid. Returns \{ attachments \} — a bare passthrough, no pagination.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `defect_ulid` — obligatorio
- `project_id` — opcional

### `delete_defect_attachment`

Call this to permanently delete a defect attachment. Requires defect_ulid and attachment_ulid. Returns \{ deleted: true \}.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `defect_ulid` — obligatorio
- `attachment_ulid` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `upload_test_case_attachment`

Call this to upload ONE loose file of ANY content type directly onto a test case: a file the case holds outside its texts (this is an immediate upload, not a stage-then-commit flow). Requires case_ulid, filename, content_type, and data_base64 (the raw file bytes, base64-encoded). Decoded bytes stay within 33554432 bytes (32 MiB). Content type is not pre-checked here: an executable file type is rejected by the server after upload. Returns \{ attachments \} with one persisted file. Inline images of the case texts are not uploaded here: insert them into the text.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `case_ulid` — obligatorio
- `filename` — obligatorio
- `content_type` — obligatorio
- `data_base64` — obligatorio
- `idempotency_key` — opcional

### `list_test_case_attachments`

Call this to list the attachments section of a test case: every inline image its texts reference, tagged with the field it sits in (description, a paragraph field, or a step part), then its loose files, oldest first. Requires case_ulid. Returns \{ attachments \} with no pagination.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `case_ulid` — obligatorio

### `delete_test_case_attachment`

Call this to permanently delete one loose file of a test case. Requires case_ulid and attachment_ulid. An inline image is not a loose file: it leaves the case by leaving its text. Returns \{ deleted: true \}.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `case_ulid` — obligatorio
- `attachment_ulid` — obligatorio
- `idempotency_key` — opcional

### `set_defect_custom_field_values`

Call this to reconcile a defect's custom-field values to an EXACT desired set: values for fields not present in the input are cleared (or rejected if the field is required). Requires defect_ulid and values (an array of \{ fieldUlid, value \}). Returns \{ customFieldValues \} with the full resulting set.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `defect_ulid` — obligatorio
- `values` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `patch_defect_custom_field_value`

Call this to update ONE of a defect's custom-field values, leaving every other value untouched. Requires defect_ulid, field_ulid, and value (an empty/null value clears the field, or is rejected if the field is required). Returns \{ customFieldValues \} with the full resulting set.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `defect_ulid` — obligatorio
- `field_ulid` — obligatorio
- `value` — opcional
- `valueDocument` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional

### `list_custom_fields`

Call this to list an organization's custom-field definitions (system + custom) with offset pagination. Requires org_id (from get_current_organization). Accepts page, pageSize, q (title substring), entity/type/group (each an array, ANDed as one CSV filter). Returns items plus page, pageSize, and total. By default a call returns at most 50 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `org_id` — obligatorio
- `page` — opcional
- `pageSize` — opcional
- `q` — opcional
- `entity` — opcional
- `type` — opcional
- `group` — opcional
- `locales` — opcional

### `get_custom_field_usage`

Call this to see what deleting a custom field would erase. Requires org_id and field_ulid. Returns \{ valueCount, projectCount \}: the values stored for the field across test cases, test runs and defects, and how many projects hold them.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `org_id` — obligatorio
- `field_ulid` — obligatorio

### `create_custom_field`

Call this to create a new custom-field definition in an organization. Requires org_id, title, and type. Optionally entity (`test_case`, `test_run`, or `defect`; defaults to `test_case`), placeholder, defaultValue, isRequired, allProjects, projectUlids, and options (required for select/radio types). Needs a plan that includes custom field configuration; otherwise it is refused as forbidden. Returns \{ field \}.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `org_id` — obligatorio
- `title` — obligatorio
- `titleI18n` — opcional
- `entity` — opcional
- `type` — obligatorio
- `placeholder` — opcional
- `placeholderI18n` — opcional
- `defaultValue` — opcional
- `isRequired` — opcional
- `allProjects` — opcional
- `projectUlids` — opcional
- `options` — opcional
- `idempotency_key` — opcional

### `update_custom_field`

Call this to update one or more fields of an existing custom-field definition. Requires org_id, field_ulid, and at least one of title, entity, type, placeholder, defaultValue, isRequired, allProjects, projectUlids, or options. Applies to system and custom fields and needs a plan that includes custom field configuration; otherwise it is refused as forbidden. Returns \{ field \}.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `org_id` — obligatorio
- `field_ulid` — obligatorio
- `title` — opcional
- `titleI18n` — opcional
- `entity` — opcional
- `type` — opcional
- `placeholder` — opcional
- `placeholderI18n` — opcional
- `defaultValue` — opcional
- `isRequired` — opcional
- `allProjects` — opcional
- `projectUlids` — opcional
- `options` — opcional
- `idempotency_key` — opcional

### `delete_custom_field`

Call this to permanently delete a custom-field definition. Requires org_id and field_ulid. A field that still holds values is refused with field_has_values unless delete_values is true; delete_values permanently deletes every stored value of the field too (check get_custom_field_usage first). Returns \{ deleted: true \}. The first call returns a preview and a confirm_token; the call executes only when it is repeated with that confirm_token.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `org_id` — obligatorio
- `field_ulid` — obligatorio
- `delete_values` — opcional
- `idempotency_key` — opcional
- `confirm_token` — opcional

### `reset_custom_field`

Call this to restore a system/default custom field to its original definition. Requires org_id and field_ulid. Returns \{ field \}.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva

**Parámetros**

- `org_id` — obligatorio
- `field_ulid` — obligatorio
- `idempotency_key` — opcional

### `set_test_case_custom_field_values`

Call this to reconcile a test case's custom-field values to an EXACT desired set: values for fields not present in the input are cleared (or rejected if the field is required). Requires case_ulid and values (an array of \{ fieldUlid, value \}). Optional expectedRevision (the case’s currentRevision you last read) refuses the write with 409 conflict when the case changed since. Returns \{ customFieldValues, currentRevision \}: the full resulting set and the case’s new revision.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `case_ulid` — obligatorio
- `values` — obligatorio
- `expectedRevision` — opcional
- `idempotency_key` — opcional

### `patch_test_case_custom_field_value`

Call this to update ONE of a test case's custom-field values, leaving every other value untouched. Requires case_ulid, field_ulid, and value (an empty/null value clears the field, or is rejected if the field is required). Optional expectedRevision (the case’s currentRevision you last read) refuses the write with 409 conflict when the case changed since. Returns \{ customFieldValues, currentRevision \}: the full resulting set and the case’s new revision.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `case_ulid` — obligatorio
- `field_ulid` — obligatorio
- `value` — opcional
- `valueDocument` — opcional
- `expectedRevision` — opcional
- `idempotency_key` — opcional

### `get_current_organization`

Call this FIRST to discover the caller organization: returns the ulid and name of the organization bound to the caller's API token. No arguments. Every org-scoped tool below (members, invitations) requires the returned ulid as org_id.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**: Esta herramienta no recibe parámetros.

### `list_org_members`

Call this to list an organization's members with offset pagination. Requires org_id (from get_current_organization). Accepts page, pageSize, q (email/name substring), role (one or more role slugs from this organization's roles list, system or custom, ANDed as one CSV filter), and status (active or deactivated; absent lists every member). Each member carries deactivatedAt, null while the member is active. Returns items plus page, pageSize, and total. By default a call returns at most 50 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `org_id` — obligatorio
- `page` — opcional
- `pageSize` — opcional
- `q` — opcional
- `role` — opcional
- `status` — opcional

### `update_org_member_role`

Call this to change a member's role. Requires org_id, user_ulid, and role — a role slug from this organization's roles list (system or custom). The owner role cannot be assigned here: an organization has exactly one owner, and ownership moves only through transfer_org_ownership. Returns \{ member \}.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `org_id` — obligatorio
- `user_ulid` — obligatorio
- `role` — obligatorio
- `idempotency_key` — opcional

### `transfer_org_ownership`

Call this to transfer ownership of the organization to another current member. Only the current owner may call it. Requires org_id and userUlid (the member who becomes the owner). In one step the target becomes the owner and the caller becomes an admin; an organization has exactly one owner at all times. This cannot be undone by the caller: only the new owner can transfer it back. A viewer target needs a free seat. Returns \{ previousOwner, newOwner \}.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva

**Parámetros**

- `org_id` — obligatorio
- `userUlid` — obligatorio
- `idempotency_key` — opcional

### `deactivate_org_member`

Call this to deactivate a member instead of removing them. Requires org_id and user_ulid. The member loses access to the organization at once: their API tokens, OAuth and MCP grants and single sign-on sessions are revoked, and their seat is freed. Their role, project access and groups are kept, and reactivate_org_member restores access later. An owner cannot be deactivated, and you cannot deactivate yourself. Returns \{ member \}. The first call returns a preview and a confirm_token; the call executes only when it is repeated with that confirm_token.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva

**Parámetros**

- `org_id` — obligatorio
- `user_ulid` — obligatorio
- `idempotency_key` — opcional
- `confirm_token` — opcional

### `reactivate_org_member`

Call this to reactivate a deactivated member. Requires org_id and user_ulid. The member takes a seat back, so the organization needs a free seat; otherwise the call fails with seat_limit_exceeded. Revoked tokens and grants are not restored, so the member needs new ones. Returns \{ member \}.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `org_id` — obligatorio
- `user_ulid` — obligatorio
- `idempotency_key` — opcional

### `list_org_invitations`

Call this to list an organization's invitations with offset pagination. Requires org_id. Accepts page, pageSize, q (email substring), status (array, ANDed as one CSV filter — pending|accepted|revoked|expired), and role (one or more role slugs from this organization's roles list, system or custom, ANDed as one CSV filter). Returns items plus page, pageSize, and total. By default a call returns at most 50 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `org_id` — obligatorio
- `page` — opcional
- `pageSize` — opcional
- `q` — opcional
- `status` — opcional
- `role` — opcional

### `create_invitation`

Call this to invite a new member by email. Requires org_id, email, and role — a role slug from this organization's roles list (system or custom); owner cannot be invited. Optionally locale. Returns \{ ulid \} of the created invitation.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura, sistemas externos

**Parámetros**

- `org_id` — obligatorio
- `email` — obligatorio
- `role` — obligatorio
- `locale` — opcional
- `grantAllPrivateProjects` — opcional
- `idempotency_key` — opcional

### `bulk_create_invitations`

Call this to invite up to 50 addresses at once with ONE shared role. Requires org_id, emails (1-50), and role — a role slug from this organization's roles list (system or custom); owner cannot be invited. Optionally locale, applied to the WHOLE list (no per-address role or locale). Optionally grantAllPrivateProjects, applied to the WHOLE list: when true, each invitee is granted every private project of the organization once they accept. Returns \{ results: [\{ email, status \}] \}, one entry per distinct submitted address, status one of created, duplicate_pending, already_member, invalid_email.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura, sistemas externos

**Parámetros**

- `org_id` — obligatorio
- `emails` — obligatorio
- `role` — obligatorio
- `grantAllPrivateProjects` — opcional
- `locale` — opcional
- `idempotency_key` — opcional

### `resend_invitation`

Call this to resend a pending invitation email. Requires org_id and invitation_ulid. Returns \{ ulid \}.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura, sistemas externos

**Parámetros**

- `org_id` — obligatorio
- `invitation_ulid` — obligatorio
- `idempotency_key` — opcional

### `revoke_invitation`

Call this to revoke a pending invitation. Requires org_id and invitation_ulid. Returns \{ deleted: true \}.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `org_id` — obligatorio
- `invitation_ulid` — obligatorio
- `idempotency_key` — opcional

### `list_audit_events`

Call this to list an organization's audit trail with cursor pagination. Requires org_id (from get_current_organization). Gated to owner/admin roles — any other role is rejected as forbidden. Accepts limit, cursor (feed the returned nextCursor back to fetch the next page), actorUlid, and entityType (a comma-separated string of one or more entity types, ANDed — e.g. "defect" or "defect,test_case"). Returns \{ events, nextCursor \} — nextCursor is null on the last page. By default a call returns at most 50 items; pass limit to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `org_id` — obligatorio
- `limit` — opcional
- `cursor` — opcional
- `actorUlid` — opcional
- `entityType` — opcional

### `list_defect_audit_events`

Call this to list a defect's audit trail with cursor pagination. Requires defect_ulid. Accepts limit and cursor (feed the returned nextCursor back to fetch the next page). Returns \{ events, nextCursor \} — nextCursor is null on the last page. By default a call returns at most 50 items; pass limit to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `defect_ulid` — obligatorio
- `limit` — opcional
- `cursor` — opcional
- `project_id` — opcional

### `list_run_audit_events`

Call this to list a run's audit trail with cursor pagination. Requires run_ulid. Accepts limit and cursor (feed the returned nextCursor back to fetch the next page). Returns \{ events, nextCursor \} — nextCursor is null on the last page. By default a call returns at most 50 items; pass limit to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `run_ulid` — obligatorio
- `limit` — opcional
- `cursor` — opcional
- `project_id` — opcional

### `stage_import`

Call this FIRST to stage a suite/case tree file (e.g. CSV) for import. Requires project_id, filename, content_type, and data_base64 (the raw file bytes, base64-encoded). Decoded bytes stay within 10485760 bytes (10 MiB). Returns \{ uploadUlid, byteSize, filename \} — pass uploadUlid to create_import to commit the tree.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `filename` — obligatorio
- `content_type` — obligatorio
- `data_base64` — obligatorio
- `idempotency_key` — opcional

### `create_import`

Call this to commit a staged import into an atomic suite/case tree. Requires project_id, uploadUlid (from stage_import), sourceFormat, targetSuiteUlid (a suite ulid to import under, or null for the project root), and replaceMatching (whether same-titled existing cases are replaced instead of duplicated). Returns the import report: counts (suitesCreated, casesCreated, casesReplaced, rowsFailed, reviewRequestsCreated), created, replaced, reviewRequests, failures, and warnings. When the project requires review (Pro or Enterprise plan, review enabled and mandatory) no case is written: suites are created, each new case becomes a create review request and each replaced case an update request (listed in reviewRequests with their RV-n), and a case that already has an open request is a review_already_open failure.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `uploadUlid` — obligatorio
- `sourceFormat` — obligatorio
- `targetSuiteUlid` — obligatorio
- `replaceMatching` — obligatorio
- `idempotency_key` — opcional

### `report_defect_from_result`

Composite convenience tool: creates a defect, links it to a run result, and links it to one or more test cases — three SEPARATE sequential dispatched calls. Requires project_id, title, resultUlid, and testCaseUlids (at least one). Optionally description. This tool is NOT ATOMIC at the MCP layer: if a later step fails, earlier steps are NOT rolled back (e.g. the defect may already exist even though a case link 422s) — the failure result includes a partial-state JSON block (defect, and resultLink/caseLinks if those steps already succeeded); call get_defect or list_defect_affected_cases afterward to confirm the actual state, then retry the failed step with link_defect_case/link_defect_result directly. For an atomic run-result submission use bulk_submit_run_result instead. Returns \{ defect, resultLink, caseLinks \}.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `title` — obligatorio
- `resultUlid` — obligatorio
- `testCaseUlids` — obligatorio
- `description` — opcional
- `idempotency_key` — opcional

### `list_tags`

Call this to list an organization's tag catalog with offset pagination. Requires org_id (from get_current_organization). Accepts page, pageSize, q (name substring) and authorUlid (one or more member ULIDs, comma-separated). Each tag carries its author and its usage counts across test cases, defects and test runs; the counts cover only the projects the caller may read. Returns items plus page, pageSize, and total. By default a call returns at most 50 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `org_id` — obligatorio
- `page` — opcional
- `pageSize` — opcional
- `q` — opcional
- `authorUlid` — opcional

### `create_tag`

Call this to add a tag to the organization catalog. Requires org_id and name. Names are trimmed and unique per organization; a duplicate is refused as a conflict. Returns the created tag, including its ulid and its three (zero) usage counts.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `org_id` — obligatorio
- `name` — obligatorio
- `idempotency_key` — opcional

### `update_tag`

Call this to rename a tag. Requires org_id, tag_ulid and name. The rename reaches every test case, defect and test run the tag is attached to without changing any of them. Returns the renamed tag with its current usage counts.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `org_id` — obligatorio
- `tag_ulid` — obligatorio
- `name` — opcional
- `idempotency_key` — opcional

### `delete_tag`

Call this to permanently delete a tag. Requires org_id and tag_ulid. Deleting a tag detaches it from every test case, defect and test run that carries it, leaving those otherwise intact. Returns \{ deleted: true \}. The first call returns a preview and a confirm_token; the call executes only when it is repeated with that confirm_token.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `org_id` — obligatorio
- `tag_ulid` — obligatorio
- `idempotency_key` — opcional
- `confirm_token` — opcional

### `bulk_delete_tags`

Call this to delete several tags at once. Requires org_id and ulids (1-100 tag ULIDs). Each deletion detaches the tag from every test case, defect and test run that carries it. A ULID this organization does not own, or one already removed, is skipped rather than failing the request. Returns \{ deleted: \<count actually removed> \}. The first call returns a preview and a confirm_token; the call executes only when it is repeated with that confirm_token.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `org_id` — obligatorio
- `ulids` — obligatorio
- `idempotency_key` — opcional
- `confirm_token` — opcional

### `list_defect_jira_issues`

Call this to list the Jira issues linked to a defect. Requires defect_ulid. Each link carries issueKey, issueUrl, the cached summary/status/priority/issue type, syncState (synced, pending while Jira-side work is queued, error) and lastError.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `defect_ulid` — obligatorio
- `project_id` — opcional

### `create_defect_jira_issue`

Call this to create a Jira issue from a defect and link it. Requires defect_ulid. Files into the project’s default Jira mapping unless mappingUlid or jiraProjectKey names another, with the mapping’s issue type unless issueTypeId is given. Optionally resultUlid (a result linked to the defect, whose run, case and steps go into the description), includeAttachments, and fields (Jira field id -> value, for fields Jira requires). If Jira requires more fields the error lists them: call again with fields. If an earlier create’s answer was lost the error says when to retry; force: true (create anyway) is accepted only after that window. Returns the link.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura, sistemas externos

**Parámetros**

- `defect_ulid` — obligatorio
- `mappingUlid` — opcional
- `jiraProjectKey` — opcional
- `issueTypeId` — opcional
- `fields` — opcional
- `resultUlid` — opcional
- `includeAttachments` — opcional
- `force` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional

### `link_defect_jira_issue`

Call this to link an existing Jira issue to a defect. Requires defect_ulid and issueKey (e.g. "WEB-12"; find one with search_jira_issues). The issue has to live in a Jira project the defect’s project maps. Returns the link.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura, sistemas externos

**Parámetros**

- `defect_ulid` — obligatorio
- `issueKey` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `unlink_defect_jira_issue`

Call this to unlink a Jira issue from a defect. Requires defect_ulid and link_ulid (the link’s ulid from list_defect_jira_issues). The issue stays in Jira. Returns \{ unlinked: true \}.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente, sistemas externos

**Parámetros**

- `defect_ulid` — obligatorio
- `link_ulid` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `list_test_case_jira_issues`

Call this to list the Jira issues (stories, epics, any type) a test case covers. Requires case_ulid. Each link carries issueKey, issueUrl, the cached summary/status/priority/issue type, syncState (synced, pending while Jira-side work is queued, error) and lastError.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `case_ulid` — obligatorio

### `link_test_case_jira_issue`

Call this to link a Jira issue to a test case, for traceability. Requires case_ulid and issueKey (e.g. "WEB-12"). The issue has to live in a Jira project the case’s project maps. Returns the link.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura, sistemas externos

**Parámetros**

- `case_ulid` — obligatorio
- `issueKey` — obligatorio
- `idempotency_key` — opcional

### `unlink_test_case_jira_issue`

Call this to unlink a Jira issue from a test case. Requires case_ulid and link_ulid (the link’s ulid from list_test_case_jira_issues). The issue stays in Jira. Returns \{ unlinked: true \}.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente, sistemas externos

**Parámetros**

- `case_ulid` — obligatorio
- `link_ulid` — obligatorio
- `idempotency_key` — opcional

### `search_jira_issues`

Call this to find Jira issues in the Jira projects a Probara project maps, e.g. before link_defect_jira_issue or link_test_case_jira_issue. Requires project_id and q: an issue key ("WEB-12") finds that issue, any other text searches summaries and descriptions (at most 20 hits). Returns items with issueKey, summary, status and issueUrl; empty when the project maps no Jira project.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `q` — obligatorio

### `list_test_case_github_issues`

Call this to list the GitHub issues linked to a test case. Requires case_ulid. Each link carries repositoryFullName, issueNumber, linkKind (requirement or defect), and the cached title, state (open or closed), labels and htmlUrl as GitHub last answered.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `case_ulid` — obligatorio

### `link_test_case_github_issue`

Call this to link an existing GitHub issue to a test case (a requirement unless linkKind says otherwise). Requires case_ulid. Requires connectionUlid and repository (owner/name, both from list_project_github_repositories) and issueNumber (find one with search_github_issues). Optionally linkKind: requirement or defect. Pull requests are refused. Returns the link.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `case_ulid` — obligatorio
- `connectionUlid` — obligatorio
- `repository` — obligatorio
- `issueNumber` — obligatorio
- `linkKind` — opcional
- `idempotency_key` — opcional

### `unlink_test_case_github_issue`

Call this to unlink a GitHub issue from a test case. Requires case_ulid and link_ulid (the link's ulid from list_test_case_github_issues). The issue stays in GitHub. Returns \{ unlinked: true \}.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `case_ulid` — obligatorio
- `link_ulid` — obligatorio
- `idempotency_key` — opcional

### `list_run_github_issues`

Call this to list the GitHub issues linked to a run. Requires run_ulid. Each link carries repositoryFullName, issueNumber, linkKind (requirement or defect), and the cached title, state (open or closed), labels and htmlUrl as GitHub last answered.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `run_ulid` — obligatorio
- `project_id` — opcional

### `link_run_github_issue`

Call this to link an existing GitHub issue to a run (a defect unless linkKind says otherwise). Requires run_ulid. Requires connectionUlid and repository (owner/name, both from list_project_github_repositories) and issueNumber (find one with search_github_issues). Optionally linkKind: requirement or defect. Pull requests are refused. Returns the link.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `run_ulid` — obligatorio
- `connectionUlid` — obligatorio
- `repository` — obligatorio
- `issueNumber` — obligatorio
- `linkKind` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional

### `unlink_run_github_issue`

Call this to unlink a GitHub issue from a run. Requires run_ulid and link_ulid (the link's ulid from list_run_github_issues). The issue stays in GitHub. Returns \{ unlinked: true \}.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `run_ulid` — obligatorio
- `link_ulid` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `list_defect_github_issues`

Call this to list the GitHub issues linked to a defect. Requires defect_ulid. Each link carries repositoryFullName, issueNumber, linkKind (requirement or defect), and the cached title, state (open or closed), labels and htmlUrl as GitHub last answered.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `defect_ulid` — obligatorio
- `project_id` — opcional

### `link_defect_github_issue`

Call this to link an existing GitHub issue to a defect (a defect unless linkKind says otherwise). Requires defect_ulid. Requires connectionUlid and repository (owner/name, both from list_project_github_repositories) and issueNumber (find one with search_github_issues). Optionally linkKind: requirement or defect. Pull requests are refused. Returns the link.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `defect_ulid` — obligatorio
- `connectionUlid` — obligatorio
- `repository` — obligatorio
- `issueNumber` — obligatorio
- `linkKind` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional

### `unlink_defect_github_issue`

Call this to unlink a GitHub issue from a defect. Requires defect_ulid and link_ulid (the link's ulid from list_defect_github_issues). The issue stays in GitHub. Returns \{ unlinked: true \}.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `defect_ulid` — obligatorio
- `link_ulid` — obligatorio
- `idempotency_key` — opcional
- `project_id` — opcional

### `list_project_github_repositories`

Call this to list the GitHub repositories a project may link issues from. Requires project_id. Returns items with connectionUlid, repositoryId, fullName (owner/name) and isDefault, and mapped: true when the project restricts itself to these repositories, false when every repository the GitHub App can see is offered.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio

### `search_github_issues`

Call this to find GitHub issues in one repository, e.g. before link_test_case_github_issue. Requires project_id, connectionUlid and repository (owner/name, from list_project_github_repositories). q is an issue number ("123" or "#123") or text matched by GitHub search; pull requests are not returned (at most 20 hits). Returns items with issueNumber, title, state, labels and htmlUrl.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `connectionUlid` — obligatorio
- `repository` — obligatorio
- `q` — opcional

### `create_defect_github_issue`

Call this to open a new GitHub issue from a defect and link it to the defect. Requires defect_ulid, connectionUlid and repository (owner/name, from list_project_github_repositories). Optionally resultUlid (a result linked to the defect: the issue then describes that result's test case — title, preconditions, steps with expected and actual results — its failure, environment and links back to the defect, case and run), title and body (Markdown; default to that prefilled draft) and labels (names from list_github_repository_labels). Safe to retry: a defect opens at most one issue per repository, and a repeat returns the same link. If GitHub’s answer was lost the error says when to retry. Returns the link.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura, sistemas externos

**Parámetros**

- `defect_ulid` — obligatorio
- `connectionUlid` — obligatorio
- `repository` — obligatorio
- `title` — opcional
- `body` — opcional
- `labels` — opcional
- `resultUlid` — opcional
- `idempotency_key` — opcional
- `project_id` — opcional

### `list_github_repository_labels`

Call this to list the labels of a GitHub repository, e.g. before create_defect_github_issue. Requires project_id, connectionUlid and repository (owner/name, from list_project_github_repositories). Returns items with name, color and description (the first 100 labels).

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `connectionUlid` — obligatorio
- `repository` — obligatorio

### `list_github_workflows`

Call this to list the GitHub Actions workflows of a repository, e.g. before launch_github_workflow. Requires project_id, connectionUlid and repository (owner/name, from list_project_github_repositories). Returns items with id, name and path of every active workflow; only those with a 'workflow_dispatch' trigger can be launched.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `connectionUlid` — obligatorio
- `repository` — obligatorio

### `list_github_branches`

Call this to list the branches of a GitHub repository, e.g. before launch_github_workflow. Requires project_id, connectionUlid and repository (owner/name, from list_project_github_repositories). Returns defaultBranch, items with name (the default branch first, at most 500) and truncated when the repository has more.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `connectionUlid` — obligatorio
- `repository` — obligatorio

### `launch_github_workflow`

Call this to start an automated run in GitHub Actions: Probara creates the run and dispatches the repository's workflow on a branch with the run's ULID as the probara_run_id input; the workflow's reporters then report into that run. Requires project_id, connectionUlid and repository (owner/name, from list_project_github_repositories), ref (a branch, from list_github_branches) and workflowId (an id from list_github_workflows, or the workflow file name). Optionally runName and inputs (extra workflow_dispatch string inputs). Each call starts another run unless retried with the same idempotency_key. Returns the run with githubBuild (launchStatus dispatched, or unconfirmed when GitHub's answer was lost); the build's link, status and conclusion fill in as the workflow runs (get_run).

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura, sistemas externos

**Parámetros**

- `project_id` — obligatorio
- `connectionUlid` — obligatorio
- `repository` — obligatorio
- `ref` — obligatorio
- `workflowId` — obligatorio
- `runName` — opcional
- `inputs` — opcional
- `idempotency_key` — opcional

### `list_shared_steps`

Call this to list shared steps (reusable step sets that test cases reference) with offset pagination. Pass exactly one of project_id (the project code: its own sets plus the organization-wide ones) or org_id (from get_current_organization: the organization-wide sets only). Accepts scope (project, organization or all; project_id only), folderUlid (a folder, or `__none__` for sets in no folder), q (title substring), page and pageSize. Rows carry itemCount and usageCount but not the items; use get_shared_step for those. Returns items plus page, pageSize, and total. By default a call returns at most 50 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — opcional
- `org_id` — opcional
- `page` — opcional
- `pageSize` — opcional
- `scope` — opcional
- `folderUlid` — opcional
- `q` — opcional
- `stepFormat` — opcional

### `get_shared_step`

Call this to read one shared step with its ordered items. Requires shared_step_ulid. Pass exactly one of project_id (the project code: its own sets plus the organization-wide ones) or org_id (from get_current_organization: the organization-wide sets only).

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — opcional
- `org_id` — opcional
- `shared_step_ulid` — obligatorio

### `create_shared_step`

Call this to create a shared step: a titled, ordered set of steps that test cases reference instead of copying. Pass exactly one of project_id (the project code: its own sets plus the organization-wide ones) or org_id (from get_current_organization: the organization-wide sets only). project_id creates a project set; org_id creates an organization-wide set (needs an admin or owner and a plan that includes them). Requires title; optionally folderUlid (a folder of the same scope), stepFormat (classic by default, or gherkin; fixed once created, and only cases of the same format can use the set) and items (an array of \{ position, action, data?, expected?, keyword? \}; a gherkin item needs a keyword (Given, When, Then, And, But, or the asterisk keyword) and sends no expected, a classic item sends no keyword; items take no attachments: their images are inline in the item text). Returns the set, including its ulid, SS-n displayId and stepFormat.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — opcional
- `org_id` — opcional
- `title` — obligatorio
- `folderUlid` — opcional
- `stepFormat` — opcional
- `items` — opcional
- `idempotency_key` — opcional

### `update_shared_step`

Call this to retitle a shared step, move it to another folder (folderUlid, null for the root) or replace ALL its items (items, upserted by ulid like replace_test_case_steps). Requires shared_step_ulid and at least one field. Pass exactly one of project_id (the project code: its own sets plus the organization-wide ones) or org_id (from get_current_organization: the organization-wide sets only). Every case that uses the set reads the change at once; runs keep the steps they already snapshotted. Returns the updated set.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — opcional
- `org_id` — opcional
- `shared_step_ulid` — obligatorio
- `title` — opcional
- `folderUlid` — opcional
- `items` — opcional
- `idempotency_key` — opcional

### `delete_shared_step`

Call this to permanently delete a shared step. Requires shared_step_ulid. Pass exactly one of project_id (the project code: its own sets plus the organization-wide ones) or org_id (from get_current_organization: the organization-wide sets only). mode "keep" (the default) replaces every use of the set in test cases with inline copies of its items; mode "remove" removes those uses. Returns \{ deleted: true \}.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `project_id` — opcional
- `org_id` — opcional
- `shared_step_ulid` — obligatorio
- `mode` — opcional
- `idempotency_key` — opcional

### `list_shared_step_usage`

Call this to list the test cases that use a shared step, one row per case, with offset pagination. Requires shared_step_ulid. Pass exactly one of project_id (the project code: its own sets plus the organization-wide ones) or org_id (from get_current_organization: the organization-wide sets only). Accepts page and pageSize. Returns items (each case with its ulid, displayId, title, projectId and suite) plus page, pageSize, and total. By default a call returns at most 50 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — opcional
- `org_id` — opcional
- `shared_step_ulid` — obligatorio
- `page` — opcional
- `pageSize` — opcional

### `promote_shared_step`

Call this to make a project shared step organization-wide, so every project of the organization can use it. Irreversible. Requires project_id and shared_step_ulid; needs an admin or owner and a plan that includes organization-wide shared steps. The set keeps its displayId, items and every use, and moves to the organization root folder. Returns the promoted set.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `shared_step_ulid` — obligatorio
- `idempotency_key` — opcional

### `list_shared_step_folders`

Call this to list shared step folders, flat. Pass exactly one of project_id (the project code: its own sets plus the organization-wide ones) or org_id (from get_current_organization: the organization-wide sets only). Each folder carries its parentUlid (null at the root), scope and position among its siblings; nest them by parentUlid. Returns \{ items \}.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — opcional
- `org_id` — opcional

### `create_shared_step_folder`

Call this to create a shared step folder. Pass exactly one of project_id (the project code: its own sets plus the organization-wide ones) or org_id (from get_current_organization: the organization-wide sets only). Requires name; optionally parentUlid (a folder of the same scope; omitted creates it at the root). Names are unique among siblings. Returns the created folder, including its ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — opcional
- `org_id` — opcional
- `name` — obligatorio
- `parentUlid` — opcional
- `idempotency_key` — opcional

### `update_shared_step_folder`

Call this to rename a shared step folder, move it (parentUlid, null for the root) or reorder it among its siblings (position). Requires folder_ulid and at least one field. Pass exactly one of project_id (the project code: its own sets plus the organization-wide ones) or org_id (from get_current_organization: the organization-wide sets only). Returns the updated folder.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — opcional
- `org_id` — opcional
- `folder_ulid` — obligatorio
- `name` — opcional
- `parentUlid` — opcional
- `position` — opcional
- `idempotency_key` — opcional

### `delete_shared_step_folder`

Call this to delete a shared step folder. Requires folder_ulid. Pass exactly one of project_id (the project code: its own sets plus the organization-wide ones) or org_id (from get_current_organization: the organization-wide sets only). Its subfolders and shared steps move to its parent folder; no shared step is deleted. Returns \{ deleted: true \}.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `project_id` — opcional
- `org_id` — opcional
- `folder_ulid` — obligatorio
- `idempotency_key` — opcional

### `detach_shared_step_reference`

Call this to stop one test case from using a shared step while keeping its steps: it converts the whole shared step — the reference row and every reference to the same set right next to it in the case (the contiguous run, e.g. two ranges left after removing an item between them) — into inline copies of the items they cover, in place, in this case only. A use of the set separated by another step stays shared. Requires case_ulid and step_ulid (the ulid of a kind "shared" row from get_test_case_steps). Optional expectedRevision (the case’s currentRevision you last read) refuses the write with 409 conflict when the case changed since. Returns \{ steps, currentRevision \}: the case’s new step list and revision.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `case_ulid` — obligatorio
- `step_ulid` — obligatorio
- `expectedRevision` — opcional
- `idempotency_key` — opcional

### `convert_steps_to_shared_step`

Call this to turn consecutive inline steps of a test case into a new project shared step that the case then references in their place. Requires case_ulid, stepUlids (the consecutive inline steps, from get_test_case_steps) and title; optionally folderUlid (a folder of the case’s project). Optional expectedRevision (the case’s currentRevision you last read) refuses the write with 409 conflict when the case changed since. Returns \{ sharedStep, steps, currentRevision \}: the new set, the case’s new step list and its new revision.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `case_ulid` — obligatorio
- `stepUlids` — obligatorio
- `title` — obligatorio
- `folderUlid` — opcional
- `expectedRevision` — opcional
- `idempotency_key` — opcional

### `list_shared_parameters`

Call this to list the shared parameters a project can use: its own, and the organization-wide ones it can reach. Requires project_id. Accepts scope (project, organization or all), q (a name substring), page and pageSize. Returns items plus page, pageSize and total. Each item carries its kind and its values (a single) or its columns and rows (a group). By default a call returns at most 50 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `page` — opcional
- `pageSize` — opcional
- `scope` — opcional
- `q` — opcional

### `get_shared_parameter`

Call this to read one shared parameter with its values (a single) or its columns and rows (a group). Requires project_id and shared_parameter_ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `shared_parameter_ulid` — obligatorio

### `create_shared_parameter`

Call this to create a shared parameter that cases of the project reuse. Requires project_id, name, kind (single or group), scope and the content of the kind: values for a single, columns and rows for a group. A project scope needs projectUlid (this project’s ULID, from get_project); an organization scope takes projectUlids to limit it, or none for every project. Returns the shared parameter.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `name` — obligatorio
- `kind` — obligatorio
- `values` — opcional
- `columns` — opcional
- `rows` — opcional
- `scope` — obligatorio
- `projectUlid` — opcional
- `projectUlids` — opcional

### `update_shared_parameter`

Call this to change a shared parameter. Requires project_id and shared_parameter_ulid, and the fields to change: name, values (a single), columns and rows (a group), or projectUlids (an organization scope). Cases that use it see the change in their next revision. Returns the shared parameter.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `shared_parameter_ulid` — obligatorio
- `name` — opcional
- `values` — opcional
- `columns` — opcional
- `rows` — opcional
- `projectUlids` — opcional

### `delete_shared_parameter`

Call this to permanently delete a shared parameter. Requires project_id and shared_parameter_ulid. mode says what happens to the cases that reference it: convert_to_local (the default) gives each one its own copy of the values, remove drops the reference from every case. Returns \{ deleted: true \} on success. The first call returns a preview and a confirm_token; the call executes only when it is repeated with that confirm_token.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `project_id` — obligatorio
- `shared_parameter_ulid` — obligatorio
- `mode` — opcional
- `confirm_token` — opcional

### `list_shared_parameter_usage`

Call this to see which projects hold cases that reference a shared parameter, and how many cases each holds. Requires project_id and shared_parameter_ulid. Accepts page and pageSize. Returns items (projectUlid, projectName, caseCount) plus page, pageSize and total. By default a call returns at most 50 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `shared_parameter_ulid` — obligatorio
- `page` — opcional
- `pageSize` — opcional

### `preview_test_case_combinations`

Call this to preview the combinations a case produces, read-only. Requires project_id and case_ulid (its ULID or display ID). Returns combinationCount (the combinations its parameters produce before exclusions), combinations (the ones left after them, in expansion order) and excludedCombinations. A run creates one run case per remaining combination.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `case_ulid` — obligatorio

### `detach_test_case_parameter`

Call this to convert one shared reference of a case to a local copy: the case keeps the values as its own parameter, and the shared parameter stays for the other cases. Requires project_id, case_ulid (or its display ID) and position, the 1-based place of the parameter in the case’s list. Pass expectedRevision (the case revision you read) to refuse a stale write. Returns the case’s parameters after the convert.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `case_ulid` — obligatorio
- `position` — obligatorio
- `expectedRevision` — opcional

### `list_dashboards`

Call this to list the dashboards you can see, most recently updated first, with offset pagination. Accepts page (1-based) and pageSize. Each item carries canEdit (whether you may edit and delete it). Returns items plus page, pageSize and total. Needs a plan that includes dashboards. By default a call returns at most 50 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `page` — opcional
- `pageSize` — opcional

### `get_dashboard`

Call this to fetch one dashboard by its ulid. Requires dashboard_ulid. A dashboard you cannot see answers not_found. Needs a plan that includes dashboards.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `dashboard_ulid` — obligatorio

### `create_dashboard`

Call this to create an organization dashboard. Requires name; optionally description and accessMode: public (the default; every organization member can see it) or private (only you, organization owners and admins, plus the members (user ULIDs) and groups (user group ULIDs) you list, at most 200 each, no repeats; both lists have to be empty on a public dashboard). Everything is created in one step or not at all. You become its owner. Requires the dashboards.write permission (owner, admin, member) and a plan that includes dashboards. Returns the created dashboard, including its ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `name` — obligatorio
- `description` — opcional
- `accessMode` — opcional
- `members` — opcional
- `groups` — opcional
- `idempotency_key` — opcional

### `update_dashboard`

Call this to rename a dashboard or change its description (null clears it). Requires dashboard_ulid and at least one field. A dashboard you cannot see answers not_found; one you can see but neither created nor administer (organization owner/admin) answers forbidden. Needs a plan that includes dashboards. Returns the updated dashboard.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `dashboard_ulid` — obligatorio
- `name` — opcional
- `description` — opcional
- `idempotency_key` — opcional

### `replace_dashboard_settings`

Call this to replace all of a dashboard’s settings in one step (all or nothing). Requires dashboard_ulid, name, description (null clears it), accessMode, members (user ULIDs of current organization members) and groups (user group ULIDs), at most 200 each, no repeats. Afterwards exactly those members hold a granted grant and exactly those groups are granted; revoked grants you do not list stay. A public dashboard takes empty lists and loses every granted member and group. A dashboard you cannot see answers not_found; one you can see but neither created nor administer (organization owner/admin) answers forbidden. Needs a plan that includes dashboards. Returns the dashboard.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `dashboard_ulid` — obligatorio
- `name` — obligatorio
- `description` — obligatorio
- `accessMode` — obligatorio
- `members` — obligatorio
- `groups` — obligatorio
- `idempotency_key` — opcional

### `delete_dashboard`

Call this to permanently delete a dashboard and the access grants it holds. Requires dashboard_ulid. A dashboard you cannot see answers not_found; one you can see but neither created nor administer (organization owner/admin) answers forbidden. Needs a plan that includes dashboards. Returns \{ deleted: true \}. The first call returns a preview and a confirm_token; the call executes only when it is repeated with that confirm_token.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `dashboard_ulid` — obligatorio
- `idempotency_key` — opcional
- `confirm_token` — opcional

### `get_dashboard_access`

Call this to read who can see a dashboard: its mode (public: every member; private: only granted members and groups), its creator (ownerUserUlid), and how many members hold a granted or a revoked grant. Requires dashboard_ulid. Only the dashboard’s creator and organization owners and admins manage its access: a dashboard you cannot see answers not_found, one you can see but did not create answers forbidden. Needs a plan that includes dashboards.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `dashboard_ulid` — obligatorio

### `update_dashboard_access`

Call this to make a dashboard public or private. Requires dashboard_ulid and mode. Making it public removes every granted member and every group grant (revoked members stay out); making it private hides it from every member without a grant, except its creator and organization owners and admins. Only the dashboard’s creator and organization owners and admins manage its access: a dashboard you cannot see answers not_found, one you can see but did not create answers forbidden. Needs a plan that includes dashboards. Returns the access state.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `dashboard_ulid` — obligatorio
- `mode` — obligatorio
- `idempotency_key` — opcional

### `list_dashboard_access_members`

Call this to list the current organization members holding a grant on a dashboard, each with its access (granted or revoked), with offset pagination. Requires dashboard_ulid. Only the dashboard’s creator and organization owners and admins manage its access: a dashboard you cannot see answers not_found, one you can see but did not create answers forbidden. Needs a plan that includes dashboards. By default a call returns at most 50 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `dashboard_ulid` — obligatorio
- `page` — opcional
- `pageSize` — opcional

### `set_dashboard_access_member`

Call this to set one organization member’s grant on a dashboard. Requires dashboard_ulid and userUlid; access is granted (the default: lets them into a private dashboard; refused with conflict on a public one) or revoked (keeps them out, even of a public dashboard or one a group of theirs was granted). Only the dashboard’s creator and organization owners and admins manage its access: a dashboard you cannot see answers not_found, one you can see but did not create answers forbidden. Needs a plan that includes dashboards. Returns the member with its access.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura, idempotente

**Parámetros**

- `dashboard_ulid` — obligatorio
- `userUlid` — obligatorio
- `access` — opcional
- `idempotency_key` — opcional

### `remove_dashboard_access_member`

Call this to remove a member’s grant (granted or revoked) from a dashboard, so the dashboard’s mode decides again. Requires dashboard_ulid and user_ulid. Only the dashboard’s creator and organization owners and admins manage its access: a dashboard you cannot see answers not_found, one you can see but did not create answers forbidden. Needs a plan that includes dashboards. Returns \{ removed: true \}.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `dashboard_ulid` — obligatorio
- `user_ulid` — obligatorio
- `idempotency_key` — opcional

### `list_dashboard_access_groups`

Call this to list the user groups granted a dashboard, by name, with offset pagination. Requires dashboard_ulid. Only the dashboard’s creator and organization owners and admins manage its access: a dashboard you cannot see answers not_found, one you can see but did not create answers forbidden. Needs a plan that includes dashboards. By default a call returns at most 50 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `dashboard_ulid` — obligatorio
- `page` — opcional
- `pageSize` — opcional

### `add_dashboard_access_group`

Call this to grant a user group a private dashboard: its current and future members see it. Refused with conflict on a public dashboard, which every member already sees. Requires dashboard_ulid and userGroupUlid. Only the dashboard’s creator and organization owners and admins manage its access: a dashboard you cannot see answers not_found, one you can see but did not create answers forbidden. Needs a plan that includes dashboards. Returns the group.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `dashboard_ulid` — obligatorio
- `userGroupUlid` — obligatorio
- `idempotency_key` — opcional

### `remove_dashboard_access_group`

Call this to remove a user group’s grant from a dashboard. Requires dashboard_ulid and user_group_ulid. Only the dashboard’s creator and organization owners and admins manage its access: a dashboard you cannot see answers not_found, one you can see but did not create answers forbidden. Needs a plan that includes dashboards. Returns \{ removed: true \}.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `dashboard_ulid` — obligatorio
- `user_group_ulid` — obligatorio
- `idempotency_key` — opcional

### `list_dashboard_widgets`

Call this to list the widgets of a dashboard in grid order (position), at most 50, not paged. Requires dashboard_ulid. Anyone who can see the dashboard reads its widgets; only its creator and organization owners and admins change them (a dashboard you cannot see answers not_found, one you cannot change answers forbidden). Needs a plan that includes dashboards. Returns \{ items \}.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `dashboard_ulid` — obligatorio

### `create_dashboard_widget`

Call this to add a widget at the end of a dashboard (at most 50 per dashboard). Requires dashboard_ulid and type. type is one of: section_title (a heading; requires title; no other field), count (metric: test_cases, open_defects, active_runs, suites, flaky_cases), created_count (metric: test_cases, defects, results, runs; rangeDays), distribution (customFieldUlid of a test case custom field of type select_single, radio, checkbox, select_multi or user_picker; chart: donut or bar), time_series (metric: open_defects, result_events, case_events, run_pass_rate, run_average_duration; rangeDays; chart: auto, the default (bars or a line), or heatmap, a calendar with one day per cell, for result_events and case_events only and at most 365 days) and pass_rate (the pooled pass rate of the runs closed in the range; rangeDays; chart: gauge or bullet, default gauge; target: a whole number from 1 to 100, default 80, which the bullet is measured against). rangeDays is 7, 14, 30, 90, 180, 365 or 730 (default 30). width is third, half, two_thirds or full (defaults per type). height is the number of grid rows, 2 to 8, on every type but section_title (default 2 for count and created_count, 3 for pass_rate, 4 for distribution and time_series). query (savedQueryUlid: the saved query it draws, read live, so an edit of the query reaches the widget; render: table, bar, line, donut or value to override the query’s own visualization, or null, the default, to keep it; no filters and no rangeDays). title is optional except on section_title (null shows a default title). projects is \{ mode: "all" \} (every project the viewer can open, the default) or \{ mode: "selected", projectUlids: [...] \} (1 to 50 projects you can open). Each widget returns projects.projectUlids (the selected projects you can open) and projects.hiddenProjectCount (selected projects you cannot). filters (every type but section_title) narrows the number for every viewer: at most 10, one per field (one per custom field), each \{ field, values \} with 1 to 50 distinct values; values of a filter are OR’ed, filters are AND’ed. An update’s filters replace the whole list ([] removes all). Fields by what the metric counts: test cases (count test_cases, flaky_cases; created_count test_cases; time_series case_events; distribution) author, suite (sub-suites included), tag, milestone, custom_field; defects (count and time_series open_defects; created_count defects) reporter, assignee, milestone, tag, custom_field, plus status on created_count defects only; runs (count active_runs; created_count runs; time_series run_pass_rate, run_average_duration; pass_rate) author, environment, milestone, test_plan, tag, custom_field; results (created_count results; time_series result_events) status, executed_by, environment, milestone. count suites takes none. Values are member, suite, tag, milestone, environment or test plan ULIDs; status takes the defect or result status tokens; custom_field adds customFieldUlid (a select_single, radio, checkbox, select_multi or user_picker field of the counted entity) and takes option ULIDs, member ULIDs (user_picker) or "true"/"false" (checkbox). An unknown value, or one in a project you cannot open, is a validation error; a stored value whose entity was later deleted stays (label null) and matches nothing until removed. A metric change that leaves a filter the new metric does not offer is a validation error unless the same call sends filters. Widgets return filters in a fixed order, values sorted, each value with a label. Anyone who can see the dashboard reads its widgets; only its creator and organization owners and admins change them (a dashboard you cannot see answers not_found, one you cannot change answers forbidden). Needs a plan that includes dashboards. Returns the widget, including its ulid.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `dashboard_ulid` — obligatorio
- `type` — obligatorio
- `title` — opcional
- `width` — opcional
- `height` — opcional
- `projects` — opcional
- `metric` — opcional
- `rangeDays` — opcional
- `customFieldUlid` — opcional
- `chart` — opcional
- `target` — opcional
- `filters` — opcional
- `savedQueryUlid` — opcional
- `render` — opcional
- `idempotency_key` — opcional

### `update_dashboard_widget`

Call this to change a widget’s title, width, height, projects or the configuration fields of its type (a field its type does not have is a validation error; the type does not change). Requires dashboard_ulid, widget_ulid and at least one field. A selected projects list replaces only the projects you can open; projects hidden from you stay, so the list may be empty while some remain (a selected scope left with no project at all is a validation error). \{ mode: "all" \} removes every selected project. type is one of: section_title (a heading; requires title; no other field), count (metric: test_cases, open_defects, active_runs, suites, flaky_cases), created_count (metric: test_cases, defects, results, runs; rangeDays), distribution (customFieldUlid of a test case custom field of type select_single, radio, checkbox, select_multi or user_picker; chart: donut or bar), time_series (metric: open_defects, result_events, case_events, run_pass_rate, run_average_duration; rangeDays; chart: auto, the default (bars or a line), or heatmap, a calendar with one day per cell, for result_events and case_events only and at most 365 days) and pass_rate (the pooled pass rate of the runs closed in the range; rangeDays; chart: gauge or bullet, default gauge; target: a whole number from 1 to 100, default 80, which the bullet is measured against). rangeDays is 7, 14, 30, 90, 180, 365 or 730 (default 30). width is third, half, two_thirds or full (defaults per type). height is the number of grid rows, 2 to 8, on every type but section_title (default 2 for count and created_count, 3 for pass_rate, 4 for distribution and time_series). query (savedQueryUlid: the saved query it draws, read live, so an edit of the query reaches the widget; render: table, bar, line, donut or value to override the query’s own visualization, or null, the default, to keep it; no filters and no rangeDays). filters (every type but section_title) narrows the number for every viewer: at most 10, one per field (one per custom field), each \{ field, values \} with 1 to 50 distinct values; values of a filter are OR’ed, filters are AND’ed. An update’s filters replace the whole list ([] removes all). Fields by what the metric counts: test cases (count test_cases, flaky_cases; created_count test_cases; time_series case_events; distribution) author, suite (sub-suites included), tag, milestone, custom_field; defects (count and time_series open_defects; created_count defects) reporter, assignee, milestone, tag, custom_field, plus status on created_count defects only; runs (count active_runs; created_count runs; time_series run_pass_rate, run_average_duration; pass_rate) author, environment, milestone, test_plan, tag, custom_field; results (created_count results; time_series result_events) status, executed_by, environment, milestone. count suites takes none. Values are member, suite, tag, milestone, environment or test plan ULIDs; status takes the defect or result status tokens; custom_field adds customFieldUlid (a select_single, radio, checkbox, select_multi or user_picker field of the counted entity) and takes option ULIDs, member ULIDs (user_picker) or "true"/"false" (checkbox). An unknown value, or one in a project you cannot open, is a validation error; a stored value whose entity was later deleted stays (label null) and matches nothing until removed. A metric change that leaves a filter the new metric does not offer is a validation error unless the same call sends filters. Widgets return filters in a fixed order, values sorted, each value with a label. Anyone who can see the dashboard reads its widgets; only its creator and organization owners and admins change them (a dashboard you cannot see answers not_found, one you cannot change answers forbidden). Needs a plan that includes dashboards. Returns the widget.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `dashboard_ulid` — obligatorio
- `widget_ulid` — obligatorio
- `title` — opcional
- `width` — opcional
- `height` — opcional
- `projects` — opcional
- `metric` — opcional
- `rangeDays` — opcional
- `customFieldUlid` — opcional
- `chart` — opcional
- `target` — opcional
- `filters` — opcional
- `savedQueryUlid` — opcional
- `render` — opcional
- `idempotency_key` — opcional

### `delete_dashboard_widget`

Call this to remove a widget from a dashboard; the widgets after it move up one position. Requires dashboard_ulid and widget_ulid. Anyone who can see the dashboard reads its widgets; only its creator and organization owners and admins change them (a dashboard you cannot see answers not_found, one you cannot change answers forbidden). Needs a plan that includes dashboards. Returns \{ deleted: true \}.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `dashboard_ulid` — obligatorio
- `widget_ulid` — obligatorio
- `idempotency_key` — opcional

### `reorder_dashboard_widgets`

Call this to put a dashboard’s widgets in a new order. Requires dashboard_ulid and widgetUlids: every widget of the dashboard exactly once, in the new order (anything else is a validation error). Anyone who can see the dashboard reads its widgets; only its creator and organization owners and admins change them (a dashboard you cannot see answers not_found, one you cannot change answers forbidden). Needs a plan that includes dashboards. Returns \{ items \} in the new order.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura, idempotente

**Parámetros**

- `dashboard_ulid` — obligatorio
- `widgetUlids` — obligatorio
- `idempotency_key` — opcional

### `get_dashboard_widget_data`

Call this to read what a widget shows. Requires dashboard_ulid and widget_ulid. The numbers are computed with YOUR project access: they cover only the projects of the widget’s scope you can open (locked and archived projects do not count). scope.includedProjectCount is how many projects were counted and scope.hiddenProjectCount how many were left out, so report a partial number as partial; scope.emptySelection is true when every selected project was deleted. count returns data.value: test_cases (not archived), open_defects (status open or in_progress), active_runs (not closed), suites, flaky_cases (not archived, "Is flaky" checked). created_count returns data.value created in range [from, to) (epoch ms, ending now): the widget’s rangeDays cut to the plan’s history retention (range.clampedByRetention). distribution returns data.buckets of the cases not archived in projects where the field applies, by value: option (options’ order, name, color), checkbox (true/false) or user (largest first; formerMember, label null when unknown) buckets, then not_set (key null: no value, or a deleted option); past 20 the smallest fold into other. data.totalCases counts the cases; data.multiValue true (a multi-select field) means a case counts in each option it holds, so buckets can exceed it. data.fieldRemoved true means the field was deleted. time_series returns range plus data.buckets [start, end) by day (up to 31 days), week (Monday, up to 186) or month, on your profile time zone’s midnights (data.timeZone, UTC if unset), and data.series with one value per bucket: open_defects (open at each bucket end), result_events (passed, failed, skipped, blocked), case_events (created, edited), run_pass_rate (passRate: passed ÷ executed of runs closed in the bucket, aborted excluded, null if none executed), run_average_duration (averageDurationMs, null if no run closed). pass_rate returns range and data.value (passed ÷ executed of the runs closed in the range, aborted excluded, null if none executed) with data.passed, data.executed and data.closedRuns. A section_title has no data: a validation error. Anyone who can see the dashboard can call this; needs a plan that includes dashboards.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `dashboard_ulid` — obligatorio
- `widget_ulid` — obligatorio

### `list_dashboard_widget_records`

Call this to list the records behind one value of a widget. Requires dashboard_ulid and widget_ulid, and the selector the widget takes: none for count, created_count and pass_rate (the whole value), bucket for a distribution (a bucket key from get_dashboard_widget_data), bucket_start (and series where the metric has several) for a time series. Returns \{ kind, total, page, pageSize, items \}: total is the number the widget shows for that selector, items are one page of its records, all of one kind: test_case, defect, run, result or suite, each with its project (projectUlid, projectCode, projectName) and display ids to link it. Only projects you can open are listed (the same ones the number counts). Newest first. A selector the widget does not take is a validation error; a bucket the widget does not show is not_found. Anyone who can see the dashboard can call this; needs a plan that includes dashboards. By default a call returns at most 50 items; pass page_size to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `dashboard_ulid` — obligatorio
- `widget_ulid` — obligatorio
- `bucket` — opcional
- `bucket_start` — opcional
- `series` — opcional
- `page` — opcional
- `page_size` — opcional

### `run_pql_query`

Call this to run one PQL query and read one page of its result: rows, or groups when the query aggregates. Requires query (1 to 4,000 characters); offset (0 to 10,000) pages through the result. A CSV file is not an MCP result: export the rows with POST /api/v1/query/export. PQL (Probara Query Language) reads the organization’s test data: a query names one entity, filters it, and can group and aggregate it. For example, FROM results WHERE status = failed lists the failed results you can open. Syntax and examples: https://docs.probara.net/en/developers/api/pql/. Needs a plan that includes dashboards.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `query` — obligatorio
- `offset` — opcional

### `get_pql_schema`

Call this to read the schema a PQL query is checked against: the custom fields that apply to your projects, the active members and user groups, the saved query names you can see, and the value lists for suites, milestones, plans, environments, projects and tags (each capped at 500 names). Takes no arguments; call it before writing a query that names those values. PQL (Probara Query Language) reads the organization’s test data: a query names one entity, filters it, and can group and aggregate it. For example, FROM results WHERE status = failed lists the failed results you can open. Syntax and examples: https://docs.probara.net/en/developers/api/pql/. Needs a plan that includes dashboards.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**: Esta herramienta no recibe parámetros.

### `list_saved_queries`

Call this to list the saved queries you can see, most recently updated first, with offset pagination. Accepts page (1-based), pageSize (up to 200) and q (part of a name, ignoring case). Each item carries canEdit. PQL (Probara Query Language) reads the organization’s test data: a query names one entity, filters it, and can group and aggregate it. For example, FROM results WHERE status = failed lists the failed results you can open. Syntax and examples: https://docs.probara.net/en/developers/api/saved-queries/. Needs a plan that includes dashboards. By default a call returns at most 50 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `page` — opcional
- `pageSize` — opcional
- `q` — opcional

### `get_saved_query`

Call this to read one saved query by its ulid: its PQL text, drawing, visibility and, if you may edit it, its grants. Requires saved_query_ulid. A query you cannot see answers not_found. PQL (Probara Query Language) reads the organization’s test data: a query names one entity, filters it, and can group and aggregate it. For example, FROM results WHERE status = failed lists the failed results you can open. Syntax and examples: https://docs.probara.net/en/developers/api/saved-queries/. Needs a plan that includes dashboards.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `saved_query_ulid` — obligatorio

### `get_saved_query_usage`

Call this to list the dashboards that draw one saved query, before you delete it. Requires saved_query_ulid. It answers the dashboards you can open (ulid and name), and hiddenDashboardCount, how many more draw it that you cannot open (those are counted, not named). A query you cannot see answers not_found. Needs a plan that includes dashboards.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `saved_query_ulid` — obligatorio

### `create_saved_query`

Call this to save a PQL query under a name in the organization. Requires name (1 to 120 characters) and pql (1 to 4,000 characters). Optional: description; visualization, an object whose render is table, bar, line, donut or value (table by default) and whose series names the column that splits a chart; accessMode, public by default or private with members (user ULIDs) and groups (user group ULIDs) who may see it, each list at most 200 with no repeats and empty on a public query. The text is checked before anything is saved: invalid text answers pql_invalid and a construct not yet available answers pql_unsupported. A name another query of the organization holds, ignoring case, answers conflict. Requires the queries.write permission (owner, admin, member). Returns the saved query, including its ulid. PQL (Probara Query Language) reads the organization’s test data: a query names one entity, filters it, and can group and aggregate it. For example, FROM results WHERE status = failed lists the failed results you can open. Syntax and examples: https://docs.probara.net/en/developers/api/saved-queries/. Needs a plan that includes dashboards.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `name` — obligatorio
- `description` — opcional
- `pql` — obligatorio
- `visualization` — opcional
- `accessMode` — opcional
- `members` — opcional
- `groups` — opcional
- `idempotency_key` — opcional

### `update_saved_query`

Call this to replace all the settings of a saved query in one step, all or nothing. Requires saved_query_ulid and every other field: name, description (null clears it), pql, visualization, accessMode, members and groups. Send the current value of each field you keep, because a missing field is refused. Afterwards the grants are exactly the members and groups you list; a public query takes empty members and groups. Only its creator and organization owners and admins may change it: a query you can see but may not change answers forbidden, and one you cannot see answers not_found. The text is checked as on create. Returns the saved query. PQL (Probara Query Language) reads the organization’s test data: a query names one entity, filters it, and can group and aggregate it. For example, FROM results WHERE status = failed lists the failed results you can open. Syntax and examples: https://docs.probara.net/en/developers/api/saved-queries/. Needs a plan that includes dashboards.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `saved_query_ulid` — obligatorio
- `name` — obligatorio
- `description` — obligatorio
- `pql` — obligatorio
- `visualization` — obligatorio
- `accessMode` — obligatorio
- `members` — obligatorio
- `groups` — obligatorio
- `idempotency_key` — opcional

### `delete_saved_query`

Call this to permanently delete a saved query and its grants. Requires saved_query_ulid. Only its creator and organization owners and admins may delete it: a query you can see but may not delete answers forbidden, and one you cannot see answers not_found. Returns \{ deleted: true \}. PQL (Probara Query Language) reads the organization’s test data: a query names one entity, filters it, and can group and aggregate it. For example, FROM results WHERE status = failed lists the failed results you can open. Syntax and examples: https://docs.probara.net/en/developers/api/saved-queries/. Needs a plan that includes dashboards.

**Listado**: No se lista por defecto. Ejecútala con `run_delete_operation`.

**Anotaciones**: escritura, destructiva, idempotente

**Parámetros**

- `saved_query_ulid` — obligatorio
- `idempotency_key` — opcional

### `run_saved_query`

Call this to run a saved query with your own project access. It answers the same page the PQL query endpoint answers for the saved text: rows, or groups when the query aggregates. Requires saved_query_ulid; offset (0 to 10,000) pages through the result. A query you cannot see answers not_found. PQL (Probara Query Language) reads the organization’s test data: a query names one entity, filters it, and can group and aggregate it. For example, FROM results WHERE status = failed lists the failed results you can open. Syntax and examples: https://docs.probara.net/en/developers/api/saved-queries/. Needs a plan that includes dashboards.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `saved_query_ulid` — obligatorio
- `offset` — opcional

### `list_traceability_sources`

Call this before create_traceability_report to find which issue tracker connections a report can read. Requires project_id. Returns items: each active connection with its provider (jira or github), connectionUlid and label (the Jira site, or the GitHub account the app is installed on). An empty list means the organization has no active integration and no report can be created.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio

### `list_traceability_source_projects`

Call this to list the Jira projects one Jira connection allows, to choose a report’s projects. Requires project_id and connection_ulid (a jira source from list_traceability_sources). Returns items: each project’s projectKey and name.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `connection_ulid` — obligatorio

### `list_traceability_source_issue_types`

Call this to list the issue types of chosen projects on one Jira connection, to narrow a report’s filters.issueTypes. Requires project_id, connection_ulid and projects (one or more project keys, at most 20). Returns items: the distinct issue type names, sorted.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `connection_ulid` — obligatorio
- `projects` — obligatorio

### `list_traceability_source_repositories`

Call this to list the GitHub repositories a report may read through one GitHub connection: the project’s repository mapping, or every repository the installation can see when the project maps none. Requires project_id and connection_ulid (a github source from list_traceability_sources). Returns items: each repository’s repositoryId and fullName (owner/name).

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `connection_ulid` — obligatorio

### `list_traceability_source_labels`

Call this to list the labels of one GitHub repository, to narrow a report’s filters.labels. Requires project_id, connection_ulid and repository (owner/name, from list_traceability_source_repositories). Refused with github_repository_not_mapped when the project’s mapping leaves the repository out. Returns items: the label names.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `connection_ulid` — obligatorio
- `repository` — obligatorio

### `list_traceability_source_milestones`

Call this to list the milestones (open and closed) of one GitHub repository, to pick a report’s filters.milestone. Requires project_id, connection_ulid and repository (owner/name). Refused with github_repository_not_mapped when the project’s mapping leaves the repository out. Returns items: each milestone’s title and state.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `connection_ulid` — obligatorio
- `repository` — obligatorio

### `list_traceability_reports`

Call this to list a project’s traceability reports, newest first. Requires project_id. Returns items: each report with its ulid, name, filters (the tracker, and the Jira projects and issue types or the GitHub repositories, labels, milestone and state it covers), versionCount and latestVersion (the newest version whatever its status, or null). Use a report’s ulid as report_ulid in list_traceability_report_versions (every version, not only the latest) or in generate_traceability_version.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio

### `list_traceability_report_versions`

Call this to list every version of a traceability report, newest first, not only its latest one. Requires project_id and report_ulid (from list_traceability_reports). Returns items: each version with its ulid, number, name, status (pending, generating, ready or failed), errorCode, totals (null until ready) and generatedAt. Use a version’s ulid as version_ulid in get_traceability_version or list_traceability_version_requirements.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `report_ulid` — obligatorio

### `create_traceability_report`

Call this to save a traceability report for a project and queue its first version. Requires project_id, name and filters. Jira: \{ provider: "jira", projects: [\{ connectionUlid, projectKey \}], issueTypes \}; find project keys with list_traceability_source_projects; issueTypes is optional (omit it to keep every issue type of the projects, or pick names from list_traceability_source_issue_types); each project has to be allowed by an active Jira connection, or the call answers traceability_project_not_allowed and saves nothing. GitHub: \{ provider: "github", repositories: [\{ connectionUlid, fullName \}], labels, milestone, state \}; find repositories with list_traceability_source_repositories; labels (an issue has to carry all of them), milestone (a title) and state (open, closed or all, the default) are optional; pull requests are not requirements; a repository the project’s mapping leaves out answers github_repository_not_mapped and saves nothing. Find connectionUlid with list_traceability_sources. Returns the report and its version (status pending): poll get_traceability_version until the version is ready.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `name` — obligatorio
- `filters` — obligatorio
- `idempotency_key` — opcional

### `generate_traceability_version`

Call this to generate a new version of a traceability report: a snapshot, taken as it generates, of the requirements its filters match and of their linked test cases. Requires project_id and report_ulid. Refused with traceability_version_in_progress while another version of the report is pending or generating. Returns the new version with status pending: read it with get_traceability_version until its status is ready.

**Listado**: No se lista por defecto. Ejecútala con `run_write_operation`.

**Anotaciones**: escritura

**Parámetros**

- `project_id` — obligatorio
- `report_ulid` — obligatorio
- `idempotency_key` — opcional

### `get_traceability_version`

Call this to read a traceability version. Requires project_id and version_ulid. Returns its status (pending, generating, ready or failed; errorCode says why a version failed), its name, its totals (requirements, linked, unlinked; null until ready) and, once ready, the report’s filters and the totals of each selected project. Poll it after generate_traceability_version until its status is ready.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `version_ulid` — obligatorio

### `list_traceability_version_requirements`

Call this to list the requirements (Jira issues or GitHub issues) of a ready traceability version, one tab at a time. Tab "linked" (the default) lists requirements with their linked test cases, their latest results and their defects; tab "unlinked" lists requirements no test case covers. Requires project_id and version_ulid; a version that is not ready answers traceability_version_not_ready. Optional filters: q (an issue key such as WEB-12 or owner/name#12, or text over the title and key), projectKey (a Jira project key or a GitHub owner/name), status (any of to_do, in_progress, done), resultStatus and defectStatus (ignored on the unlinked tab). Pages with page (from 1, default 1) and pageSize (1-100, default 25). Returns items, page, pageSize and total (the requirements matching every filter, over all pages); a page past the last one has no items. By default a call returns at most 25 items; pass pageSize to change it.

**Listado**: No se lista por defecto. Ejecútala con `run_read_operation`.

**Anotaciones**: solo lectura, idempotente

**Parámetros**

- `project_id` — obligatorio
- `version_ulid` — obligatorio
- `tab` — opcional
- `q` — opcional
- `projectKey` — opcional
- `status` — opcional
- `resultStatus` — opcional
- `defectStatus` — opcional
- `page` — opcional
- `pageSize` — opcional
