foldkit-agent-webmcp 0.2.1 → 0.4.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 +154 -120
- package/package.json +9 -8
package/README.md
CHANGED
|
@@ -1,24 +1,39 @@
|
|
|
1
1
|
# `foldkit-agent-webmcp`
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
contract into browser tools, so an agent running **inside the page** can use the
|
|
5
|
-
application the person in front of it is already using.
|
|
3
|
+
Publishes a [`foldkit-agent`](../agent) runtime as browser-native WebMCP tools.
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
no DOM automation, no scraping, no second copy of the application's logic, and
|
|
12
|
-
no browser-session bridge.
|
|
5
|
+
Use it when an agent runs **inside the page** and should operate the same live
|
|
6
|
+
Foldkit application the person is using. This package does not define new agent
|
|
7
|
+
capabilities and does not automate the DOM. It translates the capabilities your
|
|
8
|
+
agent contract already exposes into `document.modelContext` tools.
|
|
13
9
|
|
|
14
10
|
```text
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
Foldkit application
|
|
12
|
+
|
|
|
13
|
+
v
|
|
14
|
+
AssistantAgent protocol-neutral contract
|
|
15
|
+
|
|
|
16
|
+
| AgentBuilder.bind(...)
|
|
17
|
+
v
|
|
18
|
+
AgentRuntime live Model + dispatch + policy
|
|
19
|
+
|
|
|
20
|
+
v
|
|
21
|
+
foldkit-agent-webmcp adapter only
|
|
22
|
+
|
|
|
23
|
+
v
|
|
24
|
+
document.modelContext browser tools
|
|
25
|
+
|
|
|
26
|
+
v
|
|
27
|
+
Message -> update -> Model + Commands
|
|
17
28
|
```
|
|
18
29
|
|
|
19
|
-
The
|
|
20
|
-
|
|
21
|
-
|
|
30
|
+
The authority boundary stays in `foldkit-agent`: the contract decides what the
|
|
31
|
+
agent may observe and which Messages it may cause. WebMCP only makes those
|
|
32
|
+
capabilities available to a browser-resident agent.
|
|
33
|
+
|
|
34
|
+
The adapter uses the WebMCP producer API when the host exposes it. Feature
|
|
35
|
+
detect `document.modelContext` at runtime; tool registration is optional
|
|
36
|
+
for browsers that do not provide it.
|
|
22
37
|
|
|
23
38
|
## Install
|
|
24
39
|
|
|
@@ -28,92 +43,89 @@ pnpm add foldkit-agent foldkit-agent-webmcp
|
|
|
28
43
|
|
|
29
44
|
`foldkit`, `effect`, and `foldkit-agent` are peer dependencies.
|
|
30
45
|
|
|
31
|
-
##
|
|
46
|
+
## Sixty seconds: publish a bound runtime
|
|
47
|
+
|
|
48
|
+
Start with the [Agent contract and host guide](../agent/README.md#usage).
|
|
49
|
+
The integration below assumes `AssistantAgent` is your declared contract and
|
|
50
|
+
`AgentBuilder` is its application-specialized builder. `mounted` below
|
|
51
|
+
is an application host (for example, the value returned by `Sync.mount`),
|
|
52
|
+
not an arbitrary Foldkit runtime handle.
|
|
32
53
|
|
|
33
|
-
|
|
54
|
+
|
|
55
|
+
Bind that contract to the running application, then register its currently
|
|
56
|
+
available capabilities with WebMCP:
|
|
34
57
|
|
|
35
58
|
```ts
|
|
36
59
|
import { AgentWebMcp } from 'foldkit-agent-webmcp'
|
|
37
60
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const agent = TodoAgent.bind({
|
|
41
|
-
definition: AppAgent,
|
|
61
|
+
const agentRuntime = AgentBuilder.bind({
|
|
62
|
+
definition: AssistantAgent,
|
|
42
63
|
host: {
|
|
43
64
|
model: mounted.model,
|
|
44
|
-
dispatch:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
observe: mounted.observe, // required by a capability with a completion contract
|
|
65
|
+
dispatch: message => mounted.dispatch(message),
|
|
66
|
+
subscribe: mounted.subscribe,
|
|
67
|
+
observe: mounted.observe, // Message-based completion
|
|
68
|
+
principal: () => currentPrincipal, // your authenticated application principal
|
|
49
69
|
},
|
|
50
70
|
})
|
|
51
71
|
|
|
52
|
-
// 2. Publish it, if this browser speaks WebMCP.
|
|
53
72
|
const modelContext = AgentWebMcp.documentModelContext()
|
|
73
|
+
|
|
54
74
|
if (modelContext !== undefined) {
|
|
55
|
-
const registration = AgentWebMcp.register({
|
|
56
|
-
|
|
75
|
+
const registration = AgentWebMcp.register({
|
|
76
|
+
agent: agentRuntime,
|
|
77
|
+
modelContext,
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
await registration.refresh()
|
|
81
|
+
window.addEventListener('pagehide', () => registration.unregister())
|
|
57
82
|
}
|
|
58
83
|
```
|
|
59
84
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
85
|
+
Also call `registration.unregister()` when the owning component or runtime
|
|
86
|
+
is disposed; a page unload listener alone does not handle in-page teardown.
|
|
87
|
+
`currentPrincipal` above comes from your application, not tool arguments.
|
|
88
|
+
|
|
89
|
+
That is the whole architecture: the contract remains protocol-neutral, the
|
|
90
|
+
runtime remains the one authority for availability/authorization/dispatch, and
|
|
91
|
+
this package translates it to the browser API.
|
|
66
92
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
registration.refresh()` when you want to know the tools are live.
|
|
93
|
+
`documentModelContext()` is safe during SSR and returns `undefined` when no
|
|
94
|
+
WebMCP producer surface exists. Calling `register()` without a model context
|
|
95
|
+
throws rather than silently pretending registration succeeded.
|
|
71
96
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
97
|
+
Registration starts immediately, but browser registration is asynchronous.
|
|
98
|
+
`register()` returns after reconciliation is scheduled; use
|
|
99
|
+
`await registration.refresh()` when the caller needs to know that the browser
|
|
100
|
+
has accepted the current tool set, and `onError` for background reconcile
|
|
101
|
+
failures.
|
|
76
102
|
|
|
77
103
|
## What a capability becomes
|
|
78
104
|
|
|
79
|
-
Each **currently available** capability becomes one tool:
|
|
105
|
+
Each **currently available** capability becomes one WebMCP tool:
|
|
80
106
|
|
|
81
107
|
```text
|
|
82
|
-
|
|
83
|
-
variant description -> description
|
|
84
|
-
derived JSON Schema -> inputSchema
|
|
85
|
-
Runtime dispatch -> execute
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
No schema and no handler is written twice. `execute` decodes its input through
|
|
89
|
-
the capability's own Effect Schema, runs `available` and `authorize`, and
|
|
90
|
-
dispatches; a capability with a `completion` contract resolves when the
|
|
91
|
-
correlated Message is applied, so the agent learns the outcome rather than that
|
|
92
|
-
the call was received.
|
|
108
|
+
foldkit-agent WebMCP
|
|
93
109
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
| `signal` | — | Unregisters everything when aborted. An already-aborted signal unregisters at once. |
|
|
101
|
-
| `followModel` | `true` | Reconcile registrations as the Model changes. Inert unless the host supports `subscribe`. |
|
|
102
|
-
| `invocationId` | `crypto.randomUUID()` | Supplies the invocation id. |
|
|
103
|
-
| `onError` | — | Reports a failure from a reconcile no caller is awaiting: the initial registration, or one a Model change triggered. |
|
|
110
|
+
capability name -> tool name
|
|
111
|
+
variant description -> description
|
|
112
|
+
encoded input Schema -> inputSchema
|
|
113
|
+
AgentRuntime dispatch -> execute
|
|
114
|
+
completion contract -> resolved tool outcome
|
|
115
|
+
```
|
|
104
116
|
|
|
105
|
-
|
|
117
|
+
No input schema or handler is written again. `execute` delegates to the bound
|
|
118
|
+
runtime, which performs the same decode, availability check, authorization,
|
|
119
|
+
dispatch, cancellation, and completion handling as every other adapter.
|
|
106
120
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
registration.unregister() // abort every registration and stop following
|
|
111
|
-
```
|
|
121
|
+
A capability with a completion contract resolves when its correlated success or
|
|
122
|
+
failure Message is observed. Without one, validated dispatch is the completion
|
|
123
|
+
boundary.
|
|
112
124
|
|
|
113
|
-
## Tools
|
|
125
|
+
## Tools follow the Model
|
|
114
126
|
|
|
115
|
-
Availability is
|
|
116
|
-
|
|
127
|
+
Availability is Model-dependent, so the advertised WebMCP tool set may change as
|
|
128
|
+
the application changes:
|
|
117
129
|
|
|
118
130
|
```ts
|
|
119
131
|
RequestedDeleteTodo: {
|
|
@@ -124,40 +136,52 @@ RequestedDeleteTodo: {
|
|
|
124
136
|
```
|
|
125
137
|
|
|
126
138
|
```text
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
139
|
+
no selection tools: create_todo
|
|
140
|
+
|
|
|
141
|
+
user selects todo
|
|
142
|
+
v
|
|
143
|
+
selected todo tools: create_todo, delete_todo
|
|
130
144
|
```
|
|
131
145
|
|
|
132
|
-
With `followModel`
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
146
|
+
With `followModel: true` (the default) and a host with `subscribe`, a Model
|
|
147
|
+
change reconciles registrations. A capability that disappears is unregistered;
|
|
148
|
+
a newly available capability is registered.
|
|
149
|
+
|
|
150
|
+
Availability is stronger than discoverability. A capability absent from the
|
|
151
|
+
tool list is also refused if a caller somehow invokes its known name.
|
|
137
152
|
|
|
138
|
-
|
|
139
|
-
|
|
153
|
+
Reconciliation is serialized, so overlapping Model changes cannot register the
|
|
154
|
+
same capability twice. A failed browser registration is not recorded as live and
|
|
155
|
+
is retried on a later reconcile.
|
|
156
|
+
|
|
157
|
+
## Registration lifecycle
|
|
158
|
+
|
|
159
|
+
`AgentWebMcp.register` returns:
|
|
140
160
|
|
|
141
161
|
```ts
|
|
142
|
-
|
|
162
|
+
registration.registered() // capability names currently registered
|
|
163
|
+
await registration.refresh() // reconcile against the current Model
|
|
164
|
+
registration.unregister() // stop following and abort every registration
|
|
143
165
|
```
|
|
144
166
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
167
|
+
The main options are:
|
|
168
|
+
|
|
169
|
+
| Option | Default | Purpose |
|
|
170
|
+
| --- | --- | --- |
|
|
171
|
+
| `agent` | — | The bound `AgentRuntime`. |
|
|
172
|
+
| `modelContext` | `document.modelContext` | Producer surface to register against; pass a stand-in in tests. |
|
|
173
|
+
| `followModel` | `true` | Reconcile tools as capability availability changes. |
|
|
174
|
+
| `signal` | — | Unregister everything when aborted. |
|
|
175
|
+
| `invocationId` | `crypto.randomUUID()` | Supplies protocol invocation ids. |
|
|
176
|
+
| `onError` | — | Receives failures from background reconciliation. |
|
|
149
177
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
next reconcile; `refresh()` rejects with the first failure after attempting the
|
|
154
|
-
rest. Nothing is registered once `unregister()` has run, and a registration that
|
|
155
|
-
was in flight when it ran is aborted.
|
|
178
|
+
Each registered tool has its own registration `AbortController`. That signal is
|
|
179
|
+
for the WebMCP registration itself. It is distinct from a tool invocation's
|
|
180
|
+
execution signal, which is forwarded to `AgentRuntime` as `Invocation.signal`.
|
|
156
181
|
|
|
157
182
|
## Errors
|
|
158
183
|
|
|
159
|
-
|
|
160
|
-
calling agent can read it and decide what to do:
|
|
184
|
+
Application refusals become tool errors the calling agent can reason about:
|
|
161
185
|
|
|
162
186
|
```text
|
|
163
187
|
No such capability: delete_todo
|
|
@@ -167,42 +191,52 @@ Invalid input for "create_todo"
|
|
|
167
191
|
Capability "create_todo" failed unexpectedly
|
|
168
192
|
```
|
|
169
193
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
written for it.
|
|
194
|
+
`execute` does not expose underlying application errors to the caller. A host or
|
|
195
|
+
adapter defect is flattened to a generic failure; the detailed error stays in
|
|
196
|
+
the application.
|
|
174
197
|
|
|
175
198
|
## Testing without a browser
|
|
176
199
|
|
|
177
|
-
`modelContext` is the only
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
200
|
+
`modelContext` is the only browser-specific seam this package needs. Pass an
|
|
201
|
+
object with `registerTool` to capture descriptors and invoke their `execute`
|
|
202
|
+
functions directly:
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
const registration = AgentWebMcp.register({
|
|
206
|
+
agent: agentRuntime,
|
|
207
|
+
modelContext: fakeModelContext,
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
await registration.refresh()
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
That is how the package test suite and [`examples/todo`](../../examples/todo)
|
|
214
|
+
exercise the real adapter path without requiring browser support.
|
|
215
|
+
|
|
216
|
+
[`examples/todo-app`](../../examples/todo-app) shows the same adapter attached to
|
|
217
|
+
a real local-first Foldkit application.
|
|
182
218
|
|
|
183
219
|
## Choosing an adapter
|
|
184
220
|
|
|
221
|
+
All four adapters serve the same `AssistantAgent` contract through a bound
|
|
222
|
+
runtime. Serving more than one does not duplicate capability declarations.
|
|
223
|
+
|
|
185
224
|
| Where the agent runs | Adapter |
|
|
186
225
|
| --- | --- |
|
|
187
226
|
| In the page, beside the user | `foldkit-agent-webmcp` |
|
|
188
|
-
| An external MCP client, over stdio or HTTP | [`foldkit-agent-mcp`](
|
|
189
|
-
| Another agent, over A2A | [`foldkit-agent-a2a`](
|
|
190
|
-
| An Agent Native host | [`foldkit-agent-native`](
|
|
191
|
-
|
|
192
|
-
They share one contract, so serving two at once is two calls, not two
|
|
193
|
-
definitions.
|
|
227
|
+
| An external MCP client, over stdio or HTTP | [`foldkit-agent-mcp`](../agent-mcp) |
|
|
228
|
+
| Another agent, over A2A | [`foldkit-agent-a2a`](../agent-a2a) |
|
|
229
|
+
| An Agent Native host | [`foldkit-agent-native`](../agent-native) |
|
|
194
230
|
|
|
195
231
|
## Why this stays an adapter
|
|
196
232
|
|
|
197
|
-
WebMCP is experimental and
|
|
198
|
-
|
|
199
|
-
without the
|
|
200
|
-
|
|
233
|
+
WebMCP is experimental and may change independently of Foldkit Plus. Its browser
|
|
234
|
+
shape is intentionally isolated here. If the proposal changes, this adapter can
|
|
235
|
+
change without changing the protocol-neutral `foldkit-agent` contract or the
|
|
236
|
+
application's Messages.
|
|
201
237
|
|
|
202
238
|
## See also
|
|
203
239
|
|
|
204
|
-
- [
|
|
205
|
-
|
|
206
|
-
- [
|
|
207
|
-
— the contract this adapter serves.
|
|
208
|
-
- [WebMCP](https://github.com/webmachinelearning/webmcp) — the proposal.
|
|
240
|
+
- [Agents guide](../../docs/agents.md) — the full builder → contract → runtime mental model.
|
|
241
|
+
- [`foldkit-agent`](../agent) — declares and binds the contract this package serves.
|
|
242
|
+
- [WebMCP](https://github.com/webmachinelearning/webmcp) — the browser proposal.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "foldkit-agent-webmcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "WebMCP adapter for foldkit-agent. Projects exposed Foldkit Messages into document.modelContext tools.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"foldkit",
|
|
@@ -37,17 +37,18 @@
|
|
|
37
37
|
"LICENSE"
|
|
38
38
|
],
|
|
39
39
|
"peerDependencies": {
|
|
40
|
-
"effect": "^4.0.0-rc.
|
|
41
|
-
"foldkit": "^0.
|
|
42
|
-
"foldkit-agent": "^0.
|
|
40
|
+
"effect": "^4.0.0-rc.116",
|
|
41
|
+
"foldkit": "^0.163.0",
|
|
42
|
+
"foldkit-agent": "^0.4.0"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"effect": "4.0.0-rc.
|
|
47
|
-
"
|
|
46
|
+
"@effect/platform-browser": "4.0.0-rc.116",
|
|
47
|
+
"effect": "4.0.0-rc.116",
|
|
48
|
+
"foldkit": "0.163.0",
|
|
48
49
|
"tsdown": "^0.23.0",
|
|
49
|
-
"typescript": "
|
|
50
|
-
"foldkit-agent": "0.
|
|
50
|
+
"typescript": "5.9.3",
|
|
51
|
+
"foldkit-agent": "0.4.0"
|
|
51
52
|
},
|
|
52
53
|
"scripts": {
|
|
53
54
|
"build": "tsdown",
|