@skyramp/mcp 0.4.3 → 0.4.4-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 (168) hide show
  1. package/build/index.js +239 -210
  2. package/build/playwright/PlaywrightTraceService.d.ts +1 -1
  3. package/build/playwright/blueprintDigestStore.d.ts +1 -1
  4. package/build/playwright/registerPlaywrightTools.d.ts +1 -1
  5. package/build/playwright/traceRecordingPrompt.d.ts +1 -1
  6. package/build/playwright/traceRecordingPrompt.js +4 -3
  7. package/build/prompts/local-dev/local-dev-prompts.d.ts +1 -1
  8. package/build/prompts/local-dev/local-dev-prompts.js +2 -2
  9. package/build/prompts/orderAssertionRule.d.ts +9 -0
  10. package/build/prompts/orderAssertionRule.js +9 -0
  11. package/build/prompts/startTraceCollectionPrompts.d.ts +1 -1
  12. package/build/prompts/startTraceCollectionPrompts.js +2 -3
  13. package/build/prompts/test-maintenance/driftAnalysisShared.js +2 -0
  14. package/build/prompts/test-recommendation/registerRecommendTestsPrompt.d.ts +1 -1
  15. package/build/prompts/test-recommendation/registerRecommendTestsPrompt.js +2 -2
  16. package/build/prompts/testbot/testbot-prompts.d.ts +1 -1
  17. package/build/prompts/testbot/testbot-prompts.js +9 -9
  18. package/build/recommendation/answers.d.ts +3 -3
  19. package/build/recommendation/answers.js +3 -2
  20. package/build/recommendation/verifierContracts.d.ts +4 -4
  21. package/build/recommendation/verifierContracts.js +6 -5
  22. package/build/recommendation/verifiers/coverage.js +25 -25
  23. package/build/recommendation/verifiers/deliveredMatchesPlan.js +3 -3
  24. package/build/resources/analysisResources.d.ts +1 -1
  25. package/build/resources/analysisResources.js +1 -1
  26. package/build/resources/progressResource.d.ts +1 -1
  27. package/build/resources/sutSetupResource.d.ts +1 -1
  28. package/build/resources/sutSetupResource.js +1 -1
  29. package/build/resources/testbotResource.d.ts +1 -1
  30. package/build/resources/testbotResource.js +1 -1
  31. package/build/services/AnalyticsService.d.ts +1 -1
  32. package/build/services/FleetExecutionService.d.ts +130 -0
  33. package/build/services/FleetExecutionService.js +391 -0
  34. package/build/services/ModularizationService.d.ts +1 -1
  35. package/build/services/TestExecutionService.d.ts +7 -0
  36. package/build/services/TestExecutionService.js +21 -0
  37. package/build/services/TestGenerationService.d.ts +1 -1
  38. package/build/tool-phases.js +2 -0
  39. package/build/tools/auth/loginTool.d.ts +1 -1
  40. package/build/tools/auth/loginTool.js +2 -2
  41. package/build/tools/auth/logoutTool.d.ts +1 -1
  42. package/build/tools/auth/logoutTool.js +2 -2
  43. package/build/tools/code-refactor/alignAssertionsTool.d.ts +1 -1
  44. package/build/tools/code-refactor/alignAssertionsTool.js +1 -1
  45. package/build/tools/code-refactor/assertionSkillTools.d.ts +1 -1
  46. package/build/tools/code-refactor/assertionSkillTools.js +2 -2
  47. package/build/tools/code-refactor/codeReuseTool.d.ts +1 -1
  48. package/build/tools/code-refactor/codeReuseTool.js +2 -2
  49. package/build/tools/code-refactor/enhanceAssertionsTool.d.ts +1 -1
  50. package/build/tools/code-refactor/enhanceAssertionsTool.js +1 -1
  51. package/build/tools/code-refactor/modularizationTool.d.ts +1 -1
  52. package/build/tools/code-refactor/modularizationTool.js +2 -2
  53. package/build/tools/enrichTestWithMocksTool.d.ts +1 -1
  54. package/build/tools/enrichTestWithMocksTool.js +2 -2
  55. package/build/tools/executeFleetTool.d.ts +28 -0
  56. package/build/tools/executeFleetTool.js +107 -0
  57. package/build/tools/executeSkyrampTestTool.d.ts +8 -5
  58. package/build/tools/executeSkyrampTestTool.js +13 -5
  59. package/build/tools/fixErrorTool.d.ts +1 -1
  60. package/build/tools/fixErrorTool.js +1 -1
  61. package/build/tools/generate-tests/batchMockGenerationTool.d.ts +6 -57
  62. package/build/tools/generate-tests/batchMockGenerationTool.js +3 -3
  63. package/build/tools/generate-tests/generateBatchScenarioRestTool.d.ts +9 -45
  64. package/build/tools/generate-tests/generateBatchScenarioRestTool.js +29 -11
  65. package/build/tools/generate-tests/generateContractRestTool.d.ts +1 -2
  66. package/build/tools/generate-tests/generateContractRestTool.js +1 -1
  67. package/build/tools/generate-tests/generateE2ERestTool.d.ts +2 -2
  68. package/build/tools/generate-tests/generateE2ERestTool.js +10 -2
  69. package/build/tools/generate-tests/generateFuzzRestTool.d.ts +1 -1
  70. package/build/tools/generate-tests/generateFuzzRestTool.js +1 -1
  71. package/build/tools/generate-tests/generateIntegrationRestTool.d.ts +1 -2
  72. package/build/tools/generate-tests/generateIntegrationRestTool.js +1 -1
  73. package/build/tools/generate-tests/generateLoadRestTool.d.ts +1 -2
  74. package/build/tools/generate-tests/generateLoadRestTool.js +2 -1
  75. package/build/tools/generate-tests/generateMockRestTool.d.ts +1 -2
  76. package/build/tools/generate-tests/generateMockRestTool.js +3 -3
  77. package/build/tools/generate-tests/generateSmokeRestTool.d.ts +1 -1
  78. package/build/tools/generate-tests/generateSmokeRestTool.js +1 -1
  79. package/build/tools/generate-tests/generateUILoadRestTool.d.ts +64 -0
  80. package/build/tools/generate-tests/generateUILoadRestTool.js +512 -0
  81. package/build/tools/generate-tests/generateUIRestTool.d.ts +8 -7
  82. package/build/tools/generate-tests/generateUIRestTool.js +10 -2
  83. package/build/tools/generate-tests/loadTestSchema.d.ts +15 -15
  84. package/build/tools/generate-tests/trace-reuse-guard.d.ts +1 -1
  85. package/build/tools/generateEnrichedIntegrationTestTool.d.ts +1 -2
  86. package/build/tools/generateEnrichedIntegrationTestTool.js +1 -1
  87. package/build/tools/localDevWorkerComposeTool.d.ts +1 -1
  88. package/build/tools/localDevWorkerComposeTool.js +2 -2
  89. package/build/tools/one-click/oneClickTool.d.ts +1 -1
  90. package/build/tools/one-click/oneClickTool.js +1 -1
  91. package/build/tools/preflightMockCheckTool.d.ts +1 -1
  92. package/build/tools/preflightMockCheckTool.js +3 -3
  93. package/build/tools/queryProxyMocksTool.d.ts +1 -1
  94. package/build/tools/queryProxyMocksTool.js +3 -3
  95. package/build/tools/submitReportTool.d.ts +111 -414
  96. package/build/tools/submitReportTool.js +88 -38
  97. package/build/tools/test-management/actionsTool.d.ts +1 -1
  98. package/build/tools/test-management/actionsTool.js +7 -7
  99. package/build/tools/test-management/analyzeChangesTool.d.ts +22 -118
  100. package/build/tools/test-management/analyzeChangesTool.js +4 -4
  101. package/build/tools/test-management/analyzeTestHealthTool.d.ts +1 -1
  102. package/build/tools/test-management/analyzeTestHealthTool.js +4 -4
  103. package/build/tools/test-management/registerTestPlanTool.d.ts +107 -733
  104. package/build/tools/test-management/registerTestPlanTool.js +12 -12
  105. package/build/tools/test-management/resolveScreenTool.d.ts +1 -1
  106. package/build/tools/test-management/resolveScreenTool.js +6 -2
  107. package/build/tools/trace/startTraceCollectionTool.d.ts +1 -1
  108. package/build/tools/trace/startTraceCollectionTool.js +4 -4
  109. package/build/tools/trace/stopTraceCollectionTool.d.ts +1 -1
  110. package/build/tools/trace/stopTraceCollectionTool.js +2 -2
  111. package/build/tools/verifyTestDependenciesTool.d.ts +1 -1
  112. package/build/tools/verifyTestDependenciesTool.js +1 -1
  113. package/build/tools/workspace/initScanWorkspaceTool.d.ts +1 -1
  114. package/build/tools/workspace/initScanWorkspaceTool.js +1 -1
  115. package/build/tools/workspace/initializeWorkspaceTool.d.ts +1 -1
  116. package/build/tools/workspace/initializeWorkspaceTool.js +1 -1
  117. package/build/types/RepositoryAnalysis.d.ts +164 -1170
  118. package/build/types/RepositoryAnalysis.js +12 -10
  119. package/build/types/StepMethod.d.ts +24 -1
  120. package/build/types/StepMethod.js +1 -1
  121. package/build/types/TestTypes.d.ts +41 -133
  122. package/build/types/TestTypes.js +25 -3
  123. package/build/types/TestbotReport.d.ts +4 -1
  124. package/build/utils/assertion-verify/metrics.js +12 -0
  125. package/build/utils/docker.d.ts +29 -7
  126. package/build/utils/docker.js +83 -27
  127. package/build/utils/findUp.d.ts +20 -0
  128. package/build/utils/findUp.js +59 -0
  129. package/build/utils/initAgent.d.ts +3 -3
  130. package/build/utils/initAgent.js +58 -26
  131. package/build/utils/language-helper.js +1 -1
  132. package/build/utils/progress.d.ts +3 -4
  133. package/build/utils/progress.js +4 -4
  134. package/build/utils/repoSdkVersion.d.ts +16 -0
  135. package/build/utils/repoSdkVersion.js +88 -0
  136. package/build/utils/testDependencyPolicy.d.ts +20 -0
  137. package/build/utils/testDependencyPolicy.js +64 -4
  138. package/build/utils/utils-verify/typecheck.js +3 -18
  139. package/build/utils/utils.d.ts +1 -1
  140. package/build/utils/versions.d.ts +3 -3
  141. package/build/utils/versions.js +1 -1
  142. package/build/workspace/workspace.d.ts +117 -538
  143. package/build/workspace/workspace.js +2 -2
  144. package/node_modules/playwright/lib/mcp/skyramp/assertOrder.js +66 -0
  145. package/node_modules/playwright/lib/mcp/skyramp/assertTool.js +12 -3
  146. package/node_modules/playwright/lib/mcp/skyramp/common/liveReadError.js +40 -0
  147. package/node_modules/playwright/lib/mcp/skyramp/loadTraceTool.js +21 -0
  148. package/node_modules/playwright/lib/mcp/skyramp/skyRampImport.js +2 -1
  149. package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +96 -16
  150. package/node_modules/playwright/lib/mcp/test/skyRampExport.js +10 -0
  151. package/node_modules/playwright/node_modules/playwright-core/lib/server/codegen/skyramp/jsonlReader.js +1 -0
  152. package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderRunner.js +22 -0
  153. package/node_modules/playwright/node_modules/playwright-core/lib/utils/isomorphic/elementOrder.js +57 -0
  154. package/node_modules/playwright/node_modules/playwright-core/lib/utils.js +2 -0
  155. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.uiPTFncY.js → index.0MJW0uG7.js} +1 -1
  156. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +1 -1
  157. package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
  158. package/node_modules/playwright/node_modules/playwright-core/src/server/codegen/skyramp/jsonlReader.ts +1 -1
  159. package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderRunner.ts +26 -1
  160. package/node_modules/playwright/node_modules/playwright-core/src/utils/isomorphic/elementOrder.ts +83 -0
  161. package/node_modules/playwright/node_modules/playwright-core/src/utils.ts +1 -0
  162. package/node_modules/playwright/package.json +1 -1
  163. package/package.json +9 -6
  164. package/plugin/prompts/plan-tests.md +9 -17
  165. package/plugin/skills/enhance-assertions/reference/shared-rules.md +1 -1
  166. package/plugin/skills/enhance-assertions/reference/ui.md +2 -2
  167. package/plugin/skills/fix-test-import-errors/SKILL.md +1 -1
  168. package/plugin/skills/repair-staged-files/SKILL.md +17 -0
package/build/index.js CHANGED
@@ -1,15 +1,17 @@
1
1
  #!/usr/bin/env node
2
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { serveStdio } from "@modelcontextprotocol/server/stdio";
3
+ import { McpServer } from "@modelcontextprotocol/server";
3
4
  import { runnableLanguagesText } from "./types/TestTypes.js";
4
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
5
  import { registerStartTraceCollectionPrompt } from "./prompts/startTraceCollectionPrompts.js";
6
6
  import { registerTraceTool } from "./tools/trace/startTraceCollectionTool.js";
7
7
  import { registerTraceStopTool } from "./tools/trace/stopTraceCollectionTool.js";
8
8
  import { registerExecuteSkyrampTestTool } from "./tools/executeSkyrampTestTool.js";
9
+ import { registerExecuteFleetTool } from "./tools/executeFleetTool.js";
9
10
  import { AUTH_PLACEHOLDER_TOKEN } from "./types/TestTypes.js";
10
11
  import { AUTH_CONFLICT_ERROR_MSG } from "./prompts/test-recommendation/recommendationSections.js";
11
12
  import { logger } from "./utils/logger.js";
12
13
  import { registerUITestTool } from "./tools/generate-tests/generateUIRestTool.js";
14
+ import { registerUILoadTestTool } from "./tools/generate-tests/generateUILoadRestTool.js";
13
15
  import { registerSmokeTestTool } from "./tools/generate-tests/generateSmokeRestTool.js";
14
16
  import { registerFuzzTestTool } from "./tools/generate-tests/generateFuzzRestTool.js";
15
17
  import { registerContractTestTool } from "./tools/generate-tests/generateContractRestTool.js";
@@ -83,220 +85,249 @@ If the workspace root is a git repo AND \`.skyramp/workspace.yml\` does not exis
83
85
  3. Proceed with the originally requested tool, if it is not one of the two workspace-init tools above.
84
86
  Skip only if: not a git repo, \`.skyramp/workspace.yml\` already exists, or user explicitly declines.
85
87
  `;
86
- const server = new McpServer({
87
- name: "Skyramp MCP Server",
88
- version: "1.0.0",
89
- }, {
90
- capabilities: {
91
- tools: {
92
- listChanged: true,
88
+ /**
89
+ * Builds one fully-registered server. `serveStdio` calls this per connection,
90
+ * so every registration must happen here rather than at module load.
91
+ */
92
+ async function createServer({ era }) {
93
+ const server = new McpServer({
94
+ name: "Skyramp MCP Server",
95
+ version: "1.0.0",
96
+ }, {
97
+ capabilities: {
98
+ tools: {
99
+ listChanged: true,
100
+ },
101
+ prompts: {
102
+ listChanged: true,
103
+ },
104
+ resources: {
105
+ listChanged: true,
106
+ },
93
107
  },
94
- prompts: {
95
- listChanged: true,
96
- },
97
- resources: {
98
- listChanged: true,
99
- },
100
- },
101
- instructions: `Skyramp MCP Server — generates and executes API tests (fuzz, contract, integration, E2E, UI).
102
- ${workspaceInitInstructions}
103
- ## Rules
104
- - NEVER show CLI commands. NEVER attempt to install or configure the Skyramp CLI. ALWAYS use the MCP tools provided.
105
- - For UI and E2E tests, there are TWO recording modes:
106
- 1. **AI-driven recording** (default): Use the browser_* tools (browser_navigate, browser_click, etc.) to record interactions, then call skyramp_export_zip to export the trace, then call skyramp_ui_test_generation with the zip path.
107
- 2. **Manual recording**: ONLY when the user explicitly says "manual recording", "record myself", "I will interact", or "Docker trace" — use skyramp_start_trace_collection / skyramp_stop_trace_collection to let the user interact with the browser themselves.
108
- - To capture an authentication session for re-use (e.g. "save session", "store login", "record auth state"): call \`skyramp_start_trace_collection\` (\`playwrightSaveStoragePath\` defaults to saving the session in \`outputDir\` — no need to set it). Tell the user to log in once and then call \`skyramp_stop_trace_collection\` IMMEDIATELY after login completes. The saved file can be passed back as \`playwrightStoragePath\` on later trace runs, or referenced as \`storageState\` in generated tests.${oneClickInstructions}
108
+ instructions: `Skyramp MCP Server — generates and executes API tests (fuzz, contract, integration, E2E, UI).
109
+ ${workspaceInitInstructions}
110
+ ## Rules
111
+ - NEVER show CLI commands. NEVER attempt to install or configure the Skyramp CLI. ALWAYS use the MCP tools provided.
112
+ - For UI and E2E tests, there are TWO recording modes:
113
+ 1. **AI-driven recording** (default): Use the browser_* tools (browser_navigate, browser_click, etc.) to record interactions, then call skyramp_export_zip to export the trace, then call skyramp_ui_test_generation with the zip path.
114
+ 2. **Manual recording**: ONLY when the user explicitly says "manual recording", "record myself", "I will interact", or "Docker trace" — use skyramp_start_trace_collection / skyramp_stop_trace_collection to let the user interact with the browser themselves.
115
+ - To capture an authentication session for re-use (e.g. "save session", "store login", "record auth state"): call \`skyramp_start_trace_collection\` (\`playwrightSaveStoragePath\` defaults to saving the session in \`outputDir\` — no need to set it). Tell the user to log in once and then call \`skyramp_stop_trace_collection\` IMMEDIATELY after login completes. The saved file can be passed back as \`playwrightStoragePath\` on later trace runs, or referenced as \`storageState\` in generated tests.${oneClickInstructions}
109
116
 
110
- ## Test Management Flow
111
- Use \`skyramp_analyze_changes\` as the single entry point for both test recommendations and test health analysis.
117
+ ## Test Management Flow
118
+ Use \`skyramp_analyze_changes\` as the single entry point for both test recommendations and test health analysis.
112
119
 
113
- ### Recommendations (2-step)
114
- 1. Call \`skyramp_analyze_changes\` with \`repositoryPath\` and \`scope\` → scans endpoints, computes branch diff, discovers existing tests → returns a \`stateFile\` **and** inline ranked test recommendations.
115
- 2. (Optional) Use the \`skyramp_recommend_tests\` prompt with \`stateFile\` in Cursor/IDE chat for a focused recommendation view.
120
+ ### Recommendations (2-step)
121
+ 1. Call \`skyramp_analyze_changes\` with \`repositoryPath\` and \`scope\` → scans endpoints, computes branch diff, discovers existing tests → returns a \`stateFile\` **and** inline ranked test recommendations.
122
+ 2. (Optional) Use the \`skyramp_recommend_tests\` prompt with \`stateFile\` in Cursor/IDE chat for a focused recommendation view.
116
123
 
117
- ### Health Analysis (5-step)
118
- 1. Call \`skyramp_analyze_changes\` with \`repositoryPath\` and \`scope\` → returns a \`stateFile\`.
119
- 2. Run each external test in ${runnableLanguagesText()} with \`skyramp_execute_test\`, \`phase: "before"\`, and \`stateFile\` → the server runs your command and writes the verdict back to stateFile. The tool takes no other language, and the gate in step 3 does not ask for those.
120
- 3. Call \`skyramp_analyze_test_health\` with \`stateFile\` → runs drift analysis + health scoring + LLM semantic assessment. It refuses until step 2 has run.
121
- 4. (Optional) Run Skyramp-generated tests with \`skyramp_execute_test\` and \`stateFile\` → writes each verdict back for health scoring.
122
- 5. Call \`skyramp_actions\` with \`stateFile\` → executes UPDATE/REGENERATE/ADD recommendations (with execution-aware prioritization if step 4 ran).
124
+ ### Health Analysis (5-step)
125
+ 1. Call \`skyramp_analyze_changes\` with \`repositoryPath\` and \`scope\` → returns a \`stateFile\`.
126
+ 2. Run each external test in ${runnableLanguagesText()} with \`skyramp_execute_test\`, \`phase: "before"\`, and \`stateFile\` → the server runs your command and writes the verdict back to stateFile. The tool takes no other language, and the gate in step 3 does not ask for those.
127
+ 3. Call \`skyramp_analyze_test_health\` with \`stateFile\` → runs drift analysis + health scoring + LLM semantic assessment. It refuses until step 2 has run.
128
+ 4. (Optional) Run Skyramp-generated tests with \`skyramp_execute_test\` and \`stateFile\` → writes each verdict back for health scoring.
129
+ 5. Call \`skyramp_actions\` with \`stateFile\` → executes UPDATE/REGENERATE/ADD recommendations (with execution-aware prioritization if step 4 ran).
123
130
 
124
- After \`skyramp_analyze_changes\`, inspect enriched data via MCP Resources (use the \`sessionId\` returned in the output):
125
- - \`skyramp://analysis/{sessionId}/summary\` — high-level overview
126
- - \`skyramp://analysis/{sessionId}/endpoints\` — compact endpoint listing
127
- - \`skyramp://analysis/{sessionId}/endpoints/{path}\` — full path detail
128
- - \`skyramp://analysis/{sessionId}/endpoints/{path}/{method}\` — single method detail
129
- - \`skyramp://analysis/{sessionId}/scenarios\` — drafted scenarios
130
- - \`skyramp://analysis/{sessionId}/diff\` — branch diff context
131
+ After \`skyramp_analyze_changes\`, inspect enriched data via MCP Resources (use the \`sessionId\` returned in the output):
132
+ - \`skyramp://analysis/{sessionId}/summary\` — high-level overview
133
+ - \`skyramp://analysis/{sessionId}/endpoints\` — compact endpoint listing
134
+ - \`skyramp://analysis/{sessionId}/endpoints/{path}\` — full path detail
135
+ - \`skyramp://analysis/{sessionId}/endpoints/{path}/{method}\` — single method detail
136
+ - \`skyramp://analysis/{sessionId}/scenarios\` — drafted scenarios
137
+ - \`skyramp://analysis/{sessionId}/diff\` — branch diff context
131
138
 
132
- ## Workspace Defaults for Test Generation (MANDATORY)
133
- Before calling ANY test generation tool, you MUST follow this flow:
139
+ ## Workspace Defaults for Test Generation (MANDATORY)
140
+ Before calling ANY test generation tool, you MUST follow this flow:
134
141
 
135
- 1. **Read** the .skyramp/workspace.yml file to get the configured defaults.
136
- 2. **Extract** the \`language\`, \`framework\`, \`testDirectory\`, \`api.baseUrl\`, \`api.authHeader\`, and \`api.authType\` from the matching service in the services section.
137
- 3. **Use those values** as defaults for the test generation tool call. Pass the service \`testDirectory\` as the generation tool \`outputDir\`. Do NOT ask the user for these values if they are already configured in the workspace file.
138
- 4. **CRITICAL — endpointURL**: The \`endpointURL\` parameter MUST be the full URL to the specific endpoint being tested, NOT just the base URL. Construct it by combining \`api.baseUrl\` with the endpoint path. Example: if \`api.baseUrl\` is \`http://localhost:8000\` and the endpoint is \`/api/v1/products\`, pass \`endpointURL: "http://localhost:8000/api/v1/products"\`. NEVER pass just the base URL (e.g. \`http://localhost:8000\`) as \`endpointURL\`.
139
- 5. **CRITICAL — scenario generation**: When calling \`skyramp_batch_scenario_test_generation\`, ALWAYS pass:
140
- - \`baseURL\`: The full base URL from \`api.baseUrl\` (e.g., \`http://localhost:3000\`). This determines the scheme, host, and port in the generated trace. Without it, the trace defaults to https:443 which is almost always wrong for local development.
141
- - \`authHeader\`: Which HTTP header carries the auth credential. Get it from \`api.authHeader\` in workspace config. Examples: \`Authorization\` (Bearer/Token auth), \`X-Api-Key\` (API key auth), \`Cookie\` (session/cookie auth like NextAuth). Pass \`""\` to document the endpoint as unauthenticated in the generated test's assertions (unauthenticated endpoints or \`api.authType: "none"\`).
142
- - \`authScheme\`: Only when \`authHeader\` is \`Authorization\`. The prefix before the token (e.g., \`"Bearer"\` → \`Authorization: Bearer <token>\`). **Derive from**: (1) OpenAPI spec \`securitySchemes\`/\`securityDefinitions\`, (2) source code auth middleware, (3) workspace \`api.authType\`. **Do NOT guess.**
143
- - \`authToken\`: The full header value, used verbatim. When omitted, \`SKYRAMP_PLACEHOLDER_TOKEN\` is auto-generated. Only provide when the header needs a specific format (e.g., \`"session=${AUTH_PLACEHOLDER_TOKEN}"\` for Cookie). **Do NOT fabricate token values.**
144
- - \`apiSchema\` is OPTIONAL — omit it for code-first apps without OpenAPI specs.
145
- 6. **CRITICAL — integration test from scenario**: When calling \`skyramp_integration_test_generation\` with a \`scenarioFile\`:
146
- - If workspace has \`api.authType\` set: omit auth params entirely — passing auth here alongside workspace \`authType\` causes "${AUTH_CONFLICT_ERROR_MSG}".
147
- - If workspace has no \`api.authType\`: pass \`authHeader\` only (no \`authScheme\`).
148
- 7. **If the workspace file does not exist**, or the needed values (language, framework, testDirectory) are missing from the workspace config, ASK the user which language, framework, and outputDir they want before calling the tool.
149
- 8. The user can always override workspace defaults by explicitly specifying values in their request.
150
- `,
151
- });
152
- // Register prompts
153
- logger.info("Starting prompt registration process");
154
- const prompts = [
155
- registerStartTraceCollectionPrompt,
156
- registerRecommendTestsPrompt,
157
- registerTraceRecordingPrompt,
158
- ];
159
- if (isTestbotEnabled()) {
160
- prompts.push(registerTestbotPrompt);
161
- registerTestbotResource(server);
162
- logger.info("Testbot prompt enabled via SKYRAMP_FEATURE_TESTBOT");
163
- registerSutSetupResource(server);
164
- logger.info("Testbot SUT setup resource enabled via SKYRAMP_FEATURE_TESTBOT");
165
- }
166
- if (isLocalDevEnabled()) {
167
- prompts.push(registerLocalDevPrompt);
168
- logger.info("Local-dev prompt enabled via SKYRAMP_FEATURE_LOCAL_DEV");
169
- }
170
- prompts.forEach((registerPrompt) => registerPrompt(server));
171
- logger.info("All prompts registered successfully");
172
- // Register test generation tools
173
- const testGenerationTools = [
174
- registerSmokeTestTool,
175
- registerFuzzTestTool,
176
- registerContractTestTool,
177
- registerLoadTestTool,
178
- registerIntegrationTestTool,
179
- registerE2ETestTool,
180
- registerUITestTool,
181
- registerBatchScenarioTestTool,
182
- registerMockTool,
183
- registerBatchMockTool,
184
- ];
185
- testGenerationTools.forEach((registerTool) => registerTool(server));
186
- // Register modularization and code quality tools
187
- const codeQualityTools = [
188
- registerModularizationTool,
189
- registerCodeReuseTool,
190
- registerEnhanceAssertionsTool,
191
- registerAssertionVerifyTool,
192
- registerVerifyTestDependenciesTool,
193
- ];
194
- // With the Skyramp plugin loaded the agent already has the
195
- // fix-test-import-errors skill; the tool would be a second copy of
196
- // the same text (SKYR-4296).
197
- if (!isSkillsLoaded()) {
198
- // Same reasoning as fix-test-import-errors: with the plugin loaded the agent
199
- // has the validate-assertion-alignment-post-execution skill directly; this
200
- // tool exists so an agent without skill support still gets the same
201
- // procedure, rendered from the same markdown with real arguments filled in.
202
- codeQualityTools.push(registerFixErrorTool, registerAlignAssertionsTool);
203
- }
204
- else {
205
- // The enhance-assertions skill drives its own snapshot step itself (SKYR-4310).
206
- codeQualityTools.push(registerAssertionSnapshotTool);
207
- }
208
- codeQualityTools.forEach((registerTool) => registerTool(server));
209
- // Register analysis resources (MCP Resources for enriched data access)
210
- registerAnalysisResources(server);
211
- registerProgressResource(server);
212
- // Register unified test-management tools (replaces separate test-maintenance tools)
213
- registerAnalyzeChangesTool(server);
214
- registerAnalyzeTestHealthTool(server);
215
- registerActionsTool(server);
216
- registerRegisterTestPlanTool(server);
217
- registerResolveScreenTool(server);
218
- // Register workspace management tools
219
- registerInitScanWorkspaceTool(server);
220
- registerInitializeWorkspaceTool(server);
221
- // Register one-click orchestrated workflows
222
- if (oneClickEnabled || localDevEnabled) {
223
- registerOneClickTool(server);
224
- logger.info("One-click tools enabled", {
225
- enabledBy: {
226
- SKYRAMP_FEATURE_ONE_CLICK: oneClickEnabled,
227
- SKYRAMP_FEATURE_LOCAL_DEV: localDevEnabled,
228
- },
142
+ 1. **Read** the .skyramp/workspace.yml file to get the configured defaults.
143
+ 2. **Extract** the \`language\`, \`framework\`, \`testDirectory\`, \`api.baseUrl\`, \`api.authHeader\`, and \`api.authType\` from the matching service in the services section.
144
+ 3. **Use those values** as defaults for the test generation tool call. Pass the service \`testDirectory\` as the generation tool \`outputDir\`. Do NOT ask the user for these values if they are already configured in the workspace file.
145
+ 4. **CRITICAL — endpointURL**: The \`endpointURL\` parameter MUST be the full URL to the specific endpoint being tested, NOT just the base URL. Construct it by combining \`api.baseUrl\` with the endpoint path. Example: if \`api.baseUrl\` is \`http://localhost:8000\` and the endpoint is \`/api/v1/products\`, pass \`endpointURL: "http://localhost:8000/api/v1/products"\`. NEVER pass just the base URL (e.g. \`http://localhost:8000\`) as \`endpointURL\`.
146
+ 5. **CRITICAL — scenario generation**: When calling \`skyramp_batch_scenario_test_generation\`, ALWAYS pass:
147
+ - \`baseURL\`: The full base URL from \`api.baseUrl\` (e.g., \`http://localhost:3000\`). This determines the scheme, host, and port in the generated trace. Without it, the trace defaults to https:443 which is almost always wrong for local development.
148
+ - \`authHeader\`: Which HTTP header carries the auth credential. Get it from \`api.authHeader\` in workspace config. Examples: \`Authorization\` (Bearer/Token auth), \`X-Api-Key\` (API key auth), \`Cookie\` (session/cookie auth like NextAuth). Pass \`""\` to document the endpoint as unauthenticated in the generated test's assertions (unauthenticated endpoints or \`api.authType: "none"\`).
149
+ - \`authScheme\`: Only when \`authHeader\` is \`Authorization\`. The prefix before the token (e.g., \`"Bearer"\` → \`Authorization: Bearer <token>\`). **Derive from**: (1) OpenAPI spec \`securitySchemes\`/\`securityDefinitions\`, (2) source code auth middleware, (3) workspace \`api.authType\`. **Do NOT guess.**
150
+ - \`authToken\`: The full header value, used verbatim. When omitted, \`SKYRAMP_PLACEHOLDER_TOKEN\` is auto-generated. Only provide when the header needs a specific format (e.g., \`"session=${AUTH_PLACEHOLDER_TOKEN}"\` for Cookie). **Do NOT fabricate token values.**
151
+ - \`apiSchema\` is OPTIONAL — omit it for code-first apps without OpenAPI specs.
152
+ 6. **CRITICAL — integration test from scenario**: When calling \`skyramp_integration_test_generation\` with a \`scenarioFile\`:
153
+ - If workspace has \`api.authType\` set: omit auth params entirely — passing auth here alongside workspace \`authType\` causes "${AUTH_CONFLICT_ERROR_MSG}".
154
+ - If workspace has no \`api.authType\`: pass \`authHeader\` only (no \`authScheme\`).
155
+ 7. **If the workspace file does not exist**, or the needed values (language, framework, testDirectory) are missing from the workspace config, ASK the user which language, framework, and outputDir they want before calling the tool.
156
+ 8. The user can always override workspace defaults by explicitly specifying values in their request.
157
+ `,
229
158
  });
230
- }
231
- // Register other Skyramp tools
232
- const infrastructureTools = [
233
- registerLoginTool,
234
- registerLogoutTool,
235
- registerExecuteSkyrampTestTool,
236
- registerTraceTool,
237
- registerTraceStopTool,
238
- ];
239
- if (isLocalDevEnabled()) {
240
- infrastructureTools.push(registerEnrichTestWithMocksTool, registerPreflightMockCheckTool, registerGenerateEnrichedIntegrationTestTool, registerLocalDevWorkerComposeTool, registerQueryProxyMocksTool);
241
- logger.info("Local-dev tools enabled via SKYRAMP_FEATURE_LOCAL_DEV");
242
- }
243
- if (isTestbotEnabled()) {
244
- infrastructureTools.push(registerSubmitReportTool);
245
- logger.info("Testbot tools enabled via SKYRAMP_FEATURE_TESTBOT");
246
- }
247
- infrastructureTools.forEach((registerTool) => registerTool(server));
248
- // Register Playwright browser tools (trace recording via browser automation).
249
- //
250
- // Tell the capture which attribute this repository uses for test ids. Without
251
- // it the capture assumes Playwright's `data-testid`, and a repo using anything
252
- // else gets no test-id anchor and a positional xpath for every element — which
253
- // resolves at capture time and breaks later, so the miss never announces itself
254
- // (SKYR-4136). This server is the only component that knows which repository it
255
- // is running against.
256
- //
257
- // A declared `use.testIdAttribute` is authoritative. Failing that, the
258
- // repository's own tests name the attribute whenever they select on it, so read
259
- // it out of their selectors rather than guessing from a list of popular names.
260
- // Never fatal: no answer leaves the capture on Playwright's default.
261
- (async () => {
262
- let testIdAttribute;
263
- try {
264
- const root = await findRoot(process.cwd());
265
- testIdAttribute = await readTestIdAttribute(root);
266
- if (testIdAttribute) {
267
- logger.info(`testIdAttribute declared by the Playwright config: ${testIdAttribute}`);
268
- }
269
- else {
270
- const discovered = await discoverTestIdAttribute(root);
271
- testIdAttribute = discovered.attribute;
159
+ // Register prompts
160
+ logger.info("Starting prompt registration process");
161
+ const prompts = [
162
+ registerStartTraceCollectionPrompt,
163
+ registerRecommendTestsPrompt,
164
+ registerTraceRecordingPrompt,
165
+ ];
166
+ if (isTestbotEnabled()) {
167
+ prompts.push(registerTestbotPrompt);
168
+ registerTestbotResource(server);
169
+ logger.info("Testbot prompt enabled via SKYRAMP_FEATURE_TESTBOT");
170
+ registerSutSetupResource(server);
171
+ logger.info("Testbot SUT setup resource enabled via SKYRAMP_FEATURE_TESTBOT");
172
+ }
173
+ if (isLocalDevEnabled()) {
174
+ prompts.push(registerLocalDevPrompt);
175
+ logger.info("Local-dev prompt enabled via SKYRAMP_FEATURE_LOCAL_DEV");
176
+ }
177
+ prompts.forEach((registerPrompt) => registerPrompt(server));
178
+ logger.info("All prompts registered successfully");
179
+ // Register test generation tools
180
+ const testGenerationTools = [
181
+ registerSmokeTestTool,
182
+ registerFuzzTestTool,
183
+ registerContractTestTool,
184
+ registerLoadTestTool,
185
+ registerIntegrationTestTool,
186
+ registerE2ETestTool,
187
+ registerUITestTool,
188
+ registerUILoadTestTool,
189
+ registerBatchScenarioTestTool,
190
+ registerMockTool,
191
+ registerBatchMockTool,
192
+ ];
193
+ testGenerationTools.forEach((registerTool) => registerTool(server));
194
+ // Register modularization and code quality tools
195
+ const codeQualityTools = [
196
+ registerModularizationTool,
197
+ registerCodeReuseTool,
198
+ registerEnhanceAssertionsTool,
199
+ registerAssertionVerifyTool,
200
+ registerVerifyTestDependenciesTool,
201
+ ];
202
+ // With the Skyramp plugin loaded the agent already has the
203
+ // fix-test-import-errors skill; the tool would be a second copy of
204
+ // the same text (SKYR-4296).
205
+ if (!isSkillsLoaded()) {
206
+ // Same reasoning as fix-test-import-errors: with the plugin loaded the agent
207
+ // has the validate-assertion-alignment-post-execution skill directly; this
208
+ // tool exists so an agent without skill support still gets the same
209
+ // procedure, rendered from the same markdown with real arguments filled in.
210
+ codeQualityTools.push(registerFixErrorTool, registerAlignAssertionsTool);
211
+ }
212
+ else {
213
+ // The enhance-assertions skill drives its own snapshot step itself (SKYR-4310).
214
+ codeQualityTools.push(registerAssertionSnapshotTool);
215
+ }
216
+ codeQualityTools.forEach((registerTool) => registerTool(server));
217
+ // Register analysis resources (MCP Resources for enriched data access)
218
+ registerAnalysisResources(server);
219
+ registerProgressResource(server);
220
+ // Register unified test-management tools (replaces separate test-maintenance tools)
221
+ registerAnalyzeChangesTool(server);
222
+ registerAnalyzeTestHealthTool(server);
223
+ registerActionsTool(server);
224
+ registerRegisterTestPlanTool(server);
225
+ registerResolveScreenTool(server);
226
+ // Register workspace management tools
227
+ registerInitScanWorkspaceTool(server);
228
+ registerInitializeWorkspaceTool(server);
229
+ // Register one-click orchestrated workflows
230
+ if (oneClickEnabled || localDevEnabled) {
231
+ registerOneClickTool(server);
232
+ logger.info("One-click tools enabled", {
233
+ enabledBy: {
234
+ SKYRAMP_FEATURE_ONE_CLICK: oneClickEnabled,
235
+ SKYRAMP_FEATURE_LOCAL_DEV: localDevEnabled,
236
+ },
237
+ });
238
+ }
239
+ // Register other Skyramp tools
240
+ const infrastructureTools = [
241
+ registerLoginTool,
242
+ registerLogoutTool,
243
+ registerExecuteSkyrampTestTool,
244
+ registerExecuteFleetTool,
245
+ registerTraceTool,
246
+ registerTraceStopTool,
247
+ ];
248
+ if (isLocalDevEnabled()) {
249
+ infrastructureTools.push(registerEnrichTestWithMocksTool, registerPreflightMockCheckTool, registerGenerateEnrichedIntegrationTestTool, registerLocalDevWorkerComposeTool, registerQueryProxyMocksTool);
250
+ logger.info("Local-dev tools enabled via SKYRAMP_FEATURE_LOCAL_DEV");
251
+ }
252
+ if (isTestbotEnabled()) {
253
+ infrastructureTools.push(registerSubmitReportTool);
254
+ logger.info("Testbot tools enabled via SKYRAMP_FEATURE_TESTBOT");
255
+ }
256
+ infrastructureTools.forEach((registerTool) => registerTool(server));
257
+ // Register Playwright browser tools (trace recording via browser automation).
258
+ //
259
+ // Tell the capture which attribute this repository uses for test ids. Without
260
+ // it the capture assumes Playwright's `data-testid`, and a repo using anything
261
+ // else gets no test-id anchor and a positional xpath for every element — which
262
+ // resolves at capture time and breaks later, so the miss never announces itself
263
+ // (SKYR-4136). This server is the only component that knows which repository it
264
+ // is running against.
265
+ //
266
+ // A declared `use.testIdAttribute` is authoritative. Failing that, the
267
+ // repository's own tests name the attribute whenever they select on it, so read
268
+ // it out of their selectors rather than guessing from a list of popular names.
269
+ // Never fatal: no answer leaves the capture on Playwright's default.
270
+ //
271
+ // Awaited, and worth the wait: `PlaywrightTraceService.initialize()` is 206ms
272
+ // of it and the browser tools come out of that service, so nothing can
273
+ // register them earlier. Running it in the background instead delays every
274
+ // connection's first response by nothing but leaves the client holding 27
275
+ // tools of 52 — Claude Code does not re-list on `tools/list_changed`, so all
276
+ // 23 `browser_*` tools stay invisible for the whole run.
277
+ await (async () => {
278
+ let testIdAttribute;
279
+ try {
280
+ const root = await findRoot(process.cwd());
281
+ testIdAttribute = await readTestIdAttribute(root);
272
282
  if (testIdAttribute) {
273
- // Log the runner-up counts: on a part-migrated repo the most-used
274
- // attribute wins, and a wrong pick should be visible here rather than
275
- // showing up later as brittle selectors.
276
- logger.info(`testIdAttribute discovered from test selectors: ${testIdAttribute}`, { counts: discovered.counts, filesScanned: discovered.filesScanned });
283
+ logger.info(`testIdAttribute declared by the Playwright config: ${testIdAttribute}`);
277
284
  }
278
285
  else {
279
- // No name rule can reach an arbitrary convention like `xyz-id`, so name
280
- // what the selectors did contain and the variable that overrides it.
281
- // Editing the repo's own Playwright config is the wrong instruction: a
282
- // suite may have several, and none of them at the root.
283
- logger.info("No testIdAttribute declared or discovered — capture stays on Playwright's data-testid. " +
284
- "Set PLAYWRIGHT_MCP_TEST_ID_ATTRIBUTE to override.", {
285
- attributesSeenInSelectors: discovered.observed,
286
- filesScanned: discovered.filesScanned,
287
- });
286
+ const discovered = await discoverTestIdAttribute(root);
287
+ testIdAttribute = discovered.attribute;
288
+ if (testIdAttribute) {
289
+ // Log the runner-up counts: on a part-migrated repo the most-used
290
+ // attribute wins, and a wrong pick should be visible here rather than
291
+ // showing up later as brittle selectors.
292
+ logger.info(`testIdAttribute discovered from test selectors: ${testIdAttribute}`, {
293
+ counts: discovered.counts,
294
+ filesScanned: discovered.filesScanned,
295
+ });
296
+ }
297
+ else {
298
+ // No name rule can reach an arbitrary convention like `xyz-id`, so name
299
+ // what the selectors did contain and the variable that overrides it.
300
+ // Editing the repo's own Playwright config is the wrong instruction: a
301
+ // suite may have several, and none of them at the root.
302
+ logger.info("No testIdAttribute declared or discovered — capture stays on Playwright's data-testid. " +
303
+ "Set PLAYWRIGHT_MCP_TEST_ID_ATTRIBUTE to override.", {
304
+ attributesSeenInSelectors: discovered.observed,
305
+ filesScanned: discovered.filesScanned,
306
+ });
307
+ }
288
308
  }
289
309
  }
290
- }
291
- catch (err) {
292
- logger.warning("Could not determine this repository's testIdAttribute", {
293
- error: err,
310
+ catch (err) {
311
+ logger.warning("Could not determine this repository's testIdAttribute", {
312
+ error: err,
313
+ });
314
+ }
315
+ await registerPlaywrightTools(server, { testIdAttribute });
316
+ })().catch((err) => {
317
+ logger.error("Failed to register Playwright tools", { error: err });
318
+ });
319
+ const runInitTrigger = () => {
320
+ registerInitTriggerOnMCPInitialized().catch((err) => {
321
+ logger.error("Failed to run MCP initialized trigger", { error: err });
294
322
  });
295
- }
296
- await registerPlaywrightTools(server, { testIdAttribute });
297
- })().catch((err) => {
298
- logger.error("Failed to register Playwright tools", { error: err });
299
- });
323
+ };
324
+ server.server.oninitialized = runInitTrigger;
325
+ // A 2026-07-28 connection has no `initialize` exchange, so `oninitialized`
326
+ // never fires on it.
327
+ if (era === "modern")
328
+ runInitTrigger();
329
+ return server;
330
+ }
300
331
  // Global error handlers for crash telemetry
301
332
  process.on("uncaughtException", async (error) => {
302
333
  // Don't send analytics for EPIPE errors
@@ -350,13 +381,11 @@ process.on("unhandledRejection", async (reason) => {
350
381
  });
351
382
  // Start MCP server
352
383
  async function main() {
353
- const transport = new StdioServerTransport();
354
- server.server.oninitialized = () => {
355
- registerInitTriggerOnMCPInitialized().catch((err) => {
356
- logger.error("Failed to run MCP initialized trigger", { error: err });
357
- });
358
- };
359
- await server.connect(transport);
384
+ // `serveStdio` owns the era decision: it reads the opening exchange and pins
385
+ // one instance from the factory for the connection. `StdioServerTransport`
386
+ // serves the 2025 handshake only, so the 2026-07-28 revision is unreachable
387
+ // through it however the client asks.
388
+ const handle = serveStdio(createServer);
360
389
  logger.info("MCP Server started successfully");
361
390
  // Listen for stdin closure (parent process disconnected)
362
391
  // Using multiple events for robustness across different disconnection scenarios
@@ -372,7 +401,7 @@ async function main() {
372
401
  ? playwrightService.shutdown().catch(() => { })
373
402
  : Promise.resolve();
374
403
  cleanupPlaywright.finally(() => {
375
- server.close().finally(() => {
404
+ handle.close().finally(() => {
376
405
  process.exit(0);
377
406
  });
378
407
  });
@@ -5,7 +5,7 @@
5
5
  * The Playwright fork is CJS; this project is ESM. We use createRequire
6
6
  * to bridge the module systems.
7
7
  */
8
- import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
8
+ import type { CallToolResult } from "@modelcontextprotocol/server";
9
9
  interface McpTool {
10
10
  name: string;
11
11
  description?: string;
@@ -1,4 +1,4 @@
1
- import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
1
+ import type { CallToolResult } from "@modelcontextprotocol/server";
2
2
  import type { BlueprintCaptureDigest } from "../types/BlueprintDigest.js";
3
3
  /** A run that takes more captures than this has a browsing problem of its own;
4
4
  * the cap is here so a runaway loop cannot grow the process without bound. */
@@ -3,6 +3,6 @@
3
3
  * tools in the Skyramp MCP server, delegating calls to PlaywrightTraceService.
4
4
  */
5
5
  import { PlaywrightTraceService, type PlaywrightTraceOptions } from "./PlaywrightTraceService.js";
6
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
+ import type { McpServer } from "@modelcontextprotocol/server";
7
7
  export declare function getPlaywrightTraceService(): PlaywrightTraceService | null;
8
8
  export declare function registerPlaywrightTools(server: McpServer, options?: PlaywrightTraceOptions): Promise<void>;
@@ -2,7 +2,7 @@
2
2
  * MCP prompt that guides the LLM through the Playwright-based trace recording
3
3
  * and Skyramp test generation flow.
4
4
  */
5
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import type { McpServer } from "@modelcontextprotocol/server";
6
6
  export interface TraceRecordingPromptOpts {
7
7
  /** Directory where zip files should be written. Defaults to same dir as test file. */
8
8
  outputDir?: string;
@@ -5,6 +5,7 @@
5
5
  import { z } from "zod";
6
6
  import { logger } from "../utils/logger.js";
7
7
  import { getPersonaPrefix } from "../prompts/personas.js";
8
+ import { ORDER_ASSERTION_RULE } from "../prompts/orderAssertionRule.js";
8
9
  export function getTraceRecordingPromptText(opts) {
9
10
  const outputDir = opts?.outputDir;
10
11
  const modularize = opts?.modularize ?? true;
@@ -65,7 +66,7 @@ Call \`browser_assert\` when assertions are needed. Provide \`expected\` for eve
65
66
 
66
67
  **Visual / screenshot assertions.** When the instruction asks to take a screenshot, capture a baseline, or verify how a page/element/region *looks* (as opposed to its text or value), call \`browser_visual_snapshot\` — it records a \`toHaveScreenshot()\` assertion so the generated test pixel-compares against a baseline image on every run.
67
68
 
68
- **When the change under test is how something looks** — a colour, class or attribute the diff changes — assert it directly with \`browser_assert\` \`type: css/class/attribute\` on the element that OWNS the property (the flex container for \`flex-direction\`, not its children); if it has no stable ref, note a recording gap rather than assert the wrong element. Order has no kind: \`flex-direction\` when the diff flipped it, else a \`boundingBox()\` comparison at enhance. \`browser_visual_snapshot\` only for a look no property expresses (image, gradient, chart).
69
+ **When the change under test is how something looks** — a colour, class or attribute the diff changes — assert it directly with \`browser_assert\` \`type: css/class/attribute\` on the element that OWNS the property (the flex container for \`flex-direction\`, not its children); if it has no stable ref, note a recording gap rather than assert the wrong element. ${ORDER_ASSERTION_RULE} \`browser_visual_snapshot\` only for a look no property expresses (image, gradient, chart).
69
70
 
70
71
  Do NOT use \`browser_take_screenshot\` to record a visual check: it only captures a throwaway image, is dropped at export (it produces no assertion), and will NOT appear in the generated test. Use it only to view the page yourself while recording. \`filename\` is optional on \`browser_visual_snapshot\`: if the instruction names a baseline file (e.g. \`page-001.png\`) pass it as \`filename\`, otherwise omit it and one is auto-generated per \`snapshotType\` (\`page-NNN.png\` for a page, \`el-NNN.png\` for an element, \`region-NNN.png\` for a region).
71
72
 
@@ -81,7 +82,7 @@ export function registerTraceRecordingPrompt(server) {
81
82
  logger.info("Registering trace recording prompt");
82
83
  server.registerPrompt("skyramp_trace_recording_prompt", {
83
84
  description: "Guide for recording browser interactions as a Skyramp trace and generating UI tests",
84
- argsSchema: {
85
+ argsSchema: z.object({
85
86
  outputDir: z
86
87
  .string()
87
88
  .optional()
@@ -91,7 +92,7 @@ export function registerTraceRecordingPrompt(server) {
91
92
  .default(true)
92
93
  .optional()
93
94
  .describe("Whether to run skyramp_modularization after generation. Default: true. Set to false in CI."),
94
- },
95
+ }),
95
96
  }, (args) => ({
96
97
  messages: [
97
98
  {
@@ -1,4 +1,4 @@
1
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
1
+ import { McpServer } from "@modelcontextprotocol/server";
2
2
  import { type Service } from "../../workspace/workspace.js";
3
3
  export declare function getLocalDevPrompt(baseCommit: string, repositoryPath: string, repository: string, sandboxWorkerUrl?: string, maxRecommendations?: number, maxGenerate?: number, services?: Service[], stateOutputFile?: string, servicesToMock?: string[], realServices?: string[], mockProtocols?: string[], proxyKubeconfig?: string, proxyNamespace?: string): string;
4
4
  export declare function registerLocalDevPrompt(server: McpServer): void;
@@ -121,7 +121,7 @@ export function registerLocalDevPrompt(server) {
121
121
  logger.info("Registering local-dev prompt");
122
122
  server.registerPrompt("skyramp_local_dev", {
123
123
  description: "Run Skyramp local-dev workflow: diff from a base commit, discover downstream dependencies, document mock requirements, generate mocks and backend tests, execute them against a local environment, and report results.",
124
- argsSchema: {
124
+ argsSchema: z.object({
125
125
  baseCommit: z
126
126
  .string()
127
127
  .describe("Base commit reference to diff against — accepts a branch name (e.g. 'main'), a commit SHA (e.g. 'abc123f'), or a tag (e.g. 'v1.2.0'). The diff is computed between this ref and the current working tree (committed plus uncommitted changes)."),
@@ -167,7 +167,7 @@ export function registerLocalDevPrompt(server) {
167
167
  .string()
168
168
  .optional()
169
169
  .describe("Comma-separated list of downstream service names that are already running locally and should receive real traffic (e.g. 'postgres,redis,envoy'). These will NOT be mocked."),
170
- },
170
+ }),
171
171
  }, async (args) => {
172
172
  const services = await readWorkspaceServices(args.repositoryPath);
173
173
  // skyramp_analyze_changes and skyramp_execute_test both name their repository
@@ -0,0 +1,9 @@
1
+ /**
2
+ * When to record a swap, move or reorder as an order assertion. It lives in the
3
+ * trace-recording prompt, which the testbot flow includes; the top-level testbot
4
+ * prompt does not repeat test-type details. The type's parameters are
5
+ * documented by browser_assert's own description, and the pairing rule (two
6
+ * sibling blocks, not a block and something inside it) is enforced and
7
+ * explained by the tool, so neither is repeated here.
8
+ */
9
+ export declare const ORDER_ASSERTION_RULE = "**A swap, move or reorder** (left/right columns swapped, a sidebar moved, blocks reordered) is recorded with `browser_assert` `type: \"order\"` on two blocks (`ref` and `otherRef`), so the test fails if the swap is undone; `flex-direction` alone is not enough when the diff reorders markup.";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * When to record a swap, move or reorder as an order assertion. It lives in the
3
+ * trace-recording prompt, which the testbot flow includes; the top-level testbot
4
+ * prompt does not repeat test-type details. The type's parameters are
5
+ * documented by browser_assert's own description, and the pairing rule (two
6
+ * sibling blocks, not a block and something inside it) is enforced and
7
+ * explained by the tool, so neither is repeated here.
8
+ */
9
+ export const ORDER_ASSERTION_RULE = '**A swap, move or reorder** (left/right columns swapped, a sidebar moved, blocks reordered) is recorded with `browser_assert` `type: "order"` on two blocks (`ref` and `otherRef`), so the test fails if the swap is undone; `flex-direction` alone is not enough when the diff reorders markup.';