@skyramp/mcp 0.0.65 → 0.1.0-rc.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 (50) hide show
  1. package/build/playwright/traceRecordingPrompt.js +30 -36
  2. package/build/prompts/architectPersona.js +19 -0
  3. package/build/prompts/test-maintenance/drift-analysis-prompt.js +11 -6
  4. package/build/prompts/test-maintenance/drift-analysis-prompt.test.js +49 -0
  5. package/build/prompts/test-maintenance/driftAnalysisSections.js +4 -2
  6. package/build/prompts/test-recommendation/analysisOutputPrompt.js +42 -50
  7. package/build/prompts/test-recommendation/mergeEnrichedScenarios.test.js +125 -0
  8. package/build/prompts/test-recommendation/recommendationSections.js +121 -4
  9. package/build/prompts/test-recommendation/registerRecommendTestsPrompt.js +151 -9
  10. package/build/prompts/test-recommendation/test-recommendation-prompt.js +416 -61
  11. package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +455 -63
  12. package/build/prompts/testbot/testbot-prompts.js +111 -100
  13. package/build/prompts/testbot/testbot-prompts.test.js +142 -0
  14. package/build/resources/analysisResources.js +13 -5
  15. package/build/services/ScenarioGenerationService.js +2 -2
  16. package/build/services/ScenarioGenerationService.test.js +35 -0
  17. package/build/services/TestExecutionService.js +1 -1
  18. package/build/tools/code-refactor/modularizationTool.js +2 -2
  19. package/build/tools/executeSkyrampTestTool.js +4 -3
  20. package/build/tools/generate-tests/generateBatchScenarioRestTool.js +51 -21
  21. package/build/tools/generate-tests/generateContractRestTool.js +26 -4
  22. package/build/tools/generate-tests/generateIntegrationRestTool.js +44 -13
  23. package/build/tools/generate-tests/generateScenarioRestTool.js +17 -39
  24. package/build/tools/generate-tests/generateUIRestTool.js +69 -4
  25. package/build/tools/submitReportTool.js +27 -13
  26. package/build/tools/test-management/analyzeChangesTool.js +32 -10
  27. package/build/tools/test-management/analyzeChangesTool.test.js +85 -0
  28. package/build/types/RepositoryAnalysis.js +25 -3
  29. package/build/types/TestRecommendation.js +5 -4
  30. package/build/types/TestTypes.js +44 -9
  31. package/build/utils/AnalysisStateManager.js +43 -9
  32. package/build/utils/AnalysisStateManager.test.js +35 -0
  33. package/build/utils/routeParsers.js +35 -0
  34. package/build/utils/routeParsers.test.js +66 -1
  35. package/build/utils/scenarioDrafting.js +207 -360
  36. package/build/utils/scenarioDrafting.test.js +191 -256
  37. package/build/utils/trace-parser.js +24 -6
  38. package/build/utils/trace-parser.test.js +140 -0
  39. package/node_modules/playwright/lib/mcp/browser/browserServerBackend.js +3 -0
  40. package/node_modules/playwright/lib/mcp/browser/tab.js +8 -1
  41. package/node_modules/playwright/lib/mcp/browser/tools/keyboard.js +3 -2
  42. package/node_modules/playwright/lib/mcp/browser/tools/navigate.js +1 -1
  43. package/node_modules/playwright/lib/mcp/browser/tools/snapshot.js +4 -4
  44. package/node_modules/playwright/lib/mcp/browser/tools/tabs.js +5 -4
  45. package/node_modules/playwright/lib/mcp/browser/tools/wait.js +1 -1
  46. package/node_modules/playwright/lib/mcp/skyramp/exportTool.js +10 -9
  47. package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +304 -7
  48. package/node_modules/playwright/lib/mcp/test/skyRampExport.js +128 -20
  49. package/package.json +2 -2
  50. package/node_modules/playwright/lib/mcp/terminal/help.json +0 -32
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import fs from "fs";
3
3
  import { logger } from "../../utils/logger.js";
4
- import { TestType } from "../../types/TestTypes.js";
4
+ import { ProgrammingLanguage, TestType } from "../../types/TestTypes.js";
5
5
  import { ModularizationService, } from "../../services/ModularizationService.js";
6
6
  import { AnalyticsService } from "../../services/AnalyticsService.js";
7
7
  import { normalizeLanguageParams, resolveParamAliases, } from "../../utils/normalizeParams.js";
@@ -10,7 +10,7 @@ const modularizationSchema = {
10
10
  .string()
11
11
  .describe("The test file to process with modularization principles applied"),
12
12
  language: z
13
- .string()
13
+ .nativeEnum(ProgrammingLanguage)
14
14
  .optional()
15
15
  .describe("The programming language of the test file. Inferred from file extension if not provided."),
16
16
  testType: z
@@ -3,6 +3,7 @@ import { stripVTControlCharacters } from "util";
3
3
  import { TestExecutionService } from "../services/TestExecutionService.js";
4
4
  import { AnalyticsService } from "../services/AnalyticsService.js";
5
5
  import { getWorkspaceBaseUrl } from "../utils/workspaceAuth.js";
6
+ import { ProgrammingLanguage, TestType } from "../types/TestTypes.js";
6
7
  const TOOL_NAME = "skyramp_execute_test";
7
8
  export function registerExecuteSkyrampTestTool(server) {
8
9
  server.registerTool(TOOL_NAME, {
@@ -36,11 +37,11 @@ For detailed documentation visit: https://www.skyramp.dev/docs/quickstart`,
36
37
  .string()
37
38
  .describe("The path to the workspace directory where the test file is located"),
38
39
  language: z
39
- .string()
40
+ .nativeEnum(ProgrammingLanguage)
40
41
  .describe("Programming language of the test file to execute (e.g., python, javascript, typescript, java)"),
41
42
  testType: z
42
- .string()
43
- .describe("Type of the test to execute (e.g., integration, contract, smoke, fuzz, load, e2e, ui). TEST TYPE MUST BE FROM [integration, contract, smoke, fuzz, load, e2e, ui]."),
43
+ .nativeEnum(TestType)
44
+ .describe("Type of the test to execute."),
44
45
  testFile: z
45
46
  .string()
46
47
  .describe("ALWAYS USE ABSOLUTE PATH to the test file to execute"),
@@ -2,25 +2,51 @@ import { z } from "zod";
2
2
  import path from "path";
3
3
  import { ScenarioGenerationService } from "../../services/ScenarioGenerationService.js";
4
4
  import fs from "fs";
5
- import { baseSchema, AUTH_PLACEHOLDER_TOKEN } from "../../types/TestTypes.js";
5
+ import { baseSchema, AUTH_PLACEHOLDER_TOKEN, HttpMethod } from "../../types/TestTypes.js";
6
6
  import { AnalyticsService } from "../../services/AnalyticsService.js";
7
7
  import { getWorkspaceAuthConfig, WorkspaceAuthType } from "../../utils/workspaceAuth.js";
8
8
  import { logger } from "../../utils/logger.js";
9
+ import { getPersonaPrefix } from "../../prompts/architectPersona.js";
10
+ function isJsonValue(v) {
11
+ if (v === undefined || v === null)
12
+ return true;
13
+ try {
14
+ JSON.parse(v);
15
+ return true;
16
+ }
17
+ catch {
18
+ return false;
19
+ }
20
+ }
21
+ function isJsonObject(v) {
22
+ if (v === undefined || v === null)
23
+ return true;
24
+ try {
25
+ const p = JSON.parse(v);
26
+ return typeof p === "object" && !Array.isArray(p) && p !== null;
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ }
9
32
  const stepSchema = z.object({
10
33
  method: z
11
- .string()
12
- .describe("HTTP method (GET, POST, PUT, DELETE, PATCH) for this step"),
34
+ .nativeEnum(HttpMethod)
35
+ .describe("HTTP method for this step."),
13
36
  path: z
14
37
  .string()
15
- .describe("API path for this step. CRITICAL: For requests that reference an ID created by a prior step, use the ACTUAL ID value from the prior step's responseBody, NOT a template variable."),
38
+ .startsWith("/", { message: "path must begin with '/' (e.g. '/api/v1/products/123')" })
39
+ .describe("API path for this step, must start with '/'. CRITICAL: For requests that reference an ID created by a prior step, use the ACTUAL ID value from the prior step's responseBody, NOT a template variable."),
16
40
  requestBody: z
17
41
  .string()
18
42
  .optional()
19
- .describe("JSON string of the request body for POST/PUT/PATCH requests"),
43
+ .refine(isJsonValue, { message: "requestBody must be valid JSON (e.g. '{\"name\":\"product\"}')." })
44
+ .describe("JSON string of the request body for POST/PUT/PATCH requests."),
20
45
  queryParams: z
21
46
  .string()
22
47
  .optional()
23
- .describe("JSON string of URL query parameters for GET search/filter/list requests"),
48
+ .refine(isJsonObject, { message: "queryParams must be a JSON object string (e.g. '{\"limit\":\"10\"}')." })
49
+ .describe("JSON string of URL query parameters as a flat object for GET search/filter/list requests."),
24
50
  responseBody: z
25
51
  .string()
26
52
  .optional()
@@ -61,7 +87,7 @@ const batchScenarioSchema = {
61
87
  authHeader: z
62
88
  .string()
63
89
  .optional()
64
- .describe("Which HTTP header carries the auth credential. Pass empty string or omit for unauthenticated endpoints."),
90
+ .describe("Which HTTP header carries the auth credential (e.g., 'Authorization', 'X-Api-Key'). Omit entirely to auto-resolve from workspace config. Pass empty string only for confirmed unauthenticated endpoints — empty string bypasses workspace auth resolution."),
65
91
  authScheme: z
66
92
  .string()
67
93
  .optional()
@@ -75,24 +101,26 @@ const batchScenarioSchema = {
75
101
  const TOOL_NAME = "skyramp_batch_scenario_test_generation";
76
102
  export function registerBatchScenarioTestTool(server) {
77
103
  server.registerTool(TOOL_NAME, {
78
- description: `Generate a complete multi-step scenario file in a single call.
104
+ description: getPersonaPrefix() + `Generate a complete multi-step scenario file in a single call.
79
105
 
80
- This tool generates ALL TraceRequest objects for a multi-step scenario at once, producing
81
- the complete scenario JSON file in one invocation. Use this instead of calling
82
- \`skyramp_scenario_test_generation\` multiple times for multi-step integration tests.
106
+ To ensure deterministic mapping, output a <thinking> block before calling this tool: (1) what test behavior is targeted, (2) which endpoint maps to this intent, (3) where each parameter value comes from.
83
107
 
84
- **When to use:**
85
- - Multi-step integration test scenarios (e.g., create product → create order → update order → verify)
86
- - Any scenario requiring 2+ sequential API requests
108
+ Generate a complete multi-step scenario file in a single call.
87
109
 
88
- **What it does:**
89
- 1. Accepts an ordered array of steps, each with method, path, requestBody, etc.
90
- 2. Generates a TraceRequest for each step
91
- 3. Writes the complete scenario JSON file with all steps
110
+ ---
111
+
112
+ This tool generates the complete scenario JSON file for a multi-step integration test in a single call. Use this instead of calling skyramp_scenario_test_generation multiple times.
113
+
114
+ **Mandatory spec mapping (do this before every call):**
115
+ For each step in the \`steps\` array, confirm the method+path combination exists as a real endpoint (from OpenAPI spec, source code routes, or skyramp_analyze_changes output) before submitting. Do NOT invent paths. Do NOT use template variables — use CONCRETE ID values in paths (e.g. '/api/v1/products/70885', not '/api/v1/products/{id}').
116
+
117
+ **When to use:**
118
+ - Any scenario requiring 2+ sequential API requests (create → update → verify, etc.)
119
+ - Single-step scenarios where you need the output scenarioFile path for skyramp_integration_test_generation
92
120
 
93
- **After this tool:** Call \`skyramp_integration_test_generation\` with the returned \`scenarioFile\` path.
121
+ **After this tool succeeds:** immediately call \`skyramp_integration_test_generation\` with the \`scenarioFile\` path returned in this tool's output.
94
122
 
95
- **CRITICAL:** Use CONCRETE ID values in paths (e.g., '/api/v1/products/70885'), not template variables.`,
123
+ **Error recovery:** If this tool returns an error for a specific step, the error message will tell you exactly which step failed (step N/total), the method+path, and the reason. Fix only the reported step and resubmit the full \`steps\` array — do NOT split into separate calls.`,
96
124
  inputSchema: batchScenarioSchema,
97
125
  }, async (params) => {
98
126
  if (params.authHeader === undefined) {
@@ -122,7 +150,9 @@ the complete scenario JSON file in one invocation. Use this instead of calling
122
150
  const resolvedOut = path.resolve(params.outputDir);
123
151
  if (!path.resolve(filePath).startsWith(resolvedOut + path.sep) && path.resolve(filePath) !== resolvedOut) {
124
152
  return {
125
- content: [{ type: "text", text: `Error: scenarioName produced a path outside outputDir.` }],
153
+ content: [{ type: "text", text: `Validation Error: scenarioName '${params.scenarioName}' produced a file path outside outputDir.\n`
154
+ + `Expected: File path within '${params.outputDir}'.\n`
155
+ + `Fix: Use a simpler scenarioName without path separators or special characters.` }],
126
156
  isError: true,
127
157
  };
128
158
  }
@@ -4,6 +4,7 @@ import { baseTestSchema, TestType } from "../../types/TestTypes.js";
4
4
  import { TestGenerationService, } from "../../services/TestGenerationService.js";
5
5
  import { AnalyticsService } from "../../services/AnalyticsService.js";
6
6
  import { ENHANCE_ASSERTIONS_FOR_INTEGRATION_AND_CONTRACTPROVIDER } from "../../prompts/test-maintenance/enhanceAssertionSection.js";
7
+ import { getPersonaPrefix } from "../../prompts/architectPersona.js";
7
8
  const contractTestSchema = {
8
9
  ...baseTestSchema,
9
10
  pathParams: z
@@ -273,7 +274,21 @@ The generated consumer contract test contains a stub test function that uses Sky
273
274
  const TOOL_NAME = "skyramp_contract_test_generation";
274
275
  export function registerContractTestTool(server) {
275
276
  server.registerTool(TOOL_NAME, {
276
- description: `Generate a contract test using Skyramp's deterministic test generation platform.
277
+ description: `${getPersonaPrefix()}Before calling this tool, you MUST output a <thinking> block that covers:
278
+ 1. The endpoint URL and HTTP method being tested
279
+ 2. Whether the endpoint is a nested resource (URL contains a path parameter like \`{id}\`, \`{flow_id}\`, etc.) — if YES, decide: do I have the request body to provision the parent, or should I use skipProvisionParents?
280
+ 3. Which assertions this test should validate (status code + key response schema fields with non-default values)
281
+ 4. Each required parameter and what value it will take, with source (workspace config / diff / schema / user input)
282
+ NEVER use a hardcoded ID (UUID or integer) as a path parameter value. If a real resource ID is needed and cannot be provisioned, use skipProvisionParents instead.
283
+
284
+ **Dynamic context (use this before generating):**
285
+ If \`skyramp_analyze_changes\` has already run and returned a \`sessionId\`, fetch the endpoint detail before generating:
286
+ \`skyramp://analysis/{sessionId}/endpoints/{path}/{method}\`
287
+ This gives you the exact request body shape, response schema, and auth config for this endpoint. Use it to fill parameters and write accurate assertions — do not infer from source code when this resource is available.
288
+
289
+ ---
290
+
291
+ Generate a contract test using Skyramp's deterministic test generation platform.
277
292
 
278
293
  Contract tests ensure your API implementation matches its OpenAPI/Swagger specification exactly. They validate request/response schemas, status codes, headers, and data types to prevent contract violations and API breaking changes.
279
294
 
@@ -281,6 +296,13 @@ Contract tests ensure your API implementation matches its OpenAPI/Swagger specif
281
296
 
282
297
  **IMPORTANT: If the endpoint URL contains path parameter placeholders (e.g., \`/products/{product_id}/reviews\`), pass the URL exactly as provided — do NOT substitute values for the placeholders. Leave \`pathParams\` empty unless the user has explicitly provided specific values.**
283
298
 
299
+ **CRITICAL — Nested resource decision tree (follow this every time):**
300
+ Does the endpoint URL contain a path parameter (e.g. \`/flows/{id}\`, \`/work_queues/{id}/stats\`)?
301
+ - **YES, and \`apiSchema\` is provided** → use \`parentRequestData\` to supply the request body that creates the parent resource. The key must be the exact path parameter name (e.g. \`id\`, \`flow_id\`). The backend will provision the parent, extract the real ID, and inject it into the test.
302
+ - **YES, but \`apiSchema\` is NOT available** → set \`skipProvisionParents: true\` (with \`providerMode: true\`). The test will verify the error-path contract (404) rather than the success path.
303
+ - **NO path parameters** → no action needed; proceed normally.
304
+ NEVER substitute a hardcoded UUID or integer for a path parameter. A hardcoded ID will always 404 in a clean environment and produces a useless test.
305
+
284
306
  **Modes:**
285
307
  - Default (no mode set): both \`providerMode\` and \`consumerMode\` default to false. This generates both provider and consumer contract tests — equivalent to setting both modes to true.
286
308
  - \`providerMode\`: set to true ONLY if the user explicitly requests a provider-side contract test. Optionally specify \`providerOutput\` for the output file path.
@@ -288,11 +310,11 @@ Contract tests ensure your API implementation matches its OpenAPI/Swagger specif
288
310
  - Both \`providerMode\` and \`consumerMode\` can be enabled simultaneously to generate both sides.
289
311
 
290
312
  **Chaining (requires \`apiSchema\`):**
291
- - \`parentRequestData\`: map of parent request data for chained test generation. Not allowed with \`consumerMode\` or \`skipProvisionParents\`.
292
- - \`parentStatusCode\`: map of parent response status codes for chained test generation. Not allowed with \`consumerMode\` or \`skipProvisionParents\`.
313
+ - \`parentRequestData\`: map of parent request data for chained test generation. Key = exact path parameter name. Value = JSON string of the request body to create that parent resource. Not allowed with \`consumerMode\` or \`skipProvisionParents\`.
314
+ - \`parentStatusCode\`: expected HTTP status code for each parent provisioning call (e.g. \`{"id": "201"}\`). Not allowed with \`consumerMode\` or \`skipProvisionParents\`.
293
315
 
294
316
  **Provider setup/teardown:**
295
- - \`skipProvisionParents\`: when true, skips generating setup/teardown functions for the provider contract test. Requires \`providerMode\`. Not allowed with \`parentRequestData\` or \`parentStatusCode\`.`,
317
+ - \`skipProvisionParents\`: when true, skips generating setup/teardown functions for the provider contract test. Use this when \`apiSchema\` is unavailable and the endpoint requires a parent resource. Requires \`providerMode\`. Not allowed with \`parentRequestData\` or \`parentStatusCode\`.`,
296
318
  inputSchema: contractTestSchema,
297
319
  }, async (params) => {
298
320
  const service = new ContractTestService();
@@ -3,6 +3,7 @@ import { baseTestSchema, baseTraceSchema, TestType, codeRefactoringSchema, } fro
3
3
  import { TestGenerationService, } from "../../services/TestGenerationService.js";
4
4
  import { AnalyticsService } from "../../services/AnalyticsService.js";
5
5
  import { ENHANCE_ASSERTIONS_FOR_INTEGRATION_AND_CONTRACTPROVIDER } from "../../prompts/test-maintenance/enhanceAssertionSection.js";
6
+ import { getPersonaPrefix } from "../../prompts/architectPersona.js";
6
7
  const integrationTestSchema = z
7
8
  .object({
8
9
  ...baseTestSchema,
@@ -15,19 +16,20 @@ const integrationTestSchema = z
15
16
  exclude: baseTraceSchema.shape.exclude.optional(),
16
17
  scenarioFile: z
17
18
  .string()
18
- .describe("Path to the scenario file to be used for test generation. This file is generated by the skyramp_scenario_test_generation tool.")
19
- .optional(),
19
+ .endsWith(".json", { message: "scenarioFile must be a path to a .json file." })
20
+ .optional()
21
+ .describe("Absolute path to the scenario JSON file produced by skyramp_batch_scenario_test_generation. " +
22
+ "When provided, DO NOT also pass apiSchema or endpointURL — the scenario file already contains all endpoint information."),
20
23
  ...codeRefactoringSchema.shape,
21
24
  ...baseTestSchema,
22
25
  output: baseTestSchema.output.describe("Name of the output test file. " +
23
- "If the user does not specify a filename and a scenarioFile is provided, derive the output name from the scenario filename to avoid overwriting other tests. " +
24
- "The backend default 'integration_test.py' is generic and will collide when multiple tests are generated. " +
25
- "Derivation rule: take the scenario filename (no path, no extension), strip the leading 'scenario_' prefix, " +
26
- "replace every hyphen and non-alphanumeric character with an underscore, then append '_integration_test' and the language extension. " +
26
+ "When scenarioFile is provided and user did not specify a name, derive it: " +
27
+ "strip the path and 'scenario_' prefix, replace hyphens/non-alphanum with underscores, append '_integration_test' + language extension. " +
27
28
  "Examples: " +
28
29
  "'scenario_orders-patch-add-items-recalculate.json' → 'orders_patch_add_items_recalculate_integration_test.py' (Python) or 'orders_patch_add_items_recalculate_integration_test.spec.ts' (Playwright). " +
29
30
  "'scenario_products-crud.json' → 'products_crud_integration_test.py'. " +
30
- "Extensions: '.py' for pytest, '.spec.ts'/'.spec.js' for Playwright, '.java' for JUnit."),
31
+ "Extensions: '.py' for pytest, '.spec.ts'/'.spec.js' for Playwright, '.java' for JUnit. " +
32
+ "NEVER use the default 'integration_test.py' when scenarioFile is set — it collides with other generated tests."),
31
33
  endpointURL: baseTestSchema.endpointURL.default(""),
32
34
  })
33
35
  .omit({ method: true }).shape;
@@ -48,7 +50,7 @@ export class IntegrationTestService extends TestGenerationService {
48
50
  }
49
51
  buildAssertionEnhancementInstructions() {
50
52
  return `
51
- ⏭️ **CRITICAL NEXT STEP — Enhance response body assertions after each request:**
53
+ **CRITICAL NEXT STEP — Enhance response body assertions after each request:**
52
54
 
53
55
  The generated integration test contains only basic status-code assertions after each \`send_request\` / \`sendRequest\` call. For every request in the test (especially POST, PUT, and GET), add meaningful assertions on the response body using the rules below.
54
56
 
@@ -72,16 +74,45 @@ ${ENHANCE_ASSERTIONS_FOR_INTEGRATION_AND_CONTRACTPROVIDER}
72
74
  const TOOL_NAME = "skyramp_integration_test_generation";
73
75
  export function registerIntegrationTestTool(server) {
74
76
  server.registerTool(TOOL_NAME, {
75
- description: `Generate an integration test using Skyramp's deterministic test generation platform.
77
+ description: `${getPersonaPrefix()}Before calling this tool, you MUST output a <thinking> block that covers:
78
+ 1. The endpoint URL(s) and HTTP method(s) involved in this multi-step workflow
79
+ 2. Why an integration test (multi-step workflow validation) is the right choice for this intent
80
+ 3. Which assertions this test should validate at each step (status code + key chained response fields)
81
+ 4. Each required parameter and what value it will take, with source (workspace config / diff / scenario file / user input)
82
+ If any required parameter cannot be determined without guessing, STOP and ask the user before calling the tool.
83
+
84
+ ---
85
+
86
+ Generate an integration test from a scenario file or a live endpoint trace.
87
+
88
+ **Two mutually exclusive modes — choose exactly one:**
89
+ 1. **Scenario mode** (preferred for multi-step flows): pass \`scenarioFile\` (absolute path to the .json file returned by skyramp_batch_scenario_test_generation). Do NOT pass \`apiSchema\` or \`endpointURL\` in this mode. Passing both causes: "scenarioFile is mutually exclusive with apiSchema and endpointURL."
90
+ 2. **Direct mode**: pass \`endpointURL\` and optionally \`apiSchema\`. Do NOT pass \`scenarioFile\`.
76
91
 
77
- Integration tests validate that multiple services, components, or modules work together correctly. They test complex user workflows, service interactions, data flow between systems, and ensure that integrated components function as expected in realistic scenarios.
92
+ **Auth scenario mode only:**
93
+ - If workspace has \`api.authType\` set: omit ALL auth params — workspace config handles the Bearer prefix. Passing auth alongside workspace authType causes: "Auth header and auth type cannot be supported at the same time."
94
+ - If workspace has no \`api.authType\`: pass \`authHeader\` only (no \`authScheme\`, no \`authToken\`).
78
95
 
79
- **IMPORTANT: If an apiSchema parameter (OpenAPI/Swagger file path or URL) is provided, DO NOT attempt to read or analyze the file contents. These files can be very large. Simply pass the path/URL to the tool - the backend will handle reading and processing the schema file.**
96
+ **Output filename:** When \`scenarioFile\` is provided and user did not specify a name, derive it: strip path and 'scenario_' prefix, replace hyphens/non-alphanum with underscores, append '_integration_test' + language extension. Example: 'scenario_orders-patch.json' 'orders_patch_integration_test.py'. Never use the default 'integration_test.py' when scenarioFile is set it collides.
80
97
 
81
- **CRITICAL - When using scenarioFile or trace parameter:**
82
- If \`scenarioFile\` or \`trace\` parameter is provided, DO NOT pass \`apiSchema\` or \`endpointURL\` parameters. The scenario/trace file already contains all necessary endpoint and schema information. Passing both will cause test generation to fail.`,
98
+ **IMPORTANT:** If \`apiSchema\` is provided in direct mode, pass the path/URL as-is do NOT read the file contents. The backend processes it.`,
83
99
  inputSchema: integrationTestSchema,
84
100
  }, async (params) => {
101
+ if (params.scenarioFile && (params.apiSchema || params.endpointURL)) {
102
+ return {
103
+ content: [{
104
+ type: "text",
105
+ text: "**skyramp_integration_test_generation Error: Conflicting parameters**\n\n" +
106
+ "`scenarioFile` is mutually exclusive with `apiSchema` and `endpointURL`.\n\n" +
107
+ "**Received:** scenarioFile=" + params.scenarioFile +
108
+ (params.apiSchema ? ", apiSchema=" + params.apiSchema : "") +
109
+ (params.endpointURL ? ", endpointURL=" + params.endpointURL : "") + "\n\n" +
110
+ "**How to fix:** Remove `apiSchema` and `endpointURL` when passing `scenarioFile` — " +
111
+ "the scenario file already contains all endpoint and schema information.",
112
+ }],
113
+ isError: true,
114
+ };
115
+ }
85
116
  const service = new IntegrationTestService();
86
117
  const result = await service.generateTest(params);
87
118
  AnalyticsService.pushTestGenerationToolEvent(TOOL_NAME, result, params).catch(() => {
@@ -4,6 +4,7 @@ import { baseSchema, AUTH_PLACEHOLDER_TOKEN } from "../../types/TestTypes.js";
4
4
  import { AnalyticsService } from "../../services/AnalyticsService.js";
5
5
  import { getWorkspaceAuthConfig, WorkspaceAuthType } from "../../utils/workspaceAuth.js";
6
6
  import { logger } from "../../utils/logger.js";
7
+ import { getPersonaPrefix } from "../../prompts/architectPersona.js";
7
8
  const scenarioTestSchema = {
8
9
  scenarioName: z
9
10
  .string()
@@ -54,7 +55,7 @@ const scenarioTestSchema = {
54
55
  .string()
55
56
  .optional()
56
57
  .default("")
57
- .describe("Which HTTP header carries the auth credential. Examples: 'Authorization' (Bearer/Token auth), 'X-Api-Key' (API key auth), 'Cookie' (session auth). Pass empty string to skip auth for unauthenticated endpoints."),
58
+ .describe("Which HTTP header carries the auth credential (e.g., 'Authorization', 'X-Api-Key', 'Cookie'). Omit or pass empty string to auto-resolve from workspace config. To force an unauthenticated request, omit AND ensure no workspace auth is configured."),
58
59
  authScheme: z
59
60
  .string()
60
61
  .optional()
@@ -78,50 +79,27 @@ const scenarioTestSchema = {
78
79
  const TOOL_NAME = "skyramp_scenario_test_generation";
79
80
  export function registerScenarioTestTool(server) {
80
81
  server.registerTool(TOOL_NAME, {
81
- description: `Generate a single trace request from AI-parsed scenario parameters.
82
+ description: `${getPersonaPrefix()}Before calling this tool, you MUST output a <thinking> block that covers:
83
+ 1. The specific API endpoint (method + concrete path with real IDs, not templates)
84
+ 2. The request body fields and their values, with source (schema / prior step response / user input)
85
+ 3. The expected response status code and key response fields to chain into subsequent steps
86
+ 4. Whether this step depends on a prior step's response ID — if so, confirm the ID value is known
87
+ If a required path parameter or request body field cannot be determined without guessing, STOP and ask the user before calling the tool.
82
88
 
83
- This tool generates a single TraceRequest object using parameters that have been parsed by AI from a natural language scenario. The AI should analyze the scenario and provide structured parameters instead of relying on hardcoded parsing logic.
89
+ ---
84
90
 
85
- **What it does:**
86
- 1. **Accept AI-Parsed Data**: Takes structured parameters parsed by AI from natural language
87
- 2. **Generate Trace Request**: Creates a single TraceRequest object with proper format
88
- 3. **File Management**: Appends the request to an existing trace file or creates a new one
89
- 4. **Dynamic Source**: IF DNS NAME IS PROVIDED, USE IT FOR SOURCE IP AND PORT
91
+ **Dynamic context (use this before generating):**
92
+ If \`skyramp_analyze_changes\` has already run and returned a \`sessionId\`, fetch the endpoint detail before building this step:
93
+ \`skyramp://analysis/{sessionId}/endpoints/{path}/{method}\`
94
+ This gives you the exact request body fields, types, and required vs optional distinction — use it to construct accurate request bodies instead of guessing from field names.
90
95
 
91
- **Output:**
92
- Returns a single TraceRequest object with:
93
- - Dynamic source IP and port
94
- - Destination host (extracted from API schema)
95
- - HTTP method and path (provided by AI)
96
- - Request and response bodies (provided by AI or generated)
97
- - Request and response headers
98
- - Status code and timestamp
99
- - Network details (port, scheme)
96
+ ---
100
97
 
101
- **AI Responsibilities:**
102
- The AI should parse the natural language scenario and provide:
103
- - HTTP method (POST, GET, PUT, DELETE)
104
- - API path with CONCRETE ID values, not templates (e.g., /api/v1/products/70885, NOT /api/v1/products/{product_id})
105
- - Request body (JSON string) for POST/PUT/PATCH requests
106
- - Query parameters (JSON string) for GET search/filter/list requests — NEVER put query params in requestBody
107
- - Response body (JSON string, if applicable)
108
- - Status code (optional, defaults based on method)
109
- - Entity details (name, price, quantity, ID as needed)
98
+ Generate a single-step scenario trace request. For multi-step scenarios, prefer \`skyramp_batch_scenario_test_generation\` which generates all steps in one call.
110
99
 
111
- **Requirements:**
112
- - Natural language scenario description
113
- - API schema (OpenAPI/Swagger file or URL) for destination extraction
114
- - AI-parsed HTTP method and path (required)
115
- - AI-parsed request/response bodies (optional)
100
+ **Path must use CONCRETE ID values** (e.g. '/api/v1/products/70885', not '/api/v1/products/{id}'). Use \`queryParams\` for GET filters/search — never \`requestBody\`.
116
101
 
117
- **Note:** This tool generates one request at a time. Call multiple times for multi-step scenarios.
118
-
119
- **CRITICAL - Integration Test Generation After Scenario Creation:**
120
- When generating an integration test using the scenario file created by this tool:
121
- 1. Pass the scenario file path to the \`scenarioFile\` parameter
122
- 2. DO NOT pass \`apiSchema\` or \`endpointURL\` parameters - the scenario file already contains all necessary endpoint and schema information
123
- 3. Provide: \`language\`, \`framework\`, \`outputDir\`, \`prompt\`, and \`scenarioFile\`. Auth parameters are automatically extracted from the scenario trace; only pass \`authHeader\`/\`authScheme\` if you need to override the trace values.
124
- Passing both scenarioFile and apiSchema/endpointURL will cause the test generation to fail.`,
102
+ **After this tool:** call \`skyramp_integration_test_generation\` with the returned \`scenarioFile\` path. Do NOT also pass \`apiSchema\` or \`endpointURL\` — the scenario file contains all endpoint information.`,
125
103
  inputSchema: scenarioTestSchema,
126
104
  }, async (params) => {
127
105
  if (!params.authHeader) {
@@ -3,6 +3,7 @@ import { z } from "zod";
3
3
  import { AnalyticsService } from "../../services/AnalyticsService.js";
4
4
  import { TestGenerationService, } from "../../services/TestGenerationService.js";
5
5
  import { normalizeLanguageParams, resolveParamAliases, } from "../../utils/normalizeParams.js";
6
+ import { getPersonaPrefix } from "../../prompts/architectPersona.js";
6
7
  const TOOL_NAME = "skyramp_ui_test_generation";
7
8
  export class UITestService extends TestGenerationService {
8
9
  getTestType() {
@@ -18,6 +19,53 @@ export class UITestService extends TestGenerationService {
18
19
  async handleApiAnalysis(params, generateOptions) {
19
20
  return null;
20
21
  }
22
+ async generateTest(params) {
23
+ const result = await super.generateTest(params);
24
+ if (result.isError)
25
+ return result;
26
+ const content = [...result.content];
27
+ content.push({
28
+ type: "text",
29
+ text: this.buildUIAssertionInstructions(),
30
+ });
31
+ return { ...result, content };
32
+ }
33
+ buildUIAssertionInstructions() {
34
+ return `
35
+ ⏭️ **CRITICAL NEXT STEP — Review and fix assertions in the generated UI test:**
36
+
37
+ After generating a UI test from a recorded trace, you MUST review and fix assertions to catch real app bugs — not just replay what happened.
38
+
39
+ **Process:**
40
+ 1. **Replay the scenario mentally**: At each state-changing action (form submit, item delete/add/edit), ask: "What is the EXPECTED outcome based on the action performed?"
41
+ 2. **Identify expectation mismatches**: If the recorded trace shows a result that contradicts the action (e.g., removing 1 item from 2 but the page shows 3 items, submitting a form but getting a blank page, editing a field but the old value persists), this is an app bug the test should catch.
42
+ 3. **Fix or add assertions**: For each mismatch:
43
+ - If the generated test has an assertion using the WRONG (buggy) value, edit it to assert the CORRECT expected value.
44
+ - If no assertion exists for the buggy behavior, ADD one immediately after the action that triggers it.
45
+ - Use \`toContainText\`, \`toHaveText\`, or \`toBeVisible\`/\`toBeHidden\` as appropriate.
46
+ - **CRITICAL**: Only use selectors that already appear in the generated test file. Do NOT invent new data-testid values or guess aria attributes. If no suitable selector exists, call \`browser_assert\` on the live page to record one, then re-export and regenerate.
47
+
48
+ **Strategic assertion placement — call at key checkpoints only (3–5 per test):**
49
+ - **After the main action completes**: verify the outcome is visible (new item appears, form saves, confirmation shows)
50
+ - **State transitions**: verify counts, totals, or status fields update correctly after add/remove/edit
51
+ - **Navigation results**: verify you landed on the right page after a redirect
52
+ - **List integrity after form save**: after any form submit that modifies a list (order items, cart), assert the item count is unchanged unless the action explicitly added or removed items — catches duplication bugs
53
+
54
+ **Common bug patterns to assert against:**
55
+ - Item count not updating after add/remove
56
+ - Form values not persisting after save
57
+ - Page crashes or blank renders after navigation
58
+ - Stale data showing after state changes
59
+
60
+ **What NOT to assert:**
61
+ - Static page headings or boilerplate labels
62
+ - Intermediate states (typing, dropdown opening)
63
+ - Values already guaranteed by the action you just took
64
+ - The same value with multiple selectors
65
+
66
+ The goal is tests that FAIL when the app has bugs, not tests that simply replay what happened.
67
+ `;
68
+ }
21
69
  }
22
70
  // Only include the original params in the schema
23
71
  const uiTestSchema = {
@@ -39,10 +87,27 @@ const uiTestSchema = {
39
87
  };
40
88
  export function registerUITestTool(server) {
41
89
  server.registerTool(TOOL_NAME, {
42
- description: `Generate a UI test using Skyramp's deterministic test generation platform.
43
-
44
- UI tests validate user interface functionality by simulating real user interactions with your web application. They test user workflows, form submissions, navigation, responsive design, and ensure that your frontend works correctly across different browsers and devices. UI tests use Playwright recordings as input to generate comprehensive test suites that replay user interactions, validate UI elements, and verify expected behaviors in browser environments.
45
- **CRITICAL: To collect a Playwright trace, use the browser_* tools (browser_navigate, browser_click, browser_type, etc.) to interact with the application, then call skyramp_export_zip to export the trace zip. Do NOT use skyramp_start_trace_collection/skyramp_stop_trace_collection.**`,
90
+ description: `${getPersonaPrefix()}Before calling this tool, you MUST output a <thinking> block that covers:
91
+ 1. The user-facing flow(s) captured in the Playwright trace (pages visited, actions taken)
92
+ 2. Why a UI test (Playwright-based interaction replay) is the right choice for this intent
93
+ 3. Which assertions this test should validate (page content, element state, navigation results)
94
+ 4. The absolute path to the trace zip and the output directory, with source confirmation
95
+ If the trace zip path cannot be confirmed as an absolute path to an existing file, STOP and re-export before calling this tool.
96
+
97
+ ---
98
+
99
+ Generate a UI test using Skyramp's deterministic test generation platform.
100
+
101
+ UI tests validate user interface functionality by simulating real user interactions with your web application. They test user workflows, form submissions, navigation, and ensure that your frontend works correctly across different browsers. UI tests use Playwright recordings as input to generate comprehensive test suites that replay user interactions, validate UI elements, and verify expected behaviors.
102
+
103
+ **Dynamic context (use this before recording):**
104
+ If \`skyramp_analyze_changes\` has already run and returned a \`sessionId\`, check the diff summary before deciding which flows to record:
105
+ \`skyramp://analysis/{sessionId}/diff\`
106
+ This tells you exactly which frontend files changed so you record traces for the right user flows — not just any page.
107
+
108
+ **Typical pipeline:** Use the \`browser_*\` tools (\`browser_navigate\`, \`browser_click\`, \`browser_type\`, etc.) to record user interactions, then call \`skyramp_export_zip\` to export a trace zip, then pass the absolute path to that zip as \`playwrightInput\` here.
109
+
110
+ **CRITICAL: Do NOT use skyramp_start_trace_collection/skyramp_stop_trace_collection for UI test recording — use browser_* tools + skyramp_export_zip instead.**`,
46
111
  inputSchema: uiTestSchema,
47
112
  _meta: {
48
113
  keywords: ["ui test", "playwright"],