underwritten-mcp 0.0.1 → 0.0.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.
Files changed (2) hide show
  1. package/README.md +114 -146
  2. package/package.json +4 -1
package/README.md CHANGED
@@ -1,28 +1,23 @@
1
- # Underwritten MCP Bridge
1
+ # underwritten-mcp
2
2
 
3
- `underwritten-mcp` is the local MCP companion for `underwritten.app`.
3
+ `underwritten-mcp` is the published MCP server package for [Underwritten](https://underwritten.app). It runs as a local process, speaks MCP over `stdio`, exposes a localhost HTTP bridge for the browser app, and routes tool calls into the active Underwritten editor session.
4
4
 
5
- It is the MCP server. The browser app is not.
5
+ The package exists because Underwritten itself is a browser app. External MCP clients need a local companion that can:
6
6
 
7
- ## Why this package exists
7
+ - start from a normal MCP command
8
+ - bind a localhost API the browser can reach
9
+ - pair with a live Underwritten tab
10
+ - operate on the real editor and workspace state already open in the app
8
11
 
9
- `underwritten.app` is a PWA and the editor state lives in the browser. External MCP clients need a local process that can:
12
+ ## Install
10
13
 
11
- - speak MCP over `stdio`
12
- - bind a localhost API
13
- - coordinate with the live browser editor session
14
+ You can run the published package directly:
14
15
 
15
- This package provides that bridge while the PWA remains the source of truth for document and workspace state.
16
-
17
- ## Monorepo layout
18
-
19
- - `apps/mcp`: publishable MCP server package and localhost bridge
20
- - `apps/mcp/src/contract.ts`: shared browser-safe protocol types and markdown edit helpers
21
- - `apps/website`: PWA integration, bridge discovery, session registration, polling, and settings UI
22
-
23
- ## User setup
16
+ ```bash
17
+ npx -y underwritten-mcp
18
+ ```
24
19
 
25
- The intended end-user MCP config is:
20
+ The intended end-user MCP configuration is:
26
21
 
27
22
  ```json
28
23
  {
@@ -35,178 +30,151 @@ The intended end-user MCP config is:
35
30
  }
36
31
  ```
37
32
 
38
- After that:
33
+ After the process starts:
39
34
 
40
- 1. The MCP client starts `underwritten-mcp`.
41
- 2. The bridge binds a localhost API on `127.0.0.1`.
42
- 3. The PWA discovers the bridge automatically and pairs without a manual token-copy step.
43
- 4. The active editor tab registers itself and starts polling for pending actions.
35
+ 1. `underwritten-mcp` binds a free localhost port on `127.0.0.1`.
36
+ 2. The Underwritten web app discovers the bridge with `GET /discover`.
37
+ 3. The browser pairs with `POST /pair`.
38
+ 4. The active tab syncs session state and executes queued actions.
44
39
 
45
- ## Running locally
40
+ ## What It Does
46
41
 
47
- Install dependencies first:
42
+ - Runs an MCP server over `stdio`
43
+ - Exposes a localhost bridge for the browser app
44
+ - Routes file and document operations to the most relevant live Underwritten tab
45
+ - Shares browser-safe contract types through `underwritten-mcp/contract`
46
+ - Keeps the browser app as the source of truth for workspace and editor state
48
47
 
49
- ```bash
50
- vp install
51
- ```
48
+ This is the published bridge package. The website app is not the MCP server.
52
49
 
53
- Run the website in one terminal:
50
+ ## Tool Surface
54
51
 
55
- ```bash
56
- vp run website#dev
57
- ```
52
+ Workspace tools:
58
53
 
59
- Run the MCP bridge from source in another terminal:
54
+ - `get_workspace_status`
55
+ - `list_files`
56
+ - `read_file`
57
+ - `open_file`
58
+ - `create_file`
59
+ - `create_folder`
60
+ - `move_path`
61
+ - `delete_path`
62
+ - `save_document`
60
63
 
61
- ```bash
62
- vp dlx tsx apps/mcp/src/cli.ts
63
- ```
64
+ Document tools:
64
65
 
65
- If you want to run the built output instead:
66
+ - `get_current_document`
67
+ - `replace_current_document`
68
+ - `apply_markdown_edits`
66
69
 
67
- ```bash
68
- vp exec tsc -p apps/mcp/tsconfig.build.json
69
- node apps/mcp/dist/cli.js
70
- ```
70
+ `apply_markdown_edits` works on raw markdown text with literal anchored matching. Targets use a literal `text` value and optional 1-based `occurrence`. Edits apply sequentially against the updated buffer, and ambiguous or missing matches fail explicitly.
71
71
 
72
- If you want to test the user-facing bootstrap shape locally, the equivalent MCP client entry is still:
72
+ ## Bridge Behavior
73
73
 
74
- ```json
75
- {
76
- "mcpServers": {
77
- "underwritten": {
78
- "command": "npx",
79
- "args": ["-y", "underwritten-mcp"]
80
- }
81
- }
82
- }
83
- ```
74
+ The bridge only listens on `127.0.0.1` and uses a reserved port range of `45261-45271` by default. The browser app pairs first, receives a bearer token, and then uses that token for sync and status requests.
84
75
 
85
- ## Validation
76
+ Endpoints:
86
77
 
87
- Repo-level validation:
78
+ - `GET /discover`
79
+ - `POST /pair`
80
+ - `POST /session/sync`
81
+ - `POST /session/disconnect`
82
+ - `GET /status`
88
83
 
89
- ```bash
90
- vp check
91
- vp test
92
- vp run test:e2e
93
- ```
84
+ The browser polls every `1000ms`, and sessions expire after `15000ms` without a fresh heartbeat.
94
85
 
95
- MCP package build only:
86
+ ## Session Routing
96
87
 
97
- ```bash
98
- vp exec tsc -p apps/mcp/tsconfig.build.json
99
- ```
88
+ Tool calls target one live browser session at a time. Routing is deterministic:
100
89
 
101
- ## Discovery and pairing
90
+ 1. Keep only live, non-disconnected sessions.
91
+ 2. Prefer the most recent `lastFocusAt`.
92
+ 3. Break ties with the most recent `lastHeartbeatAt`.
93
+ 4. Break any remaining tie lexically by `sessionId`.
102
94
 
103
- By default the PWA scans a reserved localhost port range and probes `GET /discover`.
95
+ If no live session is available, the bridge returns an explicit error instead of guessing.
104
96
 
105
- - Reserved range: `45261-45271`
106
- - Bind address: `127.0.0.1`
107
- - Pairing: automatic `POST /pair` from an approved Underwritten origin
108
- - Session sync: `POST /session/sync`
109
- - Status: `GET /status`
97
+ ## Package Exports
110
98
 
111
- For test isolation and local debugging, the browser also honors a `localStorage` override at `underwritten.mcp.bridgePorts` with an explicit JSON array of ports to probe.
99
+ Default package exports:
112
100
 
113
- ## Browser session model
101
+ - `startUnderwrittenBridge`
102
+ - `resolveBridgePort`
103
+ - `UnderwrittenBridgeService`
104
+ - `UnderwrittenBridgeError`
114
105
 
115
- Each tab registers as a distinct session with:
106
+ Contract exports are available from `underwritten-mcp/contract`, including:
116
107
 
117
- - `sessionId`
118
- - `activeFilePath`
119
- - `title`
120
- - `markdown`
121
- - `dirty`
122
- - `storageMode`
123
- - `nativeFolderSelected`
124
- - `revision`
125
- - `lastHeartbeatAt`
126
- - `lastFocusAt`
127
- - `visibilityState`
128
- - `pageUrl`
129
- - `windowLabel`
108
+ - `underwrittenBridgePortRange`
109
+ - `underwrittenBridgeApiVersion`
110
+ - markdown edit types and helpers
111
+ - session, status, and action types shared with the browser app
130
112
 
131
- The browser polls the bridge, receives queued actions, applies them through the existing `EditorPage` editor/workspace code paths, and posts the result back in the next sync.
113
+ Example:
132
114
 
133
- ## Tool surface
115
+ ```ts
116
+ import { startUnderwrittenBridge } from "underwritten-mcp";
117
+ import { underwrittenBridgePortRange } from "underwritten-mcp/contract";
134
118
 
135
- Workspace tools:
136
-
137
- - `get_workspace_status`
138
- - `list_files`
139
- - `read_file`
140
- - `open_file`
141
- - `create_file`
142
- - `create_folder`
143
- - `move_path`
144
- - `delete_path`
145
- - `save_document`
119
+ const bridge = await startUnderwrittenBridge({
120
+ connectStdio: true,
121
+ portRange: underwrittenBridgePortRange,
122
+ });
123
+ ```
146
124
 
147
- Markdown tools:
125
+ ## Local Development In This Monorepo
148
126
 
149
- - `get_current_document`
150
- - `replace_current_document`
151
- - `apply_markdown_edits`
127
+ Install dependencies:
152
128
 
153
- ### Markdown edit semantics
129
+ ```bash
130
+ vp install
131
+ ```
154
132
 
155
- `apply_markdown_edits` operates on raw markdown text, not editor-internal node ids.
133
+ Run the website:
156
134
 
157
- - Target matching is literal text matching.
158
- - `occurrence` is 1-based when provided.
159
- - If `occurrence` is omitted, the target text must match exactly once.
160
- - Ambiguous or missing targets fail with a clear error.
161
- - Edits apply sequentially to the updated markdown buffer.
135
+ ```bash
136
+ vp run website#dev
137
+ ```
162
138
 
163
- ## Deterministic routing
139
+ Run the bridge from source in another terminal:
164
140
 
165
- Current-document and workspace tools route to one live session:
141
+ ```bash
142
+ vp dlx tsx apps/mcp/src/cli.ts
143
+ ```
166
144
 
167
- 1. Keep only live sessions. A live session has a fresh heartbeat within `15s` and has not disconnected.
168
- 2. Sort by most recent `lastFocusAt`.
169
- 3. Break ties with most recent `lastHeartbeatAt`.
170
- 4. Break remaining ties lexically by `sessionId`.
171
- 5. If no live session remains, return an explicit error instead of guessing.
145
+ Build the package:
172
146
 
173
- This keeps tool routing deterministic across multiple open tabs while leaving room for future explicit `sessionId` targeting.
147
+ ```bash
148
+ vp run mcp#build
149
+ ```
174
150
 
175
- ## Security model
151
+ If you want to run the built entrypoint directly:
176
152
 
177
- - The HTTP bridge binds only to `127.0.0.1`.
178
- - Requests are limited to Underwritten origins plus local dev origins (`localhost` and `127.0.0.1`).
179
- - The browser must pair first and then present a bridge-issued bearer token.
180
- - The bridge does not expose shell execution.
181
- - Workspace operations are restricted to Underwritten’s real browser workspace model.
182
- - Native folder access still depends on the browser-granted File System Access handle already chosen in the app.
153
+ ```bash
154
+ node apps/mcp/dist/cli.js
155
+ ```
183
156
 
184
- ### Threat model limits
157
+ ## Validation
185
158
 
186
- - Pairing is automatic for approved origins, so trust is anchored in the origin allowlist and localhost-only binding.
187
- - A malicious script running on an approved Underwritten origin would inherit the same bridge access as the app.
188
- - Multiple simultaneous MCP bridge processes are supported, but the browser only probes the configured port list/range and pairs with bridges it can discover.
159
+ Run repo checks:
189
160
 
190
- ## Settings UI
161
+ ```bash
162
+ vp check
163
+ vp test
164
+ ```
191
165
 
192
- The existing settings dialog now includes an `MCP Bridge` section with:
166
+ Run the package build:
193
167
 
194
- - reachability / connection status
195
- - current session id
196
- - detected localhost port
197
- - known session count
198
- - copyable MCP config snippet
199
- - reconnect action
168
+ ```bash
169
+ vp run mcp#build
170
+ ```
200
171
 
201
- ## Current limitations
172
+ ## Monorepo Context
202
173
 
203
- - The public tool surface does not yet support explicit `sessionId` targeting.
204
- - `delete_path.force` only bypasses unsaved-current-document protection; it is not a separate recursive-delete flag because the browser file APIs already define directory deletion behavior.
205
- - The settings panel reports bridge/session status from the local browser’s perspective; it is not a multi-client admin console.
174
+ - `apps/mcp`: publishable MCP bridge package
175
+ - `apps/mcp/src/contract.ts`: shared protocol types and markdown edit helpers
176
+ - `apps/website`: Underwritten web app that discovers, pairs with, and drives the bridge
206
177
 
207
- ## Future improvements
178
+ ## License
208
179
 
209
- - explicit `sessionId` tool targeting
210
- - richer bridge diagnostics and per-session inspection
211
- - optional Streamable HTTP exposure in addition to `stdio`
212
- - stronger persisted pairing state if the product needs cross-restart trust continuity
180
+ MIT
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "underwritten-mcp",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "private": false,
5
5
  "license": "MIT",
6
+ "repository": {
7
+ "url": "https://github.com/rjerue/underwritten.app"
8
+ },
6
9
  "bin": {
7
10
  "underwritten-mcp": "./dist/cli.js"
8
11
  },