@browserstack/mcp-server 1.5.0-beta.21 → 1.5.0-beta.22

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 (95) hide show
  1. package/README.md +32 -48
  2. package/capability/loadtesting.capability-index.json +1876 -0
  3. package/capability/tm.capability-index.json +50104 -0
  4. package/dist/config.d.ts +1 -4
  5. package/dist/config.js +2 -23
  6. package/dist/index.d.ts +1 -2
  7. package/dist/index.js +3 -7
  8. package/dist/lib/constants.d.ts +0 -3
  9. package/dist/lib/constants.js +0 -3
  10. package/dist/lib/instrumentation.d.ts +2 -14
  11. package/dist/lib/instrumentation.js +29 -104
  12. package/dist/lib/upload-validator.d.ts +2 -3
  13. package/dist/lib/upload-validator.js +15 -18
  14. package/dist/lib/utils.js +3 -10
  15. package/dist/lib/version-resolver.d.ts +5 -0
  16. package/dist/lib/version-resolver.js +2 -8
  17. package/dist/logger.js +10 -1
  18. package/dist/server-factory.js +6 -10
  19. package/dist/tools/accessibility.js +5 -9
  20. package/dist/tools/accessiblity-utils/accessibility-rag.js +1 -3
  21. package/dist/tools/appautomate-utils/native-execution/constants.js +2 -4
  22. package/dist/tools/appautomate.js +1 -3
  23. package/dist/tools/applive.js +1 -1
  24. package/dist/tools/automate.d.ts +0 -7
  25. package/dist/tools/automate.js +1 -104
  26. package/dist/tools/bstack-sdk.js +2 -2
  27. package/dist/tools/build-insights.js +2 -41
  28. package/dist/tools/capability-registry/bind.d.ts +29 -0
  29. package/dist/tools/capability-registry/bind.js +329 -0
  30. package/dist/tools/capability-registry/config.d.ts +62 -0
  31. package/dist/tools/capability-registry/config.js +218 -0
  32. package/dist/tools/capability-registry/discovery.d.ts +44 -0
  33. package/dist/tools/capability-registry/discovery.js +99 -0
  34. package/dist/tools/capability-registry/egress.d.ts +44 -0
  35. package/dist/tools/capability-registry/egress.js +174 -0
  36. package/dist/tools/capability-registry/index-loader.d.ts +133 -0
  37. package/dist/tools/capability-registry/index-loader.js +404 -0
  38. package/dist/tools/capability-registry/register.d.ts +48 -0
  39. package/dist/tools/capability-registry/register.js +604 -0
  40. package/dist/tools/capability-registry/resolve.d.ts +38 -0
  41. package/dist/tools/capability-registry/resolve.js +45 -0
  42. package/dist/tools/capability-registry/search.d.ts +170 -0
  43. package/dist/tools/capability-registry/search.js +709 -0
  44. package/dist/tools/capability-registry/types.d.ts +282 -0
  45. package/dist/tools/capability-registry/types.js +33 -0
  46. package/dist/tools/failurelogs-utils/app-automate.js +3 -4
  47. package/dist/tools/failurelogs-utils/automate.js +4 -5
  48. package/dist/tools/get-failure-logs.js +11 -30
  49. package/dist/tools/observability.js +1 -3
  50. package/dist/tools/percy-sdk.js +9 -9
  51. package/dist/tools/rca-agent-utils/format-rca.js +4 -5
  52. package/dist/tools/rca-agent-utils/get-failed-test-id.js +0 -12
  53. package/dist/tools/rca-agent-utils/types.d.ts +0 -1
  54. package/dist/tools/rca-agent.js +2 -5
  55. package/dist/tools/review-agent.js +1 -2
  56. package/dist/tools/sdk-utils/common/constants.d.ts +1 -1
  57. package/dist/tools/sdk-utils/common/constants.js +1 -2
  58. package/dist/tools/selfheal.js +3 -6
  59. package/dist/tools/testmanagement-utils/TCG-utils/api.js +7 -26
  60. package/dist/tools/testmanagement-utils/TCG-utils/config.d.ts +0 -2
  61. package/dist/tools/testmanagement-utils/TCG-utils/config.js +0 -5
  62. package/dist/tools/testmanagement-utils/testcase-from-file.js +1 -2
  63. package/dist/tools/testmanagement-utils/upload-file.js +2 -16
  64. package/dist/tools/testmanagement.js +17 -39
  65. package/package.json +11 -5
  66. package/dist/lib/node-version-notice.d.ts +0 -1
  67. package/dist/lib/node-version-notice.js +0 -9
  68. package/dist/lib/tool-latency.d.ts +0 -4
  69. package/dist/lib/tool-latency.js +0 -16
  70. package/dist/lib/untrusted-content.d.ts +0 -15
  71. package/dist/lib/untrusted-content.js +0 -24
  72. package/dist/tools/ask-browserstack/central-oauth.d.ts +0 -120
  73. package/dist/tools/ask-browserstack/central-oauth.js +0 -277
  74. package/dist/tools/ask-browserstack/config.d.ts +0 -102
  75. package/dist/tools/ask-browserstack/config.js +0 -140
  76. package/dist/tools/ask-browserstack/egress.d.ts +0 -34
  77. package/dist/tools/ask-browserstack/egress.js +0 -31
  78. package/dist/tools/ask-browserstack/register.d.ts +0 -61
  79. package/dist/tools/ask-browserstack/register.js +0 -416
  80. package/dist/tools/ask-browserstack/relay.d.ts +0 -201
  81. package/dist/tools/ask-browserstack/relay.js +0 -577
  82. package/dist/tools/ask-browserstack/stream.d.ts +0 -116
  83. package/dist/tools/ask-browserstack/stream.js +0 -236
  84. package/dist/tools/ask-browserstack/types.d.ts +0 -196
  85. package/dist/tools/ask-browserstack/types.js +0 -14
  86. package/dist/tools/automate-utils/list-session-ids.d.ts +0 -28
  87. package/dist/tools/automate-utils/list-session-ids.js +0 -87
  88. package/dist/tools/automate-utils/resolve-hashed-build-id.d.ts +0 -30
  89. package/dist/tools/automate-utils/resolve-hashed-build-id.js +0 -124
  90. package/dist/tools/failurelogs-utils/resolve-app-build-id.d.ts +0 -2
  91. package/dist/tools/failurelogs-utils/resolve-app-build-id.js +0 -5
  92. package/dist/tools/failurelogs-utils/video.d.ts +0 -3
  93. package/dist/tools/failurelogs-utils/video.js +0 -25
  94. package/dist/tools/tool-handoff.d.ts +0 -66
  95. package/dist/tools/tool-handoff.js +0 -78
@@ -1,87 +0,0 @@
1
- import { SessionType } from "../../lib/constants.js";
2
- import { getBrowserStackAuth } from "../../lib/get-auth.js";
3
- import { apiClient } from "../../lib/apiClient.js";
4
- export const DEFAULT_SESSION_LIST_LIMIT = 10;
5
- /** The REST session list returned 404: no Automate/App Automate build has this hashed id. */
6
- export class UnknownBuildError extends Error {
7
- constructor(message) {
8
- super(message);
9
- this.name = "UnknownBuildError";
10
- }
11
- }
12
- export function sessionsListUrl(sessionType, buildId) {
13
- const encodedBuildId = encodeURIComponent(buildId);
14
- switch (sessionType) {
15
- case SessionType.Automate:
16
- return `https://api.browserstack.com/automate/builds/${encodedBuildId}/sessions.json`;
17
- case SessionType.AppAutomate:
18
- return `https://api-cloud.browserstack.com/app-automate/builds/${encodedBuildId}/sessions.json`;
19
- default: {
20
- const _exhaustive = sessionType;
21
- throw new Error(`Unsupported session type: ${_exhaustive}`);
22
- }
23
- }
24
- }
25
- export function mapSessionRecords(payload, statusFilter) {
26
- const items = Array.isArray(payload) ? payload : [];
27
- const normalizedFilter = statusFilter?.trim().toLowerCase();
28
- const records = [];
29
- for (const item of items) {
30
- const session = item?.automation_session;
31
- if (!session) {
32
- continue;
33
- }
34
- const sessionId = session.hashed_id?.trim();
35
- if (!sessionId) {
36
- continue;
37
- }
38
- if (normalizedFilter &&
39
- (session.status ?? "").toLowerCase() !== normalizedFilter) {
40
- continue;
41
- }
42
- records.push({
43
- sessionId,
44
- name: session.name,
45
- status: session.status,
46
- os: session.os,
47
- osVersion: session.os_version,
48
- browser: session.browser,
49
- device: session.device,
50
- browserUrl: session.browser_url,
51
- videoUrl: session.video_url,
52
- });
53
- }
54
- return records;
55
- }
56
- export async function listSessionIds(args, config) {
57
- const buildId = args.buildId.trim();
58
- if (!buildId) {
59
- throw new Error("Hashed Automate/App Automate build ID is required");
60
- }
61
- const authString = getBrowserStackAuth(config);
62
- const auth = Buffer.from(authString).toString("base64");
63
- const limit = args.limit ?? DEFAULT_SESSION_LIST_LIMIT;
64
- const params = { limit };
65
- if (args.offset !== undefined) {
66
- params.offset = args.offset;
67
- }
68
- const response = await apiClient.get({
69
- url: sessionsListUrl(args.sessionType, buildId),
70
- headers: {
71
- "Content-Type": "application/json",
72
- Authorization: `Basic ${auth}`,
73
- },
74
- params,
75
- raise_error: false,
76
- });
77
- if (!response.ok) {
78
- if (response.status === 404) {
79
- throw new UnknownBuildError(`No ${args.sessionType} build found for id "${buildId}". ` +
80
- "Pass the Automate/App Automate dashboard hashed build id or the " +
81
- "observability build id from getBuildId / listBuildId, and check that " +
82
- "sessionType matches the product the build ran on.");
83
- }
84
- throw new Error(`Failed to list sessions: ${response.status} ${response.statusText}`);
85
- }
86
- return mapSessionRecords(response.data, args.status);
87
- }
@@ -1,30 +0,0 @@
1
- import { SessionType } from "../../lib/constants.js";
2
- import { BrowserStackConfig } from "../../lib/types.js";
3
- export declare function isObservabilityBuildUuid(id: string): boolean;
4
- export declare function isHashedBuildId(id: string): boolean;
5
- export declare function sessionDetailsUrl(sessionType: SessionType, sessionId: string): string;
6
- /**
7
- * Resolve the hashed build id that a session belongs to via the Automate /
8
- * App Automate session detail endpoint. Returns undefined when the session
9
- * cannot be fetched or does not report a build.
10
- */
11
- export declare function resolveBuildIdFromSession(sessionId: string, sessionType: SessionType, config: BrowserStackConfig): Promise<string | undefined>;
12
- /**
13
- * Find any BrowserStack session id attached to an observability build by
14
- * walking its test runs. Returns undefined when no test reports a session
15
- * (e.g. JUnit-uploaded builds that never ran on BrowserStack).
16
- */
17
- export declare function findSessionIdForObservabilityBuild(observabilityBuildId: string, config: BrowserStackConfig): Promise<string | undefined>;
18
- export interface ResolvedHashedBuildId {
19
- hashedBuildId: string;
20
- sessionId: string;
21
- sessionType: SessionType;
22
- }
23
- /**
24
- * Convert an observability build UUID into the Automate / App Automate hashed
25
- * build id in two deterministic API calls: pick any session of the build from
26
- * its test runs, then read `build_hashed_id` from that session's details.
27
- *
28
- * When `sessionType` is omitted, Automate is tried first, then App Automate.
29
- */
30
- export declare function resolveHashedBuildId(observabilityBuildId: string, config: BrowserStackConfig, sessionType?: SessionType): Promise<ResolvedHashedBuildId>;
@@ -1,124 +0,0 @@
1
- import { SessionType } from "../../lib/constants.js";
2
- import { getBrowserStackAuth } from "../../lib/get-auth.js";
3
- import { apiClient } from "../../lib/apiClient.js";
4
- import logger from "../../logger.js";
5
- import { getAutomationBaseUrl } from "../rca-agent-utils/constants.js";
6
- import { extractTestIds } from "../rca-agent-utils/get-failed-test-id.js";
7
- // Observability (Test Reporting & Analytics) build ids are usually UUIDs but
8
- // some are 40-char hex, the same shape as Automate / App Automate "hashed ids".
9
- // The two are never interchangeable, and the observability build API does not
10
- // expose the hashed id. The deterministic bridge is any BrowserStack session that belongs to the
11
- // build: the session detail endpoint reports its parent `build_hashed_id`.
12
- const OBSERVABILITY_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
13
- const HASHED_BUILD_ID_RE = /^[a-f0-9]{40}$/i;
14
- // Only the first session id is needed; most builds surface one on page one.
15
- const MAX_TEST_RUN_PAGES = 5;
16
- export function isObservabilityBuildUuid(id) {
17
- return OBSERVABILITY_UUID_RE.test(id.trim());
18
- }
19
- export function isHashedBuildId(id) {
20
- return HASHED_BUILD_ID_RE.test(id.trim());
21
- }
22
- export function sessionDetailsUrl(sessionType, sessionId) {
23
- const encoded = encodeURIComponent(sessionId);
24
- switch (sessionType) {
25
- case SessionType.Automate:
26
- return `https://api.browserstack.com/automate/sessions/${encoded}.json`;
27
- case SessionType.AppAutomate:
28
- return `https://api.browserstack.com/app-automate/sessions/${encoded}.json`;
29
- default: {
30
- const _exhaustive = sessionType;
31
- throw new Error(`Unsupported session type: ${_exhaustive}`);
32
- }
33
- }
34
- }
35
- /**
36
- * Resolve the hashed build id that a session belongs to via the Automate /
37
- * App Automate session detail endpoint. Returns undefined when the session
38
- * cannot be fetched or does not report a build.
39
- */
40
- export async function resolveBuildIdFromSession(sessionId, sessionType, config) {
41
- const authString = getBrowserStackAuth(config);
42
- const auth = Buffer.from(authString).toString("base64");
43
- const response = await apiClient.get({
44
- url: sessionDetailsUrl(sessionType, sessionId),
45
- headers: {
46
- "Content-Type": "application/json",
47
- Authorization: `Basic ${auth}`,
48
- },
49
- raise_error: false,
50
- });
51
- if (!response.ok) {
52
- logger.warn(`Could not resolve build id for ${sessionType} session ${sessionId}: ${response.status}`);
53
- return undefined;
54
- }
55
- const session = response.data?.automation_session;
56
- const buildId = session?.build_hashed_id;
57
- return typeof buildId === "string" && buildId.trim()
58
- ? buildId.trim()
59
- : undefined;
60
- }
61
- /**
62
- * Find any BrowserStack session id attached to an observability build by
63
- * walking its test runs. Returns undefined when no test reports a session
64
- * (e.g. JUnit-uploaded builds that never ran on BrowserStack).
65
- */
66
- export async function findSessionIdForObservabilityBuild(observabilityBuildId, config) {
67
- const authString = getBrowserStackAuth(config);
68
- const auth = Buffer.from(authString).toString("base64");
69
- const baseUrl = `${getAutomationBaseUrl()}/ext/v1/builds/${encodeURIComponent(observabilityBuildId)}/testRuns`;
70
- let nextPage;
71
- for (let page = 0; page < MAX_TEST_RUN_PAGES; page++) {
72
- const response = await apiClient.get({
73
- url: baseUrl,
74
- headers: {
75
- "Content-Type": "application/json",
76
- Authorization: `Basic ${auth}`,
77
- },
78
- ...(nextPage ? { params: { next_page: nextPage } } : {}),
79
- raise_error: false,
80
- });
81
- if (!response.ok) {
82
- throw new Error(`Failed to fetch test runs for observability build "${observabilityBuildId}": ` +
83
- `${response.status} ${response.statusText}`);
84
- }
85
- const data = response.data;
86
- const withSession = extractTestIds(data?.hierarchy ?? []).find((test) => test.session_id);
87
- if (withSession?.session_id) {
88
- return withSession.session_id;
89
- }
90
- if (!data?.pagination?.has_next || !data.pagination.next_page) {
91
- return undefined;
92
- }
93
- nextPage = data.pagination.next_page;
94
- }
95
- logger.warn(`resolveHashedBuildId: no session id in first ${MAX_TEST_RUN_PAGES} pages of build ${observabilityBuildId}`);
96
- return undefined;
97
- }
98
- /**
99
- * Convert an observability build UUID into the Automate / App Automate hashed
100
- * build id in two deterministic API calls: pick any session of the build from
101
- * its test runs, then read `build_hashed_id` from that session's details.
102
- *
103
- * When `sessionType` is omitted, Automate is tried first, then App Automate.
104
- */
105
- export async function resolveHashedBuildId(observabilityBuildId, config, sessionType) {
106
- const buildId = observabilityBuildId.trim();
107
- const sessionId = await findSessionIdForObservabilityBuild(buildId, config);
108
- if (!sessionId) {
109
- throw new Error(`No BrowserStack sessions found for observability build "${buildId}". ` +
110
- "Only builds that ran on Automate or App Automate have sessions to list; " +
111
- "uploaded-report builds (e.g. JUnit) do not.");
112
- }
113
- const candidates = sessionType
114
- ? [sessionType]
115
- : [SessionType.Automate, SessionType.AppAutomate];
116
- for (const candidate of candidates) {
117
- const hashedBuildId = await resolveBuildIdFromSession(sessionId, candidate, config);
118
- if (hashedBuildId) {
119
- return { hashedBuildId, sessionId, sessionType: candidate };
120
- }
121
- }
122
- throw new Error(`Could not resolve the hashed build id for observability build "${buildId}" ` +
123
- `from session "${sessionId}" (tried: ${candidates.join(", ")}).`);
124
- }
@@ -1,2 +0,0 @@
1
- import { BrowserStackConfig } from "../../lib/types.js";
2
- export declare function resolveAppAutomateBuildId(sessionId: string, config: BrowserStackConfig): Promise<string | undefined>;
@@ -1,5 +0,0 @@
1
- import { SessionType } from "../../lib/constants.js";
2
- import { resolveBuildIdFromSession } from "../automate-utils/resolve-hashed-build-id.js";
3
- export async function resolveAppAutomateBuildId(sessionId, config) {
4
- return resolveBuildIdFromSession(sessionId, SessionType.AppAutomate, config);
5
- }
@@ -1,3 +0,0 @@
1
- import { BrowserStackConfig } from "../../lib/types.js";
2
- import { SessionType } from "../../lib/constants.js";
3
- export declare function retrieveSessionVideo(sessionId: string, sessionType: SessionType, config: BrowserStackConfig): Promise<string>;
@@ -1,25 +0,0 @@
1
- import { getBrowserStackAuth } from "../../lib/get-auth.js";
2
- import { apiClient } from "../../lib/apiClient.js";
3
- import { SessionType } from "../../lib/constants.js";
4
- import { validateLogResponse } from "./utils.js";
5
- export async function retrieveSessionVideo(sessionId, sessionType, config) {
6
- const product = sessionType === SessionType.AppAutomate ? "app-automate" : "automate";
7
- const url = `https://api.browserstack.com/${product}/sessions/${encodeURIComponent(sessionId)}.json`;
8
- const authString = getBrowserStackAuth(config);
9
- const auth = Buffer.from(authString).toString("base64");
10
- const response = await apiClient.get({
11
- url,
12
- headers: {
13
- "Content-Type": "application/json",
14
- Authorization: `Basic ${auth}`,
15
- },
16
- raise_error: false,
17
- });
18
- const validationError = validateLogResponse(response, "session video");
19
- if (validationError)
20
- return validationError.message;
21
- const videoUrl = response.data?.automation_session?.video_url;
22
- return typeof videoUrl === "string" && videoUrl.trim()
23
- ? `Session video: ${videoUrl.trim()}`
24
- : "No session video available for this session";
25
- }
@@ -1,66 +0,0 @@
1
- /**
2
- * Precondition sentences appended to a tool's description.
3
- *
4
- * WHY THESE EXIST. Nothing routes an MCP call: the client's model picks a tool from the
5
- * descriptions alone. A tool that needs an identifier the caller does not have is a dead
6
- * end — the model either asks the user for something they also do not know, or gives up.
7
- * These sentences turn that dead end into a HANDOFF by naming, in the description itself,
8
- * where the missing identifier comes from.
9
- *
10
- * Point at a sibling tool whenever one can produce the id — it is faster and more
11
- * predictable than an agent. Point at `askBrowserStackAI` only when NO tool here can.
12
- *
13
- * The one that matters most: 15 of the 17 Test Management tools require a project
14
- * identifier and NONE of them accepts its absence, yet no tool in this server lists
15
- * projects. "List my projects" is the first step of nearly every Test Management journey
16
- * and it was unserved, which is exactly why that request did not reach any tool unless a
17
- * user named one explicitly.
18
- *
19
- * Keep these as shared constants, not per-tool prose: the wording is a routing signal, and
20
- * twenty hand-written variants drift into twenty different signals.
21
- */
22
- /** No tool lists projects, so this genuinely has to go to the agent. */
23
- export declare const NEEDS_PROJECT_ID: string;
24
- /** A sibling tool can produce the id — prefer it over the agent. */
25
- export declare function needsIdFrom(idLabel: string, sourceTool: string): string;
26
- /**
27
- * createProjectOrFolder must NOT carry NEEDS_PROJECT_ID: `project_identifier` is optional
28
- * there, and the create-a-PROJECT half needs no id at all. With the generic constant the
29
- * tool read "Requires a project identifier ... call askBrowserStackAI", which routed
30
- * "create me a project" through the agent before letting the tool run.
31
- */
32
- export declare const PROJECT_ID_ONLY_FOR_FOLDER: string;
33
- /** A test plan id (TP-*) comes from listTestPlans. */
34
- export declare const NEEDS_TEST_PLAN_ID: string;
35
- /**
36
- * The ONLY capability handoff here: every other constant points at a tool that produces a
37
- * missing *id*, but plan WRITES have no tool at all — the surface is `listTestPlans`,
38
- * `getTestPlan`, `listSubTestPlans`, `getSubTestPlan` and nothing else. Atlas can do them
39
- * (the tm harness allows POST /api/v1/projects/{id}/test-plans plus /update, /delete,
40
- * /clone, /test-runs and /test-runs/unlink), so without this line the model reads the four
41
- * read tools, finds no create, and reports the capability as absent — which is exactly what
42
- * a QA eval concluded.
43
- *
44
- * Deliberately narrow: it names the specific operations that are missing rather than
45
- * inviting the model to route plan work to the agent generally, because the tool
46
- * descriptions otherwise say to prefer a specific tool whenever one fits.
47
- *
48
- * Caveat worth knowing: askBrowserStackAI pins every write to human approval, so this path
49
- * only completes on a client that can show a prompt. On one that cannot, the intended write
50
- * comes back in `needs_approval` instead of happening.
51
- */
52
- export declare const PLAN_WRITES_VIA_AGENT: string;
53
- /** A build id comes from either build-lookup tool. */
54
- export declare const NEEDS_BUILD_ID: string;
55
- /**
56
- * Session ids ARE listable now — PR #395 added `listSessions`, which merged into main while
57
- * this branch was open. This used to send the model to askBrowserStackAI for want of a tool;
58
- * pointing at the agent when a real tool exists is exactly what the header above forbids.
59
- */
60
- export declare const NEEDS_SESSION_ID: string;
61
- /** A completed scan's ids come from startAccessibilityScan, or from the agent. */
62
- export declare const NEEDS_A11Y_SCAN_ID: string;
63
- /** Auth-config ids are not listable by any tool here. */
64
- export declare const NEEDS_A11Y_CONFIG_ID: string;
65
- /** Test ids come from listTestIds, which itself needs a build id. */
66
- export declare const NEEDS_TEST_IDS: string;
@@ -1,78 +0,0 @@
1
- /**
2
- * Precondition sentences appended to a tool's description.
3
- *
4
- * WHY THESE EXIST. Nothing routes an MCP call: the client's model picks a tool from the
5
- * descriptions alone. A tool that needs an identifier the caller does not have is a dead
6
- * end — the model either asks the user for something they also do not know, or gives up.
7
- * These sentences turn that dead end into a HANDOFF by naming, in the description itself,
8
- * where the missing identifier comes from.
9
- *
10
- * Point at a sibling tool whenever one can produce the id — it is faster and more
11
- * predictable than an agent. Point at `askBrowserStackAI` only when NO tool here can.
12
- *
13
- * The one that matters most: 15 of the 17 Test Management tools require a project
14
- * identifier and NONE of them accepts its absence, yet no tool in this server lists
15
- * projects. "List my projects" is the first step of nearly every Test Management journey
16
- * and it was unserved, which is exactly why that request did not reach any tool unless a
17
- * user named one explicitly.
18
- *
19
- * Keep these as shared constants, not per-tool prose: the wording is a routing signal, and
20
- * twenty hand-written variants drift into twenty different signals.
21
- */
22
- /** No tool lists projects, so this genuinely has to go to the agent. */
23
- export const NEEDS_PROJECT_ID = " Requires a project identifier (PR-*). No tool here lists projects, so if you do not " +
24
- 'have one, call askBrowserStackAI with product "tm" and ask which projects exist, then ' +
25
- "retry this tool with the identifier it returns.";
26
- /** A sibling tool can produce the id — prefer it over the agent. */
27
- export function needsIdFrom(idLabel, sourceTool) {
28
- return ` Requires ${idLabel}. Call ${sourceTool} first if you do not have it.`;
29
- }
30
- /**
31
- * createProjectOrFolder must NOT carry NEEDS_PROJECT_ID: `project_identifier` is optional
32
- * there, and the create-a-PROJECT half needs no id at all. With the generic constant the
33
- * tool read "Requires a project identifier ... call askBrowserStackAI", which routed
34
- * "create me a project" through the agent before letting the tool run.
35
- */
36
- export const PROJECT_ID_ONLY_FOR_FOLDER = " Creating a project needs no identifier. Creating a folder inside an EXISTING project " +
37
- "needs that project's identifier (PR-*); no tool here lists projects, so ask " +
38
- 'askBrowserStackAI with product "tm" for it.';
39
- /** A test plan id (TP-*) comes from listTestPlans. */
40
- export const NEEDS_TEST_PLAN_ID = needsIdFrom("a test plan identifier (TP-*)", "listTestPlans");
41
- /**
42
- * The ONLY capability handoff here: every other constant points at a tool that produces a
43
- * missing *id*, but plan WRITES have no tool at all — the surface is `listTestPlans`,
44
- * `getTestPlan`, `listSubTestPlans`, `getSubTestPlan` and nothing else. Atlas can do them
45
- * (the tm harness allows POST /api/v1/projects/{id}/test-plans plus /update, /delete,
46
- * /clone, /test-runs and /test-runs/unlink), so without this line the model reads the four
47
- * read tools, finds no create, and reports the capability as absent — which is exactly what
48
- * a QA eval concluded.
49
- *
50
- * Deliberately narrow: it names the specific operations that are missing rather than
51
- * inviting the model to route plan work to the agent generally, because the tool
52
- * descriptions otherwise say to prefer a specific tool whenever one fits.
53
- *
54
- * Caveat worth knowing: askBrowserStackAI pins every write to human approval, so this path
55
- * only completes on a client that can show a prompt. On one that cannot, the intended write
56
- * comes back in `needs_approval` instead of happening.
57
- */
58
- export const PLAN_WRITES_VIA_AGENT = " Creating a test plan or sub-plan, and linking or unlinking test runs on one, are not " +
59
- 'available as tools here: call askBrowserStackAI with product "tm" and describe what you ' +
60
- "want. It asks you to confirm before changing anything.";
61
- /** A build id comes from either build-lookup tool. */
62
- export const NEEDS_BUILD_ID = needsIdFrom("a BrowserStack build id", "getBuildId or listBuildId");
63
- /**
64
- * Session ids ARE listable now — PR #395 added `listSessions`, which merged into main while
65
- * this branch was open. This used to send the model to askBrowserStackAI for want of a tool;
66
- * pointing at the agent when a real tool exists is exactly what the header above forbids.
67
- */
68
- export const NEEDS_SESSION_ID = " Requires a session id. listSessions lists them for a build; if you do not have the " +
69
- "build either, getBuildId or listBuildId resolves one from a project and build name.";
70
- /** A completed scan's ids come from startAccessibilityScan, or from the agent. */
71
- export const NEEDS_A11Y_SCAN_ID = " Requires the ids of a completed scan, which are returned by startAccessibilityScan. " +
72
- "No tool here lists past scans, so if you do not have the ids, start a new scan rather " +
73
- "than guessing.";
74
- /** Auth-config ids are not listable by any tool here. */
75
- export const NEEDS_A11Y_CONFIG_ID = " Requires the numeric id returned by createAccessibilityAuthConfig. No tool here lists " +
76
- "existing configurations, so if you do not have the id, create one rather than guessing.";
77
- /** Test ids come from listTestIds, which itself needs a build id. */
78
- export const NEEDS_TEST_IDS = needsIdFrom("test ids", "listTestIds");