@peekdev/mcp 0.1.0-alpha.2 → 0.1.0-alpha.20
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 +163 -0
- package/dist/db/open.d.ts +19 -1
- package/dist/db/open.d.ts.map +1 -1
- package/dist/db/open.js +25 -1
- package/dist/db/open.js.map +1 -1
- package/dist/entrypoint.d.ts +8 -0
- package/dist/entrypoint.d.ts.map +1 -0
- package/dist/entrypoint.js +33 -0
- package/dist/entrypoint.js.map +1 -0
- package/dist/mcp/action-schema.d.ts +156 -0
- package/dist/mcp/action-schema.d.ts.map +1 -1
- package/dist/mcp/action-schema.js +69 -0
- package/dist/mcp/action-schema.js.map +1 -1
- package/dist/mcp/event-blobs.d.ts +12 -2
- package/dist/mcp/event-blobs.d.ts.map +1 -1
- package/dist/mcp/event-blobs.js +55 -6
- package/dist/mcp/event-blobs.js.map +1 -1
- package/dist/mcp/event-walker.d.ts +30 -0
- package/dist/mcp/event-walker.d.ts.map +1 -1
- package/dist/mcp/event-walker.js +67 -1
- package/dist/mcp/event-walker.js.map +1 -1
- package/dist/mcp/host-bridge.d.ts +50 -1
- package/dist/mcp/host-bridge.d.ts.map +1 -1
- package/dist/mcp/host-bridge.js +133 -0
- package/dist/mcp/host-bridge.js.map +1 -1
- package/dist/mcp/index.d.ts +6 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +11 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/playwright-repro.d.ts.map +1 -1
- package/dist/mcp/playwright-repro.js +58 -9
- package/dist/mcp/playwright-repro.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +292 -58
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/summary.d.ts +7 -0
- package/dist/mcp/summary.d.ts.map +1 -1
- package/dist/mcp/summary.js +7 -1
- package/dist/mcp/summary.js.map +1 -1
- package/dist/native-host/action-protocol.d.ts +15 -2
- package/dist/native-host/action-protocol.d.ts.map +1 -1
- package/dist/native-host/audit.d.ts +1 -1
- package/dist/native-host/audit.d.ts.map +1 -1
- package/dist/native-host/audit.js +1 -1
- package/dist/native-host/audit.js.map +1 -1
- package/dist/native-host/host-socket.d.ts +125 -0
- package/dist/native-host/host-socket.d.ts.map +1 -0
- package/dist/native-host/host-socket.js +338 -0
- package/dist/native-host/host-socket.js.map +1 -0
- package/dist/native-host/host.d.ts +11 -0
- package/dist/native-host/host.d.ts.map +1 -1
- package/dist/native-host/host.js +54 -0
- package/dist/native-host/host.js.map +1 -1
- package/dist/native-host/ingest.js +4 -4
- package/dist/native-host/ingest.js.map +1 -1
- package/dist/native-host/installer.d.ts +21 -0
- package/dist/native-host/installer.d.ts.map +1 -1
- package/dist/native-host/installer.js +64 -2
- package/dist/native-host/installer.js.map +1 -1
- package/dist/native-host/manifest.d.ts +7 -2
- package/dist/native-host/manifest.d.ts.map +1 -1
- package/dist/native-host/manifest.js +29 -15
- package/dist/native-host/manifest.js.map +1 -1
- package/dist/native-host/socket-path.d.ts +10 -0
- package/dist/native-host/socket-path.d.ts.map +1 -0
- package/dist/native-host/socket-path.js +31 -0
- package/dist/native-host/socket-path.js.map +1 -0
- package/dist/postinstall.d.ts.map +1 -1
- package/dist/postinstall.js +15 -15
- package/dist/postinstall.js.map +1 -1
- package/package.json +34 -4
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
// generate_playwright_repro (Task 3.13): turn a window of extracted user
|
|
2
2
|
// actions into a runnable Playwright test string. Each action maps to the
|
|
3
3
|
// idiomatic Playwright call:
|
|
4
|
-
// navigate
|
|
5
|
-
// click
|
|
6
|
-
// input -> await page.
|
|
4
|
+
// navigate -> await page.goto('url')
|
|
5
|
+
// click -> await page.click('selector')
|
|
6
|
+
// input (select) -> await page.selectOption('selector', 'value')
|
|
7
|
+
// input (checkbox/radio) -> await page.check/uncheck('selector')
|
|
8
|
+
// input (other) -> await page.fill('selector', 'value')
|
|
7
9
|
// The first navigation seeds the opening goto; subsequent navigations are
|
|
8
10
|
// emitted inline (e.g. an in-app route change that triggered a full load).
|
|
11
|
+
// After the actions, a final `await expect(page).toHaveURL(...)` is emitted
|
|
12
|
+
// for the last navigation so the repro verifies the end state, not just replays.
|
|
9
13
|
import { extractUserActions } from './event-walker.js';
|
|
10
14
|
/** Default ceiling on emitted actions — caps the output size (PRD §B token budget). */
|
|
11
15
|
const DEFAULT_MAX_ACTIONS = 200;
|
|
@@ -26,10 +30,43 @@ function actionToStatement(action) {
|
|
|
26
30
|
return action.url ? ` await page.goto(${jsString(action.url)});` : undefined;
|
|
27
31
|
case 'click':
|
|
28
32
|
return action.selector ? ` await page.click(${jsString(action.selector)});` : undefined;
|
|
29
|
-
case 'input':
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
case 'input': {
|
|
34
|
+
if (!action.selector)
|
|
35
|
+
return undefined;
|
|
36
|
+
const sel = jsString(action.selector);
|
|
37
|
+
if (action.elementTag === 'input') {
|
|
38
|
+
if (action.inputType === 'checkbox' || action.inputType === 'radio') {
|
|
39
|
+
if (action.checked === true)
|
|
40
|
+
return ` await page.check(${sel});`;
|
|
41
|
+
if (action.checked === false)
|
|
42
|
+
return ` await page.uncheck(${sel});`;
|
|
43
|
+
return ` // TODO: <input type="${action.inputType}"> ${action.selector} — checked state unknown; add check()/uncheck()`;
|
|
44
|
+
}
|
|
45
|
+
if (action.inputType === 'hidden' ||
|
|
46
|
+
action.inputType === 'submit' ||
|
|
47
|
+
action.inputType === 'button' ||
|
|
48
|
+
action.inputType === 'reset' ||
|
|
49
|
+
action.inputType === 'image') {
|
|
50
|
+
return ` // TODO: skipped <input type="${action.inputType}"> (not a user text entry)`;
|
|
51
|
+
}
|
|
52
|
+
if (action.inputType === 'file') {
|
|
53
|
+
return ` // TODO: file input ${action.selector} — setInputFiles can't be reconstructed from a recording`;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (action.elementTag === 'select') {
|
|
57
|
+
// rrweb captures only a single text value per input event, so only
|
|
58
|
+
// single-value <select> interactions are representable here. A
|
|
59
|
+
// <select multiple> repro would be incorrect (only one option captured);
|
|
60
|
+
// no multi-select detection is attempted — this is a known v1 limitation.
|
|
61
|
+
const value = action.value ?? '';
|
|
62
|
+
// I1: an empty value would make Playwright throw at runtime
|
|
63
|
+
// ("did not find some options"). Emit a TODO so the script stays runnable.
|
|
64
|
+
if (value === '')
|
|
65
|
+
return ' // TODO: <select> reset to placeholder — selectOption needs a value or { index: 0 }';
|
|
66
|
+
return ` await page.selectOption(${jsString(action.selector)}, ${jsString(value)});`;
|
|
67
|
+
}
|
|
68
|
+
return ` await page.fill(${jsString(action.selector)}, ${jsString(action.value ?? '')});`;
|
|
69
|
+
}
|
|
33
70
|
default:
|
|
34
71
|
return undefined;
|
|
35
72
|
}
|
|
@@ -69,10 +106,22 @@ export function generatePlaywrightRepro(events, options = {}) {
|
|
|
69
106
|
lines.push(` // TODO: ${action.summary} (target selector unresolved)`);
|
|
70
107
|
}
|
|
71
108
|
}
|
|
109
|
+
// T0.5: assert the end state. Find the last navigate (with a url) in the
|
|
110
|
+
// capped window and verify the page landed there — turning a blind replay
|
|
111
|
+
// into a test with a real oracle for the final URL.
|
|
112
|
+
for (let i = actions.length - 1; i >= 0; i -= 1) {
|
|
113
|
+
const a = actions[i];
|
|
114
|
+
if (a && a.type === 'navigate' && a.url) {
|
|
115
|
+
lines.push(` await expect(page).toHaveURL(${jsString(a.url)});`);
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
72
119
|
lines.push('});');
|
|
73
120
|
lines.push('');
|
|
74
121
|
return lines.join('\n');
|
|
75
122
|
}
|
|
76
|
-
// `expect` is
|
|
77
|
-
//
|
|
123
|
+
// `expect` is used for the final-URL assertion above (the one oracle we can
|
|
124
|
+
// derive from a recording). We don't synthesize other assertions — we have no
|
|
125
|
+
// ground truth for "correct" intermediate state — so the author still adds
|
|
126
|
+
// content/visibility checks as needed.
|
|
78
127
|
//# sourceMappingURL=playwright-repro.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright-repro.js","sourceRoot":"","sources":["../../src/mcp/playwright-repro.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,0EAA0E;AAC1E,6BAA6B;AAC7B,
|
|
1
|
+
{"version":3,"file":"playwright-repro.js","sourceRoot":"","sources":["../../src/mcp/playwright-repro.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,0EAA0E;AAC1E,6BAA6B;AAC7B,gDAAgD;AAChD,sDAAsD;AACtD,sEAAsE;AACtE,mEAAmE;AACnE,8DAA8D;AAC9D,0EAA0E;AAC1E,2EAA2E;AAC3E,4EAA4E;AAC5E,iFAAiF;AAGjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAcvD,uFAAuF;AACvF,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEhC,kFAAkF;AAClF,SAAS,QAAQ,CAAC,KAAa;IAC7B,0EAA0E;IAC1E,4EAA4E;IAC5E,OAAO,IAAI,KAAK;SACb,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC;AAC9B,CAAC;AAED,8EAA8E;AAC9E,SAAS,iBAAiB,CAAC,MAAkB;IAC3C,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,UAAU;YACb,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,qBAAqB,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QAChF,KAAK,OAAO;YACV,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,sBAAsB,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3F,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,QAAQ;gBAAE,OAAO,SAAS,CAAC;YACvC,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACtC,IAAI,MAAM,CAAC,UAAU,KAAK,OAAO,EAAE,CAAC;gBAClC,IAAI,MAAM,CAAC,SAAS,KAAK,UAAU,IAAI,MAAM,CAAC,SAAS,KAAK,OAAO,EAAE,CAAC;oBACpE,IAAI,MAAM,CAAC,OAAO,KAAK,IAAI;wBAAE,OAAO,sBAAsB,GAAG,IAAI,CAAC;oBAClE,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK;wBAAE,OAAO,wBAAwB,GAAG,IAAI,CAAC;oBACrE,OAAO,2BAA2B,MAAM,CAAC,SAAS,MAAM,MAAM,CAAC,QAAQ,iDAAiD,CAAC;gBAC3H,CAAC;gBACD,IACE,MAAM,CAAC,SAAS,KAAK,QAAQ;oBAC7B,MAAM,CAAC,SAAS,KAAK,QAAQ;oBAC7B,MAAM,CAAC,SAAS,KAAK,QAAQ;oBAC7B,MAAM,CAAC,SAAS,KAAK,OAAO;oBAC5B,MAAM,CAAC,SAAS,KAAK,OAAO,EAC5B,CAAC;oBACD,OAAO,mCAAmC,MAAM,CAAC,SAAS,4BAA4B,CAAC;gBACzF,CAAC;gBACD,IAAI,MAAM,CAAC,SAAS,KAAK,MAAM,EAAE,CAAC;oBAChC,OAAO,yBAAyB,MAAM,CAAC,QAAQ,0DAA0D,CAAC;gBAC5G,CAAC;YACH,CAAC;YACD,IAAI,MAAM,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;gBACnC,mEAAmE;gBACnE,+DAA+D;gBAC/D,yEAAyE;gBACzE,0EAA0E;gBAC1E,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;gBACjC,4DAA4D;gBAC5D,2EAA2E;gBAC3E,IAAI,KAAK,KAAK,EAAE;oBACd,OAAO,uFAAuF,CAAC;gBACjG,OAAO,6BAA6B,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;YACxF,CAAC;YACD,OAAO,qBAAqB,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,QAAQ,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC;QAC7F,CAAC;QACD;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CACrC,MAAuB,EACvB,UAAgC,EAAE;IAElC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,MAAM,CAAC,iBAAiB,CAAC;IAC5D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAAC,iBAAiB,CAAC;IACxD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,uBAAuB,CAAC;IACvD,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,mBAAmB,CAAC;IAE7D,MAAM,WAAW,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,OAAO,IAAI,CAAC,CAAC,EAAE,IAAI,KAAK,CAAC,CAAC;IAC/F,2EAA2E;IAC3E,4EAA4E;IAC5E,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,GAAG,UAAU,CAAC;IAClD,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;IAE7F,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;IAC/D,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,QAAQ,QAAQ,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAE7D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,oDAAoD,CAAC,CAAC;IACnE,CAAC;SAAM,IAAI,SAAS,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CACR,gCAAgC,UAAU,OAAO,WAAW,CAAC,MAAM,8CAA8C,CAClH,CAAC;IACJ,CAAC;IAED,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,cAAc,MAAM,CAAC,OAAO,+BAA+B,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;IAED,yEAAyE;IACzE,0EAA0E;IAC1E,oDAAoD;IACpD,KAAK,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACrB,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;YACxC,KAAK,CAAC,IAAI,CAAC,kCAAkC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAClE,MAAM;QACR,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,4EAA4E;AAC5E,8EAA8E;AAC9E,2EAA2E;AAC3E,uCAAuC"}
|
package/dist/mcp/server.d.ts
CHANGED
|
@@ -46,5 +46,5 @@ export interface CreatePeekMcpServerOptions {
|
|
|
46
46
|
*/
|
|
47
47
|
export declare function createPeekMcpServer(options?: CreatePeekMcpServerOptions): PeekMcpServer;
|
|
48
48
|
/** The tool names this server registers, for smoke tests / docs. */
|
|
49
|
-
export declare const PEEK_MCP_TOOLS: readonly ["list_recent_sessions", "get_session_summary", "get_session_console_errors", "get_session_network_errors", "get_user_action_before_error", "generate_playwright_repro", "get_dom_snapshot", "query_dom_history", "request_authorization", "execute_action"];
|
|
49
|
+
export declare const PEEK_MCP_TOOLS: readonly ["list_recent_sessions", "get_session_summary", "get_session_console_errors", "get_session_network_errors", "get_user_action_before_error", "generate_playwright_repro", "get_dom_snapshot", "query_dom_history", "request_authorization", "execute_action", "suggest_element", "clear_highlight", "request_user_input", "set_intent"];
|
|
50
50
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/mcp/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAUpE,eAAO,MAAM,cAAc,QAAe,CAAC;AAU3C,OAAO,EAAE,KAAK,UAAU,EAAqB,MAAM,kBAAkB,CAAC;AAUtE,OAAO,EAAE,KAAK,UAAU,EAAqB,MAAM,YAAY,CAAC;AAGhE,eAAO,MAAM,WAAW,aAAa,CAAC;AACtC,eAAO,MAAM,mBAAmB,QAUqB,CAAC;AAsBtD,MAAM,WAAW,aAAa;IAC5B,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,sCAAsC;IACtC,KAAK,IAAI,IAAI,CAAC;IACd,sFAAsF;IACtF,iBAAiB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC3D,0DAA0D;IAC1D,QAAQ,CAAC,UAAU,EAAE,UAAU,GAAG,SAAS,CAAC;CAC7C;AAED,MAAM,WAAW,0BAA0B;IACzC,qEAAqE;IACrE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,kDAAkD;IAClD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,sDAAsD;IACtD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC;;;;;;;OAOG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;IACjC;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,0BAA+B,GAAG,aAAa,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAUpE,eAAO,MAAM,cAAc,QAAe,CAAC;AAU3C,OAAO,EAAE,KAAK,UAAU,EAAqB,MAAM,kBAAkB,CAAC;AAUtE,OAAO,EAAE,KAAK,UAAU,EAAqB,MAAM,YAAY,CAAC;AAGhE,eAAO,MAAM,WAAW,aAAa,CAAC;AACtC,eAAO,MAAM,mBAAmB,QAUqB,CAAC;AAsBtD,MAAM,WAAW,aAAa;IAC5B,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,sCAAsC;IACtC,KAAK,IAAI,IAAI,CAAC;IACd,sFAAsF;IACtF,iBAAiB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC3D,0DAA0D;IAC1D,QAAQ,CAAC,UAAU,EAAE,UAAU,GAAG,SAAS,CAAC;CAC7C;AAED,MAAM,WAAW,0BAA0B;IACzC,qEAAqE;IACrE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,kDAAkD;IAClD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,sDAAsD;IACtD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC;;;;;;;OAOG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;IACjC;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,0BAA+B,GAAG,aAAa,CAiwB3F;AAED,oEAAoE;AACpE,eAAO,MAAM,cAAc,iVAoBjB,CAAC"}
|
package/dist/mcp/server.js
CHANGED
|
@@ -125,19 +125,29 @@ export function createPeekMcpServer(options = {}) {
|
|
|
125
125
|
function registerTools() {
|
|
126
126
|
// 1. list_recent_sessions ------------------------------------------------
|
|
127
127
|
server.registerTool('list_recent_sessions', {
|
|
128
|
-
|
|
129
|
-
|
|
128
|
+
title: 'List recent browser sessions',
|
|
129
|
+
description: "List the user's recorded browser sessions, newest first — the entry point for the get_session_* and DOM tools. Returns compact JSON rows ({ sessionId, origin, url, title, startedAt, ... }); free-text fields are clipped (origin 100, url 300, title 200 chars). If the MCP client scoped roots to specific origins and no origin filter is given, results are restricted to the client's scoped origins. Start here to obtain a sessionId, then call get_session_summary.",
|
|
130
130
|
inputSchema: {
|
|
131
|
-
limit: z
|
|
132
|
-
|
|
131
|
+
limit: z
|
|
132
|
+
.number()
|
|
133
|
+
.int()
|
|
134
|
+
.min(1)
|
|
135
|
+
.max(50)
|
|
136
|
+
.default(10)
|
|
137
|
+
.describe('Maximum sessions to return (1-50, newest first; default 10).'),
|
|
138
|
+
origin: z
|
|
139
|
+
.string()
|
|
140
|
+
.optional()
|
|
141
|
+
.describe("Filter to one origin, e.g. 'https://app.example.com'. Omit to list across all recorded origins (subject to client roots scoping)."),
|
|
133
142
|
},
|
|
143
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
134
144
|
}, ({ limit, origin }) => {
|
|
135
145
|
const handle = getDb();
|
|
136
146
|
if (!handle)
|
|
137
147
|
return textResult(NO_DB_MESSAGE);
|
|
138
148
|
// Apply the roots soft-scope: if the client scoped to origins and the
|
|
139
|
-
// caller didn't ask for a specific one, restrict to the
|
|
140
|
-
//
|
|
149
|
+
// caller didn't ask for a specific one, restrict to the scoped origin
|
|
150
|
+
// set by filtering post-query against all allowedOrigins (origins are few).
|
|
141
151
|
const rows = listRecentSessions(handle, {
|
|
142
152
|
limit,
|
|
143
153
|
...(origin !== undefined ? { origin } : {}),
|
|
@@ -155,9 +165,12 @@ export function createPeekMcpServer(options = {}) {
|
|
|
155
165
|
});
|
|
156
166
|
// 2. get_session_summary -------------------------------------------------
|
|
157
167
|
server.registerTool('get_session_summary', {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
inputSchema: {
|
|
168
|
+
title: 'Summarize a session',
|
|
169
|
+
description: 'Get an LLM-readable narrative summary of one session: pages visited, click/input/navigation counts, and error counts. Use this first for an overview before drilling into get_session_console_errors / get_session_network_errors. Returns a structured JSON summary.',
|
|
170
|
+
inputSchema: {
|
|
171
|
+
sessionId: z.string().describe('Session id from list_recent_sessions.'),
|
|
172
|
+
},
|
|
173
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
161
174
|
}, ({ sessionId }) => {
|
|
162
175
|
const handle = getDb();
|
|
163
176
|
if (!handle)
|
|
@@ -173,13 +186,24 @@ export function createPeekMcpServer(options = {}) {
|
|
|
173
186
|
});
|
|
174
187
|
// 3. get_session_console_errors -----------------------------------------
|
|
175
188
|
server.registerTool('get_session_console_errors', {
|
|
176
|
-
|
|
177
|
-
|
|
189
|
+
title: 'List console errors',
|
|
190
|
+
description: 'List console error messages recorded in a session, oldest first. Each row has a numeric id to pass to get_user_action_before_error. Returns JSON rows ({ id, ts, level, message, stack }); message clipped to 500 and stack to 800 chars. For error counts at a glance, use get_session_summary first.',
|
|
178
191
|
inputSchema: {
|
|
179
|
-
sessionId: z.string(),
|
|
180
|
-
since: z
|
|
181
|
-
|
|
192
|
+
sessionId: z.string().describe('Session id from list_recent_sessions.'),
|
|
193
|
+
since: z
|
|
194
|
+
.number()
|
|
195
|
+
.int()
|
|
196
|
+
.optional()
|
|
197
|
+
.describe('Only return errors with ts >= this epoch-ms timestamp (to page forward through a long session). Omit to start from the beginning.'),
|
|
198
|
+
limit: z
|
|
199
|
+
.number()
|
|
200
|
+
.int()
|
|
201
|
+
.min(1)
|
|
202
|
+
.max(200)
|
|
203
|
+
.default(50)
|
|
204
|
+
.describe('Maximum errors to return (1-200, oldest first; default 50).'),
|
|
182
205
|
},
|
|
206
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
183
207
|
}, ({ sessionId, since, limit }) => {
|
|
184
208
|
const handle = getDb();
|
|
185
209
|
if (!handle)
|
|
@@ -198,13 +222,26 @@ export function createPeekMcpServer(options = {}) {
|
|
|
198
222
|
});
|
|
199
223
|
// 4. get_session_network_errors -----------------------------------------
|
|
200
224
|
server.registerTool('get_session_network_errors', {
|
|
201
|
-
|
|
202
|
-
|
|
225
|
+
title: 'List failed network requests',
|
|
226
|
+
description: 'List failed or notable network requests in a session (HTTP status >= statusGte, or a transport-level network error), oldest first. Returns JSON rows ({ id, ts, method, url, status, statusText, resourceType, durationMs, errorText }); url and errorText clipped to 300 chars.',
|
|
203
227
|
inputSchema: {
|
|
204
|
-
sessionId: z.string(),
|
|
205
|
-
statusGte: z
|
|
206
|
-
|
|
228
|
+
sessionId: z.string().describe('Session id from list_recent_sessions.'),
|
|
229
|
+
statusGte: z
|
|
230
|
+
.number()
|
|
231
|
+
.int()
|
|
232
|
+
.min(100)
|
|
233
|
+
.max(599)
|
|
234
|
+
.default(400)
|
|
235
|
+
.describe('Minimum HTTP status treated as notable (100-599; default 400, i.e. 4xx/5xx). Transport-level errors are always included regardless.'),
|
|
236
|
+
limit: z
|
|
237
|
+
.number()
|
|
238
|
+
.int()
|
|
239
|
+
.min(1)
|
|
240
|
+
.max(200)
|
|
241
|
+
.default(50)
|
|
242
|
+
.describe('Maximum requests to return (1-200, oldest first; default 50).'),
|
|
207
243
|
},
|
|
244
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
208
245
|
}, ({ sessionId, statusGte, limit }) => {
|
|
209
246
|
const handle = getDb();
|
|
210
247
|
if (!handle)
|
|
@@ -224,14 +261,20 @@ export function createPeekMcpServer(options = {}) {
|
|
|
224
261
|
});
|
|
225
262
|
// 5. get_user_action_before_error ---------------------------------------
|
|
226
263
|
server.registerTool('get_user_action_before_error', {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
'get_session_console_errors.',
|
|
264
|
+
title: 'Actions before an error',
|
|
265
|
+
description: 'Reconstruct what the user did right before a console error: returns the last `window` user actions (click/type/navigate) preceding the error, to explain how it was triggered. Returns JSON { errorId, errorTs, actions }. Get errorId from get_session_console_errors first.',
|
|
230
266
|
inputSchema: {
|
|
231
|
-
sessionId: z.string(),
|
|
232
|
-
errorId: z.number().int(),
|
|
233
|
-
window: z
|
|
267
|
+
sessionId: z.string().describe('Session id from list_recent_sessions.'),
|
|
268
|
+
errorId: z.number().int().describe('Console error id from get_session_console_errors.'),
|
|
269
|
+
window: z
|
|
270
|
+
.number()
|
|
271
|
+
.int()
|
|
272
|
+
.min(1)
|
|
273
|
+
.max(50)
|
|
274
|
+
.default(10)
|
|
275
|
+
.describe('How many preceding user actions to return (1-50; default 10).'),
|
|
234
276
|
},
|
|
277
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
235
278
|
}, ({ sessionId, errorId, window }) => {
|
|
236
279
|
const handle = getDb();
|
|
237
280
|
if (!handle)
|
|
@@ -248,13 +291,22 @@ export function createPeekMcpServer(options = {}) {
|
|
|
248
291
|
});
|
|
249
292
|
// 6. generate_playwright_repro ------------------------------------------
|
|
250
293
|
server.registerTool('generate_playwright_repro', {
|
|
251
|
-
|
|
252
|
-
|
|
294
|
+
title: 'Generate Playwright repro',
|
|
295
|
+
description: 'Generate a runnable Playwright test (TypeScript) reproducing the user actions in a session: clicks, typing, navigation, and <select> changes. Optionally limit to a [startTs, endTs] epoch-ms window. Returns the test source as text. Note: only single-value <select> is represented (rrweb captures one value per input).',
|
|
253
296
|
inputSchema: {
|
|
254
|
-
sessionId: z.string(),
|
|
255
|
-
startTs: z
|
|
256
|
-
|
|
297
|
+
sessionId: z.string().describe('Session id from list_recent_sessions.'),
|
|
298
|
+
startTs: z
|
|
299
|
+
.number()
|
|
300
|
+
.int()
|
|
301
|
+
.optional()
|
|
302
|
+
.describe('Only include actions at or after this epoch-ms timestamp. Omit to start at the session beginning.'),
|
|
303
|
+
endTs: z
|
|
304
|
+
.number()
|
|
305
|
+
.int()
|
|
306
|
+
.optional()
|
|
307
|
+
.describe('Only include actions at or before this epoch-ms timestamp. Omit to run through the session end.'),
|
|
257
308
|
},
|
|
309
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
258
310
|
}, ({ sessionId, startTs, endTs }) => {
|
|
259
311
|
const handle = getDb();
|
|
260
312
|
if (!handle)
|
|
@@ -274,14 +326,20 @@ export function createPeekMcpServer(options = {}) {
|
|
|
274
326
|
});
|
|
275
327
|
// 7. get_dom_snapshot ----------------------------------------------------
|
|
276
328
|
server.registerTool('get_dom_snapshot', {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
'on top of the nearest full snapshot.',
|
|
329
|
+
title: 'Reconstruct DOM at a time',
|
|
330
|
+
description: 'Reconstruct the page DOM as it existed at a timestamp (or a selector subtree within it) and return it as HTML. Applies structural/attribute/text mutations on top of the nearest full snapshot at or before ts. Returns JSON { baseSnapshotTs, mutationsApplied, html }; html clipped to 24000 chars. Fails if no full snapshot exists at or before ts.',
|
|
280
331
|
inputSchema: {
|
|
281
|
-
sessionId: z.string(),
|
|
282
|
-
ts: z
|
|
283
|
-
|
|
332
|
+
sessionId: z.string().describe('Session id from list_recent_sessions.'),
|
|
333
|
+
ts: z
|
|
334
|
+
.number()
|
|
335
|
+
.int()
|
|
336
|
+
.describe('Epoch-ms timestamp to reconstruct the DOM at. Use timestamps from get_session_summary, error rows, or get_user_action_before_error.'),
|
|
337
|
+
selector: z
|
|
338
|
+
.string()
|
|
339
|
+
.optional()
|
|
340
|
+
.describe('CSS selector to return only that subtree. Omit to return the full document.'),
|
|
284
341
|
},
|
|
342
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
285
343
|
}, ({ sessionId, ts, selector }) => {
|
|
286
344
|
const handle = getDb();
|
|
287
345
|
if (!handle)
|
|
@@ -302,14 +360,26 @@ export function createPeekMcpServer(options = {}) {
|
|
|
302
360
|
});
|
|
303
361
|
// 8. query_dom_history ---------------------------------------------------
|
|
304
362
|
server.registerTool('query_dom_history', {
|
|
305
|
-
|
|
306
|
-
|
|
363
|
+
title: 'DOM change timeline',
|
|
364
|
+
description: 'Timeline of attribute and/or text changes over a session for the node matching a CSS selector - useful for tracking how one element evolved. Returns JSON { selector, changes }. Use op to restrict to attribute changes or innerText; omit for both.',
|
|
307
365
|
inputSchema: {
|
|
308
|
-
sessionId: z.string(),
|
|
309
|
-
selector: z
|
|
310
|
-
|
|
311
|
-
|
|
366
|
+
sessionId: z.string().describe('Session id from list_recent_sessions.'),
|
|
367
|
+
selector: z
|
|
368
|
+
.string()
|
|
369
|
+
.describe("CSS selector for the node to track, e.g. '#status' or '.cart-count'."),
|
|
370
|
+
op: z
|
|
371
|
+
.enum(['attributeChanges', 'innerText'])
|
|
372
|
+
.optional()
|
|
373
|
+
.describe("Restrict to 'attributeChanges' or 'innerText'. Omit to include both."),
|
|
374
|
+
limit: z
|
|
375
|
+
.number()
|
|
376
|
+
.int()
|
|
377
|
+
.min(1)
|
|
378
|
+
.max(500)
|
|
379
|
+
.default(100)
|
|
380
|
+
.describe('Maximum changes to return (1-500; default 100).'),
|
|
312
381
|
},
|
|
382
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
313
383
|
}, ({ sessionId, selector, op, limit }) => {
|
|
314
384
|
const handle = getDb();
|
|
315
385
|
if (!handle)
|
|
@@ -329,13 +399,19 @@ export function createPeekMcpServer(options = {}) {
|
|
|
329
399
|
// banner. On Allow the host returns a one-shot confirmToken the AI passes
|
|
330
400
|
// to execute_action. EVERY call (including denied ones) is audit-logged.
|
|
331
401
|
server.registerTool('request_authorization', {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
'confirmToken to pass to execute_action, or denies the request. ' +
|
|
335
|
-
'Every call is recorded to ~/.peek/audit.log.',
|
|
402
|
+
title: 'Request action authorization',
|
|
403
|
+
description: 'Ask the user to authorize a browser action via the side-panel banner (Level-3 act-with-confirm). On Allow, returns a one-shot confirmToken to pass to execute_action; on Deny, returns the denial. Every call - allowed or denied - is recorded to ~/.peek/audit.log. Use before execute_action when the origin is at permission Level 3, or to pre-authorize.',
|
|
336
404
|
inputSchema: {
|
|
337
|
-
sessionId: z
|
|
338
|
-
|
|
405
|
+
sessionId: z
|
|
406
|
+
.string()
|
|
407
|
+
.describe('Session id (origin context) from list_recent_sessions; determines the per-origin permission level.'),
|
|
408
|
+
action: ActionSchema.describe('The browser action to authorize (e.g. click/type/navigate; see the action schema).'),
|
|
409
|
+
},
|
|
410
|
+
annotations: {
|
|
411
|
+
readOnlyHint: false,
|
|
412
|
+
destructiveHint: false,
|
|
413
|
+
idempotentHint: false,
|
|
414
|
+
openWorldHint: true,
|
|
339
415
|
},
|
|
340
416
|
}, async ({ sessionId, action }) => {
|
|
341
417
|
return await dispatchActTool({
|
|
@@ -351,17 +427,23 @@ export function createPeekMcpServer(options = {}) {
|
|
|
351
427
|
// banner step at Level 3. Without it: Level 3 raises a banner; Level 4
|
|
352
428
|
// proceeds (unless destructive); Level <3 denies.
|
|
353
429
|
server.registerTool('execute_action', {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
'Level 4 auto-allows non-destructive). The destructive-action ' +
|
|
357
|
-
'override (delete/remove/transfer/send/pay/purchase/buy/confirm/' +
|
|
358
|
-
'subscribe/logout/sign out/unsubscribe/cancel subscription/wire/' +
|
|
359
|
-
'withdraw) always prompts, even at Level 4. Every call is ' +
|
|
360
|
-
'recorded to ~/.peek/audit.log.',
|
|
430
|
+
title: 'Execute a browser action',
|
|
431
|
+
description: "Execute an action (click/type/navigate/...) in the user's live browser. Requires per-origin permission Level 3+: Level 3 raises a confirm banner unless a valid confirmToken from request_authorization is passed; Level 4 auto-allows non-destructive actions; Level <3 denies. The destructive-action override (delete/remove/transfer/send/pay/purchase/buy/confirm/subscribe/logout/sign out/unsubscribe/cancel subscription/wire/withdraw) always prompts, even at Level 4. Every call is recorded to ~/.peek/audit.log.",
|
|
361
432
|
inputSchema: {
|
|
362
|
-
sessionId: z
|
|
363
|
-
|
|
364
|
-
|
|
433
|
+
sessionId: z
|
|
434
|
+
.string()
|
|
435
|
+
.describe('Session id (origin context) from list_recent_sessions; determines the per-origin permission level.'),
|
|
436
|
+
action: ActionSchema.describe('The browser action to execute (e.g. click/type/navigate; see the action schema).'),
|
|
437
|
+
confirmToken: z
|
|
438
|
+
.string()
|
|
439
|
+
.optional()
|
|
440
|
+
.describe('One-shot token from a prior request_authorization Allow, to skip the Level-3 banner. Omit to trigger the banner (Level 3) or rely on Level-4 auto-allow.'),
|
|
441
|
+
},
|
|
442
|
+
annotations: {
|
|
443
|
+
readOnlyHint: false,
|
|
444
|
+
destructiveHint: true,
|
|
445
|
+
idempotentHint: false,
|
|
446
|
+
openWorldHint: true,
|
|
365
447
|
},
|
|
366
448
|
}, async ({ sessionId, action, confirmToken }) => {
|
|
367
449
|
return await dispatchActTool({
|
|
@@ -371,6 +453,150 @@ export function createPeekMcpServer(options = {}) {
|
|
|
371
453
|
...(confirmToken !== undefined ? { confirmToken } : {}),
|
|
372
454
|
});
|
|
373
455
|
});
|
|
456
|
+
// 11. suggest_element (Level-2 Suggest tier) -----------------------------
|
|
457
|
+
// Non-mutating highlight overlay. Routed through the execute_action audit
|
|
458
|
+
// path (wire tool='execute_action'); the SW intercepts the `highlight`
|
|
459
|
+
// action BEFORE the act gate and auto-allows it at per-origin Level 2+.
|
|
460
|
+
server.registerTool('suggest_element', {
|
|
461
|
+
title: 'Highlight an element in the live browser',
|
|
462
|
+
description: "Draw a non-destructive highlight overlay on a CSS selector in the user's live browser, with an optional label, to point something out. Available at per-origin permission Level 2 (Suggest) and above; it never clicks, types, or navigates. The overlay persists until clear_highlight is called. Every call is recorded to ~/.peek/audit.log.",
|
|
463
|
+
inputSchema: {
|
|
464
|
+
sessionId: z
|
|
465
|
+
.string()
|
|
466
|
+
.describe('Session id (origin context) from list_recent_sessions; determines the per-origin permission level.'),
|
|
467
|
+
selector: z.string().min(1).describe('CSS selector of the element to highlight.'),
|
|
468
|
+
label: z
|
|
469
|
+
.string()
|
|
470
|
+
.max(120)
|
|
471
|
+
.optional()
|
|
472
|
+
.describe('Optional short text shown in a badge next to the highlight (<=120 chars).'),
|
|
473
|
+
},
|
|
474
|
+
annotations: {
|
|
475
|
+
readOnlyHint: false,
|
|
476
|
+
destructiveHint: false,
|
|
477
|
+
idempotentHint: true,
|
|
478
|
+
openWorldHint: true,
|
|
479
|
+
},
|
|
480
|
+
}, async ({ sessionId, selector, label }) => {
|
|
481
|
+
return await dispatchActTool({
|
|
482
|
+
tool: 'execute_action',
|
|
483
|
+
sessionId,
|
|
484
|
+
action: { type: 'highlight', selector, ...(label !== undefined ? { label } : {}) },
|
|
485
|
+
});
|
|
486
|
+
});
|
|
487
|
+
// 12. clear_highlight (Level-2 Suggest tier) -----------------------------
|
|
488
|
+
server.registerTool('clear_highlight', {
|
|
489
|
+
title: 'Clear the highlight overlay',
|
|
490
|
+
description: "Remove the highlight overlay previously drawn by suggest_element in the user's live browser. Available at per-origin permission Level 2 (Suggest) and above. Idempotent. Recorded to ~/.peek/audit.log.",
|
|
491
|
+
inputSchema: {
|
|
492
|
+
sessionId: z
|
|
493
|
+
.string()
|
|
494
|
+
.describe('Session id (origin context) from list_recent_sessions; determines the per-origin permission level.'),
|
|
495
|
+
},
|
|
496
|
+
annotations: {
|
|
497
|
+
readOnlyHint: false,
|
|
498
|
+
destructiveHint: false,
|
|
499
|
+
idempotentHint: true,
|
|
500
|
+
openWorldHint: true,
|
|
501
|
+
},
|
|
502
|
+
}, async ({ sessionId }) => {
|
|
503
|
+
return await dispatchActTool({
|
|
504
|
+
tool: 'execute_action',
|
|
505
|
+
sessionId,
|
|
506
|
+
action: { type: 'clear_highlight' },
|
|
507
|
+
});
|
|
508
|
+
});
|
|
509
|
+
// 13. set_intent (Part 2 — control-shield banner) ------------------------
|
|
510
|
+
// Set the agent's status banner shown on the Level-4 control shield so the
|
|
511
|
+
// user can follow what the agent is doing. Rides the execute_action audit
|
|
512
|
+
// path on the wire; auto-allowed at Level 4 with the shield up.
|
|
513
|
+
server.registerTool('set_intent', {
|
|
514
|
+
title: 'Set the control-shield banner text',
|
|
515
|
+
description: "Set the agent's status banner shown on the control shield (e.g. 'Applying to Senior Frontend · step 2/4'), so the user can follow what you're doing. Up to 80 chars, plain text. Requires the origin at Level 4 with the shield up; auto-allowed. Recorded to ~/.peek/audit.log.",
|
|
516
|
+
inputSchema: {
|
|
517
|
+
sessionId: z
|
|
518
|
+
.string()
|
|
519
|
+
.describe('Session id (origin context) from list_recent_sessions; determines the per-origin permission level.'),
|
|
520
|
+
text: z
|
|
521
|
+
.string()
|
|
522
|
+
.max(80)
|
|
523
|
+
.describe('Status text shown in the shield banner (<=80 chars). Pass an empty string to clear it.'),
|
|
524
|
+
},
|
|
525
|
+
annotations: {
|
|
526
|
+
readOnlyHint: false,
|
|
527
|
+
destructiveHint: false,
|
|
528
|
+
idempotentHint: true,
|
|
529
|
+
openWorldHint: true,
|
|
530
|
+
},
|
|
531
|
+
}, async ({ sessionId, text }) => {
|
|
532
|
+
return await dispatchActTool({
|
|
533
|
+
tool: 'execute_action',
|
|
534
|
+
sessionId,
|
|
535
|
+
action: { type: 'set_intent', text },
|
|
536
|
+
});
|
|
537
|
+
});
|
|
538
|
+
// 14. request_user_input (Plan B — input handoff) ------------------------
|
|
539
|
+
// Pause the agent + hand the keyboard back to the user for ONE editable,
|
|
540
|
+
// non-destructive field (or a free-text prompt), then resume. Rides the
|
|
541
|
+
// execute_action audit path on the wire; the SW gates it at per-origin
|
|
542
|
+
// Level 4 with the control shield up. The per-request bridge timeout is
|
|
543
|
+
// clamped to 30s ABOVE the handoff timeout so the SW controller's timer
|
|
544
|
+
// fires first → structured {resumed:false,'timeout'} (not a transport error).
|
|
545
|
+
server.registerTool('request_user_input', {
|
|
546
|
+
title: 'Pause and ask the user to fill something in on the page',
|
|
547
|
+
description: "Pause the agent and hand the keyboard back to the user for ONE editable, non-destructive field (or a free-text prompt), then resume. Requires the origin at Level 4 with the control shield up. Blocks until the user clicks Done, a timeout fires, or the run is stopped. Returns { resumed:true, value? } or { resumed:false, reason }. The returned value is only included when readBack:true and the field isn't a password/OTP/credit-card field. Recorded to ~/.peek/audit.log (prompt + selector only — never the value).",
|
|
548
|
+
inputSchema: {
|
|
549
|
+
sessionId: z
|
|
550
|
+
.string()
|
|
551
|
+
.describe('Session id (origin context) from list_recent_sessions; determines the per-origin permission level.'),
|
|
552
|
+
prompt: z
|
|
553
|
+
.string()
|
|
554
|
+
.max(280)
|
|
555
|
+
.describe('What to ask the user to do (shown in the card, below a peek-authored framing line).'),
|
|
556
|
+
selector: z
|
|
557
|
+
.string()
|
|
558
|
+
.optional()
|
|
559
|
+
.describe('CSS selector of the editable field to unlock for the user. Omit for a free-text prompt card.'),
|
|
560
|
+
readBack: z
|
|
561
|
+
.boolean()
|
|
562
|
+
.optional()
|
|
563
|
+
.describe('If true, return what the user typed to the agent (never for password/OTP/cc fields). Default false.'),
|
|
564
|
+
timeoutMs: z
|
|
565
|
+
.number()
|
|
566
|
+
.int()
|
|
567
|
+
.min(0)
|
|
568
|
+
.max(600000)
|
|
569
|
+
.optional()
|
|
570
|
+
.describe('How long to wait for the user (default 120000, max 600000).'),
|
|
571
|
+
scope: z
|
|
572
|
+
.enum(['field', 'page'])
|
|
573
|
+
.default('field')
|
|
574
|
+
.describe("'field' (default) unlocks one editable field; 'page' hands full page control back (CAPTCHAs, native widgets, final review) until Resume. Inherits the handoff recording-suspension."),
|
|
575
|
+
},
|
|
576
|
+
annotations: {
|
|
577
|
+
readOnlyHint: false,
|
|
578
|
+
destructiveHint: false,
|
|
579
|
+
idempotentHint: false,
|
|
580
|
+
openWorldHint: true,
|
|
581
|
+
},
|
|
582
|
+
}, async ({ sessionId, prompt, selector, readBack, timeoutMs, scope }) => {
|
|
583
|
+
const handoffTimeout = Math.min(Math.max(timeoutMs ?? 120000, 0), 600000);
|
|
584
|
+
return await dispatchActTool({
|
|
585
|
+
tool: 'execute_action',
|
|
586
|
+
sessionId,
|
|
587
|
+
action: {
|
|
588
|
+
type: 'request_user_input',
|
|
589
|
+
prompt,
|
|
590
|
+
...(selector !== undefined ? { selector } : {}),
|
|
591
|
+
scope,
|
|
592
|
+
readBack: readBack ?? false,
|
|
593
|
+
timeoutMs: handoffTimeout,
|
|
594
|
+
},
|
|
595
|
+
// The bridge waits 30s longer than the handoff so the SW controller's
|
|
596
|
+
// timer fires first → structured {resumed:false,'timeout'}.
|
|
597
|
+
timeoutMs: handoffTimeout + 30_000,
|
|
598
|
+
});
|
|
599
|
+
});
|
|
374
600
|
}
|
|
375
601
|
// --- Act-tool dispatch (shared between execute_action + request_authorization) ---
|
|
376
602
|
async function dispatchActTool(input) {
|
|
@@ -387,6 +613,7 @@ export function createPeekMcpServer(options = {}) {
|
|
|
387
613
|
action: input.action,
|
|
388
614
|
client,
|
|
389
615
|
...(input.confirmToken !== undefined ? { confirmToken: input.confirmToken } : {}),
|
|
616
|
+
...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),
|
|
390
617
|
});
|
|
391
618
|
}
|
|
392
619
|
catch (err) {
|
|
@@ -472,5 +699,12 @@ export const PEEK_MCP_TOOLS = [
|
|
|
472
699
|
// Write tools (Phase 3d, Level 3+).
|
|
473
700
|
'request_authorization',
|
|
474
701
|
'execute_action',
|
|
702
|
+
// Suggest tools (Level 2+ — non-mutating highlight overlay).
|
|
703
|
+
'suggest_element',
|
|
704
|
+
'clear_highlight',
|
|
705
|
+
// Input handoff (Plan B — Level 4 with the control shield up).
|
|
706
|
+
'request_user_input',
|
|
707
|
+
// Control-shield banner (Part 2 — Level 4 auto-allowed).
|
|
708
|
+
'set_intent',
|
|
475
709
|
];
|
|
476
710
|
//# sourceMappingURL=server.js.map
|