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 +202 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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?.(
|
|
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 (
|
|
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 (
|
|
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
|
|
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(
|
|
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.`
|