viyv-browser-mcp 0.6.5 → 0.6.6

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/dist/index.js CHANGED
@@ -124,7 +124,9 @@ function createMessageReader(stream, onMessage, onError, onClose) {
124
124
  expectedLength = buffer.readUInt32LE(0);
125
125
  buffer = buffer.subarray(4);
126
126
  if (expectedLength > MAX_INBOUND_SIZE) {
127
- onError?.(new Error(`Message too large: ${expectedLength} bytes (max ${MAX_INBOUND_SIZE})`));
127
+ onError?.(
128
+ new Error(`Message too large: ${expectedLength} bytes (max ${MAX_INBOUND_SIZE})`)
129
+ );
128
130
  expectedLength = null;
129
131
  buffer = Buffer.alloc(0);
130
132
  break;
@@ -770,7 +772,7 @@ function startBridge(options) {
770
772
 
771
773
  // src/server.ts
772
774
  import { randomUUID as randomUUID3 } from "crypto";
773
- import { existsSync, mkdirSync, statSync, writeFileSync } from "fs";
775
+ import { existsSync, mkdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "fs";
774
776
  import http from "http";
775
777
  import { createConnection as createConnection2 } from "net";
776
778
  import { dirname } from "path";
@@ -2784,7 +2786,7 @@ var SHEETS_WRITE_RETURNS = `{
2784
2786
  cleared?: boolean // clear mode
2785
2787
  range: string
2786
2788
  }`;
2787
- var SHEETS_WRITE_RELATED = ["sheets_read", "sheets_navigate"];
2789
+ var SHEETS_WRITE_RELATED = ["sheets_read", "sheets_navigate", "sheets_import"];
2788
2790
  var SHEETS_INFO_DESCRIPTION = `Get spreadsheet metadata.
2789
2791
  Returns: { title, sheets[], active_cell, url }`;
2790
2792
  var SHEETS_INFO_DETAIL = `Get metadata about the active Google Sheets spreadsheet.
@@ -2813,6 +2815,35 @@ var SHEETS_NAVIGATE_RETURNS = `{
2813
2815
  active_sheet: string
2814
2816
  }`;
2815
2817
  var SHEETS_NAVIGATE_RELATED = ["sheets_read", "sheets_write", "sheets_info"];
2818
+ var SHEETS_CREATE_DESCRIPTION = `Create a new sheet tab in the spreadsheet.
2819
+ Returns: { success, sheet_name }`;
2820
+ var SHEETS_CREATE_DETAIL = `Create a new sheet tab with the specified name.
2821
+
2822
+ Clicks the "+" button to add a sheet, then renames it. Returns an error if a sheet with the same name already exists.
2823
+ The tab must be on a Google Sheets page.`;
2824
+ var SHEETS_CREATE_RETURNS = `{
2825
+ success: boolean
2826
+ sheet_name: string
2827
+ }`;
2828
+ var SHEETS_CREATE_RELATED = ["sheets_info", "sheets_navigate", "sheets_import"];
2829
+ var SHEETS_IMPORT_DESCRIPTION = `Bulk import CSV/TSV data into spreadsheet via clipboard paste (~100x faster than sheets_write).
2830
+ Returns: { success, rows, cols, range }`;
2831
+ var SHEETS_IMPORT_DETAIL = `Import data from a local CSV/TSV file or inline text into the spreadsheet.
2832
+
2833
+ Uses clipboard paste for bulk insertion \u2014 thousands of cells in seconds vs. cell-by-cell keyboard input.
2834
+ Provide either "file_path" (local CSV/TSV file) or "data" (inline CSV/TSV string), not both.
2835
+
2836
+ Separator detection: "auto" checks first line for tabs (TSV) vs commas (CSV).
2837
+ Use "clear_sheet" to wipe the sheet before importing.
2838
+ The tab must be on a Google Sheets page.`;
2839
+ var SHEETS_IMPORT_RETURNS = `{
2840
+ success: boolean
2841
+ rows: number
2842
+ cols: number
2843
+ range: string // e.g. "A1:D100"
2844
+ sheet: string
2845
+ }`;
2846
+ var SHEETS_IMPORT_RELATED = ["sheets_write", "sheets_read", "sheets_create"];
2816
2847
 
2817
2848
  // src/tools/tabs/select-tab.ts
2818
2849
  var SELECT_TAB_DESCRIPTION = `Switch to a tab.
@@ -2959,20 +2990,6 @@ var BROWSER_HEALTH_RETURNS = `{
2959
2990
  }`;
2960
2991
  var BROWSER_HEALTH_RELATED = ["tabs_context", "switch_browser"];
2961
2992
 
2962
- // src/tools/viyv/page-data-extract.ts
2963
- var PAGE_DATA_EXTRACT_DESCRIPTION = `Extract structured data via CSS selector schema.
2964
- Returns: { extracted }`;
2965
- var PAGE_DATA_EXTRACT_DETAIL = `Extract structured data from the current page.
2966
- Parses the page content according to a provided schema object,
2967
- returning well-formed JSON. Supports extracting tables, lists,
2968
- key-value pairs, and other structured patterns.
2969
- The schema defines CSS selectors for each field to extract.`;
2970
- var PAGE_DATA_EXTRACT_RETURNS = `{
2971
- extracted: Record<string, unknown>
2972
- error?: string
2973
- }`;
2974
- var PAGE_DATA_EXTRACT_RELATED = ["get_page_text", "javascript_exec", "sm_fetch"];
2975
-
2976
2993
  // src/tools/viyv/feedback.ts
2977
2994
  var FEEDBACK_DESCRIPTION = `Visual feedback/review system. Actions: list, get, add, reply, resolve, reopen, complete.
2978
2995
  Pin comments to page elements for human-AI review workflow.`;
@@ -3004,6 +3021,20 @@ var FEEDBACK_RETURNS = `{
3004
3021
  }`;
3005
3022
  var FEEDBACK_RELATED = ["read_page", "screenshot", "find"];
3006
3023
 
3024
+ // src/tools/viyv/page-data-extract.ts
3025
+ var PAGE_DATA_EXTRACT_DESCRIPTION = `Extract structured data via CSS selector schema.
3026
+ Returns: { extracted }`;
3027
+ var PAGE_DATA_EXTRACT_DETAIL = `Extract structured data from the current page.
3028
+ Parses the page content according to a provided schema object,
3029
+ returning well-formed JSON. Supports extracting tables, lists,
3030
+ key-value pairs, and other structured patterns.
3031
+ The schema defines CSS selectors for each field to extract.`;
3032
+ var PAGE_DATA_EXTRACT_RETURNS = `{
3033
+ extracted: Record<string, unknown>
3034
+ error?: string
3035
+ }`;
3036
+ var PAGE_DATA_EXTRACT_RELATED = ["get_page_text", "javascript_exec", "sm_fetch"];
3037
+
3007
3038
  // src/tools/index.ts
3008
3039
  var navigateTool = {
3009
3040
  name: "navigate",
@@ -4357,6 +4388,35 @@ var sheetsNavigateTool = {
4357
4388
  sheet: z.string().optional().describe("Sheet name to switch to")
4358
4389
  })
4359
4390
  };
4391
+ var sheetsCreateTool = {
4392
+ name: "sheets_create",
4393
+ description: SHEETS_CREATE_DESCRIPTION,
4394
+ detail: SHEETS_CREATE_DETAIL,
4395
+ returns: SHEETS_CREATE_RETURNS,
4396
+ category: "sheets",
4397
+ related: SHEETS_CREATE_RELATED,
4398
+ inputSchema: z.object({
4399
+ tabId: z.coerce.number().describe("Tab ID of the Google Sheets page"),
4400
+ name: z.string().describe("Name for the new sheet tab")
4401
+ })
4402
+ };
4403
+ var sheetsImportTool = {
4404
+ name: "sheets_import",
4405
+ description: SHEETS_IMPORT_DESCRIPTION,
4406
+ detail: SHEETS_IMPORT_DETAIL,
4407
+ returns: SHEETS_IMPORT_RETURNS,
4408
+ category: "sheets",
4409
+ related: SHEETS_IMPORT_RELATED,
4410
+ inputSchema: z.object({
4411
+ tabId: z.coerce.number().describe("Tab ID of the Google Sheets page"),
4412
+ file_path: z.string().optional().describe("Path to local CSV/TSV file (exclusive with data)"),
4413
+ data: z.string().optional().describe("Inline CSV/TSV data string (exclusive with file_path)"),
4414
+ cell: z.string().optional().describe('Start cell for import (default: "A1")'),
4415
+ sheet: z.string().optional().describe("Target sheet tab name"),
4416
+ separator: z.enum(["comma", "tab", "auto"]).optional().describe('Field separator (default: "auto")'),
4417
+ clear_sheet: z.boolean().optional().describe("Clear entire sheet before importing (default: false)")
4418
+ })
4419
+ };
4360
4420
  var toolGuideTool = {
4361
4421
  name: "tool_guide",
4362
4422
  description: `Get detailed documentation for any tool or topic.
@@ -4423,7 +4483,7 @@ var allTools = [
4423
4483
  shortcutsExecuteTool,
4424
4484
  switchBrowserTool,
4425
4485
  savePdfTool,
4426
- // viyv Integration (7)
4486
+ // viyv Integration (8)
4427
4487
  agentTabAssignTool,
4428
4488
  agentTabListTool,
4429
4489
  browserEventSubscribeTool,
@@ -4477,11 +4537,13 @@ var allTools = [
4477
4537
  smCustomViewDeleteTool,
4478
4538
  smCustomViewListTool,
4479
4539
  smCustomViewGetTool,
4480
- // Google Sheets (4)
4540
+ // Google Sheets (6)
4481
4541
  sheetsReadTool,
4482
4542
  sheetsWriteTool,
4483
4543
  sheetsInfoTool,
4484
4544
  sheetsNavigateTool,
4545
+ sheetsCreateTool,
4546
+ sheetsImportTool,
4485
4547
  // Meta (1)
4486
4548
  toolGuideTool
4487
4549
  ];
@@ -4494,6 +4556,7 @@ var FILE_EXPORT_TOOLS = /* @__PURE__ */ new Set([
4494
4556
  "batch_fetch"
4495
4557
  ]);
4496
4558
  var RESULT_EXPORT_TOOLS = /* @__PURE__ */ new Set(["javascript_exec", "read_network_requests"]);
4559
+ var FILE_IMPORT_TOOLS = /* @__PURE__ */ new Set(["sheets_import"]);
4497
4560
  function computeToolTimeout(tool, input) {
4498
4561
  if (tool === "wait_for" && typeof input.timeout === "number") {
4499
4562
  return input.timeout + 5e3;
@@ -4509,6 +4572,9 @@ function computeToolTimeout(tool, input) {
4509
4572
  if (FILE_EXPORT_TOOLS.has(tool)) {
4510
4573
  return 3e5;
4511
4574
  }
4575
+ if (tool === "sheets_import") {
4576
+ return 3e5;
4577
+ }
4512
4578
  if (tool === "sm_scenario_run" && input.wait_for_completion === true) {
4513
4579
  const maxDuration = typeof input.max_duration_ms === "number" ? input.max_duration_ms : TIMEOUTS.SCENARIO_MAX_DURATION;
4514
4580
  return maxDuration + TIMEOUTS.MCP_SCENARIO_BUFFER;
@@ -5167,14 +5233,68 @@ function buildResponseContent(tool, result) {
5167
5233
  if (pauseNote) cleanResult._pause_note = pauseNote;
5168
5234
  if (tool === "screenshot" && typeof cleanResult.data === "string") {
5169
5235
  const { data, format, ...metadata } = cleanResult;
5170
- const mimeType = format === "png" ? "image/png" : format === "webp" ? "image/webp" : "image/jpeg";
5236
+ const fmt = format || "webp";
5237
+ const mimeType = fmt === "png" ? "image/png" : fmt === "webp" ? "image/webp" : "image/jpeg";
5171
5238
  return [
5172
5239
  { type: "image", data, mimeType },
5173
- { type: "text", text: JSON.stringify(metadata) }
5240
+ { type: "text", text: JSON.stringify({ ...metadata, format: fmt }) }
5174
5241
  ];
5175
5242
  }
5176
5243
  return [{ type: "text", text: JSON.stringify(cleanResult) }];
5177
5244
  }
5245
+ function csvToTsv(csvData, delimiter) {
5246
+ const rows = [];
5247
+ let i = 0;
5248
+ const len = csvData.length;
5249
+ while (i < len) {
5250
+ const row = [];
5251
+ while (i < len) {
5252
+ if (csvData[i] === '"') {
5253
+ i++;
5254
+ let field = "";
5255
+ while (i < len) {
5256
+ if (csvData[i] === '"') {
5257
+ if (i + 1 < len && csvData[i + 1] === '"') {
5258
+ field += '"';
5259
+ i += 2;
5260
+ } else {
5261
+ i++;
5262
+ break;
5263
+ }
5264
+ } else {
5265
+ field += csvData[i];
5266
+ i++;
5267
+ }
5268
+ }
5269
+ row.push(field);
5270
+ } else {
5271
+ let field = "";
5272
+ while (i < len && csvData[i] !== delimiter && csvData[i] !== "\n" && csvData[i] !== "\r") {
5273
+ field += csvData[i];
5274
+ i++;
5275
+ }
5276
+ row.push(field);
5277
+ }
5278
+ if (i < len && csvData[i] === delimiter) {
5279
+ i++;
5280
+ } else {
5281
+ break;
5282
+ }
5283
+ }
5284
+ if (i < len && csvData[i] === "\r") i++;
5285
+ if (i < len && csvData[i] === "\n") i++;
5286
+ rows.push(row);
5287
+ }
5288
+ while (rows.length > 0 && rows[rows.length - 1].every((c) => c === "")) rows.pop();
5289
+ return rows.map(
5290
+ (row) => row.map((cell) => {
5291
+ if (cell.includes(" ") || cell.includes("\n") || cell.includes('"')) {
5292
+ return `"${cell.replace(/"/g, '""')}"`;
5293
+ }
5294
+ return cell;
5295
+ }).join(" ")
5296
+ ).join("\n");
5297
+ }
5178
5298
  async function callExtensionTool(tool, input) {
5179
5299
  if (tool === "tool_guide") {
5180
5300
  const result = handleToolGuide(allTools, input);
@@ -5225,6 +5345,49 @@ async function callExtensionTool(tool, input) {
5225
5345
  }
5226
5346
  }
5227
5347
  }
5348
+ const tempImportFile = void 0;
5349
+ if (FILE_IMPORT_TOOLS.has(tool)) {
5350
+ let csvData;
5351
+ if (typeof input.file_path === "string") {
5352
+ const fp = input.file_path;
5353
+ if (!existsSync(fp) || !statSync(fp).isFile()) {
5354
+ return {
5355
+ content: [
5356
+ {
5357
+ type: "text",
5358
+ text: JSON.stringify({
5359
+ error: { code: "FILE_NOT_FOUND", message: `File not found: ${fp}` }
5360
+ })
5361
+ }
5362
+ ]
5363
+ };
5364
+ }
5365
+ csvData = readFileSync(fp, "utf-8");
5366
+ } else if (typeof input.data === "string") {
5367
+ csvData = input.data;
5368
+ }
5369
+ if (csvData) {
5370
+ try {
5371
+ const firstLine = csvData.split("\n")[0] ?? "";
5372
+ const delimiter = firstLine.includes(" ") ? " " : ",";
5373
+ const tsv = csvToTsv(csvData, delimiter);
5374
+ const { data: _d, file_path: _f, separator: _s, ...rest } = input;
5375
+ input = { ...rest, _tsv: tsv };
5376
+ } catch (e) {
5377
+ const msg = e instanceof Error ? e.message : String(e);
5378
+ return {
5379
+ content: [
5380
+ {
5381
+ type: "text",
5382
+ text: JSON.stringify({
5383
+ error: { code: "CLIPBOARD_ERROR", message: `Failed to prepare import: ${msg}` }
5384
+ })
5385
+ }
5386
+ ]
5387
+ };
5388
+ }
5389
+ }
5390
+ }
5228
5391
  if (!extensionSocket || extensionSocket.destroyed) {
5229
5392
  return {
5230
5393
  content: [
@@ -5287,6 +5450,10 @@ async function callExtensionTool(tool, input) {
5287
5450
  });
5288
5451
  }, toolTimeout);
5289
5452
  const { chromeProfile, file_path: filePath, ...cleanInput } = input;
5453
+ if (FILE_IMPORT_TOOLS.has(tool) && typeof filePath === "string") {
5454
+ ;
5455
+ cleanInput.file_path = filePath;
5456
+ }
5290
5457
  pendingRequests.set(requestId, {
5291
5458
  resolve: (result) => {
5292
5459
  removeErrorListener();
@@ -5357,10 +5524,22 @@ async function callExtensionTool(tool, input) {
5357
5524
  } catch {
5358
5525
  }
5359
5526
  }
5527
+ if (tempImportFile) {
5528
+ try {
5529
+ unlinkSync(tempImportFile);
5530
+ } catch {
5531
+ }
5532
+ }
5360
5533
  resolve2({ content: buildResponseContent(tool, finalResult) });
5361
5534
  },
5362
5535
  reject: (error) => {
5363
5536
  removeErrorListener();
5537
+ if (tempImportFile) {
5538
+ try {
5539
+ unlinkSync(tempImportFile);
5540
+ } catch {
5541
+ }
5542
+ }
5364
5543
  resolve2({
5365
5544
  content: [
5366
5545
  {
@@ -5452,7 +5631,7 @@ async function handleSwitchBrowser() {
5452
5631
 
5453
5632
  // src/setup.ts
5454
5633
  import { execSync } from "child_process";
5455
- import { chmodSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, writeFileSync as writeFileSync2 } from "fs";
5634
+ import { chmodSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
5456
5635
  import { homedir, platform } from "os";
5457
5636
  import { dirname as dirname2, resolve } from "path";
5458
5637
  function runSetup(options = {}) {
@@ -5565,7 +5744,7 @@ function setupClaudeDesktopConfig() {
5565
5744
  let config = {};
5566
5745
  if (existsSync2(configPath)) {
5567
5746
  try {
5568
- config = JSON.parse(readFileSync(configPath, "utf-8"));
5747
+ config = JSON.parse(readFileSync2(configPath, "utf-8"));
5569
5748
  } catch {
5570
5749
  console.error(
5571
5750
  `ERROR: Failed to parse ${configPath}. Fix the JSON manually or delete the file.`