Skip to content

Commit db1ab5f

Browse files
MimoJanraclaude
andcommitted
feat: add find_project, remove_test_cases_from_launch, and initialize instructions
- find_project: resolve a project by name/code (e.g. "TSi") to its numeric ID via a client-side scan of /api/project (no server-side name filter exists). - remove_test_cases_from_launch: trim test cases from a launch by hiding (default, non-destructive) or permanently deleting their test results. - initialize now returns an `instructions` capability overview so MCP clients tell the model what the server can do, improving tool discoverability. Tool count 112 -> 114. Docs (README, RELEASES, API.md, llms.txt, llms-full.txt, CHANGELOG) synced; validation tests and an initialize-instructions test added. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 5cd3e3d commit db1ab5f

15 files changed

Lines changed: 487 additions & 22 deletions

File tree

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [2.1.0] - 2026-06-19 - Project Search, Launch Cleanup & Tool Discoverability
11+
12+
### Added
13+
14+
- **`find_project`** — find a project by name or code (case-insensitive substring match). Resolves a human-readable name/code (e.g. `TSi`) to its numeric project ID without paging through `list_projects` or guessing IDs. Scans `/api/project` client-side since the Allure TestOps API exposes no server-side project name filter; returns `matches`, `count`, `scanned`, and a `truncated` flag when more matches may exist beyond `limit` (default 20) or the 5000-project scan cap.
15+
16+
- **`remove_test_cases_from_launch`** — remove test cases from a launch (e.g. to trim a launch with too many cases or duplicates, including already-started ones). Resolves each `test_case_id` to its test result(s) in the launch — including retries — and removes them. `mode="hide"` (default) excludes the results from the report but keeps the data (`POST /api/testresult/bulk/hide`, non-destructive); `mode="delete"` permanently deletes them (`DELETE /api/testresult/{id}`). Since the Allure API has no bulk test-result delete, `delete` issues one request per result and reports per-result failures under `failed`. Returns `removed_count`, `removed_result_ids`, `not_found_test_case_ids`, and `truncated`.
17+
18+
- **`initialize` now returns an `instructions` field** — the MCP handshake response includes a capability overview (tool groups, how to resolve names→IDs, the `search_testops_operations`/`execute_testops_operation` fallback for the 600+ endpoints, and safety notes). MCP clients add this to the model's system prompt, so Claude knows what the server can do instead of inferring it from bare tool names. Directly improves tool discoverability.
19+
20+
- Tool count: 112 → 114.
21+
22+
### Fixed
23+
24+
- **`llms.txt` listed the removed `update_launch_environment` tool** — dropped from the Launches group (it was removed in 2.0.3) and the group count corrected.
25+
1026
## [2.0.3] - 2026-06-01 - API Compliance Fixes
1127

1228
### Fixed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Integrate **Allure TestOps** with Claude using the Model Context Protocol. Launc
2626
- Works with Claude Desktop, Claude Web, and custom MCP clients
2727

2828
### 🏭 Production-Grade
29-
- **112 tools** — complete Allure TestOps test case API coverage + full OpenAPI fallback (600+ endpoints)
29+
- **114 tools** — complete Allure TestOps test case API coverage + full OpenAPI fallback (600+ endpoints)
3030
- **MCP Prompts** — built-in templates (`analyze-test-failures`, `launch-report-summary`) for one-click workflows
3131
- **MCP Resources** — attach `allure://docs/quickstart` as context; widget resources for visual dashboards
3232
- **AI analysis**`analyze_launch_failures` asks Claude to find root causes via MCP sampling
@@ -324,7 +324,7 @@ If you prefer running without Docker:
324324
- **`configure_allure_token`** — Set token in chat (if not in config)
325325

326326
### Launch Management
327-
`run_allure_launch`\*`get_launch_status``get_launch_report``list_launches``get_launch_details``close_launch``reopen_launch``copy_launch`\*`merge_launches`\*`get_launch_environment``update_launch_environment`
327+
`run_allure_launch`\*`get_launch_status``get_launch_report``list_launches``get_launch_details``close_launch``reopen_launch``copy_launch`\*`merge_launches`\*`get_launch_environment``add_test_cases_to_launch``add_test_plan_to_launch``remove_test_cases_from_launch`
328328

329329
> \* async — returns `task_id` immediately; track with `get_task_status`
330330
@@ -359,7 +359,7 @@ If you prefer running without Docker:
359359
`bulk_set_test_case_status``bulk_add_test_case_tags``bulk_remove_test_case_tags``bulk_clone_test_cases``bulk_mute_test_cases``bulk_delete_test_cases``bulk_move_test_cases``bulk_set_test_case_layer``bulk_add_test_case_members``bulk_remove_test_case_members``bulk_add_test_case_custom_fields``bulk_remove_test_case_custom_fields``bulk_add_test_case_external_links``bulk_add_test_case_issues``bulk_remove_test_case_issues``bulk_run_test_cases_new_launch``bulk_run_test_cases_existing_launch``bulk_create_test_plan`
360360

361361
### Projects & Analytics
362-
`list_projects``get_project``get_project_stats``get_launch_trend_analytics``get_launch_duration_analytics``get_test_success_rate`
362+
`list_projects``find_project``get_project``get_project_stats``get_launch_trend_analytics``get_launch_duration_analytics``get_test_success_rate`
363363

364364
### Async Task Management
365365
`get_task_status``list_running_tasks``cancel_task`

RELEASES.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ All pre-built binaries are provided for easy setup without requiring Go installa
55
## 🆕 What's New in v2.0.0
66

77
- **MCP protocol 2025-11-25** with version negotiation and stable cursor-based pagination
8-
- **112 tools** (+ `get_task_status`, `list_running_tasks`, `cancel_task`, `analyze_launch_failures`)
8+
- **114 tools** (+ `find_project`, `remove_test_cases_from_launch`, `get_task_status`, `list_running_tasks`, `cancel_task`, `analyze_launch_failures`)
99
- **Async task system**`run_allure_launch`, `copy_launch`, `merge_launches`, bulk runs return `task_id` immediately; tasks auto-expire after 1 hour
1010
- **AI failure analysis**`analyze_launch_failures` asks Claude to identify root causes via MCP sampling
1111
- **Confirmation dialogs**`delete_test_case` / `bulk_delete_test_cases` require user confirmation via elicitation (requires HTTP transport; stdio returns an error)
@@ -185,7 +185,7 @@ xattr -d com.apple.quarantine ./testops-darwin-amd64
185185
186186
## 📚 Available Tools
187187
188-
Over **112 MCP tools** for Allure TestOps integration:
188+
Over **114 MCP tools** for Allure TestOps integration:
189189
190190
- Launch management (create, run\*, close, reopen, copy\*, merge\*) — \*async
191191
- Test case operations (create, update, delete†, clone, restore, versions, audit) — †with confirmation

docs/API.md

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,25 @@ Add a test plan to a launch.
235235

236236
---
237237

238+
### 10. `remove_test_cases_from_launch`
239+
240+
Remove test cases from a launch. Resolves each `test_case_id` to its test result(s) in the launch (including retries) and removes them — use to trim a launch that has too many cases or duplicates.
241+
242+
#### Parameters
243+
244+
| Parameter | Type | Required | Description |
245+
|-----------|------|----------|-------------|
246+
| `launch_id` | integer || Launch ID to remove test cases from |
247+
| `test_case_ids` | integer[] || Test case IDs to remove |
248+
| `mode` | string | | `hide` (default) excludes results from the report but keeps the data; `delete` permanently deletes them |
249+
250+
Returns `removed_count`, `removed_result_ids`, `not_found_test_case_ids`, and `truncated`. In `delete` mode, any per-result failures are listed under `failed`. No bulk-delete API exists, so `delete` issues one `DELETE` per result.
251+
252+
---
253+
238254
## Test Results Management Tools
239255

240-
### 10. `list_test_results`
256+
### 11. `list_test_results`
241257

242258
List test results in a launch with optional status filter.
243259

@@ -252,7 +268,7 @@ List test results in a launch with optional status filter.
252268

253269
---
254270

255-
### 11. `get_test_result`
271+
### 12. `get_test_result`
256272

257273
Get detailed information about a single test result.
258274

@@ -264,7 +280,7 @@ Get detailed information about a single test result.
264280

265281
---
266282

267-
### 12. `assign_test_result`
283+
### 13. `assign_test_result`
268284

269285
Assign a test result to a team member.
270286

@@ -277,7 +293,7 @@ Assign a test result to a team member.
277293

278294
---
279295

280-
### 13. `mute_test_result`
296+
### 14. `mute_test_result`
281297

282298
Mute a failing test result (mark as known issue).
283299

@@ -405,7 +421,22 @@ List all accessible projects.
405421

406422
---
407423

408-
### 28. `get_project`
424+
### 28. `find_project`
425+
426+
Find a project by name or code (case-insensitive substring match). Resolves a human-readable name/code (e.g. `TSi`) to its numeric project ID — instead of paging through `list_projects` or guessing IDs.
427+
428+
#### Parameters
429+
430+
| Parameter | Type | Required | Description |
431+
|-----------|------|----------|-------------|
432+
| `query` | string || Name or code to match (case-insensitive substring) |
433+
| `limit` | integer | | Max matches to return (1–100, default 20) |
434+
435+
Returns `matches` (array of `{id, name, code}`), `count`, `scanned`, and `truncated` (`true` if more matches may exist beyond `limit` or the scan cap).
436+
437+
---
438+
439+
### 29. `get_project`
409440

410441
Get project details and settings.
411442

@@ -417,7 +448,7 @@ Get project details and settings.
417448

418449
---
419450

420-
### 29. `get_project_stats`
451+
### 30. `get_project_stats`
421452

422453
Get project statistics (test count, runs, automation %).
423454

@@ -429,7 +460,7 @@ Get project statistics (test count, runs, automation %).
429460

430461
---
431462

432-
### 30. `get_launch_trend_analytics`
463+
### 31. `get_launch_trend_analytics`
433464

434465
Get launch trend data over time (passed/failed/broken/skipped).
435466

@@ -441,7 +472,7 @@ Get launch trend data over time (passed/failed/broken/skipped).
441472

442473
---
443474

444-
### 31. `get_launch_duration_analytics`
475+
### 32. `get_launch_duration_analytics`
445476

446477
Get launch execution time distribution.
447478

@@ -453,7 +484,7 @@ Get launch execution time distribution.
453484

454485
---
455486

456-
### 32. `get_test_success_rate`
487+
### 33. `get_test_success_rate`
457488

458489
Get test case success rate metrics.
459490

internal/adapters/allure/client.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2108,6 +2108,64 @@ func (c *Client) BulkAssignTestResults(ctx context.Context, launchID int64, test
21082108
return nil
21092109
}
21102110

2111+
// DeleteTestResult permanently deletes a single test result by ID, removing it
2112+
// (and the test case it represents) from the launch it belongs to.
2113+
func (c *Client) DeleteTestResult(ctx context.Context, testResultID int64) error {
2114+
httpReq, err := http.NewRequestWithContext(ctx, http.MethodDelete, c.url(fmt.Sprintf("/api/testresult/%d", testResultID)), nil)
2115+
if err != nil {
2116+
return fmt.Errorf("create request: %w", err)
2117+
}
2118+
if err := c.setAuthHeader(ctx, httpReq); err != nil {
2119+
return fmt.Errorf("set auth: %w", err)
2120+
}
2121+
2122+
resp, err := c.httpClient.Do(httpReq)
2123+
if err != nil {
2124+
return fmt.Errorf("http request: %w", err)
2125+
}
2126+
defer resp.Body.Close()
2127+
2128+
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
2129+
return errFromResponse(resp)
2130+
}
2131+
return nil
2132+
}
2133+
2134+
// BulkHideTestResults hides the given test results in a launch
2135+
// (POST /api/testresult/bulk/hide). Hidden results stay in the launch data but
2136+
// are excluded from the report; unlike DeleteTestResult this is non-destructive.
2137+
func (c *Client) BulkHideTestResults(ctx context.Context, launchID int64, testResultIDs []int64) error {
2138+
body, err := json.Marshal(TestResultBulkDto{
2139+
Selection: TestResultTreeSelectionDto{
2140+
LaunchID: launchID,
2141+
LeafsInclude: testResultIDs,
2142+
},
2143+
})
2144+
if err != nil {
2145+
return fmt.Errorf("marshal request: %w", err)
2146+
}
2147+
2148+
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, c.url("/api/testresult/bulk/hide"), bytes.NewBuffer(body))
2149+
if err != nil {
2150+
return fmt.Errorf("create request: %w", err)
2151+
}
2152+
if err := c.setAuthHeader(ctx, httpReq); err != nil {
2153+
return fmt.Errorf("set auth: %w", err)
2154+
}
2155+
httpReq.Header.Set("Content-Type", "application/json")
2156+
2157+
resp, err := c.httpClient.Do(httpReq)
2158+
if err != nil {
2159+
return fmt.Errorf("http request: %w", err)
2160+
}
2161+
defer resp.Body.Close()
2162+
2163+
if resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusAccepted {
2164+
return errFromResponse(resp)
2165+
}
2166+
return nil
2167+
}
2168+
21112169
func (c *Client) BulkMuteTestResults(ctx context.Context, launchID int64, testResultIDs []int64, reason string) error {
21122170
selection := TestResultTreeSelectionDto{
21132171
LaunchID: launchID,

internal/adapters/allure/models.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,12 @@ type TestResultBulkResolveDto struct {
685685
Issues []interface{} `json:"issues,omitempty"`
686686
}
687687

688+
// TestResultBulkDto is the request body for selection-only bulk test result
689+
// operations such as POST /api/testresult/bulk/hide.
690+
type TestResultBulkDto struct {
691+
Selection TestResultTreeSelectionDto `json:"selection"`
692+
}
693+
688694
// ── Launch population DTOs ────────────────────────────────────────────────────
689695

690696
type LaunchTestCasesAddDto struct {

internal/mcp/protocol.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ type InitializeResponse struct {
7979
Name string `json:"name"`
8080
Version string `json:"version"`
8181
} `json:"serverInfo"`
82+
// Instructions is an optional overview of the server's capabilities that
83+
// MCP clients add to the model's system prompt, so the model knows what
84+
// tools/workflows exist without having to infer them from tool names alone.
85+
Instructions string `json:"instructions,omitempty"`
8286
}
8387

8488
// PaginatedRequest and PaginatedResponse support cursor-based pagination

internal/mcp/server.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,31 @@ func (s *Server) handleJSONRPCResponse(ctx context.Context, req *JSONRPCRequest)
359359
}
360360
}
361361

362+
// serverInstructions is returned in the initialize response so MCP clients can
363+
// surface an overview of this server's capabilities to the model. It is meant
364+
// to answer "what can I do here, and how do I find it" up front, so the model
365+
// does not have to guess from bare tool names.
366+
const serverInstructions = `Allure TestOps integration for test management. You have 100+ dedicated tools plus a search/execute fallback that covers 600+ API endpoints — if you don't see a dedicated tool, assume the capability still exists and find it before giving up.
367+
368+
Tool groups:
369+
- Launches: run_allure_launch, list_launches, get_launch_status/details/report, close/reopen/copy/merge_launch, add_test_cases_to_launch, add_test_plan_to_launch, remove_test_cases_from_launch.
370+
- Test results: list_test_results, get_test_result, assign/mute/resolve/unmute_test_result, plus bulk_* variants.
371+
- Test cases: list/get/create/update/delete/clone/restore, steps & scenario, tags/issues/links/members/custom-fields/relations, versions, attachments, search_test_cases, suggest_test_cases.
372+
- Folder tree: browse_test_case_tree, get_test_case_tree_folders, move_test_cases_to_folder, create_test_case_folder.
373+
- Bulk test-case ops: bulk_set/add/remove/move/run/delete_* (act on many cases at once).
374+
- Projects: list_projects, find_project (resolve a name or code like "TSi" to its numeric ID), get_project, get_project_stats.
375+
- Analytics: get_launch_trend_analytics, get_launch_duration_analytics, get_test_success_rate.
376+
- Async tasks: long operations return a task_id immediately — poll get_task_status, see list_running_tasks, cancel_task.
377+
- AI: analyze_launch_failures.
378+
379+
Finding things:
380+
- Most tools take numeric IDs. To turn a human name/code into an ID: find_project (projects), list_launches (launches in a project), list_test_cases or search_test_cases (cases).
381+
- For anything without a dedicated tool, call search_testops_operations with your intent, then execute_testops_operation to run the operation it returns.
382+
383+
Safety:
384+
- Destructive tools (delete_*, bulk_delete_*, and remove_test_cases_from_launch with mode=delete) permanently remove data — confirm intent first and prefer non-destructive options (e.g. remove_test_cases_from_launch with mode=hide) when unsure.
385+
- If no API token is configured, use configure_allure_token (stored for the session only).`
386+
362387
func (s *Server) handleInitialize(req *JSONRPCRequest) *JSONRPCResponse {
363388
var initReq InitializeRequest
364389
if len(req.Params) > 0 {
@@ -383,6 +408,7 @@ func (s *Server) handleInitialize(req *JSONRPCRequest) *JSONRPCResponse {
383408
resp.Capabilities.Resources.Subscribe = true
384409
resp.ServerInfo.Name = "allure-mcp-server"
385410
resp.ServerInfo.Version = Version
411+
resp.Instructions = serverInstructions
386412

387413
s.logger.Info("initialize response sent", map[string]any{
388414
"version": resp.ProtocolVersion,

internal/mcp/server_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,14 @@ func TestServer_InitializeOverSSE(t *testing.T) {
106106
if string(parsed.ID) != "1" {
107107
t.Errorf("id = %s, want 1", parsed.ID)
108108
}
109+
110+
result, ok := parsed.Result.(map[string]any)
111+
if !ok {
112+
t.Fatalf("result is not an object: %T", parsed.Result)
113+
}
114+
if instr, _ := result["instructions"].(string); instr == "" {
115+
t.Error("initialize response should include non-empty instructions for tool discoverability")
116+
}
109117
}
110118

111119
func TestServer_ToolsListOverSSE(t *testing.T) {

internal/tools/registry.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ func (r *Registry) GetResource(uri string) *Resource {
392392
// autoAnnotate returns MCP tool annotations derived from the tool's name.
393393
//
394394
// Naming conventions:
395-
// - get_* / list_* / search_* / suggest_* / validate_* → readOnly
395+
// - get_* / list_* / find_* / search_* / suggest_* / validate_* → readOnly
396396
// - delete_* / bulk_delete_* / detach_* → destructive write
397397
// - everything else → non-destructive write
398398
func autoAnnotate(name string) map[string]any {
@@ -415,6 +415,7 @@ func autoAnnotate(name string) map[string]any {
415415
readOnlyPrefixes := []string{
416416
"get_",
417417
"list_",
418+
"find_",
418419
"search_",
419420
"suggest_",
420421
"validate_",

0 commit comments

Comments
 (0)