@shiplightai/mcp 0.1.21 → 0.1.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 (2) hide show
  1. package/dist/index.js +5 -3
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -8090,11 +8090,13 @@ for the element at the given index.
8090
8090
 
8091
8091
  Use this to collect locator information for building test flows without
8092
8092
  actually interacting with the element.`};async function apr(e,t){let{session_id:r,element_index:n}=t;if(!e.getSession(r))throw new Error(`Session ${r} not found`);return e.getLocator(r,n)}var Gce=null,$ce=null;function upr(){return Gce||(Gce=Ea.buildActionUnionSchemaForTools([...SD],!0),Gce)}function lpr(){if($ce)return $ce;let e=upr(),t=Hce.array(e),r=Yhr();return $ce={name:r.name,description:r.description,inputSchema:nb(Hce.object({session_id:Hce.string().describe("Session ID"),actions:t.describe("List of actions to execute"),stop_on_error:Hce.boolean().optional().describe("Stop execution on first error (default: true)")}),{$refStrategy:"none"})},$ce}var zce=class lv{constructor(t){this.backend=t}static navigateTool={...Vhr,inputSchema:nb(jPe,{$refStrategy:"none"})};async navigate(t){let r=jPe.parse(t),n=await jhr(this.backend,r);return JSON.stringify(n)}static get actTool(){return lpr()}async act(t){let r=Whr.parse(t),n=await Jhr(this.backend,r);return JSON.stringify(n)}static updateVariablesTool={...zhr,inputSchema:nb(WPe,{$refStrategy:"none"})};async updateVariables(t){let r=WPe.parse(t),n=Khr(this.backend,r);return JSON.stringify(n)}static clearExecutionHistoryTool={...Xhr,inputSchema:nb(YPe,{$refStrategy:"none"})};async clearExecutionHistory(t){let r=YPe.parse(t),n=Zhr(this.backend,r);return JSON.stringify(n)}static getPageInfoTool={...epr,inputSchema:nb(JPe,{$refStrategy:"none"})};async getPageInfo(t){let r=JPe.parse(t),n=await tpr(this.backend,r);return JSON.stringify(n)}static getDomTool={...rpr,inputSchema:nb(zPe,{$refStrategy:"none"})};async getDom(t){let r=zPe.parse(t),n=await npr(this.backend,r);return JSON.stringify(n)}static takeScreenshotTool={...ipr,inputSchema:nb(KPe,{$refStrategy:"none"})};async takeScreenshot(t){let r=KPe.parse(t),n=await spr(this.backend,r);return JSON.stringify(n)}static getLocatorTool={...opr,inputSchema:nb(XPe,{$refStrategy:"none"})};async getLocator(t){let r=XPe.parse(t),n=await apr(this.backend,r);return JSON.stringify(n)}static get toolDefinitions(){return[lv.navigateTool,lv.getPageInfoTool,lv.getDomTool,lv.takeScreenshotTool,lv.actTool,lv.getLocatorTool]}},Kce=class aG{constructor(t){this.backend=t}static getConsoleLogsTool={name:"get_browser_console_logs",description:"Get console logs from the browser (errors, warnings, etc.)",inputSchema:Vce(No.object({session_id:No.string().describe("Browser session ID"),since_timestamp:No.number().optional().describe("Only return logs after this timestamp"),log_types:No.array(No.string()).optional().describe("Filter by log types (e.g., ['error', 'warning'])")}),{$refStrategy:"none"})};async getConsoleLogs(t){let{session_id:r,since_timestamp:n,log_types:i}=No.object({session_id:No.string(),since_timestamp:No.number().optional(),log_types:No.array(No.string()).optional()}).parse(t);if(!this.backend.getSession(r))throw new Error(`Session ${r} not found`);let o=this.backend.getConsoleLogs(r,{sinceTimestamp:n,logTypes:i});return JSON.stringify({session_id:r,log_count:o.length,logs:o.slice(-100)})}static getNetworkLogsTool={name:"get_browser_network_logs",description:"Get network request logs from the browser",inputSchema:Vce(No.object({session_id:No.string().describe("Browser session ID"),since_timestamp:No.number().optional().describe("Only return logs after this timestamp"),status_filter:No.enum(["errors","success"]).optional().describe("Filter by HTTP status (errors: 4xx/5xx, success: 2xx)")}),{$refStrategy:"none"})};async getNetworkLogs(t){let{session_id:r,since_timestamp:n,status_filter:i}=No.object({session_id:No.string(),since_timestamp:No.number().optional(),status_filter:No.enum(["errors","success"]).optional()}).parse(t);if(!this.backend.getSession(r))throw new Error(`Session ${r} not found`);let o=this.backend.getNetworkLogs(r,{sinceTimestamp:n,statusFilter:i});return JSON.stringify({session_id:r,log_count:o.length,logs:o.slice(-100)})}static clearLogsTool={name:"clear_logs",description:"Clear console and network logs for a session",inputSchema:Vce(No.object({session_id:No.string().describe("Browser session ID")}),{$refStrategy:"none"})};async clearLogs(t){let{session_id:r}=No.object({session_id:No.string()}).parse(t);if(!this.backend.getSession(r))throw new Error(`Session ${r} not found`);return this.backend.clearLogs(r),JSON.stringify({session_id:r,message:"Logs cleared successfully"})}static getLocalArtifactTool={name:"get_local_artifact",description:"Get a local artifact file (screenshot, DOM snapshot, etc.) from a live browser session. Only accepts local file paths, NOT S3 URIs. For test run artifacts, use get_step_artifacts instead.",inputSchema:Vce(No.object({path:No.string().describe("Artifact file path from previous tool output"),return_format:No.enum(["base64","text"]).optional().describe("Return format (auto-detected if not specified)")}),{$refStrategy:"none"})};async getLocalArtifact(t){let{path:r}=No.object({path:No.string(),return_format:No.enum(["base64","text"]).optional()}).parse(t),n=await this.backend.getLocalArtifact(r);return JSON.stringify({path:r,format:n.format,size:n.size,data:n.data})}static toolDefinitions=[aG.getConsoleLogsTool,aG.getNetworkLogsTool,aG.clearLogsTool,aG.getLocalArtifactTool]},Xce=class ib{constructor(t){this.apiClient=t}requireApi(t){if(!this.apiClient)throw new Error(hQ(t));return this.apiClient}static listEnvironmentsTool={name:"list_environments",description:"List all testing environments (staging, production, etc.)",inputSchema:av(Ci.object({}),{$refStrategy:"none"})};async listEnvironments(t){let n=await this.requireApi("list_environments").listEnvironments();return JSON.stringify({environments:n.map(i=>({id:i.id,name:i.name,url:i.url}))})}static listTestAccountsTool={name:"list_test_accounts",description:"List test accounts for a specific environment. Requires either environment_id or environment_url.",inputSchema:av(Ci.object({environment_id:Ci.number().optional().describe("Environment ID to filter by"),environment_url:Ci.string().optional().describe("Environment URL to filter by (alternative to environment_id)")}),{$refStrategy:"none"})};async listTestAccounts(t){let r=this.requireApi("list_test_accounts"),{environment_id:n,environment_url:i}=Ci.object({environment_id:Ci.number().optional(),environment_url:Ci.string().optional()}).parse(t),s=await r.listTestAccounts(n,i);return JSON.stringify({test_accounts:s.map(o=>({id:o.id,name:o.name,username:o.username,environment_id:o.environmentId||o.environment_id}))})}static getTestAccountTool={name:"get_test_account",description:"Get a specific test account by ID",inputSchema:av(Ci.object({test_account_id:Ci.number().describe("Test account ID")}),{$refStrategy:"none"})};async getTestAccount(t){let r=this.requireApi("get_test_account"),{test_account_id:n}=Ci.object({test_account_id:Ci.number()}).parse(t),i=await r.getTestAccount(n);return JSON.stringify({test_account:i})}static createTestAccountTool={name:"create_test_account",description:"Create a new test account",inputSchema:av(Ci.object({environment_id:Ci.number().describe("Environment ID"),username:Ci.string().describe("Username"),password:Ci.string().describe("Password"),login_config:Ci.any().optional().describe("Login configuration")}),{$refStrategy:"none"})};async createTestAccount(t){let r=this.requireApi("create_test_account"),{environment_id:n,username:i,password:s,login_config:o}=Ci.object({environment_id:Ci.number(),username:Ci.string(),password:Ci.string(),login_config:Ci.any().optional()}).parse(t),a=await r.createTestAccount({environmentId:n,username:i,password:s,loginConfig:o});return JSON.stringify({test_account_id:a.id,message:"Test account created successfully"})}static listFoldersTool={name:"list_folders",description:"List test case folders",inputSchema:av(Ci.object({parent_id:Ci.number().nullable().optional().describe("Parent folder ID, null for root"),search:Ci.string().optional().describe("Search query")}),{$refStrategy:"none"})};async listFolders(t){let r=this.requireApi("list_folders"),{parent_id:n,search:i}=Ci.object({parent_id:Ci.number().nullable().optional(),search:Ci.string().optional()}).parse(t),s=await r.listFolders(n,i);return JSON.stringify({folders:s.map(o=>({id:o.id,name:o.name,parent_id:o.parentId||o.parent_id,path:o.pathIds}))})}static createFolderTool={name:"create_folder",description:"Create a new test case folder",inputSchema:av(Ci.object({name:Ci.string().describe("Folder name"),description:Ci.string().optional().describe("Folder description"),parent_id:Ci.number().nullable().optional().describe("Parent folder ID")}),{$refStrategy:"none"})};async createFolder(t){let r=this.requireApi("create_folder"),{name:n,description:i,parent_id:s}=Ci.object({name:Ci.string(),description:Ci.string().optional(),parent_id:Ci.number().nullable().optional()}).parse(t),o=await r.createFolder({name:n,description:i,parentId:s});return JSON.stringify({folder_id:o.id,message:"Folder created successfully"})}static getFolderTool={name:"get_folder",description:"Get folder details with full hierarchical path",inputSchema:av(Ci.object({folder_id:Ci.number().describe("Folder ID")}),{$refStrategy:"none"})};async getFolder(t){let r=this.requireApi("get_folder"),{folder_id:n}=Ci.object({folder_id:Ci.number()}).parse(t),i=await r.getFolder(n);return JSON.stringify({folder:i})}static toolDefinitions=[ib.listEnvironmentsTool,ib.listTestAccountsTool,ib.getTestAccountTool,ib.createTestAccountTool,ib.listFoldersTool,ib.createFolderTool,ib.getFolderTool]};function Upi(e,t){let r=e.replace(/\/+$/,"");return t.find(n=>r===n.url.replace(/\/+$/,""))}var Zce=class Wce{constructor(t){this.apiClient=t}requireApi(t){if(!this.apiClient)throw new Error(hQ(t));return this.apiClient}parseFlowInput(t){return typeof t=="string"?$5.parse(AQ(t)):$5.parse(t)}static createTestCaseTool={name:"create_test_case",description:"Create a test case from a flow (test flow JSON object or YAML string). IMPORTANT: Read 'shiplight://schemas/testflow-json-v1.2.0' for the required flow format. Accepts a test flow as JSON object or YAML string. Include action_entity with locator/xpath on ACTION statements when available - this speeds up test execution.",inputSchema:sG(In.object({flow:In.union([In.string(),In.any()]).describe("Test flow as JSON object or YAML string"),folder_id:In.number().optional().describe("Folder ID"),name:In.string().optional().describe("Test case name"),environment_id:In.number().optional().describe("Environment ID for test execution"),test_account_id:In.number().optional().describe("Optional test account ID for login")}),{$refStrategy:"none"})};async createTestCase(t){let r=this.requireApi("create_test_case"),{flow:n,folder_id:i,name:s,environment_id:o,test_account_id:a}=In.object({flow:In.any(),folder_id:In.number().optional(),name:In.string().optional(),environment_id:In.number().optional(),test_account_id:In.number().optional()}).parse(t),u;try{u=this.parseFlowInput(n)}catch(m){throw m instanceof In.ZodError?new Error(`Invalid TestFlow schema: ${JSON.stringify(m.errors)}`):m}let l={...u,version:"1.2.0",statements:this.regenerateUids(u.statements),teardown:u.teardown?this.regenerateUids(u.teardown):void 0},c=o;if(c===void 0&&u.url)try{let m=await r.listEnvironments(),C=Upi(u.url,m);C&&(c=C.id)}catch{}let d;c!==void 0&&(d=[{environment_id:c,test_account_group:a?{type:"Specific",account_ids:[a]}:{type:"None",account_ids:[]},path:""}]);let p=await r.createTestCase({title:s||u.goal,testFlow:l,folderId:i,environmentConfigs:d}),f=this.computeFlowStats(u.statements,u.teardown);return JSON.stringify({...p,environment_id:c??null,test_account_id:a??null,yaml:fQ(u,{test_case_id:p.test_case_id}),flow_stats:f})}static updateTestCaseTool={name:"update_test_case",description:"Update a test case from a flow (test flow JSON object or YAML string). IMPORTANT: Read 'shiplight://schemas/testflow-json-v1.2.0' for the required flow format. Accepts a test flow as JSON object or YAML string. Include action_entity with locator/xpath on ACTION statements when available - this speeds up test execution.",inputSchema:sG(In.object({test_case_id:In.number().describe("Test case ID"),flow:In.union([In.string(),In.any()]).describe("Test flow as JSON object or YAML string"),title:In.string().optional().describe("New title")}),{$refStrategy:"none"})};async updateTestCase(t){let r=this.requireApi("update_test_case"),{test_case_id:n,flow:i,title:s}=In.object({test_case_id:In.number(),flow:In.any(),title:In.string().optional()}).parse(t),o;try{o=this.parseFlowInput(i)}catch(c){throw c instanceof In.ZodError?new Error(`Invalid TestFlow schema: ${JSON.stringify(c.errors)}`):c}let a={...o,version:"1.2.0",statements:this.regenerateUids(o.statements),teardown:o.teardown?this.regenerateUids(o.teardown):void 0},u=await r.updateTestCase(n,{title:s||o.goal,testFlow:a}),l=this.computeFlowStats(o.statements,o.teardown);return JSON.stringify({...u,flow_stats:l})}static getTestCaseTool={name:"get_test_case",description:"Get a test case by ID. Use output_format 'yaml' to get the flow as a YAML string.",inputSchema:sG(In.object({test_case_id:In.number().describe("Test case ID"),output_format:In.enum(["json","yaml"]).optional().describe("Output format for the test flow (default: json)")}),{$refStrategy:"none"})};async getTestCase(t){let r=this.requireApi("get_test_case"),{test_case_id:n,output_format:i}=In.object({test_case_id:In.number(),output_format:In.enum(["json","yaml"]).optional()}).parse(t),s=await r.getTestCase(n);if(i==="yaml"&&s.test_flow){let o=fQ(s.test_flow,{test_case_id:s.id});return JSON.stringify({test_case:{id:s.id,title:s.title,folder_id:s.folder_id},yaml:o})}return JSON.stringify({test_case:s})}static runTestCaseTool={name:"run_test_case",description:"Trigger cloud execution of a test case. Returns a test_run_id that can be used with get_test_run_details to poll for completion and results.",inputSchema:sG(In.object({test_case_id:In.number().describe("Test case ID to run"),environment_id:In.number().optional().describe("Environment ID to run against (uses test case default if not specified)")}),{$refStrategy:"none"})};async runTestCase(t){let r=this.requireApi("run_test_case"),{test_case_id:n,environment_id:i}=In.object({test_case_id:In.number(),environment_id:In.number().optional()}).parse(t),s=await r.runTestCase(n,i);return JSON.stringify({test_run_id:s.id,status:s.status,result:s.result,test_case_result_ids:s.test_case_result_ids,message:`Test run ${s.id} triggered. Use get_test_run_details(${s.id}) to check status and results.`})}static saveTestCaseTool={name:"save_test_case",description:"Save a test case from YAML \u2014 automatically creates or updates based on embedded test_case_id metadata. If the YAML contains a test_case_id field, updates that test case; otherwise creates a new one. Returns YAML with test_case_id embedded so you can write it back to the local file for future updates.",inputSchema:sG(In.object({yaml:In.string().describe("YAML test flow string"),folder_id:In.number().optional().describe("Folder ID (only used on create)"),name:In.string().optional().describe("Test case name (defaults to flow goal)"),environment_id:In.number().optional().describe("Environment ID for test execution (only used on create)"),test_account_id:In.number().optional().describe("Optional test account ID for login (only used on create)")}),{$refStrategy:"none"})};async saveTestCase(t){let{yaml:r,folder_id:n,name:i,environment_id:s,test_account_id:o}=In.object({yaml:In.string(),folder_id:In.number().optional(),name:In.string().optional(),environment_id:In.number().optional(),test_account_id:In.number().optional()}).parse(t),a=$Ee(r);if(a.test_case_id!==void 0){let u=await this.updateTestCase({test_case_id:a.test_case_id,flow:r,title:i}),l=JSON.parse(u),c=this.parseFlowInput(r);return JSON.stringify({...l,yaml:fQ(c,{test_case_id:a.test_case_id})})}else return await this.createTestCase({flow:r,folder_id:n,name:i,environment_id:s,test_account_id:o})}computeFlowStats(t,r){let n={total:0,drafts:0,actions_with_locator:0,actions_without_locator:0,verifies:0,steps:0},i=o=>{for(let a of o)if(n.total++,a.type==="DRAFT")n.drafts++;else if(a.type==="ACTION"){let u=a.action_entity;u?.action_data?.action_name==="verify"?n.verifies++:u?.locator||u?.xpath?n.actions_with_locator++:n.actions_without_locator++}else a.type==="STEP"?(n.steps++,a.statements&&i(a.statements)):a.type==="IF_ELSE"?(a.then&&i(a.then),a.else&&i(a.else)):a.type==="WHILE_LOOP"&&a.body&&i(a.body)};i(t),r&&i(r);let s={...n};return n.drafts>0&&n.actions_with_locator===0?s.hint=`${n.drafts} DRAFT statements with no action_entities (~10-15s each at runtime). Include action_entities with locator/xpath for ~1s deterministic replay.`:n.drafts>0&&(s.hint=`${n.drafts} DRAFT statements remaining (~10-15s each at runtime vs ~1s with action_entities). Consider converting DRAFTs that don't depend on runtime state to ACTIONs.`),s}regenerateUids(t){return t.map(r=>{let n={...r,uid:Ppi()};return r.type==="STEP"&&r.statements?n.statements=this.regenerateUids(r.statements):r.type==="IF_ELSE"?(r.then&&(n.then=this.regenerateUids(r.then)),r.else&&(n.else=this.regenerateUids(r.else))):r.type==="WHILE_LOOP"&&r.body&&(n.body=this.regenerateUids(r.body)),n})}static toolDefinitions=[Wce.getTestCaseTool,Wce.runTestCaseTool,Wce.saveTestCaseTool]},Gpi=3600*1e3,jce=Yce.join(qpi.tmpdir(),"shiplight-artifacts"),ede=class HT{constructor(t,r){this.apiClient=t,this.options=r||{}}options;requireApi(t){if(!this.apiClient)throw new Error(hQ(t));return this.apiClient}getProxyUrl(t){if(!this.options.artifactProxyBaseUrl)return null;let r=t.match(/^s3:\/\/[^/]+\/(.+)$/);if(!r)return null;let n=r[1];return`${this.options.artifactProxyBaseUrl}/api/artifacts/${n}`}cleanupOldArtifacts(){try{if(!uv.existsSync(jce))return;let t=Date.now(),r=uv.readdirSync(jce,{withFileTypes:!0});for(let n of r){if(!n.isDirectory())continue;let i=Yce.join(jce,n.name);try{let s=uv.statSync(i);t-s.mtimeMs>Gpi&&uv.rmSync(i,{recursive:!0,force:!0})}catch{}}}catch{}}static listTestRunsTool={name:"list_test_runs",description:"List test runs with optional filtering by test plan, trigger type, or result status",inputSchema:oG(cs.object({test_plan_id:cs.number().optional().describe("Filter by test plan ID"),trigger:cs.string().optional().describe("Filter by trigger type (Manual, Scheduled, API, GITHUB_ACTION, Webhook, Automation)"),result:cs.string().optional().describe("Filter by result (Passed, Failed, Pending, Skipped, Queued)"),limit:cs.number().optional().describe("Maximum number of results (default 20)")}),{$refStrategy:"none"})};async listTestRuns(t){let r=this.requireApi("list_test_runs"),{test_plan_id:n,trigger:i,result:s,limit:o}=cs.object({test_plan_id:cs.number().optional(),trigger:cs.string().optional(),result:cs.string().optional(),limit:cs.number().optional()}).parse(t),a=await r.listTestRuns({testPlanId:n,trigger:i,result:s,limit:o??20});return JSON.stringify({test_runs:a.map(u=>({id:u.id,status:u.status,result:u.result,trigger:u.trigger,start_time:u.startTime||u.start_time,end_time:u.endTime||u.end_time,duration_ms:u.duration,total_test_case_count:u.totalTestCaseCount||u.total_test_case_count,passed_test_case_count:u.passedTestCaseCount||u.passed_test_case_count,failed_test_case_count:u.failedTestCaseCount||u.failed_test_case_count,skipped_test_case_count:u.skippedTestCaseCount||u.skipped_test_case_count,test_plan_id:u.testPlanId||u.test_plan_id})),count:a.length})}static getTestRunDetailsTool={name:"get_test_run_details",description:"Get detailed information about a test run including all test case results",inputSchema:oG(cs.object({test_run_id:cs.number().describe("Test run ID")}),{$refStrategy:"none"})};async getTestRunDetails(t){let r=this.requireApi("get_test_run_details"),{test_run_id:n}=cs.object({test_run_id:cs.number()}).parse(t),i=await r.getTestRunDetails(n),s=i.testRun||i,o=i.testCaseResults||[];return JSON.stringify({test_run:{id:s.id,status:s.status,result:s.result,trigger:s.trigger,start_time:s.startTime||s.start_time,end_time:s.endTime||s.end_time,duration_ms:s.duration,total_test_case_count:s.totalTestCaseCount||s.total_test_case_count,passed_test_case_count:s.passedTestCaseCount||s.passed_test_case_count,failed_test_case_count:s.failedTestCaseCount||s.failed_test_case_count,skipped_test_case_count:s.skippedTestCaseCount||s.skipped_test_case_count,test_plan_id:s.testPlanId||s.test_plan_id},test_case_results:o.map(a=>({id:a.id,test_case_id:a.testCaseId||a.test_case_id,result:a.result,status:a.status,duration_ms:a.duration,environment_name:a.environmentName||a.environment_name,environment_url:a.environmentUrl||a.environment_url,error:a.error}))})}static getTestCaseResultTool={name:"get_test_case_result",description:"Get detailed test case result including status, duration, and error information",inputSchema:oG(cs.object({test_case_result_id:cs.number().describe("Test case result ID"),include_report:cs.boolean().optional().describe("Include full report with step details (default false)")}),{$refStrategy:"none"})};async getTestCaseResult(t){let r=this.requireApi("get_test_case_result"),{test_case_result_id:n,include_report:i}=cs.object({test_case_result_id:cs.number(),include_report:cs.boolean().optional()}).parse(t),s=await r.getTestCaseResult(n),o=Array.isArray(s.report)?s.report[0]:s.report,a=o?.resultJson||{},u=Object.entries(a),l=u.length,c=null;for(let p=0;p<u.length;p++){let[,f]=u[p];if(f.status==="failed"||f.status==="failure"){c=p;break}}let d={id:s.id,test_case_id:s.testCaseId||s.test_case_id,test_run_id:s.testRunId||s.test_run_id,result:s.result,status:s.status,start_time:s.startTime||s.start_time,end_time:s.endTime||s.end_time,duration_ms:s.duration,environment_name:s.environmentName||s.environment_name,environment_id:s.environmentId||s.environment_id,environment_url:s.environmentUrl||s.environment_url,device:s.device,total_steps:l,failed_step_index:c,video_s3_uri:s.video,trace_s3_uri:s.trace,report_s3_uri:s.reportS3Uri||s.report_s3_uri};if(i){let p=s.reportS3Uri||s.report_s3_uri,f=null;if(p&&r.getS3FileContents)try{let C=await r.getS3FileContents(p);if(C){let y=JSON.parse(C);f=Array.isArray(y)?y[0]:y}}catch(C){console.error("Failed to fetch S3 report, falling back to DB report:",C)}let m=f||o;d.report={stdout:m?.stdout,stderr:m?.stderr,consoleLogs:m?.consoleLogs,flaky:m?.flaky,error:m?.error,step_results:m?.resultJson,video_s3_uri:m?.videoS3Uri,trace_s3_uri:m?.traceS3Uri,source_s3_uri:m?.sourceS3Uri}}return JSON.stringify(d)}static getTestCaseResultStepsTool={name:"get_test_case_result_steps",description:"Get step-by-step execution details for a range of steps. Use get_test_case_result first to find the failed_step_index, then request the range you need.",inputSchema:oG(cs.object({test_case_result_id:cs.number().describe("Test case result ID"),from_step:cs.number().describe("Start step index (0-based, inclusive)"),to_step:cs.number().describe("End step index (0-based, inclusive)")}),{$refStrategy:"none"})};async getTestCaseResultSteps(t){let r=this.requireApi("get_test_case_result_steps"),{test_case_result_id:n,from_step:i,to_step:s}=cs.object({test_case_result_id:cs.number(),from_step:cs.number(),to_step:cs.number()}).parse(t),o=await r.getTestCaseResult(n),u=(Array.isArray(o.report)?o.report[0]:o.report)?.resultJson||{},l=Object.entries(u).map(([d,p])=>{let f=p.artifacts?.[0];return{step_id:d,description:p.description,status:p.status,message:p.message,start_time:p.startTime,duration_ms:p.duration,has_screenshot:!!(f?.screenshot_s3_path||p.screenshotS3Uri),has_messages:!!f?.messages_s3_path,has_response:!!f?.response_s3_path,has_system_prompt:!!f?.system_prompt_s3_path}}),c=l.slice(i,s+1);return JSON.stringify({test_case_result_id:n,total_steps:l.length,from_step:i,to_step:s,steps:c})}static getStepArtifactsTool={name:"get_step_artifacts",description:"Download all artifacts for a step to local files. Returns local file paths - client decides which to read.",inputSchema:oG(cs.object({test_case_result_id:cs.number().describe("Test case result ID"),step_index:cs.number().describe("Step index (0-based)")}),{$refStrategy:"none"})};async getStepArtifacts(t){let r=this.requireApi("get_step_artifacts"),{test_case_result_id:n,step_index:i}=cs.object({test_case_result_id:cs.number(),step_index:cs.number()}).parse(t),s=await r.getTestCaseResult(n),a=(Array.isArray(s.report)?s.report[0]:s.report)?.resultJson||{},u=Object.values(a);if(i<0||i>=u.length)throw new Error(`Step index ${i} out of range (0-${u.length-1})`);let l=u[i],c=l.artifacts?.[0],d={},p=c?.screenshot_s3_path||l.screenshotS3Uri;p&&(d.screenshot={s3Uri:p,filename:`step_${i}_screenshot.png`}),c?.messages_s3_path&&(d.messages={s3Uri:c.messages_s3_path,filename:`step_${i}_messages.json`}),c?.response_s3_path&&(d.response={s3Uri:c.response_s3_path,filename:`step_${i}_response.txt`}),c?.system_prompt_s3_path&&(d.system_prompt={s3Uri:c.system_prompt_s3_path,filename:`step_${i}_system_prompt.txt`});let f=!!r.getS3FileContents,m=!!this.options.artifactProxyBaseUrl,C=!!r.getArtifactPresignedUrl;if(!f&&!m&&!C)return JSON.stringify({test_case_result_id:n,step_index:i,error:"Artifact download not supported by this API client",s3_uris:Object.fromEntries(Object.entries(d).map(([D,{s3Uri:F}])=>[D,F]))});this.cleanupOldArtifacts();let y=Yce.join(jce,`tcr-${n}`);uv.existsSync(y)||uv.mkdirSync(y,{recursive:!0});let b={},v=!!r.downloadArtifact;return await Promise.all(Object.entries(d).map(async([D,{s3Uri:F,filename:S}])=>{try{let k=null;if(!k&&f){let U=await r.getS3FileContents(F);U&&(k=U)}if(!k&&m&&v){let U=this.getProxyUrl(F);U&&(k=await r.downloadArtifact(U))}if(!k&&C){let U=await r.getArtifactPresignedUrl(F);if(U){let T=await Hpi.get(U,{responseType:"arraybuffer"});k=Buffer.from(T.data)}}if(!k){b[D]=null;return}let Q=Yce.join(y,S);uv.writeFileSync(Q,k),b[D]=Q}catch(k){console.error(`Failed to download ${D} artifact:`,k),b[D]=null}})),JSON.stringify({test_case_result_id:n,step_index:i,screenshot_path:b.screenshot,messages_path:b.messages,response_path:b.response,system_prompt_path:b.system_prompt})}static toolDefinitions=[HT.listTestRunsTool,HT.getTestRunDetailsTool,HT.getTestCaseResultTool,HT.getTestCaseResultStepsTool,HT.getStepArtifactsTool]},tde=class cpr{static initLocalProjectTool={name:"init_local_project",description:"Scaffold a ready-to-run local test project with Playwright and shiplightai. Generates package.json, playwright.config.ts, .gitignore, .env.example, and a tests/ directory. IMPORTANT: You MUST call this tool before writing any .test.yaml files \u2014 it sets up the project structure required to run them. After scaffolding, the user runs: npm install && npx playwright install chromium && npx playwright test",inputSchema:$hr(A0.object({project_path:A0.string().describe("Absolute path where the project should be created")}),{$refStrategy:"none"})};async initLocalProject(t){let{project_path:r}=A0.object({project_path:A0.string()}).parse(t),n=P8.join(r,"tests");O6.mkdirSync(n,{recursive:!0});let i={name:"my-shiplight-tests",type:"module",scripts:{test:"playwright test","test:headed":"playwright test --headed"},dependencies:{"@playwright/test":"1.55.0",playwright:"1.55.0",shiplightai:"^0.1.0",dotenv:"^16.4.7"}};O6.writeFileSync(P8.join(r,"package.json"),JSON.stringify(i,null,2)+`
8093
- `),O6.writeFileSync(P8.join(r,"playwright.config.ts"),`import 'dotenv/config';
8094
- import { defineConfig } from '@playwright/test';
8093
+ `),O6.writeFileSync(P8.join(r,"playwright.config.ts"),`import { defineConfig } from '@playwright/test';
8094
+ import { shiplightConfig } from 'shiplightai';
8095
8095
 
8096
8096
  export default defineConfig({
8097
+ ...shiplightConfig(),
8097
8098
  testDir: './tests',
8099
+ testMatch: ['**/*.test.ts', '**/*.yaml.spec.ts'],
8098
8100
  timeout: 120_000,
8099
8101
  expect: { timeout: 10_000 },
8100
8102
  retries: 0,
@@ -8116,7 +8118,7 @@ The flow is executed statement by statement:
8116
8118
  - IF_ELSE: evaluates condition, then executes the matching branch
8117
8119
  - WHILE_LOOP: loops while condition is true (respects timeout_ms)
8118
8120
 
8119
- IMPORTANT: Read 'shiplight://schemas/testflow-json-v1.2.0' resource for the flow format.`,inputSchema:$pi(cv.object({session_id:cv.string().describe("Session ID from new_session"),flow:cv.any().describe("Test flow JSON object (must match TestFlow schema)")}),{$refStrategy:"none"})};async runTestFlow(t){let{session_id:r,flow:n}=cv.object({session_id:cv.string(),flow:cv.any()}).parse(t);if(!this.backend.getSession(r))throw new Error(`Session ${r} not found`);let s;try{s=$5.parse(n)}catch(p){throw p instanceof cv.ZodError?new Error(`Invalid TestFlow schema: ${JSON.stringify(p.errors)}`):p}let o=this.backend.getPage(r),a=this.backend.getWebAgent(r);s.url&&await o.goto(s.url,{waitUntil:"domcontentloaded"});let u=[],l=await this.executeStatements(s.statements,o,a,u),c=this.countResults(u),d={success:l,url:o.url(),results:u,statements_passed:c.passed,statements_failed:c.failed,statements_total:c.total};return JSON.stringify(d)}async executeStatements(t,r,n,i){let s=!0;for(let o of t){let a=await this.executeStatement(o,r,n);if(i.push(a),!a.success){s=!1;break}}return s}async executeStatement(t,r,n){let i=this.getStatementDescription(t),s={uid:t.uid,type:t.type,description:i,success:!1};try{switch(t.type){case H0.DRAFT:return await this.executeDraft(t,r,n,s);case H0.ACTION:return await this.executeAction(t,r,n,s);case H0.STEP:return await this.executeStep(t,r,n,s);case H0.IF_ELSE:return await this.executeIfElse(t,r,n,s);case H0.WHILE_LOOP:return await this.executeWhileLoop(t,r,n,s);default:return{...s,error:`Unknown statement type: ${t.type}`}}}catch(o){return{...s,error:o instanceof Error?o.message:String(o)}}}async executeDraft(t,r,n,i){let s=await n.run(r,t.description);return{...i,success:s.success,error:s.success?void 0:s.details||"Draft execution failed"}}async executeAction(t,r,n,i){if(t.action_entity){let s=t.action_entity.action_data?.action_name;return s?(await n.execAction(s,r,t.action_entity),{...i,success:!0}):{...i,error:"action_entity missing action_data.action_name"}}else{let s=await n.execute(r,t.description);return{...i,success:s.success,error:s.success?void 0:s.details||"Action execution failed"}}}async executeStep(t,r,n,i){let s=Bj(t),o=[],a=!0;for(let u of s)if(!await this.executeStatements(u.statements,r,n,o)){a=!1;break}return{...i,success:a,children:o,error:a?void 0:"One or more child statements failed"}}async executeIfElse(t,r,n,i){let s=t.condition.expression,o=await n.evaluate(r,s),a=[],u=!0;return o&&t.then&&t.then.length>0?u=await this.executeStatements(t.then,r,n,a):!o&&t.else&&t.else.length>0&&(u=await this.executeStatements(t.else,r,n,a)),{...i,success:u,children:a,description:`${i.description} (condition=${o})`}}async executeWhileLoop(t,r,n,i){let o=t.condition.expression,a=t.timeout_ms??_D,u=Date.now(),l=[],c=0;for(;;){if(Date.now()-u>a)return{...i,success:!1,children:l,error:`While loop timed out after ${a}ms (${c} iterations)`};if(c>=100)return{...i,success:!1,children:l,error:"While loop exceeded max iterations (100)"};if(!await n.evaluate(r,o))break;if(t.body&&t.body.length>0){let p=[],f=await this.executeStatements(t.body,r,n,p);if(l.push(...p),!f)return{...i,success:!1,children:l,error:`While loop body failed on iteration ${c+1}`}}c++}return{...i,success:!0,children:l,description:`${i.description} (${c} iterations)`}}getStatementDescription(t){switch(t.type){case H0.DRAFT:case H0.ACTION:case H0.STEP:return t.description||t.type;case H0.IF_ELSE:return`IF ${t.condition.expression}`;case H0.WHILE_LOOP:return`WHILE ${t.condition.expression}`;default:let r=t;return"Unknown statement"}}countResults(t){let r=0,n=0,i=0;for(let s of t)if(i++,s.success?r++:n++,s.children){let o=this.countResults(s.children);r+=o.passed,n+=o.failed,i+=o.total}return{passed:r,failed:n,total:i}}static toolDefinitions=[dpr.runTestFlowTool]};eE();var ZPe=class e{static _instance;_logLevel;constructor(){switch(process.env.LOG_LEVEL?.toUpperCase()){case"ERROR":this._logLevel=1;break;case"WARN":this._logLevel=2;break;case"INFO":this._logLevel=3;break;case"DEBUG":this._logLevel=4;break;default:this._logLevel=3}}static getInstance(){return e._instance||(e._instance=new e),e._instance}setLevel(t){this._logLevel=t}debug(...t){this._logLevel>=4&&console.error("[DEBUG]",...t)}info(...t){this._logLevel>=3&&console.error("[INFO]",...t)}warn(...t){this._logLevel>=2&&console.error("[WARN]",...t)}error(...t){this._logLevel>=1&&console.error("[ERROR]",...t)}log(...t){this.info(...t)}},Vpi=ZPe.getInstance(),rde=Vpi;import{createHash as jpi}from"node:crypto";import{hostname as Wpi,userInfo as Ypi,platform as ppr,arch as Jpi}from"node:os";import{PostHog as zpi}from"posthog-node";var Kpi="phc_5Va2dHamGwJWX9qZqyRka5Lann5ATsg3LL4uyU63qin",Xpi="https://us.i.posthog.com",Zpi=process.env.SHIPLIGHT_TELEMETRY==="0"||process.env.DO_NOT_TRACK==="1",eUe=null,hpr=!1;function efi(){return Zpi?null:(eUe||(eUe=new zpi(Kpi,{host:Xpi,flushAt:1,flushInterval:3e4})),eUe)}function tfi(){let e=`${Wpi()}:${Ypi().username}:${ppr()}:${Jpi()}`;return jpi("sha256").update(e).digest("hex")}function fpr(e,t){let r=efi();if(r){hpr||(rde.info("Anonymous usage telemetry enabled. Set SHIPLIGHT_TELEMETRY=0 to disable."),hpr=!0);try{r.capture({distinctId:tfi(),event:"mcp_new_session",properties:{product:"mcp-server",version:e,platform:ppr(),nodeVersion:process.version,cloudMode:t,$geoip_disable:!0,$ip:null}}),r.flush().catch(()=>{})}catch{}}}process.env.PWDEBUG="console";var mpr="shiplight://environments",tUe=class{constructor(t=!1){this.cloudMode=t;let r=process.env.API_BASE_URL||"https://api.shiplight.ai",n=VEe(process.env),i=process.env.TERMINATION_TIMEOUT?parseInt(process.env.TERMINATION_TIMEOUT,10):null;this.server=new rfi({name:"shiplight-mcp",version:"1.0.0"},{capabilities:{tools:{},resources:{},prompts:{}}});let s={};process.env.GOOGLE_API_KEY&&(s.GOOGLE_API_KEY=process.env.GOOGLE_API_KEY),process.env.ANTHROPIC_API_KEY&&(s.ANTHROPIC_API_KEY=process.env.ANTHROPIC_API_KEY),Object.keys(s).length>0&&hG({env:s}),this.sessionBackend=new jEe({runDir:void 0,terminationTimeout:i}),this.apiClient=this.cloudMode&&process.env.API_TOKEN?new WEe({apiBaseUrl:r,getApiToken:()=>process.env.API_TOKEN}):null,this.sessionTools=new Jce(this.sessionBackend,this.apiClient,n),this.browserTools=new zce(this.sessionBackend),this.debugTools=new Kce(this.sessionBackend),this.localTestTools=new tde,this.registry=new KEe,this.registry.registerAll(Jce,this.sessionTools).registerAll(zce,this.browserTools).registerAll(Kce,this.debugTools).registerAll(tde,this.localTestTools),this.cloudMode&&(this.testCaseTools=new Zce(this.apiClient),this.testResultTools=new ede(this.apiClient),this.dataTools=new Xce(this.apiClient),this.registry.registerAll(Zce,this.testCaseTools).registerAll(ede,this.testResultTools).registerAll(Xce,this.dataTools)),this.setupHandlers()}server;registry;sessionBackend;apiClient;sessionTools;browserTools;debugTools;testCaseTools=null;testResultTools=null;dataTools=null;localTestTools;setupHandlers(){let{tools:t,handleToolCall:r}=this.registry.build();this.server.setRequestHandler(sfi,async()=>({tools:t})),this.server.setRequestHandler(ifi,async n=>{try{let{name:i,arguments:s}=n.params,o=await r(i,s);return i==="new_session"&&fpr("0.1.21",this.cloudMode),{content:[{type:"text",text:o}]}}catch(i){let s=i instanceof Error?i.message:String(i);throw Apr.isAxiosError(i)&&i.response?.status===401&&(s="Authentication required. Contact info@shiplight.ai to get access to cloud services and advanced features."),new lG(uG.InternalError,s)}}),this.server.setRequestHandler(ofi,async()=>{let n=new Set(["shiplight://schemas/testflow-json-v1.2.0"]),i=this.cloudMode?Dj():Dj().filter(s=>!n.has(s.uri));return this.cloudMode&&i.push({uri:mpr,name:"Environments",description:"List testing environments (same data as the list_environments tool).",mimeType:"application/json"}),{resources:i}}),this.server.setRequestHandler(afi,async n=>{let{uri:i}=n.params;if(i===mpr){if(!this.apiClient)throw new lG(uG.InternalError,"The environments resource requires a Shiplight API token. Set API_TOKEN in your MCP server configuration.");try{let o=await this.apiClient.listEnvironments();return{contents:[{uri:i,mimeType:"application/json",text:JSON.stringify({environments:o.map(a=>({id:a.id,name:a.name,url:a.url}))},null,2)}]}}catch(o){let a=o instanceof Error?o.message:String(o);throw Apr.isAxiosError(o)&&o.response?.status===401&&(a="Authentication required. Provide API_TOKEN in the MCP server configuration to access cloud resources."),new lG(uG.InternalError,a)}}let s=await XEe(i);if(!s)throw new lG(uG.InvalidRequest,`Unknown resource: ${i}`);return{contents:[{uri:i,mimeType:"text/markdown",text:s}]}}),this.server.setRequestHandler(ufi,async()=>({prompts:zEe().map(i=>({name:i.name,description:i.description}))})),this.server.setRequestHandler(lfi,async n=>{let{name:i}=n.params,s=JEe(i);if(!s)throw new lG(uG.InvalidRequest,`Unknown prompt: ${i}`);return{messages:[{role:"user",content:{type:"text",text:s}}]}})}async run(){let t=new nfi;await this.server.connect(t),rde.info("Shiplight MCP Server running on stdio")}},cfi=process.argv.includes("--cloud"),dfi=new tUe(cfi);await dfi.run();
8121
+ IMPORTANT: Read 'shiplight://schemas/testflow-json-v1.2.0' resource for the flow format.`,inputSchema:$pi(cv.object({session_id:cv.string().describe("Session ID from new_session"),flow:cv.any().describe("Test flow JSON object (must match TestFlow schema)")}),{$refStrategy:"none"})};async runTestFlow(t){let{session_id:r,flow:n}=cv.object({session_id:cv.string(),flow:cv.any()}).parse(t);if(!this.backend.getSession(r))throw new Error(`Session ${r} not found`);let s;try{s=$5.parse(n)}catch(p){throw p instanceof cv.ZodError?new Error(`Invalid TestFlow schema: ${JSON.stringify(p.errors)}`):p}let o=this.backend.getPage(r),a=this.backend.getWebAgent(r);s.url&&await o.goto(s.url,{waitUntil:"domcontentloaded"});let u=[],l=await this.executeStatements(s.statements,o,a,u),c=this.countResults(u),d={success:l,url:o.url(),results:u,statements_passed:c.passed,statements_failed:c.failed,statements_total:c.total};return JSON.stringify(d)}async executeStatements(t,r,n,i){let s=!0;for(let o of t){let a=await this.executeStatement(o,r,n);if(i.push(a),!a.success){s=!1;break}}return s}async executeStatement(t,r,n){let i=this.getStatementDescription(t),s={uid:t.uid,type:t.type,description:i,success:!1};try{switch(t.type){case H0.DRAFT:return await this.executeDraft(t,r,n,s);case H0.ACTION:return await this.executeAction(t,r,n,s);case H0.STEP:return await this.executeStep(t,r,n,s);case H0.IF_ELSE:return await this.executeIfElse(t,r,n,s);case H0.WHILE_LOOP:return await this.executeWhileLoop(t,r,n,s);default:return{...s,error:`Unknown statement type: ${t.type}`}}}catch(o){return{...s,error:o instanceof Error?o.message:String(o)}}}async executeDraft(t,r,n,i){let s=await n.run(r,t.description);return{...i,success:s.success,error:s.success?void 0:s.details||"Draft execution failed"}}async executeAction(t,r,n,i){if(t.action_entity){let s=t.action_entity.action_data?.action_name;return s?(await n.execAction(s,r,t.action_entity),{...i,success:!0}):{...i,error:"action_entity missing action_data.action_name"}}else{let s=await n.execute(r,t.description);return{...i,success:s.success,error:s.success?void 0:s.details||"Action execution failed"}}}async executeStep(t,r,n,i){let s=Bj(t),o=[],a=!0;for(let u of s)if(!await this.executeStatements(u.statements,r,n,o)){a=!1;break}return{...i,success:a,children:o,error:a?void 0:"One or more child statements failed"}}async executeIfElse(t,r,n,i){let s=t.condition.expression,o=await n.evaluate(r,s),a=[],u=!0;return o&&t.then&&t.then.length>0?u=await this.executeStatements(t.then,r,n,a):!o&&t.else&&t.else.length>0&&(u=await this.executeStatements(t.else,r,n,a)),{...i,success:u,children:a,description:`${i.description} (condition=${o})`}}async executeWhileLoop(t,r,n,i){let o=t.condition.expression,a=t.timeout_ms??_D,u=Date.now(),l=[],c=0;for(;;){if(Date.now()-u>a)return{...i,success:!1,children:l,error:`While loop timed out after ${a}ms (${c} iterations)`};if(c>=100)return{...i,success:!1,children:l,error:"While loop exceeded max iterations (100)"};if(!await n.evaluate(r,o))break;if(t.body&&t.body.length>0){let p=[],f=await this.executeStatements(t.body,r,n,p);if(l.push(...p),!f)return{...i,success:!1,children:l,error:`While loop body failed on iteration ${c+1}`}}c++}return{...i,success:!0,children:l,description:`${i.description} (${c} iterations)`}}getStatementDescription(t){switch(t.type){case H0.DRAFT:case H0.ACTION:case H0.STEP:return t.description||t.type;case H0.IF_ELSE:return`IF ${t.condition.expression}`;case H0.WHILE_LOOP:return`WHILE ${t.condition.expression}`;default:let r=t;return"Unknown statement"}}countResults(t){let r=0,n=0,i=0;for(let s of t)if(i++,s.success?r++:n++,s.children){let o=this.countResults(s.children);r+=o.passed,n+=o.failed,i+=o.total}return{passed:r,failed:n,total:i}}static toolDefinitions=[dpr.runTestFlowTool]};eE();var ZPe=class e{static _instance;_logLevel;constructor(){switch(process.env.LOG_LEVEL?.toUpperCase()){case"ERROR":this._logLevel=1;break;case"WARN":this._logLevel=2;break;case"INFO":this._logLevel=3;break;case"DEBUG":this._logLevel=4;break;default:this._logLevel=3}}static getInstance(){return e._instance||(e._instance=new e),e._instance}setLevel(t){this._logLevel=t}debug(...t){this._logLevel>=4&&console.error("[DEBUG]",...t)}info(...t){this._logLevel>=3&&console.error("[INFO]",...t)}warn(...t){this._logLevel>=2&&console.error("[WARN]",...t)}error(...t){this._logLevel>=1&&console.error("[ERROR]",...t)}log(...t){this.info(...t)}},Vpi=ZPe.getInstance(),rde=Vpi;import{createHash as jpi}from"node:crypto";import{hostname as Wpi,userInfo as Ypi,platform as ppr,arch as Jpi}from"node:os";import{PostHog as zpi}from"posthog-node";var Kpi="phc_5Va2dHamGwJWX9qZqyRka5Lann5ATsg3LL4uyU63qin",Xpi="https://us.i.posthog.com",Zpi=process.env.SHIPLIGHT_TELEMETRY==="0"||process.env.DO_NOT_TRACK==="1",eUe=null,hpr=!1;function efi(){return Zpi?null:(eUe||(eUe=new zpi(Kpi,{host:Xpi,flushAt:1,flushInterval:3e4})),eUe)}function tfi(){let e=`${Wpi()}:${Ypi().username}:${ppr()}:${Jpi()}`;return jpi("sha256").update(e).digest("hex")}function fpr(e,t){let r=efi();if(r){hpr||(rde.info("Anonymous usage telemetry enabled. Set SHIPLIGHT_TELEMETRY=0 to disable."),hpr=!0);try{r.capture({distinctId:tfi(),event:"mcp_new_session",properties:{product:"mcp-server",version:e,platform:ppr(),nodeVersion:process.version,cloudMode:t,$geoip_disable:!0,$ip:null}}),r.flush().catch(()=>{})}catch{}}}process.env.PWDEBUG="console";var mpr="shiplight://environments",tUe=class{constructor(t=!1){this.cloudMode=t;let r=process.env.API_BASE_URL||"https://api.shiplight.ai",n=VEe(process.env),i=process.env.TERMINATION_TIMEOUT?parseInt(process.env.TERMINATION_TIMEOUT,10):null;this.server=new rfi({name:"shiplight-mcp",version:"1.0.0"},{capabilities:{tools:{},resources:{},prompts:{}}});let s={};process.env.GOOGLE_API_KEY&&(s.GOOGLE_API_KEY=process.env.GOOGLE_API_KEY),process.env.ANTHROPIC_API_KEY&&(s.ANTHROPIC_API_KEY=process.env.ANTHROPIC_API_KEY),Object.keys(s).length>0&&hG({env:s}),this.sessionBackend=new jEe({runDir:void 0,terminationTimeout:i}),this.apiClient=this.cloudMode&&process.env.API_TOKEN?new WEe({apiBaseUrl:r,getApiToken:()=>process.env.API_TOKEN}):null,this.sessionTools=new Jce(this.sessionBackend,this.apiClient,n),this.browserTools=new zce(this.sessionBackend),this.debugTools=new Kce(this.sessionBackend),this.localTestTools=new tde,this.registry=new KEe,this.registry.registerAll(Jce,this.sessionTools).registerAll(zce,this.browserTools).registerAll(Kce,this.debugTools).registerAll(tde,this.localTestTools),this.cloudMode&&(this.testCaseTools=new Zce(this.apiClient),this.testResultTools=new ede(this.apiClient),this.dataTools=new Xce(this.apiClient),this.registry.registerAll(Zce,this.testCaseTools).registerAll(ede,this.testResultTools).registerAll(Xce,this.dataTools)),this.setupHandlers()}server;registry;sessionBackend;apiClient;sessionTools;browserTools;debugTools;testCaseTools=null;testResultTools=null;dataTools=null;localTestTools;setupHandlers(){let{tools:t,handleToolCall:r}=this.registry.build();this.server.setRequestHandler(sfi,async()=>({tools:t})),this.server.setRequestHandler(ifi,async n=>{try{let{name:i,arguments:s}=n.params,o=await r(i,s);return i==="new_session"&&fpr("0.1.22",this.cloudMode),{content:[{type:"text",text:o}]}}catch(i){let s=i instanceof Error?i.message:String(i);throw Apr.isAxiosError(i)&&i.response?.status===401&&(s="Authentication required. Contact info@shiplight.ai to get access to cloud services and advanced features."),new lG(uG.InternalError,s)}}),this.server.setRequestHandler(ofi,async()=>{let n=new Set(["shiplight://schemas/testflow-json-v1.2.0"]),i=this.cloudMode?Dj():Dj().filter(s=>!n.has(s.uri));return this.cloudMode&&i.push({uri:mpr,name:"Environments",description:"List testing environments (same data as the list_environments tool).",mimeType:"application/json"}),{resources:i}}),this.server.setRequestHandler(afi,async n=>{let{uri:i}=n.params;if(i===mpr){if(!this.apiClient)throw new lG(uG.InternalError,"The environments resource requires a Shiplight API token. Set API_TOKEN in your MCP server configuration.");try{let o=await this.apiClient.listEnvironments();return{contents:[{uri:i,mimeType:"application/json",text:JSON.stringify({environments:o.map(a=>({id:a.id,name:a.name,url:a.url}))},null,2)}]}}catch(o){let a=o instanceof Error?o.message:String(o);throw Apr.isAxiosError(o)&&o.response?.status===401&&(a="Authentication required. Provide API_TOKEN in the MCP server configuration to access cloud resources."),new lG(uG.InternalError,a)}}let s=await XEe(i);if(!s)throw new lG(uG.InvalidRequest,`Unknown resource: ${i}`);return{contents:[{uri:i,mimeType:"text/markdown",text:s}]}}),this.server.setRequestHandler(ufi,async()=>({prompts:zEe().map(i=>({name:i.name,description:i.description}))})),this.server.setRequestHandler(lfi,async n=>{let{name:i}=n.params,s=JEe(i);if(!s)throw new lG(uG.InvalidRequest,`Unknown prompt: ${i}`);return{messages:[{role:"user",content:{type:"text",text:s}}]}})}async run(){let t=new nfi;await this.server.connect(t),rde.info("Shiplight MCP Server running on stdio")}},cfi=process.argv.includes("--cloud"),dfi=new tUe(cfi);await dfi.run();
8120
8122
  /*! Bundled license information:
8121
8123
 
8122
8124
  node-domexception/index.js:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shiplightai/mcp",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
4
4
  "type": "module",
5
5
  "description": "Shiplight MCP server for AI-powered test automation",
6
6
  "main": "dist/index.js",
@@ -34,9 +34,9 @@
34
34
  "typescript": "5.5.4",
35
35
  "mcp-tools": "1.0.0",
36
36
  "sdk-internal": "0.1.1",
37
- "shiplight-types": "0.1.0",
37
+ "shiplight-tools": "1.0.0",
38
38
  "sdk-core": "0.1.0",
39
- "shiplight-tools": "1.0.0"
39
+ "shiplight-types": "0.1.0"
40
40
  },
41
41
  "engines": {
42
42
  "node": ">=22.0.0"