@nudge-ui/mcp 0.2.0 → 0.2.2
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 +138 -53
- package/dist/adapterRouting.d.ts +16 -0
- package/dist/adapterRouting.js +59 -0
- package/dist/bridge.d.ts +4 -2
- package/dist/bridge.js +255 -16
- package/dist/cli.d.ts +3 -2
- package/dist/cli.js +75 -14
- package/dist/cli.mjs +15386 -14423
- package/dist/discovery.d.ts +46 -0
- package/dist/discovery.js +160 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/internal/agent-protocol.d.ts +65 -1
- package/dist/internal/agent-protocol.js +183 -3
- package/dist/project.d.ts +54 -0
- package/dist/project.js +151 -0
- package/dist/projectSessionClient.d.ts +33 -0
- package/dist/projectSessionClient.js +103 -0
- package/dist/protocol.d.ts +14 -2
- package/dist/protocol.js +13 -1
- package/dist/server.d.ts +15 -0
- package/dist/server.js +142 -34
- package/dist/sessionRegistry.d.ts +17 -0
- package/dist/sessionRegistry.js +121 -0
- package/dist/sessionSelection.d.ts +15 -0
- package/dist/sessionSelection.js +41 -0
- package/package.json +6 -1
package/dist/project.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { randomBytes, randomUUID } from "node:crypto";
|
|
3
|
+
import { chmod, lstat, mkdir, open, realpath, rename, rm } from "node:fs/promises";
|
|
4
|
+
import { userInfo } from "node:os";
|
|
5
|
+
import { basename, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
6
|
+
import { promisify } from "node:util";
|
|
7
|
+
import { AGENT_PROTOCOL_VERSION, canonicalOrigin } from "./internal/agent-protocol.js";
|
|
8
|
+
import { createLoopbackBridge } from "./bridge.js";
|
|
9
|
+
const execFileAsync = promisify(execFile);
|
|
10
|
+
const REGISTRY_SCHEMA_VERSION = 1;
|
|
11
|
+
export function defaultSessionRegistryRoot() {
|
|
12
|
+
// MCP hosts commonly sanitize TMPDIR and XDG_RUNTIME_DIR for stdio child
|
|
13
|
+
// processes. The OS account home is stable across the development server
|
|
14
|
+
// and agent adapter, so both processes discover the same private registry.
|
|
15
|
+
return join(userInfo().homedir, ".cache", "nudge-ui", "sessions");
|
|
16
|
+
}
|
|
17
|
+
async function gitValue(cwd, args) {
|
|
18
|
+
try {
|
|
19
|
+
const result = await execFileAsync("git", ["-C", cwd, ...args], {
|
|
20
|
+
encoding: "utf8",
|
|
21
|
+
timeout: 2_000,
|
|
22
|
+
windowsHide: true,
|
|
23
|
+
});
|
|
24
|
+
const value = result.stdout.trim();
|
|
25
|
+
return value.length > 0 ? value : undefined;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function workspaceMetadata(appRootInput, workspaceRootInput) {
|
|
32
|
+
const appRoot = await realpath(resolve(appRootInput));
|
|
33
|
+
const workspaceRoot = await resolveWorkspaceRoot(workspaceRootInput ?? appRoot);
|
|
34
|
+
assertAppInsideWorkspace(appRoot, workspaceRoot);
|
|
35
|
+
const git = await readGitMetadata(workspaceRoot);
|
|
36
|
+
return { appRoot, workspaceRoot, ...git };
|
|
37
|
+
}
|
|
38
|
+
function assertAppInsideWorkspace(appRoot, workspaceRoot) {
|
|
39
|
+
const appRelative = relative(workspaceRoot, appRoot);
|
|
40
|
+
if (appRelative === ".." || appRelative.startsWith(`..${sep}`) || isAbsolute(appRelative)) {
|
|
41
|
+
throw new TypeError("appRoot must be inside the canonical workspace root");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function readGitMetadata(workspaceRoot) {
|
|
45
|
+
const [commonDirectory, branch] = await Promise.all([
|
|
46
|
+
gitValue(workspaceRoot, ["rev-parse", "--git-common-dir"]),
|
|
47
|
+
gitValue(workspaceRoot, ["symbolic-ref", "--quiet", "--short", "HEAD"]),
|
|
48
|
+
]);
|
|
49
|
+
const gitCommonDir = commonDirectory === undefined
|
|
50
|
+
? undefined
|
|
51
|
+
: await realpath(resolve(workspaceRoot, commonDirectory));
|
|
52
|
+
return { ...(gitCommonDir ? { gitCommonDir } : {}), ...(branch ? { branch } : {}) };
|
|
53
|
+
}
|
|
54
|
+
/** Resolves a checkout or worktree without conflating related worktrees. */
|
|
55
|
+
export async function resolveWorkspaceRoot(input) {
|
|
56
|
+
const canonicalInput = await realpath(resolve(input));
|
|
57
|
+
const gitTopLevel = await gitValue(canonicalInput, ["rev-parse", "--show-toplevel"]);
|
|
58
|
+
return gitTopLevel ? await realpath(gitTopLevel) : canonicalInput;
|
|
59
|
+
}
|
|
60
|
+
async function ensurePrivateRegistryDirectory(registryRoot) {
|
|
61
|
+
await mkdir(registryRoot, { recursive: true, mode: 0o700 });
|
|
62
|
+
const directory = await lstat(registryRoot);
|
|
63
|
+
if (!directory.isDirectory() || directory.isSymbolicLink()) {
|
|
64
|
+
throw new Error("The Nudge session registry must be a real directory.");
|
|
65
|
+
}
|
|
66
|
+
if (typeof process.getuid === "function" && directory.uid !== process.getuid()) {
|
|
67
|
+
throw new Error("The Nudge session registry must be owned by the current user.");
|
|
68
|
+
}
|
|
69
|
+
await chmod(registryRoot, 0o700);
|
|
70
|
+
}
|
|
71
|
+
async function writeDescriptor(registryRoot, descriptor) {
|
|
72
|
+
await ensurePrivateRegistryDirectory(registryRoot);
|
|
73
|
+
const destination = join(registryRoot, `${descriptor.sessionId}.json`);
|
|
74
|
+
const temporary = join(registryRoot, `.${descriptor.sessionId}.${process.pid}.tmp`);
|
|
75
|
+
// Publish by renaming a private file so discovery never reads a partial record.
|
|
76
|
+
const file = await open(temporary, "wx", 0o600);
|
|
77
|
+
try {
|
|
78
|
+
await file.writeFile(`${JSON.stringify(descriptor)}\n`, "utf8").finally(() => file.close());
|
|
79
|
+
await rename(temporary, destination);
|
|
80
|
+
await chmod(destination, 0o600);
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
await rm(temporary, { force: true });
|
|
84
|
+
}
|
|
85
|
+
return destination;
|
|
86
|
+
}
|
|
87
|
+
async function closeProjectBridge(descriptorPath, bridge) {
|
|
88
|
+
try {
|
|
89
|
+
await rm(descriptorPath, { force: true });
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
await bridge.close();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Starts the project-owned browser bridge and registers it for local MCP
|
|
97
|
+
* adapters. The descriptor contains a private control credential and is never
|
|
98
|
+
* suitable for browser bundles or user-facing tool results.
|
|
99
|
+
*/
|
|
100
|
+
export async function startProjectBridge(options) {
|
|
101
|
+
const origin = canonicalOrigin(options.origin);
|
|
102
|
+
if (!origin)
|
|
103
|
+
throw new TypeError("origin must be a canonical HTTP(S) origin");
|
|
104
|
+
const metadata = await workspaceMetadata(options.appRoot, options.workspaceRoot);
|
|
105
|
+
const sessionId = randomUUID();
|
|
106
|
+
const projectId = options.projectId ?? basename(metadata.appRoot);
|
|
107
|
+
if (!projectId)
|
|
108
|
+
throw new TypeError("projectId must be a bounded non-empty string");
|
|
109
|
+
const controlToken = randomBytes(32).toString("base64url");
|
|
110
|
+
const bridge = createLoopbackBridge({
|
|
111
|
+
projectId,
|
|
112
|
+
origin,
|
|
113
|
+
allowedOrigins: options.allowedOrigins,
|
|
114
|
+
host: options.host,
|
|
115
|
+
port: options.port,
|
|
116
|
+
commandTimeoutMs: options.commandTimeoutMs,
|
|
117
|
+
agentControlToken: controlToken,
|
|
118
|
+
});
|
|
119
|
+
const address = await bridge.start();
|
|
120
|
+
const session = {
|
|
121
|
+
schemaVersion: REGISTRY_SCHEMA_VERSION,
|
|
122
|
+
protocolVersion: AGENT_PROTOCOL_VERSION,
|
|
123
|
+
sessionId,
|
|
124
|
+
projectId,
|
|
125
|
+
...metadata,
|
|
126
|
+
origin,
|
|
127
|
+
endpoint: address.url,
|
|
128
|
+
startedAt: new Date().toISOString(),
|
|
129
|
+
};
|
|
130
|
+
const registryRoot = resolve(options.registryRoot ?? defaultSessionRegistryRoot());
|
|
131
|
+
let descriptorPath;
|
|
132
|
+
try {
|
|
133
|
+
descriptorPath = await writeDescriptor(registryRoot, { ...session, pid: process.pid, controlToken });
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
await bridge.close();
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
let closePromise = null;
|
|
140
|
+
const close = () => {
|
|
141
|
+
closePromise ??= closeProjectBridge(descriptorPath, bridge);
|
|
142
|
+
return closePromise;
|
|
143
|
+
};
|
|
144
|
+
return {
|
|
145
|
+
bridge,
|
|
146
|
+
address,
|
|
147
|
+
session,
|
|
148
|
+
browser: { projectId, origin, bridgeUrl: address.url },
|
|
149
|
+
close,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { AgentPromptRequest, AgentStatusSnapshot, AgentStatusUpdate, CanvasCommandResult } from "./internal/agent-protocol.js";
|
|
2
|
+
import type { CanvasCommandInput } from "./bridge.js";
|
|
3
|
+
import type { StoredProjectSession } from "./project.js";
|
|
4
|
+
export interface SessionHealth {
|
|
5
|
+
readonly projectId: string;
|
|
6
|
+
readonly available: true;
|
|
7
|
+
readonly claimed: boolean;
|
|
8
|
+
readonly status: AgentStatusSnapshot;
|
|
9
|
+
}
|
|
10
|
+
/** Represents an HTTP response from a project bridge that could be classified. */
|
|
11
|
+
export declare class ProjectSessionResponseError extends Error {
|
|
12
|
+
readonly status: number;
|
|
13
|
+
constructor(status: number, message: string);
|
|
14
|
+
}
|
|
15
|
+
export declare function readSessionHealth(session: StoredProjectSession, timeoutMs?: number): Promise<SessionHealth>;
|
|
16
|
+
/** Owns one project's claim, heartbeat, and authenticated control requests. */
|
|
17
|
+
export declare class ProjectSessionClient {
|
|
18
|
+
readonly descriptor: StoredProjectSession;
|
|
19
|
+
readonly agentId: string;
|
|
20
|
+
private claimed;
|
|
21
|
+
private claimPromise;
|
|
22
|
+
private heartbeat;
|
|
23
|
+
private closing;
|
|
24
|
+
constructor(descriptor: StoredProjectSession, agentId: string);
|
|
25
|
+
private send;
|
|
26
|
+
private acquireClaim;
|
|
27
|
+
private claim;
|
|
28
|
+
waitForPrompt(signal?: AbortSignal): Promise<AgentPromptRequest>;
|
|
29
|
+
getStatus(): Promise<AgentStatusSnapshot>;
|
|
30
|
+
updateRequestStatus(update: AgentStatusUpdate): Promise<AgentStatusSnapshot>;
|
|
31
|
+
dispatchCanvasCommand(input: CanvasCommandInput): Promise<CanvasCommandResult>;
|
|
32
|
+
close(): Promise<void>;
|
|
33
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { AGENT_CONTROL_ENDPOINTS } from "./protocol.js";
|
|
3
|
+
const CONTROL_TIMEOUT_MS = 5_000;
|
|
4
|
+
const CANVAS_TIMEOUT_MS = 65_000;
|
|
5
|
+
/** Represents an HTTP response from a project bridge that could be classified. */
|
|
6
|
+
export class ProjectSessionResponseError extends Error {
|
|
7
|
+
status;
|
|
8
|
+
constructor(status, message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "ProjectSessionResponseError";
|
|
11
|
+
this.status = status;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
async function requestJson(session, path, init = {}, { signal, timeoutMs = CONTROL_TIMEOUT_MS } = {}) {
|
|
15
|
+
const response = await fetch(`${session.endpoint}${path}`, {
|
|
16
|
+
...init,
|
|
17
|
+
redirect: "error",
|
|
18
|
+
signal: signal ?? (timeoutMs === null ? undefined : AbortSignal.timeout(timeoutMs)),
|
|
19
|
+
headers: {
|
|
20
|
+
Authorization: `Bearer ${session.controlToken}`,
|
|
21
|
+
...(init.body === undefined ? {} : { "Content-Type": "application/json" }),
|
|
22
|
+
...init.headers,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
const body = await response.json().catch(() => undefined);
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
throw new ProjectSessionResponseError(response.status, body?.error?.message ?? `Nudge project bridge returned ${response.status}.`);
|
|
28
|
+
}
|
|
29
|
+
return body;
|
|
30
|
+
}
|
|
31
|
+
export async function readSessionHealth(session, timeoutMs = CONTROL_TIMEOUT_MS) {
|
|
32
|
+
return await requestJson(session, AGENT_CONTROL_ENDPOINTS.health, {}, { timeoutMs });
|
|
33
|
+
}
|
|
34
|
+
/** Owns one project's claim, heartbeat, and authenticated control requests. */
|
|
35
|
+
export class ProjectSessionClient {
|
|
36
|
+
descriptor;
|
|
37
|
+
agentId;
|
|
38
|
+
claimed = false;
|
|
39
|
+
claimPromise = null;
|
|
40
|
+
heartbeat = null;
|
|
41
|
+
closing = false;
|
|
42
|
+
constructor(descriptor, agentId) {
|
|
43
|
+
this.descriptor = descriptor;
|
|
44
|
+
this.agentId = agentId;
|
|
45
|
+
}
|
|
46
|
+
async send(path, payload = {}, options) {
|
|
47
|
+
return await requestJson(this.descriptor, path, {
|
|
48
|
+
method: "POST",
|
|
49
|
+
body: JSON.stringify({ agentId: this.agentId, ...payload }),
|
|
50
|
+
}, options);
|
|
51
|
+
}
|
|
52
|
+
async acquireClaim() {
|
|
53
|
+
await this.send(AGENT_CONTROL_ENDPOINTS.claim);
|
|
54
|
+
this.claimed = true;
|
|
55
|
+
this.heartbeat = setInterval(() => {
|
|
56
|
+
void this.send(AGENT_CONTROL_ENDPOINTS.heartbeat).catch(() => undefined);
|
|
57
|
+
}, 10_000);
|
|
58
|
+
this.heartbeat.unref();
|
|
59
|
+
}
|
|
60
|
+
async claim() {
|
|
61
|
+
if (this.closing)
|
|
62
|
+
throw new Error("The MCP adapter is closing.");
|
|
63
|
+
if (this.claimed)
|
|
64
|
+
return;
|
|
65
|
+
if (this.claimPromise)
|
|
66
|
+
return await this.claimPromise;
|
|
67
|
+
this.claimPromise = this.acquireClaim();
|
|
68
|
+
try {
|
|
69
|
+
await this.claimPromise;
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
this.claimPromise = null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async waitForPrompt(signal) {
|
|
76
|
+
await this.claim();
|
|
77
|
+
return await this.send(AGENT_CONTROL_ENDPOINTS.listen, {}, { signal, timeoutMs: null });
|
|
78
|
+
}
|
|
79
|
+
async getStatus() {
|
|
80
|
+
const health = await readSessionHealth(this.descriptor);
|
|
81
|
+
return health.status;
|
|
82
|
+
}
|
|
83
|
+
async updateRequestStatus(update) {
|
|
84
|
+
await this.claim();
|
|
85
|
+
return await this.send(AGENT_CONTROL_ENDPOINTS.reportStatus, { update });
|
|
86
|
+
}
|
|
87
|
+
async dispatchCanvasCommand(input) {
|
|
88
|
+
await this.claim();
|
|
89
|
+
const command = { ...input, commandId: input.commandId ?? randomUUID() };
|
|
90
|
+
return await this.send(AGENT_CONTROL_ENDPOINTS.canvas, { command }, { timeoutMs: CANVAS_TIMEOUT_MS });
|
|
91
|
+
}
|
|
92
|
+
async close() {
|
|
93
|
+
this.closing = true;
|
|
94
|
+
await this.claimPromise?.catch(() => undefined);
|
|
95
|
+
if (!this.claimed)
|
|
96
|
+
return;
|
|
97
|
+
this.claimed = false;
|
|
98
|
+
if (this.heartbeat)
|
|
99
|
+
clearInterval(this.heartbeat);
|
|
100
|
+
this.heartbeat = null;
|
|
101
|
+
await this.send(AGENT_CONTROL_ENDPOINTS.release).catch(() => undefined);
|
|
102
|
+
}
|
|
103
|
+
}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -5,18 +5,30 @@
|
|
|
5
5
|
* subpath keeps transport names discoverable for Node consumers while
|
|
6
6
|
* preserving one protocol vocabulary for browser and agent adapters.
|
|
7
7
|
*/
|
|
8
|
-
export { AGENT_PROTOCOL_LIMITS, AGENT_PROTOCOL_VERSION, CANVAS_COMMAND_TYPES, canonicalOrigin, defaultBridgePort, isAgentPromptRequest, isAgentStatusUpdate, isAllowedOrigin, isCanvasCommand, isCanvasGroup, isCanvasState, isCanonicalOrigin, isProjectIdentity, isSameOriginRoute, protocolError, validateRoutes, } from "./internal/agent-protocol.js";
|
|
9
|
-
export type { AgentConnectionState, AgentProjectIdentity, AgentPromptRequest, AgentRequestOutcome, AgentRequestStatus, AgentRoute, AgentStatusSnapshot, AgentStatusUpdate, BridgeCanvasCommandEvent, BridgeConnectedEvent, BridgeDisconnectedEvent, BridgeEnvelope, BridgeEvent, BridgeEventType, BridgeStatusEvent, CanvasCommand, CanvasCommandAcknowledgement, CanvasCommandError, CanvasCommandResult, CanvasGroup, CanvasGroupOwner, CanvasState, FitCanvasCommand, FocusCanvasGroupCommand, PairingRequest, PairingResponse, PresentRoutesCommand, PromptDispatchRequest, PromptDispatchResponse, ReadCanvasStateCommand, RemoveCanvasGroupCommand, StatusEventRequest, } from "./internal/agent-protocol.js";
|
|
8
|
+
export { AGENT_PROTOCOL_LIMITS, AGENT_PROTOCOL_VERSION, CANVAS_COMMAND_TYPES, canonicalOrigin, defaultBridgePort, isAgentPromptRequest, isAgentSketchAttachment, isAgentSketchCaptureMetadata, isAgentSketchMetadata, isAgentStatusUpdate, isAllowedOrigin, isCanvasCommand, isCanvasGroup, isCanvasState, isCanonicalOrigin, isProjectIdentity, isSameOriginRoute, protocolError, validateSketchAttachments, validateRoutes, } from "./internal/agent-protocol.js";
|
|
9
|
+
export type { AgentConnectionState, AgentProjectIdentity, AgentPromptRequest, AgentDeliveredPrompt, AgentSketchAttachment, AgentSketchCaptureMetadata, AgentSketchMetadata, AgentRequestOutcome, AgentRequestStatus, AgentRoute, AgentStatusSnapshot, AgentStatusUpdate, BridgeCanvasCommandEvent, BridgeConnectedEvent, BridgeDisconnectedEvent, BridgeEnvelope, BridgeEvent, BridgeEventType, BridgeStatusEvent, CanvasCommand, CanvasCommandAcknowledgement, CanvasCommandError, CanvasCommandResult, CanvasGroup, CanvasGroupOwner, CanvasState, FitCanvasCommand, FocusCanvasGroupCommand, PairingRequest, PairingResponse, PresentRoutesCommand, PromptDispatchRequest, PromptDispatchResponse, ReadCanvasStateCommand, RemoveCanvasGroupCommand, StatusEventRequest, } from "./internal/agent-protocol.js";
|
|
10
10
|
/** Stable endpoint names used by the loopback browser bridge. */
|
|
11
11
|
export declare const BRIDGE_ENDPOINTS: {
|
|
12
12
|
readonly health: "/health";
|
|
13
13
|
readonly pair: "/pair";
|
|
14
|
+
readonly takeover: "/takeover";
|
|
14
15
|
readonly events: "/events";
|
|
15
16
|
readonly prompt: "/prompt";
|
|
16
17
|
readonly status: "/status";
|
|
17
18
|
readonly canvasAcknowledgement: "/canvas/ack";
|
|
18
19
|
readonly disconnect: "/disconnect";
|
|
19
20
|
};
|
|
21
|
+
/** Private, loopback-only endpoints used by an MCP adapter process. */
|
|
22
|
+
export declare const AGENT_CONTROL_ENDPOINTS: {
|
|
23
|
+
readonly health: "/__nudge/agent/health";
|
|
24
|
+
readonly claim: "/__nudge/agent/claim";
|
|
25
|
+
readonly heartbeat: "/__nudge/agent/heartbeat";
|
|
26
|
+
readonly release: "/__nudge/agent/release";
|
|
27
|
+
readonly listen: "/__nudge/agent/listen";
|
|
28
|
+
readonly status: "/__nudge/agent/status";
|
|
29
|
+
readonly reportStatus: "/__nudge/agent/report-status";
|
|
30
|
+
readonly canvas: "/__nudge/agent/canvas";
|
|
31
|
+
};
|
|
20
32
|
export type BridgeEndpoint = (typeof BRIDGE_ENDPOINTS)[keyof typeof BRIDGE_ENDPOINTS];
|
|
21
33
|
/** Error body returned by every rejected bridge request. */
|
|
22
34
|
export interface BridgeHttpError {
|
package/dist/protocol.js
CHANGED
|
@@ -5,14 +5,26 @@
|
|
|
5
5
|
* subpath keeps transport names discoverable for Node consumers while
|
|
6
6
|
* preserving one protocol vocabulary for browser and agent adapters.
|
|
7
7
|
*/
|
|
8
|
-
export { AGENT_PROTOCOL_LIMITS, AGENT_PROTOCOL_VERSION, CANVAS_COMMAND_TYPES, canonicalOrigin, defaultBridgePort, isAgentPromptRequest, isAgentStatusUpdate, isAllowedOrigin, isCanvasCommand, isCanvasGroup, isCanvasState, isCanonicalOrigin, isProjectIdentity, isSameOriginRoute, protocolError, validateRoutes, } from "./internal/agent-protocol.js";
|
|
8
|
+
export { AGENT_PROTOCOL_LIMITS, AGENT_PROTOCOL_VERSION, CANVAS_COMMAND_TYPES, canonicalOrigin, defaultBridgePort, isAgentPromptRequest, isAgentSketchAttachment, isAgentSketchCaptureMetadata, isAgentSketchMetadata, isAgentStatusUpdate, isAllowedOrigin, isCanvasCommand, isCanvasGroup, isCanvasState, isCanonicalOrigin, isProjectIdentity, isSameOriginRoute, protocolError, validateSketchAttachments, validateRoutes, } from "./internal/agent-protocol.js";
|
|
9
9
|
/** Stable endpoint names used by the loopback browser bridge. */
|
|
10
10
|
export const BRIDGE_ENDPOINTS = {
|
|
11
11
|
health: "/health",
|
|
12
12
|
pair: "/pair",
|
|
13
|
+
takeover: "/takeover",
|
|
13
14
|
events: "/events",
|
|
14
15
|
prompt: "/prompt",
|
|
15
16
|
status: "/status",
|
|
16
17
|
canvasAcknowledgement: "/canvas/ack",
|
|
17
18
|
disconnect: "/disconnect",
|
|
18
19
|
};
|
|
20
|
+
/** Private, loopback-only endpoints used by an MCP adapter process. */
|
|
21
|
+
export const AGENT_CONTROL_ENDPOINTS = {
|
|
22
|
+
health: "/__nudge/agent/health",
|
|
23
|
+
claim: "/__nudge/agent/claim",
|
|
24
|
+
heartbeat: "/__nudge/agent/heartbeat",
|
|
25
|
+
release: "/__nudge/agent/release",
|
|
26
|
+
listen: "/__nudge/agent/listen",
|
|
27
|
+
status: "/__nudge/agent/status",
|
|
28
|
+
reportStatus: "/__nudge/agent/report-status",
|
|
29
|
+
canvas: "/__nudge/agent/canvas",
|
|
30
|
+
};
|
package/dist/server.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
3
3
|
import type { AgentProjectIdentity } from "./internal/agent-protocol.js";
|
|
4
4
|
import { type BrowserBridge, type BrowserBridgeOptions, type BridgeAddress } from "./bridge.js";
|
|
5
|
+
import { AdapterRouting } from "./adapterRouting.js";
|
|
6
|
+
import { DiscoveredProjectRouter } from "./discovery.js";
|
|
5
7
|
export declare const MCP_SERVER_NAME = "nudge-ui";
|
|
6
8
|
export declare const MCP_SERVER_VERSION: string;
|
|
7
9
|
/** Instructions are sent through MCP initialization for every host. */
|
|
@@ -26,3 +28,16 @@ export interface AgentCompanion {
|
|
|
26
28
|
* The returned companion is inert until `start` is called.
|
|
27
29
|
*/
|
|
28
30
|
export declare function createAgentCompanion(options: AgentCompanionOptions): AgentCompanion;
|
|
31
|
+
export interface DiscoveredAgentAdapter {
|
|
32
|
+
readonly mcpServer: McpServer;
|
|
33
|
+
readonly router: DiscoveredProjectRouter | AdapterRouting;
|
|
34
|
+
start(transport?: Transport): Promise<void>;
|
|
35
|
+
close(): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
/** Creates a stdio MCP adapter that discovers project-owned bridges. */
|
|
38
|
+
export declare function createDiscoveredAgentAdapter(options: {
|
|
39
|
+
readonly workspaceRoot?: string;
|
|
40
|
+
readonly registryRoot?: string;
|
|
41
|
+
readonly serverName?: string;
|
|
42
|
+
readonly serverVersion?: string;
|
|
43
|
+
}): Promise<DiscoveredAgentAdapter>;
|