@roxybrowser/openapi 1.0.13-beta.7 → 1.0.13

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.
package/README.md CHANGED
@@ -177,8 +177,8 @@ This allows you to build custom UIs, scripts, or other MCP servers that reuse Ro
177
177
  - `roxy_list_labels` - Get browser labels/tags for organization
178
178
 
179
179
  ### Proxy
180
- - `roxy_list_proxies` - List proxy configurations in a workspace
181
- - `roxy_store_proxies` - Get list of proxies you've purchased (store)
180
+ - `roxy_list_proxies` - List proxy configurations in a workspace
181
+ - `roxy_store_proxies` - Get list of proxies you've purchased (store)
182
182
  - `roxy_create_proxy` - Create a proxy configuration
183
183
  - `roxy_batch_create_proxies` - Create multiple proxies in batch
184
184
  - `roxy_detect_proxy` - Detect/test proxy availability
@@ -254,8 +254,38 @@ npm run dev
254
254
 
255
255
  # Build for production
256
256
  npm run build
257
+
258
+ # Build and run the test suite
259
+ npm test
260
+
261
+ # Launch MCP Inspector against the local stdio server
262
+ ROXY_API_KEY=your-key npm run inspect
257
263
  ```
258
264
 
265
+ ### Test Strategy
266
+
267
+ The repository now includes a lightweight test setup based on Node's built-in test runner, so no extra test framework is required.
268
+
269
+ - `test/utils.test.mjs`: config resolution and HTTP request wrapper behavior
270
+ - `test/tools.test.mjs`: tool handler formatting and health-check output
271
+ - `test/server.test.mjs`: in-memory MCP integration using the official SDK client and transport
272
+
273
+ `npm test` builds `lib/` first, then runs the tests against the built artifacts. This keeps the test path close to what gets published.
274
+
275
+ ### MCP Inspector
276
+
277
+ Use the official MCP Inspector to debug tool discovery and tool calls locally:
278
+
279
+ ```bash
280
+ # Web UI
281
+ ROXY_API_KEY=your-key npm run inspect
282
+
283
+ # CLI mode
284
+ ROXY_API_KEY=your-key npm run inspect:cli -- --method tools/list
285
+ ```
286
+
287
+ Set `ROXY_API_HOST` as needed before launching Inspector.
288
+
259
289
  ## API Reference
260
290
 
261
291
  ### Configuration
package/lib/cli.cjs CHANGED
@@ -5,6 +5,11 @@ var commander = require('commander');
5
5
  var index_js = require('@modelcontextprotocol/sdk/server/index.js');
6
6
  var stdio_js = require('@modelcontextprotocol/sdk/server/stdio.js');
7
7
  var types_js = require('@modelcontextprotocol/sdk/types.js');
8
+ var dotenv = require('dotenv');
9
+
10
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
+
12
+ var dotenv__default = /*#__PURE__*/_interopDefault(dotenv);
8
13
 
9
14
  // src/types.ts
10
15
  var ConfigError = class extends Error {
@@ -2145,23 +2150,29 @@ var ListBrowsers = class {
2145
2150
  const pageSize = params.pageSize ?? 15;
2146
2151
  const totalPages = Math.max(1, Math.ceil((data.total || 0) / pageSize));
2147
2152
  const hasNextPage = currentPage < totalPages;
2148
- text = `Found ${data.total} browsers in workspace ${params.workspaceId}:
2149
-
2150
- ${data.rows.map((browser) => {
2151
- const serialNo = `${browser.workspaceName?.slice(0, 3).toLocaleUpperCase()}-${browser.windowSortNum}`;
2152
- return `**${browser.windowName || "Unnamed"}** (Serial No: ${serialNo})
2153
- - CoreVersion: ${browser.coreVersion} - DirId: ${browser.dirId}
2154
- - OSVersion: ${browser.osVersion}
2155
- - OS: ${browser.os}
2156
- - Remark: ${browser.windowRemark}`;
2157
- }).join("\n\n")}
2158
-
2159
- Pagination:
2160
- - currentPage: ${currentPage}
2161
- - pageSize: ${pageSize}
2162
- - totalPages: ${totalPages}
2163
- - hasNextPage: ${hasNextPage}
2164
- ${hasNextPage ? `- nextPageHint: Call roxy_list_browsers again with pageIndex=${currentPage + 1}` : "- nextPageHint: No more pages"}`;
2153
+ const readable = [];
2154
+ if (data.total > 0) {
2155
+ readable.push(`Found ${data.total} browsers in workspace ${params.workspaceId}:`);
2156
+ const browserList = data.rows.map((browser) => {
2157
+ const serialNo = `${browser.workspaceName?.slice(0, 3).toLocaleUpperCase()}-${browser.windowSortNum}`;
2158
+ const info = [
2159
+ `Profile Name: **${browser.windowName || "Unnamed"}** (SN: ${serialNo})`,
2160
+ ` - CoreVersion: ${browser.coreVersion}`,
2161
+ ` - OS: ${browser.os} ${browser.osVersion}`
2162
+ ];
2163
+ if (browser.windowRemark) {
2164
+ info.push(` - Remark: ${browser.windowRemark}`);
2165
+ }
2166
+ return info.join("\n");
2167
+ }).join("\n\n");
2168
+ readable.push(browserList);
2169
+ if (totalPages > 1) {
2170
+ readable.push(`Pagination: page=${currentPage}, totalPages=${totalPages}, hasNext=${hasNextPage}`);
2171
+ }
2172
+ } else {
2173
+ readable.push(`No browsers found in workspace ${params.workspaceId}.`);
2174
+ }
2175
+ text = readable.join("\n\n");
2165
2176
  }
2166
2177
  return {
2167
2178
  content: [
@@ -2794,26 +2805,10 @@ ${ws.project_details.map(
2794
2805
  };
2795
2806
  var HealthCheck = class {
2796
2807
  name = "roxy_health_check";
2797
- description = "Check if the target server is alive and healthy. This tool performs a health check to verify server connectivity and status.";
2808
+ description = "Check whether the RoxyBrowser server is running and reachable.";
2798
2809
  inputSchema = {
2799
2810
  type: "object",
2800
- properties: {
2801
- includeWorkspaceCheck: {
2802
- type: "boolean",
2803
- description: "Include workspace connectivity tests (optional, default: true)",
2804
- default: true
2805
- },
2806
- includeBrowserCheck: {
2807
- type: "boolean",
2808
- description: "Include browser availability checks (optional, default: true)",
2809
- default: true
2810
- },
2811
- verbose: {
2812
- type: "boolean",
2813
- description: "Include detailed diagnostic information (optional, default: false)",
2814
- default: false
2815
- }
2816
- }
2811
+ properties: {}
2817
2812
  };
2818
2813
  get schema() {
2819
2814
  return {
@@ -2822,126 +2817,19 @@ var HealthCheck = class {
2822
2817
  inputSchema: this.inputSchema
2823
2818
  };
2824
2819
  }
2825
- async handle(params) {
2826
- const { includeWorkspaceCheck = true, includeBrowserCheck = true, verbose = false } = params || {};
2827
- let healthStatus = "unknown";
2828
- let healthError = "";
2820
+ async handle(_params) {
2821
+ let text = "";
2829
2822
  try {
2830
- const healthResult = await request("/health", {
2823
+ const result = await request("/health", {
2831
2824
  method: "GET"
2832
2825
  });
2833
- if (healthResult.code === 0 || healthResult.code === void 0) {
2834
- healthStatus = "healthy";
2835
- } else {
2836
- healthStatus = "unhealthy";
2837
- healthError = healthResult.msg || "Health check failed";
2838
- }
2826
+ text = result.code === 0 ? "\u2705 **Server is healthy**\n\nThe RoxyBrowser server is running and reachable." : `\u274C **Server health check failed**
2827
+
2828
+ ${result.msg || "Unknown server response"}`;
2839
2829
  } catch (error) {
2840
- healthStatus = "unhealthy";
2841
- healthError = error.message || "Failed to connect to server";
2842
- }
2843
- let text = `## \u{1F50D} \u5065\u5EB7\u68C0\u67E5\u62A5\u544A / Health Check Report
2830
+ text = `\u274C **Server is unavailable**
2844
2831
 
2845
- `;
2846
- text += `### \u{1F310} \u670D\u52A1\u5668\u72B6\u6001 / Server Status
2847
- `;
2848
- text += `- **\u670D\u52A1\u5668\u8FDE\u63A5 / Server Connection**: ${healthStatus === "healthy" ? "\u2705 \u6B63\u5E38" : "\u274C \u5F02\u5E38"}
2849
- `;
2850
- if (healthStatus !== "healthy" && healthError) {
2851
- text += `- **\u9519\u8BEF\u4FE1\u606F / Error**: ${healthError}
2852
- `;
2853
- }
2854
- if (includeWorkspaceCheck && healthStatus === "healthy") {
2855
- try {
2856
- const workspaceResult = await request("/browser/workspace?page_index=1&page_size=5", {
2857
- method: "GET"
2858
- });
2859
- if (workspaceResult.code === 0) {
2860
- const workspaces = workspaceResult.data;
2861
- text += `
2862
- ### \u{1F4C1} \u5DE5\u4F5C\u533A\u4FE1\u606F / Workspace Information
2863
- `;
2864
- text += `- **\u53EF\u7528\u5DE5\u4F5C\u533A / Available Workspaces**: ${workspaces.total}
2865
- `;
2866
- if (workspaces.rows && workspaces.rows.length > 0) {
2867
- text += `- **\u5DE5\u4F5C\u533A\u8BE6\u60C5 / Workspace Details**:
2868
- `;
2869
- workspaces.rows.slice(0, 3).forEach((ws) => {
2870
- const projectCount = ws.project_details?.length || 0;
2871
- text += ` - ${ws.workspaceName} (ID: ${ws.id}) - ${projectCount} projects
2872
- `;
2873
- });
2874
- if (workspaces.total > 3) {
2875
- text += ` - ... and ${workspaces.total - 3} more
2876
- `;
2877
- }
2878
- }
2879
- } else {
2880
- text += `
2881
- ### \u{1F4C1} \u5DE5\u4F5C\u533A\u4FE1\u606F / Workspace Information
2882
- `;
2883
- text += `- **\u72B6\u6001**: \u26A0\uFE0F \u65E0\u6CD5\u83B7\u53D6\u5DE5\u4F5C\u533A\u4FE1\u606F
2884
- `;
2885
- text += `- **\u9519\u8BEF**: ${workspaceResult.msg}
2886
- `;
2887
- }
2888
- } catch (error) {
2889
- text += `
2890
- ### \u{1F4C1} \u5DE5\u4F5C\u533A\u4FE1\u606F / Workspace Information
2891
- `;
2892
- text += `- **\u72B6\u6001**: \u274C \u65E0\u6CD5\u83B7\u53D6\u5DE5\u4F5C\u533A\u4FE1\u606F
2893
- `;
2894
- text += `- **\u9519\u8BEF**: ${error.message || "Unknown error"}
2895
- `;
2896
- }
2897
- }
2898
- if (includeBrowserCheck && healthStatus === "healthy") {
2899
- try {
2900
- const workspaceResult = await request("/browser/workspace?page_index=1&page_size=1", {
2901
- method: "GET"
2902
- });
2903
- if (workspaceResult.code === 0 && workspaceResult.data.rows && workspaceResult.data.rows.length > 0) {
2904
- const firstWorkspace = workspaceResult.data.rows[0];
2905
- const browserResult = await request(`/browser/list_v3?workspaceId=${firstWorkspace.id}&page_index=1&page_size=5`, {
2906
- method: "GET"
2907
- });
2908
- if (browserResult.code === 0) {
2909
- const browsers = browserResult.data;
2910
- text += `
2911
- ### \u{1F310} \u6D4F\u89C8\u5668\u4FE1\u606F / Browser Information
2912
- `;
2913
- text += `- **\u5DE5\u4F5C\u533A / Workspace**: ${firstWorkspace.workspaceName} (ID: ${firstWorkspace.id})
2914
- `;
2915
- text += `- **\u6D4F\u89C8\u5668\u603B\u6570 / Total Browsers**: ${browsers.total}
2916
- `;
2917
- if (browsers.rows && browsers.rows.length > 0) {
2918
- text += `- **\u6D4F\u89C8\u5668\u793A\u4F8B / Browser Examples**:
2919
- `;
2920
- browsers.rows.slice(0, 3).forEach((browser) => {
2921
- text += ` - ${browser.windowName || "Unnamed"} (ID: ${browser.dirId}) - ${browser.status}
2922
- `;
2923
- });
2924
- }
2925
- }
2926
- }
2927
- } catch (error) {
2928
- text += `
2929
- ### \u{1F310} \u6D4F\u89C8\u5668\u4FE1\u606F / Browser Information
2930
- `;
2931
- text += `- **\u72B6\u6001**: \u26A0\uFE0F \u65E0\u6CD5\u83B7\u53D6\u6D4F\u89C8\u5668\u4FE1\u606F
2932
- `;
2933
- text += `- **\u9519\u8BEF**: ${error.message || "Unknown error"}
2934
- `;
2935
- }
2936
- }
2937
- if (verbose && healthStatus === "healthy") {
2938
- text += `
2939
- ### \u{1F4CA} \u8BE6\u7EC6\u4FE1\u606F / Detailed Information
2940
- `;
2941
- text += `- **\u5065\u5EB7\u68C0\u67E5\u65F6\u95F4 / Check Time**: ${(/* @__PURE__ */ new Date()).toISOString()}
2942
- `;
2943
- text += `- **\u68C0\u67E5\u6A21\u5F0F / Check Mode**: ${includeWorkspaceCheck ? "Workspace + " : ""}${includeBrowserCheck ? "Browser" : ""}
2944
- `;
2832
+ ${error?.message || "Failed to connect to the server"}`;
2945
2833
  }
2946
2834
  return {
2947
2835
  content: [
@@ -3101,8 +2989,7 @@ async function runServer() {
3101
2989
  const server = new RoxyBrowserMCPServer();
3102
2990
  await server.run();
3103
2991
  }
3104
-
3105
- // src/cli.ts
2992
+ dotenv__default.default.config();
3106
2993
  var PKG_VERSION = "1.0.9";
3107
2994
  var program = new commander.Command();
3108
2995
  program.name("roxy-browser-mcp").description("RoxyBrowser MCP Server - Model Context Protocol server for RoxyBrowser automation").version(PKG_VERSION, "-V, --version", "Show version").option(