@jobshimo/browser-link 0.0.1
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/LICENSE +21 -0
- package/README.md +83 -0
- package/dist/auth/allowlist.d.ts +2 -0
- package/dist/auth/allowlist.js +53 -0
- package/dist/auth/allowlist.js.map +1 -0
- package/dist/auth/process-identity.d.ts +19 -0
- package/dist/auth/process-identity.js +106 -0
- package/dist/auth/process-identity.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +116 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/about.d.ts +23 -0
- package/dist/commands/about.js +248 -0
- package/dist/commands/about.js.map +1 -0
- package/dist/commands/doctor.d.ts +29 -0
- package/dist/commands/doctor.js +110 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/extension.d.ts +12 -0
- package/dist/commands/extension.js +76 -0
- package/dist/commands/extension.js.map +1 -0
- package/dist/commands/install.d.ts +19 -0
- package/dist/commands/install.js +52 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/menu.d.ts +26 -0
- package/dist/commands/menu.js +187 -0
- package/dist/commands/menu.js.map +1 -0
- package/dist/commands/tty.d.ts +51 -0
- package/dist/commands/tty.js +148 -0
- package/dist/commands/tty.js.map +1 -0
- package/dist/commands/uninstall.d.ts +8 -0
- package/dist/commands/uninstall.js +10 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/welcome.d.ts +33 -0
- package/dist/commands/welcome.js +177 -0
- package/dist/commands/welcome.js.map +1 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.js +35 -0
- package/dist/config.js.map +1 -0
- package/dist/entry-info.d.ts +12 -0
- package/dist/entry-info.js +15 -0
- package/dist/entry-info.js.map +1 -0
- package/dist/extension/background.d.ts +1 -0
- package/dist/extension/background.js +490 -0
- package/dist/extension/background.js.map +1 -0
- package/dist/extension/icons/icon-128.png +0 -0
- package/dist/extension/icons/icon-16.png +0 -0
- package/dist/extension/icons/icon-32.png +0 -0
- package/dist/extension/icons/icon-48.png +0 -0
- package/dist/extension/icons/icon.svg +14 -0
- package/dist/extension/manifest.json +28 -0
- package/dist/extension/popup.d.ts +13 -0
- package/dist/extension/popup.html +88 -0
- package/dist/extension/popup.js +78 -0
- package/dist/extension/popup.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/installers/claude.d.ts +2 -0
- package/dist/installers/claude.js +77 -0
- package/dist/installers/claude.js.map +1 -0
- package/dist/installers/index.d.ts +9 -0
- package/dist/installers/index.js +14 -0
- package/dist/installers/index.js.map +1 -0
- package/dist/installers/opencode.d.ts +2 -0
- package/dist/installers/opencode.js +39 -0
- package/dist/installers/opencode.js.map +1 -0
- package/dist/installers/types.d.ts +30 -0
- package/dist/installers/types.js +2 -0
- package/dist/installers/types.js.map +1 -0
- package/dist/map/db.d.ts +3 -0
- package/dist/map/db.js +81 -0
- package/dist/map/db.js.map +1 -0
- package/dist/map/paths.d.ts +12 -0
- package/dist/map/paths.js +22 -0
- package/dist/map/paths.js.map +1 -0
- package/dist/map/queries.d.ts +72 -0
- package/dist/map/queries.js +162 -0
- package/dist/map/queries.js.map +1 -0
- package/dist/map/tools.d.ts +8 -0
- package/dist/map/tools.js +143 -0
- package/dist/map/tools.js.map +1 -0
- package/dist/messages.d.ts +42 -0
- package/dist/messages.js +9 -0
- package/dist/messages.js.map +1 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.js +217 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/browser-definitions.d.ts +7 -0
- package/dist/tools/browser-definitions.js +127 -0
- package/dist/tools/browser-definitions.js.map +1 -0
- package/dist/tools/browser-dispatch.d.ts +20 -0
- package/dist/tools/browser-dispatch.js +75 -0
- package/dist/tools/browser-dispatch.js.map +1 -0
- package/dist/tools/responses.d.ts +19 -0
- package/dist/tools/responses.js +27 -0
- package/dist/tools/responses.js.map +1 -0
- package/dist/tools/server-instructions.d.ts +3 -0
- package/dist/tools/server-instructions.js +50 -0
- package/dist/tools/server-instructions.js.map +1 -0
- package/dist/tools/types.d.ts +6 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/utils/open-url.d.ts +4 -0
- package/dist/utils/open-url.js +37 -0
- package/dist/utils/open-url.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tool definitions for the browser-bridge family. Kept separate from
|
|
3
|
+
* the runtime dispatcher so the JSON schemas stay reviewable in one place
|
|
4
|
+
* and the dispatch logic stays small.
|
|
5
|
+
*/
|
|
6
|
+
export const BROWSER_TOOL_DEFINITIONS = [
|
|
7
|
+
{
|
|
8
|
+
name: 'browser.list_tabs',
|
|
9
|
+
description: 'List Chrome tabs currently connected to browser-link. A tab is connected only after the user clicks Conectar in the extension popup. Returns tab_id, url and title for each.',
|
|
10
|
+
inputSchema: { type: 'object', properties: {}, additionalProperties: false },
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: 'browser.ping',
|
|
14
|
+
description: 'Verify the bridge to a tab. Returns its current title and url.',
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: { tab_id: { type: 'string' } },
|
|
18
|
+
required: ['tab_id'],
|
|
19
|
+
additionalProperties: false,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'browser.navigate',
|
|
24
|
+
description: 'Navigate the connected tab to a URL. By default waits for the load event.',
|
|
25
|
+
inputSchema: {
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties: {
|
|
28
|
+
tab_id: { type: 'string' },
|
|
29
|
+
url: { type: 'string', description: 'Full URL including protocol.' },
|
|
30
|
+
wait_for_load: { type: 'boolean', default: true },
|
|
31
|
+
},
|
|
32
|
+
required: ['tab_id', 'url'],
|
|
33
|
+
additionalProperties: false,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'browser.snapshot',
|
|
38
|
+
description: 'Snapshot of the tab: title, url, visible text (truncated) and a list of interactive elements (buttons, links, inputs, selects, textareas) with a CSS selector and labels. Use this to understand page state before clicking or typing.',
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: { tab_id: { type: 'string' } },
|
|
42
|
+
required: ['tab_id'],
|
|
43
|
+
additionalProperties: false,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'browser.console',
|
|
48
|
+
description: 'Return recent console messages (log, info, warn, error) from the connected tab since it was attached. Rolling buffer (last 200).',
|
|
49
|
+
inputSchema: {
|
|
50
|
+
type: 'object',
|
|
51
|
+
properties: {
|
|
52
|
+
tab_id: { type: 'string' },
|
|
53
|
+
level: { type: 'string', enum: ['log', 'info', 'warn', 'error', 'debug'] },
|
|
54
|
+
},
|
|
55
|
+
required: ['tab_id'],
|
|
56
|
+
additionalProperties: false,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'browser.network',
|
|
61
|
+
description: 'Return recent network requests from the connected tab (rolling buffer, last 200). Includes request_id, method, url, status, mime, size, timing. Use browser.network_body to fetch the body of a specific request.',
|
|
62
|
+
inputSchema: {
|
|
63
|
+
type: 'object',
|
|
64
|
+
properties: {
|
|
65
|
+
tab_id: { type: 'string' },
|
|
66
|
+
url_filter: { type: 'string', description: 'Optional substring to filter request URLs.' },
|
|
67
|
+
},
|
|
68
|
+
required: ['tab_id'],
|
|
69
|
+
additionalProperties: false,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: 'browser.network_body',
|
|
74
|
+
description: 'Fetch the response body of a single network request by request_id (from browser.network).',
|
|
75
|
+
inputSchema: {
|
|
76
|
+
type: 'object',
|
|
77
|
+
properties: {
|
|
78
|
+
tab_id: { type: 'string' },
|
|
79
|
+
request_id: { type: 'string' },
|
|
80
|
+
},
|
|
81
|
+
required: ['tab_id', 'request_id'],
|
|
82
|
+
additionalProperties: false,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: 'browser.click',
|
|
87
|
+
description: 'Click an element by CSS selector in the connected tab. The selector usually comes from browser.snapshot.',
|
|
88
|
+
inputSchema: {
|
|
89
|
+
type: 'object',
|
|
90
|
+
properties: {
|
|
91
|
+
tab_id: { type: 'string' },
|
|
92
|
+
selector: { type: 'string' },
|
|
93
|
+
},
|
|
94
|
+
required: ['tab_id', 'selector'],
|
|
95
|
+
additionalProperties: false,
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: 'browser.type',
|
|
100
|
+
description: 'Focus an input by CSS selector and type text into it. If clear=true, clears the current value first.',
|
|
101
|
+
inputSchema: {
|
|
102
|
+
type: 'object',
|
|
103
|
+
properties: {
|
|
104
|
+
tab_id: { type: 'string' },
|
|
105
|
+
selector: { type: 'string' },
|
|
106
|
+
text: { type: 'string' },
|
|
107
|
+
clear: { type: 'boolean', default: false },
|
|
108
|
+
},
|
|
109
|
+
required: ['tab_id', 'selector', 'text'],
|
|
110
|
+
additionalProperties: false,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: 'browser.evaluate',
|
|
115
|
+
description: 'Run a JavaScript expression in the page context and return its result. Use an IIFE with return if you need multi-step logic.',
|
|
116
|
+
inputSchema: {
|
|
117
|
+
type: 'object',
|
|
118
|
+
properties: {
|
|
119
|
+
tab_id: { type: 'string' },
|
|
120
|
+
expression: { type: 'string' },
|
|
121
|
+
},
|
|
122
|
+
required: ['tab_id', 'expression'],
|
|
123
|
+
additionalProperties: false,
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
];
|
|
127
|
+
//# sourceMappingURL=browser-definitions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-definitions.js","sourceRoot":"","sources":["../../src/tools/browser-definitions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dispatcher for browser.* tools. Mirrors the shape of the map dispatcher
|
|
3
|
+
* (`handleMapTool` / `isMapTool`) so both families look the same from
|
|
4
|
+
* runServer().
|
|
5
|
+
*
|
|
6
|
+
* The handlers do not own state: they receive a `BrowserToolDeps` object
|
|
7
|
+
* with the live tab map and the call function. This makes the dispatcher
|
|
8
|
+
* unit-testable with a fake `callBrowserTool`.
|
|
9
|
+
*/
|
|
10
|
+
export interface TabSnapshot {
|
|
11
|
+
tab_id: string;
|
|
12
|
+
url: string;
|
|
13
|
+
title: string;
|
|
14
|
+
}
|
|
15
|
+
export interface BrowserToolDeps {
|
|
16
|
+
listTabs(): TabSnapshot[];
|
|
17
|
+
callBrowserTool(tabId: string, tool: string, params: unknown, timeoutMs?: number): Promise<unknown>;
|
|
18
|
+
}
|
|
19
|
+
export declare function isBrowserTool(name: string): boolean;
|
|
20
|
+
export declare function handleBrowserTool(name: string, args: unknown, deps: BrowserToolDeps): Promise<unknown>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dispatcher for browser.* tools. Mirrors the shape of the map dispatcher
|
|
3
|
+
* (`handleMapTool` / `isMapTool`) so both families look the same from
|
|
4
|
+
* runServer().
|
|
5
|
+
*
|
|
6
|
+
* The handlers do not own state: they receive a `BrowserToolDeps` object
|
|
7
|
+
* with the live tab map and the call function. This makes the dispatcher
|
|
8
|
+
* unit-testable with a fake `callBrowserTool`.
|
|
9
|
+
*/
|
|
10
|
+
import { requireTabId } from './responses.js';
|
|
11
|
+
export function isBrowserTool(name) {
|
|
12
|
+
return name === 'browser.list_tabs' || BROWSER_TOOL_HANDLERS.has(name);
|
|
13
|
+
}
|
|
14
|
+
const NAVIGATE_TIMEOUT_MS = 30_000;
|
|
15
|
+
const BROWSER_TOOL_HANDLERS = new Map([
|
|
16
|
+
['browser.list_tabs', (_args, deps) => deps.listTabs()],
|
|
17
|
+
['browser.ping', (args, deps) => deps.callBrowserTool(requireTabId(args), 'ping', {})],
|
|
18
|
+
[
|
|
19
|
+
'browser.navigate',
|
|
20
|
+
(args, deps) => {
|
|
21
|
+
const { url, wait_for_load = true } = args;
|
|
22
|
+
return deps.callBrowserTool(requireTabId(args), 'navigate', { url, wait_for_load }, NAVIGATE_TIMEOUT_MS);
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
['browser.snapshot', (args, deps) => deps.callBrowserTool(requireTabId(args), 'snapshot', {})],
|
|
26
|
+
[
|
|
27
|
+
'browser.console',
|
|
28
|
+
(args, deps) => {
|
|
29
|
+
const { level } = args ?? {};
|
|
30
|
+
return deps.callBrowserTool(requireTabId(args), 'console', { level });
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
[
|
|
34
|
+
'browser.network',
|
|
35
|
+
(args, deps) => {
|
|
36
|
+
const { url_filter } = args ?? {};
|
|
37
|
+
return deps.callBrowserTool(requireTabId(args), 'network', { url_filter });
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
[
|
|
41
|
+
'browser.network_body',
|
|
42
|
+
(args, deps) => {
|
|
43
|
+
const { request_id } = args;
|
|
44
|
+
return deps.callBrowserTool(requireTabId(args), 'network_body', { request_id });
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
[
|
|
48
|
+
'browser.click',
|
|
49
|
+
(args, deps) => {
|
|
50
|
+
const { selector } = args;
|
|
51
|
+
return deps.callBrowserTool(requireTabId(args), 'click', { selector });
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
[
|
|
55
|
+
'browser.type',
|
|
56
|
+
(args, deps) => {
|
|
57
|
+
const { selector, text, clear = false, } = args;
|
|
58
|
+
return deps.callBrowserTool(requireTabId(args), 'type', { selector, text, clear });
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
[
|
|
62
|
+
'browser.evaluate',
|
|
63
|
+
(args, deps) => {
|
|
64
|
+
const { expression } = args;
|
|
65
|
+
return deps.callBrowserTool(requireTabId(args), 'evaluate', { expression });
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
]);
|
|
69
|
+
export async function handleBrowserTool(name, args, deps) {
|
|
70
|
+
const handler = BROWSER_TOOL_HANDLERS.get(name);
|
|
71
|
+
if (!handler)
|
|
72
|
+
throw new Error(`Unknown browser tool: ${name}`);
|
|
73
|
+
return handler(args, deps);
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=browser-dispatch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-dispatch.js","sourceRoot":"","sources":["../../src/tools/browser-dispatch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tool-response helpers shared between the browser bridge and the map.
|
|
3
|
+
* Lifted out of runServer() so they can be unit-tested in isolation.
|
|
4
|
+
*/
|
|
5
|
+
/** Shape we emit for an MCP tool result. Index signature is intentional:
|
|
6
|
+
* newer @modelcontextprotocol/sdk schemas extend ServerResult with optional
|
|
7
|
+
* fields we do not populate (e.g. task), and we want the structural type
|
|
8
|
+
* to remain compatible without us having to track every SDK addition. */
|
|
9
|
+
export interface ToolResponse {
|
|
10
|
+
content: {
|
|
11
|
+
type: 'text';
|
|
12
|
+
text: string;
|
|
13
|
+
}[];
|
|
14
|
+
isError?: true;
|
|
15
|
+
[extra: string]: unknown;
|
|
16
|
+
}
|
|
17
|
+
export declare function toolResponse(data: unknown): ToolResponse;
|
|
18
|
+
export declare function toolError(message: string): ToolResponse;
|
|
19
|
+
export declare function requireTabId(args: unknown): string;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tool-response helpers shared between the browser bridge and the map.
|
|
3
|
+
* Lifted out of runServer() so they can be unit-tested in isolation.
|
|
4
|
+
*/
|
|
5
|
+
export function toolResponse(data) {
|
|
6
|
+
return {
|
|
7
|
+
content: [
|
|
8
|
+
{
|
|
9
|
+
type: 'text',
|
|
10
|
+
text: typeof data === 'string' ? data : JSON.stringify(data, null, 2),
|
|
11
|
+
},
|
|
12
|
+
],
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function toolError(message) {
|
|
16
|
+
return {
|
|
17
|
+
content: [{ type: 'text', text: `Error: ${message}` }],
|
|
18
|
+
isError: true,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function requireTabId(args) {
|
|
22
|
+
const id = args?.tab_id;
|
|
23
|
+
if (!id)
|
|
24
|
+
throw new Error('tab_id required');
|
|
25
|
+
return id;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=responses.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responses.js","sourceRoot":"","sources":["../../src/tools/responses.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAYH,MAAM,UAAU,YAAY,CAAC,IAAa;IACxC,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;aACtE;SACF;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,OAAO,EAAE,EAAE,CAAC;QACtD,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAa;IACxC,MAAM,EAAE,GAAI,IAAwC,EAAE,MAAM,CAAC;IAC7D,IAAI,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAC5C,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** Usage protocol pushed to the MCP client on `initialize`. Plain string,
|
|
2
|
+
* intentionally kept short. Edit here when the protocol changes. */
|
|
3
|
+
export declare const SERVER_INSTRUCTIONS = "browser-link bridges Claude Code to the Chrome tabs the user has\nexplicitly connected through the companion extension, and ships a\npersistent UI map backed by a local SQLite DB. The data dir resolves\nper-OS via env-paths ($XDG_DATA_HOME/browser-link on Linux,\n~/Library/Application Support/browser-link on macOS, %APPDATA%/browser-link\non Windows). Override with $BROWSER_LINK_DATA_DIR. The map is private\nand per-machine; never persisted in any repo.\n\n## When operating on a tab\n\n1. Before doing anything on a tab whose URL you don't already know,\n call `browser.map.recall` with { origin } (and optionally url) to load\n selectors, flows and gotchas previously learned for that app.\n2. If recall returns entries with `failed_at` more recent than\n `verified_at`, treat them as suspect: re-verify (snapshot / evaluate)\n before reusing, or replace them.\n3. After every interaction that used a map entry, call\n `browser.map.record_use` with { entry_id, ok }. ok=true updates\n verified_at; ok=false updates failed_at. Keep the map honest.\n4. After a non-trivial flow that worked end-to-end, persist it with\n `browser.map.save`. Three `kind` values:\n - selector: { selector, evidence? } \u2014 a CSS selector tied to a purpose.\n - flow: { steps: [...] } \u2014 an ordered list of actions to reach an outcome.\n - gotcha: { body } \u2014 free-form note about something non-obvious.\n Use `url_pattern` = pathname (exact). Promote to glob only if you have\n evidence of a parametric route. Provide `purpose` as a stable, reusable\n label (\"open task detail dialog\", not \"open IB0311 detail\").\n5. Never save selectors or flows you have not just successfully executed.\n6. Never store domain data (IDs, user names, dates, etc.). The map captures\n UI structure only.\n\n## Identifying the app\n\n- `origin` = scheme://host:port of the tab.\n- `app_key` distinguishes apps that share an origin over time. On first\n save you may omit it; it will be derived from the page title (slugified).\n Use `browser.map.rename_app` if that initial guess is poor.\n\n## When something is wrong\n\n- A selector from recall fails \u2192 record_use({ok:false}), learn the new\n one, save it (upsert on purpose).\n- A whole app got refactored \u2192 `browser.map.forget` the app_id and let\n the map repopulate as you learn the new structure.\n\nThe map is a cache of navigation, not a substitute for `browser.snapshot`.\nThe live snapshot is always the source of truth.";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** Usage protocol pushed to the MCP client on `initialize`. Plain string,
|
|
2
|
+
* intentionally kept short. Edit here when the protocol changes. */
|
|
3
|
+
export const SERVER_INSTRUCTIONS = `browser-link bridges Claude Code to the Chrome tabs the user has
|
|
4
|
+
explicitly connected through the companion extension, and ships a
|
|
5
|
+
persistent UI map backed by a local SQLite DB. The data dir resolves
|
|
6
|
+
per-OS via env-paths (\$XDG_DATA_HOME/browser-link on Linux,
|
|
7
|
+
~/Library/Application Support/browser-link on macOS, %APPDATA%/browser-link
|
|
8
|
+
on Windows). Override with \$BROWSER_LINK_DATA_DIR. The map is private
|
|
9
|
+
and per-machine; never persisted in any repo.
|
|
10
|
+
|
|
11
|
+
## When operating on a tab
|
|
12
|
+
|
|
13
|
+
1. Before doing anything on a tab whose URL you don't already know,
|
|
14
|
+
call \`browser.map.recall\` with { origin } (and optionally url) to load
|
|
15
|
+
selectors, flows and gotchas previously learned for that app.
|
|
16
|
+
2. If recall returns entries with \`failed_at\` more recent than
|
|
17
|
+
\`verified_at\`, treat them as suspect: re-verify (snapshot / evaluate)
|
|
18
|
+
before reusing, or replace them.
|
|
19
|
+
3. After every interaction that used a map entry, call
|
|
20
|
+
\`browser.map.record_use\` with { entry_id, ok }. ok=true updates
|
|
21
|
+
verified_at; ok=false updates failed_at. Keep the map honest.
|
|
22
|
+
4. After a non-trivial flow that worked end-to-end, persist it with
|
|
23
|
+
\`browser.map.save\`. Three \`kind\` values:
|
|
24
|
+
- selector: { selector, evidence? } — a CSS selector tied to a purpose.
|
|
25
|
+
- flow: { steps: [...] } — an ordered list of actions to reach an outcome.
|
|
26
|
+
- gotcha: { body } — free-form note about something non-obvious.
|
|
27
|
+
Use \`url_pattern\` = pathname (exact). Promote to glob only if you have
|
|
28
|
+
evidence of a parametric route. Provide \`purpose\` as a stable, reusable
|
|
29
|
+
label ("open task detail dialog", not "open IB0311 detail").
|
|
30
|
+
5. Never save selectors or flows you have not just successfully executed.
|
|
31
|
+
6. Never store domain data (IDs, user names, dates, etc.). The map captures
|
|
32
|
+
UI structure only.
|
|
33
|
+
|
|
34
|
+
## Identifying the app
|
|
35
|
+
|
|
36
|
+
- \`origin\` = scheme://host:port of the tab.
|
|
37
|
+
- \`app_key\` distinguishes apps that share an origin over time. On first
|
|
38
|
+
save you may omit it; it will be derived from the page title (slugified).
|
|
39
|
+
Use \`browser.map.rename_app\` if that initial guess is poor.
|
|
40
|
+
|
|
41
|
+
## When something is wrong
|
|
42
|
+
|
|
43
|
+
- A selector from recall fails → record_use({ok:false}), learn the new
|
|
44
|
+
one, save it (upsert on purpose).
|
|
45
|
+
- A whole app got refactored → \`browser.map.forget\` the app_id and let
|
|
46
|
+
the map repopulate as you learn the new structure.
|
|
47
|
+
|
|
48
|
+
The map is a cache of navigation, not a substitute for \`browser.snapshot\`.
|
|
49
|
+
The live snapshot is always the source of truth.`;
|
|
50
|
+
//# sourceMappingURL=server-instructions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-instructions.js","sourceRoot":"","sources":["../../src/tools/server-instructions.ts"],"names":[],"mappings":"AAAA;oEACoE;AACpE,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iDA8Cc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/tools/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { platform } from 'node:os';
|
|
3
|
+
/** Open the given URL in the user's default browser, cross-platform.
|
|
4
|
+
* Best-effort: returns true if the spawn succeeded, false if no opener
|
|
5
|
+
* is available. We never wait for the browser process. */
|
|
6
|
+
export function openUrl(url) {
|
|
7
|
+
const os = platform();
|
|
8
|
+
const opts = { detached: true, stdio: 'ignore' };
|
|
9
|
+
let cmd;
|
|
10
|
+
let args;
|
|
11
|
+
if (os === 'darwin') {
|
|
12
|
+
cmd = 'open';
|
|
13
|
+
args = [url];
|
|
14
|
+
}
|
|
15
|
+
else if (os === 'win32') {
|
|
16
|
+
// `start` is a shell builtin on Windows, so we go through cmd.
|
|
17
|
+
// The empty string ('') is the window title argument that `start` expects
|
|
18
|
+
// when the URL itself contains characters cmd would otherwise misinterpret.
|
|
19
|
+
cmd = 'cmd';
|
|
20
|
+
args = ['/c', 'start', '""', url];
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
// Linux / BSDs: rely on freedesktop's xdg-open. Most desktop installs
|
|
24
|
+
// ship it; if not, the caller falls back to printing the URL.
|
|
25
|
+
cmd = 'xdg-open';
|
|
26
|
+
args = [url];
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const child = spawn(cmd, args, opts);
|
|
30
|
+
child.unref();
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=open-url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-url.js","sourceRoot":"","sources":["../../src/utils/open-url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC;;0DAE0D;AAC1D,MAAM,UAAU,OAAO,CAAC,GAAW;IACjC,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAC;IACtB,MAAM,IAAI,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAiB,EAAE,CAAC;IAE1D,IAAI,GAAW,CAAC;IAChB,IAAI,IAAc,CAAC;IACnB,IAAI,EAAE,KAAK,QAAQ,EAAE,CAAC;QACpB,GAAG,GAAG,MAAM,CAAC;QACb,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACf,CAAC;SAAM,IAAI,EAAE,KAAK,OAAO,EAAE,CAAC;QAC1B,+DAA+D;QAC/D,0EAA0E;QAC1E,4EAA4E;QAC5E,GAAG,GAAG,KAAK,CAAC;QACZ,IAAI,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;IACpC,CAAC;SAAM,CAAC;QACN,sEAAsE;QACtE,8DAA8D;QAC9D,GAAG,GAAG,UAAU,CAAC;QACjB,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACf,CAAC;IAED,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QACrC,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jobshimo/browser-link",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "MCP server that bridges Claude (and other MCP clients) to a Chrome tab, with a persistent UI map of selectors, flows and gotchas across sessions.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"mcp",
|
|
7
|
+
"model-context-protocol",
|
|
8
|
+
"claude",
|
|
9
|
+
"chrome",
|
|
10
|
+
"browser-automation",
|
|
11
|
+
"ai-tools"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/jobshimo/browser-link#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/jobshimo/browser-link/issues"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/jobshimo/browser-link.git",
|
|
20
|
+
"directory": "packages/server"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"author": "Martín Miguel Bernal",
|
|
24
|
+
"type": "module",
|
|
25
|
+
"main": "./dist/index.js",
|
|
26
|
+
"bin": {
|
|
27
|
+
"browser-link": "./dist/cli.js"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"README.md",
|
|
32
|
+
"LICENSE"
|
|
33
|
+
],
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc",
|
|
39
|
+
"dev": "tsx watch src/cli.ts",
|
|
40
|
+
"start": "node dist/cli.js",
|
|
41
|
+
"typecheck": "tsc --noEmit",
|
|
42
|
+
"clean": "rm -rf dist",
|
|
43
|
+
"test": "vitest run",
|
|
44
|
+
"test:watch": "vitest",
|
|
45
|
+
"prepublishOnly": "npm run clean && npm run build && node ./scripts/prepare-publish.mjs"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
49
|
+
"better-sqlite3": "^11.5.0",
|
|
50
|
+
"env-paths": "^3.0.0",
|
|
51
|
+
"ws": "^8.18.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/better-sqlite3": "^7.6.11",
|
|
55
|
+
"@types/node": "^22.0.0",
|
|
56
|
+
"@types/ws": "^8.5.13",
|
|
57
|
+
"tsx": "^4.19.0",
|
|
58
|
+
"typescript": "^5.6.0",
|
|
59
|
+
"vitest": "^3.1.4"
|
|
60
|
+
}
|
|
61
|
+
}
|