@e2edev/e2e 0.11.0 → 0.12.0
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 +3 -3
- package/dist/agent/act.d.ts +30 -2
- package/dist/agent/act.d.ts.map +1 -1
- package/dist/agent/act.js +4 -4
- package/dist/agent/act.js.map +1 -1
- package/dist/agent/default-agent.d.ts +16 -7
- package/dist/agent/default-agent.d.ts.map +1 -1
- package/dist/agent/default-agent.js +35 -34
- package/dist/agent/default-agent.js.map +1 -1
- package/dist/agent/interactive-step.d.ts +38 -0
- package/dist/agent/interactive-step.d.ts.map +1 -0
- package/dist/agent/interactive-step.js +131 -0
- package/dist/agent/interactive-step.js.map +1 -0
- package/dist/agent/model/tool-choice.d.ts +21 -0
- package/dist/agent/model/tool-choice.d.ts.map +1 -0
- package/dist/agent/model/tool-choice.js +49 -0
- package/dist/agent/model/tool-choice.js.map +1 -0
- package/dist/agent/tool-loop.d.ts +4 -4
- package/dist/agent/tool-loop.d.ts.map +1 -1
- package/dist/agent/tool-loop.js +100 -23
- package/dist/agent/tool-loop.js.map +1 -1
- package/dist/cli/docs-url.d.ts +1 -1
- package/dist/cli/docs-url.d.ts.map +1 -1
- package/dist/cli/docs-url.js +1 -1
- package/dist/cli/docs-url.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +22 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/engine-versions.json +1 -1
- package/dist/cli/init/engines.d.ts +16 -15
- package/dist/cli/init/engines.d.ts.map +1 -1
- package/dist/cli/init/engines.js +23 -23
- package/dist/cli/init/engines.js.map +1 -1
- package/dist/cli/init/mcp-config.d.ts +31 -0
- package/dist/cli/init/mcp-config.d.ts.map +1 -0
- package/dist/cli/init/mcp-config.js +72 -0
- package/dist/cli/init/mcp-config.js.map +1 -0
- package/dist/cli/init/scaffold.d.ts +1 -1
- package/dist/cli/init/scaffold.js +1 -1
- package/dist/cli/init/scaffold.js.map +1 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +64 -28
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/mcp.d.ts +15 -0
- package/dist/cli/mcp.d.ts.map +1 -0
- package/dist/cli/mcp.js +63 -0
- package/dist/cli/mcp.js.map +1 -0
- package/dist/collect/registry.d.ts.map +1 -1
- package/dist/collect/registry.js +23 -2
- package/dist/collect/registry.js.map +1 -1
- package/dist/collect/select.d.ts +9 -2
- package/dist/collect/select.d.ts.map +1 -1
- package/dist/collect/select.js +57 -11
- package/dist/collect/select.js.map +1 -1
- package/dist/config/resolve.d.ts +8 -5
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/resolve.js +11 -9
- package/dist/config/resolve.js.map +1 -1
- package/dist/credentials.d.ts +6 -0
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +9 -0
- package/dist/credentials.js.map +1 -1
- package/dist/explore/index.d.ts.map +1 -1
- package/dist/explore/index.js +6 -4
- package/dist/explore/index.js.map +1 -1
- package/dist/internal/ai-trace.d.ts +2 -0
- package/dist/internal/ai-trace.d.ts.map +1 -1
- package/dist/internal/ai-trace.js +3 -0
- package/dist/internal/ai-trace.js.map +1 -1
- package/dist/internal/ids.d.ts +2 -2
- package/dist/internal/ids.d.ts.map +1 -1
- package/dist/internal/ids.js +3 -3
- package/dist/internal/ids.js.map +1 -1
- package/dist/internal/package-manager.d.ts +4 -0
- package/dist/internal/package-manager.d.ts.map +1 -1
- package/dist/internal/package-manager.js +26 -0
- package/dist/internal/package-manager.js.map +1 -1
- package/dist/mcp/catalog.d.ts +50 -0
- package/dist/mcp/catalog.d.ts.map +1 -0
- package/dist/mcp/catalog.js +172 -0
- package/dist/mcp/catalog.js.map +1 -0
- package/dist/mcp/config.d.ts +11 -0
- package/dist/mcp/config.d.ts.map +1 -0
- package/dist/mcp/config.js +16 -0
- package/dist/mcp/config.js.map +1 -0
- package/dist/mcp/server.d.ts +29 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +86 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/session.d.ts +68 -0
- package/dist/mcp/session.d.ts.map +1 -0
- package/dist/mcp/session.js +317 -0
- package/dist/mcp/session.js.map +1 -0
- package/dist/mcp/tools.d.ts +77 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +179 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/report/build.d.ts +8 -0
- package/dist/report/build.d.ts.map +1 -1
- package/dist/report/build.js +3 -1
- package/dist/report/build.js.map +1 -1
- package/dist/report/format.d.ts +19 -0
- package/dist/report/format.d.ts.map +1 -1
- package/dist/report/format.js +42 -0
- package/dist/report/format.js.map +1 -1
- package/dist/report/junit.d.ts.map +1 -1
- package/dist/report/junit.js +4 -1
- package/dist/report/junit.js.map +1 -1
- package/dist/report/list.d.ts +10 -1
- package/dist/report/list.d.ts.map +1 -1
- package/dist/report/list.js +35 -13
- package/dist/report/list.js.map +1 -1
- package/dist/run/events.d.ts +12 -3
- package/dist/run/events.d.ts.map +1 -1
- package/dist/run/events.js.map +1 -1
- package/dist/run/execute.d.ts +2 -2
- package/dist/run/execute.d.ts.map +1 -1
- package/dist/run/execute.js +19 -14
- package/dist/run/execute.js.map +1 -1
- package/dist/run/fixtures.d.ts +10 -4
- package/dist/run/fixtures.d.ts.map +1 -1
- package/dist/run/fixtures.js +23 -23
- package/dist/run/fixtures.js.map +1 -1
- package/dist/run/in-process.d.ts.map +1 -1
- package/dist/run/in-process.js +4 -3
- package/dist/run/in-process.js.map +1 -1
- package/dist/run/provision.d.ts +51 -0
- package/dist/run/provision.d.ts.map +1 -0
- package/dist/run/provision.js +94 -0
- package/dist/run/provision.js.map +1 -0
- package/dist/run/records.d.ts +4 -0
- package/dist/run/records.d.ts.map +1 -1
- package/dist/run/runner.d.ts +5 -2
- package/dist/run/runner.d.ts.map +1 -1
- package/dist/run/runner.js +21 -69
- package/dist/run/runner.js.map +1 -1
- package/dist/run/scheduler.d.ts +5 -2
- package/dist/run/scheduler.d.ts.map +1 -1
- package/dist/run/scheduler.js +13 -10
- package/dist/run/scheduler.js.map +1 -1
- package/dist/run/serial.d.ts +1 -1
- package/dist/run/serial.d.ts.map +1 -1
- package/dist/run/serial.js +7 -2
- package/dist/run/serial.js.map +1 -1
- package/dist/run/standalone.d.ts +63 -0
- package/dist/run/standalone.d.ts.map +1 -0
- package/dist/run/standalone.js +152 -0
- package/dist/run/standalone.js.map +1 -0
- package/dist/run/units.d.ts +6 -3
- package/dist/run/units.d.ts.map +1 -1
- package/dist/run/units.js +0 -0
- package/dist/run/units.js.map +1 -1
- package/dist/run/worker/entry.js +2 -2
- package/dist/run/worker/entry.js.map +1 -1
- package/dist/run/worker/protocol.d.ts +5 -0
- package/dist/run/worker/protocol.d.ts.map +1 -1
- package/dist/run/worker/protocol.js.map +1 -1
- package/dist/run/worker/session.d.ts +3 -4
- package/dist/run/worker/session.d.ts.map +1 -1
- package/dist/run/worker/session.js +5 -4
- package/dist/run/worker/session.js.map +1 -1
- package/dist/types.d.ts +8 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -6
- package/skills/e2e/SKILL.md +5 -2
- package/skills/e2e/references/agent.md +2 -2
- package/skills/e2e/references/explore.md +3 -3
- package/skills/e2e/references/mcp.md +88 -0
- package/skills/e2e/references/running.md +4 -3
- package/skills/e2e/references/setup.md +7 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAStD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,8FAA8F;IAC9F,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,yDAAyD;IACzD,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,qDAAqD;IACrD,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,sDAAsD;IACtD,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CAC3C;AAQD,2FAA2F;AAC3F,wBAAsB,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAwDrE"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `e2e mcp` server: a live session on the app for a coding agent, with
|
|
3
|
+
* the skill as resources, served over one stdio transport. The tool list is
|
|
4
|
+
* four tools and never changes: the session's own vocabulary is a catalog
|
|
5
|
+
* behind `call`, so it follows the config the agent opens rather than the
|
|
6
|
+
* config the server started next to. Streams are injected, so the CLI hands
|
|
7
|
+
* it the process's and a test hands it pipes.
|
|
8
|
+
*/
|
|
9
|
+
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
10
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
11
|
+
import { readGuide, skillTopics } from '../cli/skill.js';
|
|
12
|
+
import { ConfigurationError, errorMessage } from '../internal/errors.js';
|
|
13
|
+
import { loadProjectConfig } from './config.js';
|
|
14
|
+
import { SessionHost } from './session.js';
|
|
15
|
+
import { errorResult } from './tools.js';
|
|
16
|
+
const INSTRUCTIONS = `e2e is a local-first end-to-end test runner; this server drives an e2e project's app (its e2e.config.ts) live.
|
|
17
|
+
Call open_session (optionally with a target and a config path) to get a session, its tool catalog, and the first observation. Then call {tool, args} runs any catalog tool: observe, tap, type, press, select, scroll, navigate, type_secret, locate, screenshot, and the project's own tools; tools lists them, tools {tool} shows one tool's arguments. close_session when done.
|
|
18
|
+
Write deterministic tests (tests/*.e2e.ts) from what you saw and run them with the CLI: npx --no-install e2e run <file>. Resources e2e://guide and e2e://guide/{topic} hold the writing guide.`;
|
|
19
|
+
/** Serves until the client disconnects or `signal` aborts; resolves with the exit code. */
|
|
20
|
+
export async function serveMcp(options) {
|
|
21
|
+
const server = new McpServer({ name: 'e2e', title: 'e2e', version: options.version }, { capabilities: { logging: {}, tools: {}, resources: {} }, instructions: INSTRUCTIONS });
|
|
22
|
+
const log = (level, message) => {
|
|
23
|
+
options.log(`[${level}] ${message}`);
|
|
24
|
+
if (server.isConnected())
|
|
25
|
+
void server.sendLoggingMessage({ level, logger: 'e2e', data: message }).catch(() => undefined);
|
|
26
|
+
};
|
|
27
|
+
const host = new SessionHost({
|
|
28
|
+
loadConfig: (configPath) => loadProjectConfig({ cwd: options.cwd, configPath: configPath ?? options.configPath, env: options.env }),
|
|
29
|
+
env: options.env,
|
|
30
|
+
headed: options.headed,
|
|
31
|
+
defaultTarget: options.target,
|
|
32
|
+
log,
|
|
33
|
+
});
|
|
34
|
+
for (const spec of host.toolSpecs()) {
|
|
35
|
+
server.registerTool(spec.name, {
|
|
36
|
+
description: spec.description,
|
|
37
|
+
inputSchema: spec.inputSchema,
|
|
38
|
+
annotations: { readOnlyHint: spec.readOnly, openWorldHint: false },
|
|
39
|
+
}, async (args, extra) => {
|
|
40
|
+
// A failure is a result the agent can react to, never a protocol error.
|
|
41
|
+
try {
|
|
42
|
+
return await spec.call(args, { signal: extra.signal });
|
|
43
|
+
}
|
|
44
|
+
catch (cause) {
|
|
45
|
+
return errorResult(cause);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
registerGuide(server);
|
|
50
|
+
const transport = new StdioServerTransport(options.stdin, options.stdout);
|
|
51
|
+
const closed = new Promise((resolve) => {
|
|
52
|
+
// The SDK's Server exposes a plain callback property, not an event target.
|
|
53
|
+
// oxlint-disable-next-line unicorn/prefer-add-event-listener
|
|
54
|
+
server.server.onclose = () => resolve();
|
|
55
|
+
options.signal?.addEventListener('abort', () => resolve(), { once: true });
|
|
56
|
+
});
|
|
57
|
+
await server.connect(transport);
|
|
58
|
+
log('info', `e2e mcp ${options.version} serving ${options.cwd}`);
|
|
59
|
+
await closed;
|
|
60
|
+
try {
|
|
61
|
+
const summary = await host.close('server shutdown');
|
|
62
|
+
if (summary !== 'No session is open.')
|
|
63
|
+
options.log(summary);
|
|
64
|
+
}
|
|
65
|
+
catch (cause) {
|
|
66
|
+
options.log(`session teardown failed: ${errorMessage(cause)}`);
|
|
67
|
+
}
|
|
68
|
+
await server.close().catch((cause) => options.log(`server close failed: ${errorMessage(cause)}`));
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
/** The skill as resources, for clients that read context without a tool call. */
|
|
72
|
+
function registerGuide(server) {
|
|
73
|
+
server.registerResource('guide', 'e2e://guide', { title: 'e2e guide', description: 'How to set up e2e, write tests, use agent steps, run the CLI, and read a failing run', mimeType: 'text/markdown' }, async (uri) => ({ contents: [{ uri: uri.href, mimeType: 'text/markdown', text: readGuide(undefined) ?? 'The skill is not part of this installation.' }] }));
|
|
74
|
+
server.registerResource('guide-topic', new ResourceTemplate('e2e://guide/{topic}', {
|
|
75
|
+
list: async () => ({
|
|
76
|
+
resources: skillTopics().map((topic) => ({ uri: `e2e://guide/${topic}`, name: topic, mimeType: 'text/markdown' })),
|
|
77
|
+
}),
|
|
78
|
+
}), { title: 'e2e guide topic', description: `One topic of the guide: ${skillTopics().join(', ')}`, mimeType: 'text/markdown' }, async (uri, variables) => {
|
|
79
|
+
const topic = String(variables['topic']);
|
|
80
|
+
const text = readGuide(topic);
|
|
81
|
+
if (text === undefined)
|
|
82
|
+
throw new ConfigurationError('UNKNOWN_TOPIC', `unknown topic "${topic}"; topics: ${skillTopics().join(', ')}`);
|
|
83
|
+
return { contents: [{ uri: uri.href, mimeType: 'text/markdown', text }] };
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The live session behind `e2e mcp`: one standalone attempt on one target
|
|
3
|
+
* with one interactive agent step open on it, driven by a coding agent
|
|
4
|
+
* through a fixed, four-tool surface. `open_session` loads the project's
|
|
5
|
+
* config and opens the attempt and the step; `tools` renders the session's
|
|
6
|
+
* catalog; `call` runs one catalog tool by name inside the step;
|
|
7
|
+
* `close_session` ends the step and tears the attempt down. The catalog is
|
|
8
|
+
* data, not registrations, so it follows the config and the target without a
|
|
9
|
+
* restart and the client's tool list never changes.
|
|
10
|
+
*/
|
|
11
|
+
import type { ResolvedConfig } from '../config/resolve.ts';
|
|
12
|
+
import { type McpToolCallExtra, type McpToolResult, type McpToolSpec } from './tools.ts';
|
|
13
|
+
export interface OpenSessionOptions {
|
|
14
|
+
/** The target to open; otherwise the server's `--target`, else the config's only target. */
|
|
15
|
+
readonly target?: string | undefined;
|
|
16
|
+
/** A config file to load instead of the server's default, relative to the server's directory. */
|
|
17
|
+
readonly config?: string | undefined;
|
|
18
|
+
}
|
|
19
|
+
export interface SessionHostOptions {
|
|
20
|
+
/** Loads a config fresh for each session, so an edited config applies without a restart; `configPath` overrides the server's default. */
|
|
21
|
+
readonly loadConfig: (configPath: string | undefined) => Promise<ResolvedConfig>;
|
|
22
|
+
readonly env: NodeJS.ProcessEnv;
|
|
23
|
+
readonly headed: boolean;
|
|
24
|
+
/** The target every session opens on, from `--target`; a call may still name one. */
|
|
25
|
+
readonly defaultTarget?: string | undefined;
|
|
26
|
+
readonly log: (level: 'info' | 'warning' | 'error', message: string) => void;
|
|
27
|
+
readonly idleMs?: number | undefined;
|
|
28
|
+
readonly ttlMs?: number | undefined;
|
|
29
|
+
}
|
|
30
|
+
/** Owns at most one live session and the fixed MCP tools that drive it. */
|
|
31
|
+
export declare class SessionHost {
|
|
32
|
+
private readonly options;
|
|
33
|
+
private live;
|
|
34
|
+
private opening;
|
|
35
|
+
/** Why the previous session ended, for the error a call on a closed session gets. */
|
|
36
|
+
private lastEnd;
|
|
37
|
+
constructor(options: SessionHostOptions);
|
|
38
|
+
get isOpen(): boolean;
|
|
39
|
+
/** The server's tools: the same four whatever the project, the config, or the target. */
|
|
40
|
+
toolSpecs(): readonly McpToolSpec[];
|
|
41
|
+
/** Opens a session and returns its opening text: the summary, the catalog, and the first screen. */
|
|
42
|
+
open(options: OpenSessionOptions): Promise<string>;
|
|
43
|
+
/** Closes the live session, if any, and returns what happened. */
|
|
44
|
+
close(reason: string, session?: string): Promise<string>;
|
|
45
|
+
/** The session's catalog, or one tool's full contract. */
|
|
46
|
+
catalog(session: string | undefined, tool: string | undefined): string;
|
|
47
|
+
/** Runs one catalog tool inside the live session's step. */
|
|
48
|
+
call(session: string | undefined, name: string, args: Record<string, unknown>, extra: McpToolCallExtra): Promise<McpToolResult>;
|
|
49
|
+
private openSession;
|
|
50
|
+
/** Opens the app when the engine can navigate, then observes. */
|
|
51
|
+
private firstScreen;
|
|
52
|
+
private openingText;
|
|
53
|
+
private catalogLines;
|
|
54
|
+
private resolveTarget;
|
|
55
|
+
/** Every configured credential's password as a step secret, so `type_secret` can fill it. */
|
|
56
|
+
private secretParams;
|
|
57
|
+
private requireLive;
|
|
58
|
+
private wrongSession;
|
|
59
|
+
private unknownTool;
|
|
60
|
+
private touch;
|
|
61
|
+
/** Runs one tool body inside the live session's step. */
|
|
62
|
+
private run;
|
|
63
|
+
private openSpec;
|
|
64
|
+
private catalogSpec;
|
|
65
|
+
private callSpec;
|
|
66
|
+
private closeSpec;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/mcp/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,sBAAsB,CAAC;AAQ3E,OAAO,EAA0E,KAAK,gBAAgB,EAAE,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAwBjK,MAAM,WAAW,kBAAkB;IACjC,4FAA4F;IAC5F,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,iGAAiG;IACjG,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC;AAED,MAAM,WAAW,kBAAkB;IACjC,yIAAyI;IACzI,QAAQ,CAAC,UAAU,EAAE,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IACjF,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,qFAAqF;IACrF,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,2EAA2E;AAC3E,qBAAa,WAAW;IAMV,OAAO,CAAC,QAAQ,CAAC,OAAO;IALpC,OAAO,CAAC,IAAI,CAA0B;IACtC,OAAO,CAAC,OAAO,CAA8B;IAC7C,qFAAqF;IACrF,OAAO,CAAC,OAAO,CAAqB;IAEpC,YAA6B,OAAO,EAAE,kBAAkB,EAAI;IAE5D,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,yFAAyF;IACzF,SAAS,IAAI,SAAS,WAAW,EAAE,CAElC;IAED,oGAAoG;IACpG,IAAI,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAajD;IAED,kEAAkE;IAC5D,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAe7D;IAED,0DAA0D;IAC1D,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAQrE;IAED,4DAA4D;IAC5D,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,CAAC,CAK9H;YAEa,WAAW;IA6EzB,iEAAiE;YACnD,WAAW;IAazB,OAAO,CAAC,WAAW;IAqBnB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,aAAa;IAmBrB,6FAA6F;IAC7F,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,KAAK;IAWb,yDAAyD;IACzD,OAAO,CAAC,GAAG;IAQX,OAAO,CAAC,QAAQ;IAchB,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,QAAQ;IAehB,OAAO,CAAC,SAAS;CAWlB"}
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The live session behind `e2e mcp`: one standalone attempt on one target
|
|
3
|
+
* with one interactive agent step open on it, driven by a coding agent
|
|
4
|
+
* through a fixed, four-tool surface. `open_session` loads the project's
|
|
5
|
+
* config and opens the attempt and the step; `tools` renders the session's
|
|
6
|
+
* catalog; `call` runs one catalog tool by name inside the step;
|
|
7
|
+
* `close_session` ends the step and tears the attempt down. The catalog is
|
|
8
|
+
* data, not registrations, so it follows the config and the target without a
|
|
9
|
+
* restart and the client's tool list never changes.
|
|
10
|
+
*/
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
import { openInteractiveStep } from '../agent/interactive-step.js';
|
|
14
|
+
import { ScreenPresenter } from '../agent/screen-update.js';
|
|
15
|
+
import { credentials } from '../credentials.js';
|
|
16
|
+
import { ConfigurationError, errorMessage } from '../internal/errors.js';
|
|
17
|
+
import { uuidv7 } from '../internal/ids.js';
|
|
18
|
+
import { LocatorEngine } from '../locator/engine.js';
|
|
19
|
+
import { openStandaloneAttempt } from '../run/standalone.js';
|
|
20
|
+
import { createSessionCatalog, isGrammarVerb } from './catalog.js';
|
|
21
|
+
import { catalogLine, defineMcpTool, describeToolDetail, invokeTool, textResult } from './tools.js';
|
|
22
|
+
/** How long one session may live, whatever happens. */
|
|
23
|
+
const SESSION_TTL_MS = 4 * 60 * 60 * 1000;
|
|
24
|
+
/** A session nobody has touched for this long is closed, so no browser is left behind. */
|
|
25
|
+
const SESSION_IDLE_MS = 30 * 60 * 1000;
|
|
26
|
+
/** How long the attempt outlives its step, so a step that hit the TTL is still closed in order. */
|
|
27
|
+
const CLOSE_GRACE_MS = 60 * 1000;
|
|
28
|
+
const SESSION_INSTRUCTION = 'Interactive session: a coding agent drives the app over MCP.';
|
|
29
|
+
/** Owns at most one live session and the fixed MCP tools that drive it. */
|
|
30
|
+
export class SessionHost {
|
|
31
|
+
options;
|
|
32
|
+
live;
|
|
33
|
+
opening;
|
|
34
|
+
/** Why the previous session ended, for the error a call on a closed session gets. */
|
|
35
|
+
lastEnd;
|
|
36
|
+
constructor(options) {
|
|
37
|
+
this.options = options;
|
|
38
|
+
}
|
|
39
|
+
get isOpen() {
|
|
40
|
+
return this.live !== undefined;
|
|
41
|
+
}
|
|
42
|
+
/** The server's tools: the same four whatever the project, the config, or the target. */
|
|
43
|
+
toolSpecs() {
|
|
44
|
+
return [this.openSpec(), this.catalogSpec(), this.callSpec(), this.closeSpec()];
|
|
45
|
+
}
|
|
46
|
+
/** Opens a session and returns its opening text: the summary, the catalog, and the first screen. */
|
|
47
|
+
open(options) {
|
|
48
|
+
if (this.live !== undefined) {
|
|
49
|
+
return Promise.reject(new ConfigurationError('SESSION_OPEN', `session ${this.live.id} is already open on target "${this.live.target.name}"; use it, or close_session first`));
|
|
50
|
+
}
|
|
51
|
+
this.opening ??= this.openSession(options).finally(() => {
|
|
52
|
+
this.opening = undefined;
|
|
53
|
+
});
|
|
54
|
+
return this.opening;
|
|
55
|
+
}
|
|
56
|
+
/** Closes the live session, if any, and returns what happened. */
|
|
57
|
+
async close(reason, session) {
|
|
58
|
+
const live = this.live;
|
|
59
|
+
if (live === undefined)
|
|
60
|
+
return 'No session is open.';
|
|
61
|
+
if (session !== undefined && session !== live.id)
|
|
62
|
+
throw this.wrongSession(live, session);
|
|
63
|
+
this.live = undefined;
|
|
64
|
+
this.lastEnd = reason;
|
|
65
|
+
if (live.idleTimer !== undefined)
|
|
66
|
+
clearTimeout(live.idleTimer);
|
|
67
|
+
await live.step.end({ status: 'passed', summary: `session closed: ${reason}` });
|
|
68
|
+
const outcome = await live.step.done;
|
|
69
|
+
const cleanupErrors = await live.attempt.close();
|
|
70
|
+
live.abort.abort();
|
|
71
|
+
const lines = [`Session ${live.id} closed (${reason}); ${live.actions} tool calls ran.`];
|
|
72
|
+
if (outcome.error !== undefined)
|
|
73
|
+
lines.push(`The session step ended with: ${errorMessage(outcome.error)}`);
|
|
74
|
+
for (const error of cleanupErrors)
|
|
75
|
+
lines.push(`Cleanup: ${error.code}: ${error.message}`);
|
|
76
|
+
return lines.join('\n');
|
|
77
|
+
}
|
|
78
|
+
/** The session's catalog, or one tool's full contract. */
|
|
79
|
+
catalog(session, tool) {
|
|
80
|
+
const live = this.requireLive(session);
|
|
81
|
+
if (tool === undefined) {
|
|
82
|
+
return [`Session ${live.id} on target "${live.target.name}": ${Object.keys(live.catalog.tools).length} tools. Run one with call {tool, args}; tools {tool} shows a tool's arguments.`, ...this.catalogLines(live)].join('\n');
|
|
83
|
+
}
|
|
84
|
+
const found = live.catalog.tools[tool];
|
|
85
|
+
if (found === undefined)
|
|
86
|
+
throw this.unknownTool(live, tool);
|
|
87
|
+
return describeToolDetail(tool, found, live.catalog.readOnly.has(tool));
|
|
88
|
+
}
|
|
89
|
+
/** Runs one catalog tool inside the live session's step. */
|
|
90
|
+
call(session, name, args, extra) {
|
|
91
|
+
const live = this.requireLive(session);
|
|
92
|
+
const tool = live.catalog.tools[name];
|
|
93
|
+
if (tool === undefined)
|
|
94
|
+
throw this.unknownTool(live, name);
|
|
95
|
+
return this.run(live, () => invokeTool(name, tool, args, extra));
|
|
96
|
+
}
|
|
97
|
+
async openSession(options) {
|
|
98
|
+
const config = await this.options.loadConfig(options.config);
|
|
99
|
+
const target = this.resolveTarget(config, options.target);
|
|
100
|
+
const ttlMs = this.options.ttlMs ?? SESSION_TTL_MS;
|
|
101
|
+
const abort = new AbortController();
|
|
102
|
+
const id = uuidv7();
|
|
103
|
+
let attempt;
|
|
104
|
+
let step;
|
|
105
|
+
try {
|
|
106
|
+
attempt = await openStandaloneAttempt({
|
|
107
|
+
config,
|
|
108
|
+
target,
|
|
109
|
+
headed: this.options.headed,
|
|
110
|
+
env: this.options.env,
|
|
111
|
+
signal: abort.signal,
|
|
112
|
+
timeoutMs: ttlMs + CLOSE_GRACE_MS,
|
|
113
|
+
artifactsRoot: path.join(config.projectRoot, '.e2e', 'artifacts'),
|
|
114
|
+
notice: (scope, message) => this.options.log('info', `${scope}: ${message}`),
|
|
115
|
+
});
|
|
116
|
+
// The coding agent is the brain: the step is driven from here, and the
|
|
117
|
+
// configured model stays out of the way.
|
|
118
|
+
const params = this.secretParams(config);
|
|
119
|
+
step = await openInteractiveStep(attempt.agentRuntime, {
|
|
120
|
+
instruction: SESSION_INSTRUCTION,
|
|
121
|
+
...(params === undefined ? {} : { params }),
|
|
122
|
+
timeout: ttlMs,
|
|
123
|
+
});
|
|
124
|
+
const screen = new ScreenPresenter();
|
|
125
|
+
const catalog = createSessionCatalog({
|
|
126
|
+
context: step.context,
|
|
127
|
+
screen,
|
|
128
|
+
session: attempt.session,
|
|
129
|
+
executor: config.agent.executor,
|
|
130
|
+
locator: new LocatorEngine({
|
|
131
|
+
session: attempt.session,
|
|
132
|
+
budget: attempt.budget,
|
|
133
|
+
runId: attempt.runId,
|
|
134
|
+
attemptId: attempt.attemptId,
|
|
135
|
+
actionTimeout: config.actionTimeout,
|
|
136
|
+
assertionTimeout: config.assertionTimeout,
|
|
137
|
+
}),
|
|
138
|
+
warn: (message) => this.options.log('warning', message),
|
|
139
|
+
});
|
|
140
|
+
const live = {
|
|
141
|
+
id,
|
|
142
|
+
target,
|
|
143
|
+
configPath: config.configPath ?? options.config ?? 'e2e.config.ts',
|
|
144
|
+
attempt,
|
|
145
|
+
step,
|
|
146
|
+
screen,
|
|
147
|
+
catalog,
|
|
148
|
+
abort,
|
|
149
|
+
idleTimer: undefined,
|
|
150
|
+
actions: 0,
|
|
151
|
+
};
|
|
152
|
+
this.live = live;
|
|
153
|
+
this.lastEnd = undefined;
|
|
154
|
+
// A step that ends on its own (the TTL, a hard stop) ends the session.
|
|
155
|
+
void step.done.then(async (outcome) => {
|
|
156
|
+
if (this.live !== live)
|
|
157
|
+
return;
|
|
158
|
+
const why = outcome.error === undefined ? 'the session step concluded' : errorMessage(outcome.error);
|
|
159
|
+
this.options.log('warning', `session ${id} ended: ${why}`);
|
|
160
|
+
await this.close(why);
|
|
161
|
+
});
|
|
162
|
+
this.touch(live);
|
|
163
|
+
return this.openingText(live, config, await this.firstScreen(live));
|
|
164
|
+
}
|
|
165
|
+
catch (cause) {
|
|
166
|
+
// Whatever failed after the attempt opened, the attempt is torn down:
|
|
167
|
+
// an engine or app process left behind would block the next session.
|
|
168
|
+
this.live = undefined;
|
|
169
|
+
await step?.end({ status: 'failed', summary: 'opening the session failed' }).catch(() => undefined);
|
|
170
|
+
await attempt?.close().catch(() => undefined);
|
|
171
|
+
abort.abort();
|
|
172
|
+
throw cause;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/** Opens the app when the engine can navigate, then observes. */
|
|
176
|
+
async firstScreen(live) {
|
|
177
|
+
const base = live.target.app.base?.href;
|
|
178
|
+
if (base !== undefined && live.step.context.target.verbs.has('navigate')) {
|
|
179
|
+
try {
|
|
180
|
+
await live.step.run((context) => context.actions.navigate(base));
|
|
181
|
+
}
|
|
182
|
+
catch (cause) {
|
|
183
|
+
return `Opening ${base} failed: ${errorMessage(cause)}\nUse navigate once the app is reachable.`;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const observation = await live.step.run((context) => context.observe());
|
|
187
|
+
return live.screen.initial(observation);
|
|
188
|
+
}
|
|
189
|
+
openingText(live, config, screen) {
|
|
190
|
+
const engine = live.target.engine;
|
|
191
|
+
const lines = [
|
|
192
|
+
`Session ${live.id} open on target "${live.target.name}" (platform ${live.target.platform}, engine ${engine === undefined ? 'none' : `${engine.name} ${engine.version}`}), ${this.options.headed ? 'headed' : 'headless'}; config ${live.configPath}.`,
|
|
193
|
+
];
|
|
194
|
+
if (live.target.app.base !== undefined) {
|
|
195
|
+
lines.push(`App: ${live.target.app.base.href}; allowed origins: ${live.target.app.allowedOrigins.join(', ')}.`);
|
|
196
|
+
}
|
|
197
|
+
if (config.credentials.size > 0) {
|
|
198
|
+
const described = [...config.credentials.values()].map((credential) => `"${credential.name}" (username ${JSON.stringify(credential.username)})`);
|
|
199
|
+
lines.push(`Credentials: ${described.join(', ')}. Type the username with type; fill the password with type_secret and the credential name.`);
|
|
200
|
+
}
|
|
201
|
+
lines.push(`Tools (run one with call {tool, args}; tools {tool} shows a tool's arguments):`, ...this.catalogLines(live));
|
|
202
|
+
lines.push('Node ids ("n42") are valid only for the newest observation; every action reports what changed on screen, and observe shows the whole screen. Call close_session when you are done.');
|
|
203
|
+
return `${lines.join('\n')}\n\n${screen}`;
|
|
204
|
+
}
|
|
205
|
+
catalogLines(live) {
|
|
206
|
+
return Object.entries(live.catalog.tools).map(([name, tool]) => catalogLine(name, tool, live.catalog.readOnly.has(name)));
|
|
207
|
+
}
|
|
208
|
+
resolveTarget(config, requested) {
|
|
209
|
+
const name = requested ?? this.options.defaultTarget;
|
|
210
|
+
const names = config.targets.map((target) => target.name);
|
|
211
|
+
if (name !== undefined) {
|
|
212
|
+
const target = config.targets.find((candidate) => candidate.name === name);
|
|
213
|
+
if (target === undefined) {
|
|
214
|
+
throw new ConfigurationError('UNKNOWN_TARGET', `unknown target "${name}"; the config declares ${names.map((n) => `"${n}"`).join(', ')}`);
|
|
215
|
+
}
|
|
216
|
+
return target;
|
|
217
|
+
}
|
|
218
|
+
const [only] = config.targets;
|
|
219
|
+
if (only !== undefined && config.targets.length === 1)
|
|
220
|
+
return only;
|
|
221
|
+
if (only === undefined)
|
|
222
|
+
throw new ConfigurationError('INVALID_CONFIG', 'the config declares no targets');
|
|
223
|
+
throw new ConfigurationError('TARGET_REQUIRED', `the config declares several targets (${names.join(', ')}); pass target to open_session, or start e2e mcp --target <name>`);
|
|
224
|
+
}
|
|
225
|
+
/** Every configured credential's password as a step secret, so `type_secret` can fill it. */
|
|
226
|
+
secretParams(config) {
|
|
227
|
+
if (config.credentials.size === 0)
|
|
228
|
+
return undefined;
|
|
229
|
+
return Object.fromEntries([...config.credentials.keys()].map((name) => [name, credentials.user(name).password]));
|
|
230
|
+
}
|
|
231
|
+
requireLive(session) {
|
|
232
|
+
if (this.live === undefined) {
|
|
233
|
+
const previous = this.lastEnd === undefined ? '' : ` (the previous session ended: ${this.lastEnd})`;
|
|
234
|
+
throw new ConfigurationError('NO_SESSION', `no session is open; call open_session first${previous}`);
|
|
235
|
+
}
|
|
236
|
+
if (session !== undefined && session !== this.live.id)
|
|
237
|
+
throw this.wrongSession(this.live, session);
|
|
238
|
+
return this.live;
|
|
239
|
+
}
|
|
240
|
+
wrongSession(live, session) {
|
|
241
|
+
return new ConfigurationError('NO_SESSION', `session "${session}" is not open; the open session is ${live.id}`);
|
|
242
|
+
}
|
|
243
|
+
unknownTool(live, name) {
|
|
244
|
+
if (isGrammarVerb(name)) {
|
|
245
|
+
return new ConfigurationError('UNSUPPORTED_CAPABILITY', `tool "${name}" is not available in this session: the engine of target "${live.target.name}" declares no such action; tools lists what it can do`);
|
|
246
|
+
}
|
|
247
|
+
return new ConfigurationError('UNKNOWN_TOOL', `tool "${name}" is not available in this session; tools: ${Object.keys(live.catalog.tools).join(', ')}`);
|
|
248
|
+
}
|
|
249
|
+
touch(live) {
|
|
250
|
+
if (live.idleTimer !== undefined)
|
|
251
|
+
clearTimeout(live.idleTimer);
|
|
252
|
+
const idleMs = this.options.idleMs ?? SESSION_IDLE_MS;
|
|
253
|
+
live.idleTimer = setTimeout(() => {
|
|
254
|
+
if (this.live !== live)
|
|
255
|
+
return;
|
|
256
|
+
this.options.log('warning', `session ${live.id} idle for ${Math.round(idleMs / 60_000)} minutes; closing it`);
|
|
257
|
+
void this.close('idle timeout');
|
|
258
|
+
}, idleMs);
|
|
259
|
+
live.idleTimer.unref();
|
|
260
|
+
}
|
|
261
|
+
/** Runs one tool body inside the live session's step. */
|
|
262
|
+
run(live, body) {
|
|
263
|
+
this.touch(live);
|
|
264
|
+
live.actions += 1;
|
|
265
|
+
return live.step.run(body);
|
|
266
|
+
}
|
|
267
|
+
// --- the fixed tools ---
|
|
268
|
+
openSpec() {
|
|
269
|
+
return defineMcpTool({
|
|
270
|
+
name: 'open_session',
|
|
271
|
+
description: 'Open a live session on one target of an e2e project: loads the config, starts the app command the engine declares (if any), boots the engine (a browser, a simulator), opens the app URL, and returns the session id, the catalog of tools it can run, and the first observation. One session at a time. Then act with call and look with call {tool: "observe"}.',
|
|
272
|
+
inputSchema: z.object({
|
|
273
|
+
target: z.string().min(1).optional().describe('Target name from the config; required when the config declares several'),
|
|
274
|
+
config: z.string().min(1).optional().describe("Path to an e2e config file, relative to the server's directory; default: the nearest e2e.config.ts"),
|
|
275
|
+
}),
|
|
276
|
+
readOnly: false,
|
|
277
|
+
call: async (args) => textResult(await this.open(args)),
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
catalogSpec() {
|
|
281
|
+
return defineMcpTool({
|
|
282
|
+
name: 'tools',
|
|
283
|
+
description: "List the tools the open session can run through call: observe, the grammar its engine honors (tap, type, press, select, scroll, navigate, type_secret), locate, screenshot, and the project's own tools. With tool, shows that tool's full description and the JSON Schema of its arguments.",
|
|
284
|
+
inputSchema: z.object({
|
|
285
|
+
tool: z.string().min(1).optional().describe('A catalog tool name, for its full contract'),
|
|
286
|
+
session: z.string().min(1).optional().describe('Session id; defaults to the open session'),
|
|
287
|
+
}),
|
|
288
|
+
readOnly: true,
|
|
289
|
+
call: async (args) => textResult(this.catalog(args.session, args.tool)),
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
callSpec() {
|
|
293
|
+
return defineMcpTool({
|
|
294
|
+
name: 'call',
|
|
295
|
+
description: "Run one tool of the open session by name, with its arguments as an object: call {tool: \"tap\", args: {target: \"n42\"}}. The session's catalog (from open_session or tools) names the tools and their arguments. Actions report what changed on screen; node ids are valid only for the newest observation.",
|
|
296
|
+
inputSchema: z.object({
|
|
297
|
+
tool: z.string().min(1).describe('A catalog tool name, e.g. "observe", "tap", "locate", "screenshot"'),
|
|
298
|
+
args: z.record(z.string(), z.unknown()).optional().describe("The tool's arguments; omit for a tool without any"),
|
|
299
|
+
session: z.string().min(1).optional().describe('Session id; defaults to the open session'),
|
|
300
|
+
}),
|
|
301
|
+
readOnly: false,
|
|
302
|
+
call: (args, extra) => this.call(args.session, args.tool, args.args ?? {}, extra),
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
closeSpec() {
|
|
306
|
+
return defineMcpTool({
|
|
307
|
+
name: 'close_session',
|
|
308
|
+
description: 'Close the live session: end the attempt, dispose the engine, and stop the app processes the session started.',
|
|
309
|
+
inputSchema: z.object({
|
|
310
|
+
session: z.string().min(1).optional().describe('Session id; defaults to the open session'),
|
|
311
|
+
}),
|
|
312
|
+
readOnly: false,
|
|
313
|
+
call: async (args) => textResult(await this.close('closed by the agent', args.session)),
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/mcp/session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bridge from the agent's AI SDK tools to the MCP `call` tool. The
|
|
3
|
+
* testing agent's vocabulary (the grammar, the engine packs, the project's
|
|
4
|
+
* `defineTool` values) is a set of AI SDK tools: a description, a zod input
|
|
5
|
+
* schema, and an execute function. The server keeps its own tool list fixed
|
|
6
|
+
* and small, and serves that vocabulary as a catalog: `tools` renders it from
|
|
7
|
+
* the same descriptions and schemas the testing agent reads, and `call`
|
|
8
|
+
* validates the arguments against the schema, runs the tool, and renders its
|
|
9
|
+
* output in the MCP result envelope.
|
|
10
|
+
*/
|
|
11
|
+
import { type JSONSchema7, type ToolSet } from 'ai';
|
|
12
|
+
import type { z } from 'zod';
|
|
13
|
+
/** One MCP content part this server emits. */
|
|
14
|
+
export type McpContent = {
|
|
15
|
+
readonly type: 'text';
|
|
16
|
+
readonly text: string;
|
|
17
|
+
} | {
|
|
18
|
+
readonly type: 'image';
|
|
19
|
+
readonly data: string;
|
|
20
|
+
readonly mimeType: string;
|
|
21
|
+
};
|
|
22
|
+
/** The MCP tool result envelope, as the SDK's `CallToolResult` spells it. */
|
|
23
|
+
export interface McpToolResult {
|
|
24
|
+
readonly content: McpContent[];
|
|
25
|
+
readonly isError?: boolean;
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
/** What a tool body gets from the request. */
|
|
29
|
+
export interface McpToolCallExtra {
|
|
30
|
+
readonly signal: AbortSignal;
|
|
31
|
+
}
|
|
32
|
+
/** What the server registers: the contract and the body of one of its fixed tools. */
|
|
33
|
+
export interface McpToolSpec<Shape extends z.ZodRawShape = z.ZodRawShape> {
|
|
34
|
+
readonly name: string;
|
|
35
|
+
readonly description: string;
|
|
36
|
+
readonly inputSchema: z.ZodObject<Shape>;
|
|
37
|
+
/** Hint for clients: a tool that changes nothing on the app or disk. */
|
|
38
|
+
readonly readOnly: boolean;
|
|
39
|
+
/** Runs the tool on validated arguments; a thrown error becomes an error result. */
|
|
40
|
+
call(args: z.infer<z.ZodObject<Shape>>, extra: McpToolCallExtra): Promise<McpToolResult>;
|
|
41
|
+
}
|
|
42
|
+
/** Declares one fixed tool with `call` typed from its `inputSchema`. */
|
|
43
|
+
export declare function defineMcpTool<Shape extends z.ZodRawShape>(spec: McpToolSpec<Shape>): McpToolSpec<Shape>;
|
|
44
|
+
export declare function textResult(text: string): McpToolResult;
|
|
45
|
+
/**
|
|
46
|
+
* A failure as a tool result the agent can react to, never a protocol error:
|
|
47
|
+
* the code and message are what a test would have reported.
|
|
48
|
+
*/
|
|
49
|
+
export declare function errorResult(cause: unknown): McpToolResult;
|
|
50
|
+
/**
|
|
51
|
+
* The JSON Schema of a tool's arguments, without the draft marker clients
|
|
52
|
+
* never need. A zod schema converts synchronously; a schema that only
|
|
53
|
+
* resolves lazily is shown as an open object rather than awaited, since the
|
|
54
|
+
* catalog is rendered inline.
|
|
55
|
+
*/
|
|
56
|
+
export declare function toolJsonSchema(tool: ToolSet[string]): JSONSchema7;
|
|
57
|
+
/**
|
|
58
|
+
* One catalog line: the name, the argument names (`?` marks an optional
|
|
59
|
+
* one), the first sentence of the description, and the read-only mark.
|
|
60
|
+
* `- tap {target}: Tap or click one node.`
|
|
61
|
+
*/
|
|
62
|
+
export declare function catalogLine(name: string, tool: ToolSet[string], readOnly: boolean): string;
|
|
63
|
+
/** The full contract of one tool: its description and the JSON Schema of its arguments. */
|
|
64
|
+
export declare function describeToolDetail(name: string, tool: ToolSet[string], readOnly: boolean): string;
|
|
65
|
+
/**
|
|
66
|
+
* Validates the arguments against the tool's own schema and runs it. The
|
|
67
|
+
* server's `call` tool takes any object, so this is where a wrong argument is
|
|
68
|
+
* caught, with the same message a model would get, before the tool runs.
|
|
69
|
+
*/
|
|
70
|
+
export declare function invokeTool(name: string, tool: ToolSet[string], args: Record<string, unknown>, extra: McpToolCallExtra): Promise<McpToolResult>;
|
|
71
|
+
/**
|
|
72
|
+
* Renders a tool's output the way the tool renders it for a model: text as
|
|
73
|
+
* text, and a `toModelOutput` that yields file parts as images. Anything
|
|
74
|
+
* else is JSON, so a structured output is never lost.
|
|
75
|
+
*/
|
|
76
|
+
export declare function resultFromOutput(tool: ToolSet[string], output: unknown, input?: unknown): Promise<McpToolResult>;
|
|
77
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAY,KAAK,WAAW,EAAwC,KAAK,OAAO,EAAE,MAAM,IAAI,CAAC;AACpG,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAG7B,8CAA8C;AAC9C,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChD;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjF,6EAA6E;AAC7E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,UAAU,EAAE,CAAC;IAC/B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,8CAA8C;AAC9C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;CAC9B;AAED,sFAAsF;AACtF,MAAM,WAAW,WAAW,CAAC,KAAK,SAAS,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW;IACtE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACzC,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,oFAAoF;IACpF,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CAC1F;AAED,wEAAwE;AACxE,wBAAgB,aAAa,CAAC,KAAK,SAAS,CAAC,CAAC,WAAW,EAAE,IAAI,EAAE,WAAW,CAAC,KAAK,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,CAEvG;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAEtD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAGzD;AAKD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,WAAW,CAKjE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,CAM1F;AAED,2FAA2F;AAC3F,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,CAQjG;AAmBD;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,EACrB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,KAAK,EAAE,gBAAgB,GACtB,OAAO,CAAC,aAAa,CAAC,CAYxB;AAgDD;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,GAAE,OAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,CAOjI"}
|