@qa-gentic/agents 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +203 -0
  2. package/bin/postinstall.js +75 -0
  3. package/bin/qa-stlc.js +76 -0
  4. package/package.json +48 -0
  5. package/skills/qa-stlc/AGENT-BEHAVIOR.md +373 -0
  6. package/skills/qa-stlc/deduplication-protocol.md +303 -0
  7. package/skills/qa-stlc/generate-gherkin.md +550 -0
  8. package/skills/qa-stlc/generate-playwright-code.md +439 -0
  9. package/skills/qa-stlc/generate-test-cases.md +176 -0
  10. package/skills/qa-stlc/write-helix-files.md +349 -0
  11. package/src/cmd-init.js +84 -0
  12. package/src/cmd-mcp-config.js +177 -0
  13. package/src/cmd-skills.js +124 -0
  14. package/src/cmd-verify.js +129 -0
  15. package/src/qa_stlc_agents/__init__.py +0 -0
  16. package/src/qa_stlc_agents/__pycache__/__init__.cpython-310.pyc +0 -0
  17. package/src/qa_stlc_agents/agent_gherkin_generator/__init__.py +0 -0
  18. package/src/qa_stlc_agents/agent_gherkin_generator/__pycache__/__init__.cpython-310.pyc +0 -0
  19. package/src/qa_stlc_agents/agent_gherkin_generator/__pycache__/server.cpython-310.pyc +0 -0
  20. package/src/qa_stlc_agents/agent_gherkin_generator/server.py +502 -0
  21. package/src/qa_stlc_agents/agent_gherkin_generator/tools/__init__.py +0 -0
  22. package/src/qa_stlc_agents/agent_gherkin_generator/tools/__pycache__/__init__.cpython-310.pyc +0 -0
  23. package/src/qa_stlc_agents/agent_gherkin_generator/tools/__pycache__/ado_gherkin.cpython-310.pyc +0 -0
  24. package/src/qa_stlc_agents/agent_gherkin_generator/tools/ado_gherkin.py +854 -0
  25. package/src/qa_stlc_agents/agent_helix_writer/__init__.py +0 -0
  26. package/src/qa_stlc_agents/agent_helix_writer/__pycache__/__init__.cpython-310.pyc +0 -0
  27. package/src/qa_stlc_agents/agent_helix_writer/__pycache__/server.cpython-310.pyc +0 -0
  28. package/src/qa_stlc_agents/agent_helix_writer/server.py +529 -0
  29. package/src/qa_stlc_agents/agent_helix_writer/tools/__init__.py +0 -0
  30. package/src/qa_stlc_agents/agent_helix_writer/tools/__pycache__/__init__.cpython-310.pyc +0 -0
  31. package/src/qa_stlc_agents/agent_helix_writer/tools/__pycache__/helix_write.cpython-310.pyc +0 -0
  32. package/src/qa_stlc_agents/agent_helix_writer/tools/helix_write.py +622 -0
  33. package/src/qa_stlc_agents/agent_playwright_generator/__init__.py +0 -0
  34. package/src/qa_stlc_agents/agent_playwright_generator/__pycache__/__init__.cpython-310.pyc +0 -0
  35. package/src/qa_stlc_agents/agent_playwright_generator/__pycache__/server.cpython-310.pyc +0 -0
  36. package/src/qa_stlc_agents/agent_playwright_generator/server.py +2771 -0
  37. package/src/qa_stlc_agents/agent_playwright_generator/tools/__init__.py +0 -0
  38. package/src/qa_stlc_agents/agent_playwright_generator/tools/__pycache__/__init__.cpython-310.pyc +0 -0
  39. package/src/qa_stlc_agents/agent_playwright_generator/tools/__pycache__/ado_attach.cpython-310.pyc +0 -0
  40. package/src/qa_stlc_agents/agent_playwright_generator/tools/ado_attach.py +62 -0
  41. package/src/qa_stlc_agents/agent_test_case_manager/__init__.py +0 -0
  42. package/src/qa_stlc_agents/agent_test_case_manager/__pycache__/__init__.cpython-310.pyc +0 -0
  43. package/src/qa_stlc_agents/agent_test_case_manager/__pycache__/server.cpython-310.pyc +0 -0
  44. package/src/qa_stlc_agents/agent_test_case_manager/server.py +483 -0
  45. package/src/qa_stlc_agents/agent_test_case_manager/tools/__init__.py +0 -0
  46. package/src/qa_stlc_agents/agent_test_case_manager/tools/__pycache__/__init__.cpython-310.pyc +0 -0
  47. package/src/qa_stlc_agents/agent_test_case_manager/tools/__pycache__/ado_workitem.cpython-310.pyc +0 -0
  48. package/src/qa_stlc_agents/agent_test_case_manager/tools/ado_workitem.py +302 -0
  49. package/src/qa_stlc_agents/shared/__init__.py +0 -0
  50. package/src/qa_stlc_agents/shared/__pycache__/__init__.cpython-310.pyc +0 -0
  51. package/src/qa_stlc_agents/shared/__pycache__/auth.cpython-310.pyc +0 -0
  52. package/src/qa_stlc_agents/shared/auth.py +119 -0
@@ -0,0 +1,502 @@
1
+ """
2
+ Agent 2: QA Gherkin Generator — MCP Server
3
+
4
+ Tools:
5
+ fetch_feature_hierarchy — Fetch Feature + all child PBIs + linked TCs with steps
6
+ fetch_work_item_for_gherkin — Fetch a single PBI or Bug ready for scoped Gherkin (NEW)
7
+ attach_gherkin_to_feature — Upload and attach a .feature file to a Feature work item
8
+ attach_gherkin_to_work_item — Upload and attach a scoped .feature file to a PBI/Bug (NEW)
9
+ validate_gherkin_content — Structurally validate a .feature file before attaching (NEW)
10
+
11
+ Authentication:
12
+ Silent from ~/.msal-cache/msal-cache.json (shared with microsoft/azure-devops-mcp).
13
+ Browser opens once only if no cached token is found.
14
+
15
+ Skills: see skills/generate-gherkin.md
16
+ """
17
+ import asyncio
18
+ import json
19
+ import sys
20
+
21
+ from dotenv import load_dotenv
22
+ from mcp.server import Server
23
+ from mcp.server.stdio import stdio_server
24
+ from mcp import types
25
+
26
+ from qa_stlc_agents.shared.auth import get_auth_headers, get_signed_in_user
27
+ from qa_stlc_agents.agent_gherkin_generator.tools.ado_gherkin import (
28
+ fetch_feature_hierarchy as _fetch_feature,
29
+ fetch_work_item_for_gherkin as _fetch_wi_for_gherkin,
30
+ fetch_epic_hierarchy as _fetch_epic,
31
+ attach_feature_file as _attach_feature,
32
+ attach_work_item_file as _attach_wi_file,
33
+ validate_gherkin_content as _validate_gherkin,
34
+ )
35
+
36
+ load_dotenv()
37
+
38
+ app = Server("qa-gherkin-generator")
39
+
40
+
41
+ # ---------------------------------------------------------------------------
42
+ # Pre-output validation helpers
43
+ # ---------------------------------------------------------------------------
44
+
45
+ def _validate_hierarchy_response(result: dict, scope: str) -> dict:
46
+ """Validate fetch_feature_hierarchy / fetch_epic_hierarchy / fetch_work_item_for_gherkin
47
+ response quality before returning to the user.
48
+
49
+ Checks:
50
+ 1. Response contains no error key.
51
+ 2. Feature / work item has a title.
52
+ 3. Acceptance criteria exists (warns if missing).
53
+ 4. Child PBIs/Bugs exist for Feature/Epic scope (warns if none).
54
+ 5. Test case coverage context is present.
55
+
56
+ Returns { valid: bool, errors: list[str], warnings: list[str], gaps: list[str] }.
57
+ """
58
+ errors: list[str] = []
59
+ warnings: list[str] = []
60
+ gaps: list[str] = []
61
+
62
+ if "error" in result:
63
+ return {"valid": True, "errors": [], "warnings": [], "gaps": []}
64
+
65
+ if scope == "epic":
66
+ epic = result.get("epic", {})
67
+ if not epic.get("title"):
68
+ errors.append("Epic has no title — ADO data may be incomplete.")
69
+ features = result.get("features", [])
70
+ if not features:
71
+ gaps.append(
72
+ "Epic has no child Features. Cannot generate Gherkin without "
73
+ "Feature-level scope. Ask the user to verify the Epic ID."
74
+ )
75
+ for i, feat_data in enumerate(features):
76
+ feat = feat_data.get("feature", {})
77
+ if not feat.get("acceptance_criteria"):
78
+ feat_title = feat.get("title") or "#{0}".format(feat.get("id", i + 1))
79
+ warnings.append(
80
+ "Feature '{}' has no acceptance criteria — generated Gherkin may lack detail.".format(feat_title)
81
+ )
82
+ if not feat_data.get("child_work_items"):
83
+ warnings.append(
84
+ f"Feature '{feat.get('title', '')}' has no child PBIs/Bugs."
85
+ )
86
+ elif scope == "feature":
87
+ feat = result.get("feature", {})
88
+ if not feat.get("title"):
89
+ errors.append("Feature has no title — ADO data may be incomplete.")
90
+ if not feat.get("acceptance_criteria"):
91
+ gaps.append(
92
+ "Feature has no acceptance criteria. Gherkin generation requires "
93
+ "acceptance criteria to produce meaningful scenarios. "
94
+ "Ask the user to provide AC or check the work item in ADO."
95
+ )
96
+ children = result.get("child_work_items", [])
97
+ if not children:
98
+ warnings.append(
99
+ "Feature has no child PBIs/Bugs. The generated Gherkin will be "
100
+ "based solely on the Feature-level acceptance criteria."
101
+ )
102
+ for child in children:
103
+ if not child.get("acceptance_criteria"):
104
+ warnings.append(
105
+ f"PBI/Bug '{child.get('title', '')}' (#{child.get('id', '?')}) "
106
+ f"has no acceptance criteria."
107
+ )
108
+ elif scope == "work_item":
109
+ wi = result.get("work_item", {})
110
+ if not wi.get("title"):
111
+ errors.append("Work item has no title — ADO data may be incomplete.")
112
+ if not wi.get("acceptance_criteria"):
113
+ gaps.append(
114
+ "Work item has no acceptance criteria. Gherkin generation requires "
115
+ "acceptance criteria to produce meaningful scenarios. "
116
+ "Ask the user to provide AC or check the work item in ADO."
117
+ )
118
+
119
+ tc_count = result.get("existing_test_cases_count", 0)
120
+ if tc_count > 0:
121
+ warnings.append(
122
+ f"{tc_count} existing test case(s) found. Review them for "
123
+ f"deduplication before generating new Gherkin scenarios."
124
+ )
125
+
126
+ return {
127
+ "valid": len(errors) == 0 and len(gaps) == 0,
128
+ "errors": errors,
129
+ "warnings": warnings,
130
+ "gaps": gaps,
131
+ }
132
+
133
+
134
+ def _validate_attach_response(result: dict, scope: str) -> dict:
135
+ """Validate the response from attach_gherkin_to_feature / attach_gherkin_to_work_item
136
+ before returning to the user.
137
+
138
+ Checks:
139
+ 1. Response indicates success (no error key).
140
+ 2. Attachment URL is present.
141
+ 3. File name is present and non-empty.
142
+
143
+ Returns { valid: bool, errors: list[str], warnings: list[str] }.
144
+ """
145
+ errors: list[str] = []
146
+ warnings: list[str] = []
147
+
148
+ if "error" in result:
149
+ errors.append("Attach failed: {}".format(result["error"]))
150
+ return {"valid": False, "errors": errors, "warnings": warnings}
151
+
152
+ if not result.get("success", False):
153
+ errors.append("Attach returned success=false without an error message.")
154
+ return {"valid": False, "errors": errors, "warnings": warnings}
155
+
156
+ if not result.get("attachment_url"):
157
+ warnings.append(
158
+ "Attachment URL is missing from the response. The file may have "
159
+ "been attached, but the URL was not returned by ADO."
160
+ )
161
+
162
+ if not result.get("file_name"):
163
+ warnings.append("File name is missing from the attach response.")
164
+
165
+ return {"valid": len(errors) == 0, "errors": errors, "warnings": warnings}
166
+
167
+
168
+ @app.list_tools()
169
+ async def list_tools() -> list[types.Tool]:
170
+ return [
171
+ # ── Epic-scoped fetch (NEW — must be listed FIRST so routing fires before Feature path) ──
172
+ types.Tool(
173
+ name="fetch_epic_hierarchy",
174
+ description=(
175
+ "Fetch an Epic work item from Azure DevOps along with ALL child Features, "
176
+ "every Feature's child PBIs/Bugs, and every linked test case (including full "
177
+ "test steps). ALWAYS use this tool as Step 1 when the user provides an Epic ID. "
178
+ "Never fall through to fetch_feature_hierarchy when an Epic ID is given. "
179
+ "The response contains: epic metadata, a 'features' list where each entry "
180
+ "includes the Feature's own metadata plus its 'child_work_items' (PBIs/Bugs) "
181
+ "and 'existing_test_cases'. Analyse ALL child Features and ALL child PBIs/Bugs "
182
+ "before generating any Gherkin scenarios — the full hierarchy is the only "
183
+ "correct input for Epic-scope regression suites. "
184
+ "Generates one .feature file per child Feature; attaches each via "
185
+ "attach_gherkin_to_feature. "
186
+ "IMPORTANT: Manual test cases (create_and_link_test_cases) MUST NOT be created "
187
+ "for the Epic itself — inform the user that test cases can only be created for "
188
+ "PBIs, Bugs, or Features."
189
+ ),
190
+ inputSchema={
191
+ "type": "object",
192
+ "properties": {
193
+ "epic_id": {
194
+ "type": "integer",
195
+ "description": "ADO Epic work item ID",
196
+ },
197
+ "organization_url": {
198
+ "type": "string",
199
+ "description": "e.g. https://dev.azure.com/myorg",
200
+ },
201
+ "project_name": {
202
+ "type": "string",
203
+ "description": "ADO project name",
204
+ },
205
+ },
206
+ "required": ["epic_id", "organization_url", "project_name"],
207
+ },
208
+ ),
209
+
210
+ # ── Existing: Feature-scoped fetch ───────────────────────────────────
211
+ types.Tool(
212
+ name="fetch_feature_hierarchy",
213
+ description=(
214
+ "Fetch a Feature work item from Azure DevOps along with all child PBIs/Bugs, "
215
+ "their linked test cases with full test steps, and a Gherkin best-practice "
216
+ "reference example. Use this as Step 1 when the user provides a Feature ID "
217
+ "or when you need the full cross-PBI flow map. "
218
+ "For a single PBI or Bug ID, use fetch_work_item_for_gherkin instead."
219
+ ),
220
+ inputSchema={
221
+ "type": "object",
222
+ "properties": {
223
+ "feature_id": {"type": "integer", "description": "ADO Feature work item ID"},
224
+ "organization_url": {"type": "string", "description": "e.g. https://dev.azure.com/myorg"},
225
+ "project_name": {"type": "string", "description": "ADO project name"},
226
+ },
227
+ "required": ["feature_id", "organization_url", "project_name"],
228
+ },
229
+ ),
230
+
231
+ # ── NEW: PBI/Bug-scoped fetch ────────────────────────────────────────
232
+ types.Tool(
233
+ name="fetch_work_item_for_gherkin",
234
+ description=(
235
+ "Fetch a single PBI or Bug from Azure DevOps with all fields needed to generate "
236
+ "a scoped Gherkin feature file for that work item alone. "
237
+ "Use this when the user provides a PBI or Bug ID and wants a focused .feature "
238
+ "file covering only that work item's acceptance criteria (3–9 scenarios). "
239
+ "Returns the work item's AC, linked test cases with full steps, parent Feature "
240
+ "metadata, a suggested file name, and the Gherkin best-practice reference. "
241
+ "For Feature IDs, use fetch_feature_hierarchy instead."
242
+ ),
243
+ inputSchema={
244
+ "type": "object",
245
+ "properties": {
246
+ "work_item_id": {
247
+ "type": "integer",
248
+ "description": "ADO PBI or Bug work item ID",
249
+ },
250
+ "organization_url": {
251
+ "type": "string",
252
+ "description": "e.g. https://dev.azure.com/myorg",
253
+ },
254
+ "project_name": {
255
+ "type": "string",
256
+ "description": "ADO project name",
257
+ },
258
+ },
259
+ "required": ["work_item_id", "organization_url", "project_name"],
260
+ },
261
+ ),
262
+
263
+ # ── Existing: Feature-scoped attach (now gated by validation) ────────
264
+ types.Tool(
265
+ name="attach_gherkin_to_feature",
266
+ description=(
267
+ "Validate, upload, and attach a completed .feature file to a Feature work item "
268
+ "in ADO. Structural validation runs automatically before upload — if the file "
269
+ "is missing @smoke or @regression tags, has fewer than 5 or more than 10 "
270
+ "scenarios, contains duplicate scenario titles, or has any scenario without a "
271
+ "When step, the upload is blocked and errors are returned for correction. "
272
+ "The file is named {feature_id}_{feature_title}_regression.feature automatically. "
273
+ "Call this as the final step after generating and reviewing the Gherkin content."
274
+ ),
275
+ inputSchema={
276
+ "type": "object",
277
+ "properties": {
278
+ "feature_id": {"type": "integer", "description": "ADO Feature work item ID"},
279
+ "feature_title": {"type": "string", "description": "Feature title (used in filename)"},
280
+ "organization_url": {"type": "string"},
281
+ "project_name": {"type": "string"},
282
+ "gherkin_content": {
283
+ "type": "string",
284
+ "description": "Complete .feature file content as a string",
285
+ },
286
+ },
287
+ "required": [
288
+ "feature_id", "feature_title", "organization_url",
289
+ "project_name", "gherkin_content",
290
+ ],
291
+ },
292
+ ),
293
+
294
+ # ── NEW: PBI/Bug-scoped attach ───────────────────────────────────────
295
+ types.Tool(
296
+ name="attach_gherkin_to_work_item",
297
+ description=(
298
+ "Validate, upload, and attach a scoped .feature file directly to a PBI or Bug "
299
+ "work item in ADO. Use this after fetch_work_item_for_gherkin when the user "
300
+ "wants the feature file attached to the PBI/Bug rather than its parent Feature. "
301
+ "Structural validation runs automatically before upload — if the file is missing "
302
+ "@smoke or @regression tags, has fewer than 3 or more than 9 scenarios "
303
+ "(work-item scope limit), contains duplicate titles, or has any scenario without "
304
+ "a When step, the upload is blocked and errors are returned for correction. "
305
+ "The file is named {work_item_id}_{title_kebab}.feature automatically."
306
+ ),
307
+ inputSchema={
308
+ "type": "object",
309
+ "properties": {
310
+ "work_item_id": {
311
+ "type": "integer",
312
+ "description": "ADO PBI or Bug work item ID",
313
+ },
314
+ "work_item_title": {
315
+ "type": "string",
316
+ "description": "Work item title (used in filename)",
317
+ },
318
+ "organization_url": {"type": "string"},
319
+ "project_name": {"type": "string"},
320
+ "gherkin_content": {
321
+ "type": "string",
322
+ "description": "Complete .feature file content as a string",
323
+ },
324
+ },
325
+ "required": [
326
+ "work_item_id", "work_item_title", "organization_url",
327
+ "project_name", "gherkin_content",
328
+ ],
329
+ },
330
+ ),
331
+
332
+ # ── NEW: Standalone validation ───────────────────────────────────────
333
+ types.Tool(
334
+ name="validate_gherkin_content",
335
+ description=(
336
+ "Structurally validate a .feature file without uploading it to ADO. "
337
+ "Use this to check a file before calling attach_gherkin_to_feature or "
338
+ "attach_gherkin_to_work_item, or to verify hand-edited Gherkin. "
339
+ "Checks: @smoke tag present, @regression tag present, scenario count within "
340
+ "allowed range, every scenario has a When step, no duplicate scenario titles. "
341
+ "Pass scope='feature' for Feature-scoped files (5–10 scenarios) or "
342
+ "scope='work_item' for PBI/Bug-scoped files (3–9 scenarios)."
343
+ ),
344
+ inputSchema={
345
+ "type": "object",
346
+ "properties": {
347
+ "gherkin_content": {
348
+ "type": "string",
349
+ "description": "Complete .feature file content as a string",
350
+ },
351
+ "scope": {
352
+ "type": "string",
353
+ "enum": ["feature", "work_item"],
354
+ "description": (
355
+ "Scope determines the allowed scenario count range. "
356
+ "'feature' = 5–10 scenarios, 'work_item' = 3–9 scenarios. "
357
+ "Default: 'feature'."
358
+ ),
359
+ },
360
+ },
361
+ "required": ["gherkin_content"],
362
+ },
363
+ ),
364
+ ]
365
+
366
+
367
+ @app.call_tool()
368
+ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
369
+ try:
370
+ if name == "fetch_epic_hierarchy":
371
+ result = await asyncio.to_thread(
372
+ _fetch_epic,
373
+ arguments["organization_url"],
374
+ arguments["project_name"],
375
+ arguments["epic_id"],
376
+ )
377
+ # ── Pre-output validation ─────────────────────────────────────
378
+ result["_validation"] = _validate_hierarchy_response(result, "epic")
379
+
380
+ elif name == "fetch_feature_hierarchy":
381
+ result = await asyncio.to_thread(
382
+ _fetch_feature,
383
+ arguments["organization_url"],
384
+ arguments["project_name"],
385
+ arguments["feature_id"],
386
+ )
387
+ # ── Pre-output validation ─────────────────────────────────────
388
+ result["_validation"] = _validate_hierarchy_response(result, "feature")
389
+
390
+ elif name == "fetch_work_item_for_gherkin":
391
+ result = await asyncio.to_thread(
392
+ _fetch_wi_for_gherkin,
393
+ arguments["organization_url"],
394
+ arguments["project_name"],
395
+ arguments["work_item_id"],
396
+ )
397
+ # ── Pre-output validation ─────────────────────────────────────
398
+ result["_validation"] = _validate_hierarchy_response(result, "work_item")
399
+
400
+ elif name == "attach_gherkin_to_feature":
401
+ # ── Pre-attach validation gate ────────────────────────────────
402
+ pre_check = await asyncio.to_thread(
403
+ _validate_gherkin,
404
+ arguments["gherkin_content"],
405
+ "feature",
406
+ )
407
+ if not pre_check["valid"]:
408
+ result = {
409
+ "success": False,
410
+ "reason": "pre_attach_validation_failed",
411
+ "_validation": pre_check,
412
+ "message": (
413
+ "Gherkin content failed pre-attach validation. "
414
+ "Fix the errors below before attaching to ADO."
415
+ ),
416
+ }
417
+ return [types.TextContent(type="text", text=json.dumps(result, indent=2, ensure_ascii=False))]
418
+
419
+ result = await asyncio.to_thread(
420
+ _attach_feature,
421
+ arguments["organization_url"],
422
+ arguments["project_name"],
423
+ arguments["feature_id"],
424
+ arguments["feature_title"],
425
+ arguments["gherkin_content"],
426
+ )
427
+ # ── Post-attach validation ────────────────────────────────────
428
+ result["_validation"] = _validate_attach_response(result, "feature")
429
+
430
+ elif name == "attach_gherkin_to_work_item":
431
+ # ── Pre-attach validation gate ────────────────────────────────
432
+ pre_check = await asyncio.to_thread(
433
+ _validate_gherkin,
434
+ arguments["gherkin_content"],
435
+ "work_item",
436
+ )
437
+ if not pre_check["valid"]:
438
+ result = {
439
+ "success": False,
440
+ "reason": "pre_attach_validation_failed",
441
+ "_validation": pre_check,
442
+ "message": (
443
+ "Gherkin content failed pre-attach validation. "
444
+ "Fix the errors below before attaching to ADO."
445
+ ),
446
+ }
447
+ return [types.TextContent(type="text", text=json.dumps(result, indent=2, ensure_ascii=False))]
448
+
449
+ result = await asyncio.to_thread(
450
+ _attach_wi_file,
451
+ arguments["organization_url"],
452
+ arguments["project_name"],
453
+ arguments["work_item_id"],
454
+ arguments["work_item_title"],
455
+ arguments["gherkin_content"],
456
+ )
457
+ # ── Post-attach validation ────────────────────────────────────
458
+ result["_validation"] = _validate_attach_response(result, "work_item")
459
+
460
+ elif name == "validate_gherkin_content":
461
+ result = await asyncio.to_thread(
462
+ _validate_gherkin,
463
+ arguments["gherkin_content"],
464
+ arguments.get("scope", "feature"),
465
+ )
466
+
467
+ else:
468
+ result = {"error": f"Unknown tool: {name}"}
469
+
470
+ return [types.TextContent(type="text", text=json.dumps(result, indent=2, ensure_ascii=False))]
471
+
472
+ except Exception as exc:
473
+ return [types.TextContent(
474
+ type="text",
475
+ text=json.dumps({"error": str(exc), "tool": name}, indent=2),
476
+ )]
477
+
478
+
479
+ async def _run():
480
+ async with stdio_server() as (r, w):
481
+ await app.run(r, w, app.create_initialization_options())
482
+
483
+
484
+ def main():
485
+ try:
486
+ get_auth_headers()
487
+ except Exception as e:
488
+ print(f"[qa-gherkin-generator] Auth error: {e}", file=sys.stderr)
489
+ sys.exit(1)
490
+
491
+ user = get_signed_in_user()
492
+ if user:
493
+ print(f"[qa-gherkin-generator] Authenticated as: {user}", file=sys.stderr)
494
+
495
+ asyncio.run(_run())
496
+
497
+
498
+ if __name__ == "__main__":
499
+ main()
500
+
501
+
502
+