@nudge-ui/mcp 0.1.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 ADDED
@@ -0,0 +1,77 @@
1
+ # `@nudge-ui/mcp`
2
+
3
+ `@nudge-ui/mcp` connects a local Nudge UI inspector to an MCP-capable coding
4
+ agent. It combines a standard MCP stdio server with a project-scoped loopback
5
+ browser bridge. The browser does not speak MCP directly.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ pnpm add -D @nudge-ui/mcp
11
+ ```
12
+
13
+ ### Configure automatically
14
+
15
+ From the application project root, use [`add-mcp`](https://github.com/neon-solutions/add-mcp) to detect supported coding agents and write each host's native configuration format:
16
+
17
+ ```sh
18
+ npx add-mcp \
19
+ 'npx -y @nudge-ui/mcp@latest --project-id my-app --origin http://localhost:5173 --workspace-root .' \
20
+ --name nudge_ui
21
+ ```
22
+
23
+ The command prompts for detected project agents. Use `-a codex` (or another
24
+ supported agent name) to target one host, or `-y` to skip the prompt and use
25
+ the detected project agents. Keep this project-scoped; do not use `-g` unless
26
+ the configuration intentionally targets one fixed project.
27
+
28
+ `--origin` must exactly match the development application's browser origin.
29
+ The `--project-id` value must match the ID used by the Nudge host integration.
30
+ For standard Vite and Astro projects, `my-app` normally matches the project
31
+ directory name. Next.js and standalone HTML projects use host-specific IDs, so
32
+ retain the explicit ID from their integration configuration.
33
+
34
+ The generated command uses the published `@nudge-ui/mcp` package through
35
+ `npx`. Pin the package version instead of `@latest` when reproducible tool
36
+ versions are required. After configuration, restart or reload the agent host
37
+ so it refreshes its MCP tool catalog.
38
+
39
+ The package has no install-time project mutation. The manual configuration
40
+ below remains useful when an agent host is not supported by `add-mcp` or when
41
+ you need an explicit custom command.
42
+
43
+ After adding or changing the host configuration, restart or reload the MCP
44
+ server, or start a fresh agent task. Open **Connect MCP** from the inspector's
45
+ prompt menu for setup instructions, connection status, and explicit pairing.
46
+ You can pair a page before the agent starts listening. Ask the agent to call
47
+ `nudge_listen` and keep it active to enable sending. **Copy prompt** remains
48
+ available while the agent is idle; prompts are not queued.
49
+
50
+ For Codex desktop, confirm that `nudge_ui` appears in the current task after
51
+ the MCP restart. For Codex CLI, `codex mcp list` verifies the project entry.
52
+ Restart a long-running OpenCode service after adding or changing its MCP
53
+ configuration so it refreshes its tool catalog.
54
+
55
+ ## Run manually
56
+
57
+ ```sh
58
+ pnpm exec nudge-mcp \
59
+ --project-id my-app \
60
+ --origin http://localhost:5173 \
61
+ --workspace-root /path/to/my-app
62
+ ```
63
+
64
+ Pass the exact application origin with `--origin` (or
65
+ `NUDGE_UI_ORIGIN`). The bridge rejects browser origins that are not explicitly
66
+ configured; it never trusts the first origin that connects. The bridge chooses
67
+ a deterministic loopback port from the project ID so the inspector can find
68
+ it without a port file. Use `--port 0` for an ephemeral test port.
69
+
70
+ For integrations that establish the browser origin out of band, trusted host
71
+ code can use the `pairBrowser` API as an explicit manual approval path. An
72
+ unconfigured bridge does not accept HTTP pairing requests.
73
+
74
+ The agent should call `nudge_listen` immediately, apply the delivered prompt
75
+ through its normal source-editing workflow, call `nudge_report_status`, and
76
+ listen again. Canvas tools are limited to same-origin routes and agent-owned
77
+ groups.
package/dist/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Charles Smart
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,79 @@
1
+ import { type Server } from "node:http";
2
+ import type { AgentPromptRequest, AgentRequestOutcome, AgentStatusSnapshot, AgentStatusUpdate, CanvasCommandResult, PairingResponse } from "@nudge-ui/agent-protocol";
3
+ /** A clock port keeps command timeout tests deterministic. */
4
+ export interface BridgeClock {
5
+ now(): number;
6
+ setTimeout(callback: () => void, delayMs: number): unknown;
7
+ clearTimeout(handle: unknown): void;
8
+ }
9
+ export interface BrowserBridgeOptions {
10
+ /** Stable project identity used to scope every browser message. */
11
+ readonly projectId: string;
12
+ /** Canonical app origin that browser routes must use. */
13
+ readonly origin?: string;
14
+ /** Explicit browser origins accepted by the loopback HTTP server. */
15
+ readonly allowedOrigins?: readonly string[];
16
+ readonly host?: "127.0.0.1" | "::1" | "localhost";
17
+ /** Use port 0 in tests or when several projects run concurrently. */
18
+ readonly port?: number;
19
+ readonly commandTimeoutMs?: number;
20
+ readonly clock?: BridgeClock;
21
+ /** Test seam; production defaults to cryptographically random tokens. */
22
+ readonly tokenFactory?: () => string;
23
+ /** Test seam; production defaults to random UUIDs. */
24
+ readonly idFactory?: () => string;
25
+ /**
26
+ * Best-effort hook used by the CLI to reopen the last paired page when an
27
+ * agent rearms listening but no browser controller stream is attached.
28
+ */
29
+ readonly onControllerUnavailable?: (pageUrl: string) => Promise<void> | void;
30
+ }
31
+ export interface BridgeAddress {
32
+ readonly host: string;
33
+ readonly port: number;
34
+ readonly url: string;
35
+ }
36
+ export type BridgeLifecycle = "created" | "listening" | "closed";
37
+ export interface BrowserBridge {
38
+ readonly httpServer: Server;
39
+ readonly projectId: string;
40
+ readonly origin: string | null;
41
+ /** The configured origin, or the explicitly approved browser origin. */
42
+ readonly effectiveOrigin: string | null;
43
+ readonly lifecycle: BridgeLifecycle;
44
+ readonly address: BridgeAddress | null;
45
+ readonly sessionToken: string | null;
46
+ readonly lastPageUrl: string | null;
47
+ /** Current companion/browser state, safe to expose to the browser. */
48
+ getStatus(): AgentStatusSnapshot;
49
+ start(): Promise<BridgeAddress>;
50
+ close(): Promise<void>;
51
+ /** Blocks until a paired browser dispatches a prompt. */
52
+ waitForPrompt(signal?: AbortSignal): Promise<AgentPromptRequest>;
53
+ /** Marks the agent-side listener as stopped without ending the project. */
54
+ cancelListener(reason?: string): void;
55
+ /** Reports the terminal result for the currently running browser request. */
56
+ updateRequestStatus(update: AgentStatusUpdate | AgentRequestOutcome): void;
57
+ /** Sends a Canvas command to the browser and waits for one bounded ack. */
58
+ dispatchCanvasCommand(command: CanvasCommandInput): Promise<CanvasCommandResult>;
59
+ /** Returns the currently paired browser token to the host process only. */
60
+ pairBrowser(projectId: string, requestOrigin: string, pageUrl?: string): PairingResponse;
61
+ /** Revokes a browser pairing; no data is written to disk. */
62
+ disconnectBrowser(token: string): void;
63
+ }
64
+ export type CanvasCommandInput = Omit<import("@nudge-ui/agent-protocol").ReadCanvasStateCommand, "commandId"> & {
65
+ readonly commandId?: string;
66
+ } | Omit<import("@nudge-ui/agent-protocol").PresentRoutesCommand, "commandId"> & {
67
+ readonly commandId?: string;
68
+ } | Omit<import("@nudge-ui/agent-protocol").FocusCanvasGroupCommand, "commandId"> & {
69
+ readonly commandId?: string;
70
+ } | Omit<import("@nudge-ui/agent-protocol").FitCanvasCommand, "commandId"> & {
71
+ readonly commandId?: string;
72
+ } | Omit<import("@nudge-ui/agent-protocol").RemoveCanvasGroupCommand, "commandId"> & {
73
+ readonly commandId?: string;
74
+ };
75
+ /**
76
+ * Creates the local browser bridge. It binds only to loopback and keeps every
77
+ * pairing, prompt, command, and acknowledgement in process memory.
78
+ */
79
+ export declare function createLoopbackBridge(options: BrowserBridgeOptions): BrowserBridge;