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