foldkit-agent-webmcp 0.2.1 → 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 +147 -119
- package/package.json +3 -3
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
|
+
[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.
|
|
22
37
|
|
|
23
38
|
## Install
|
|
24
39
|
|
|
@@ -28,92 +43,83 @@ 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
|
|
32
47
|
|
|
33
|
-
|
|
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:
|
|
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
|
-
subscribe: mounted.subscribe, // lets tools appear and disappear with the Model
|
|
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, // required only by capabilities with completion contracts
|
|
49
68
|
},
|
|
50
69
|
})
|
|
51
70
|
|
|
52
|
-
// 2. Publish it, if this browser speaks WebMCP.
|
|
53
71
|
const modelContext = AgentWebMcp.documentModelContext()
|
|
72
|
+
|
|
54
73
|
if (modelContext !== undefined) {
|
|
55
|
-
const registration = AgentWebMcp.register({
|
|
74
|
+
const registration = AgentWebMcp.register({
|
|
75
|
+
agent: agentRuntime,
|
|
76
|
+
modelContext,
|
|
77
|
+
})
|
|
78
|
+
|
|
56
79
|
window.addEventListener('beforeunload', () => registration.unregister())
|
|
57
80
|
}
|
|
58
81
|
```
|
|
59
82
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
Calling `register()` without a model context **throws**, on the grounds that
|
|
64
|
-
silently registering nothing would look like a working integration. Feature-detect,
|
|
65
|
-
or pass a `modelContext` of your own.
|
|
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.
|
|
66
86
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
registration.refresh()` when you want to know the tools are live.
|
|
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.
|
|
71
90
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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.
|
|
76
96
|
|
|
77
97
|
## What a capability becomes
|
|
78
98
|
|
|
79
|
-
Each **currently available** capability becomes one tool:
|
|
99
|
+
Each **currently available** capability becomes one WebMCP tool:
|
|
80
100
|
|
|
81
101
|
```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.
|
|
102
|
+
foldkit-agent WebMCP
|
|
93
103
|
|
|
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. |
|
|
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
|
+
```
|
|
104
110
|
|
|
105
|
-
|
|
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.
|
|
106
114
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
registration.unregister() // abort every registration and stop following
|
|
111
|
-
```
|
|
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.
|
|
112
118
|
|
|
113
|
-
## Tools
|
|
119
|
+
## Tools follow the Model
|
|
114
120
|
|
|
115
|
-
Availability is
|
|
116
|
-
|
|
121
|
+
Availability is Model-dependent, so the advertised WebMCP tool set may change as
|
|
122
|
+
the application changes:
|
|
117
123
|
|
|
118
124
|
```ts
|
|
119
125
|
RequestedDeleteTodo: {
|
|
@@ -124,40 +130,52 @@ RequestedDeleteTodo: {
|
|
|
124
130
|
```
|
|
125
131
|
|
|
126
132
|
```text
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
133
|
+
no selection tools: create_todo
|
|
134
|
+
|
|
|
135
|
+
user selects todo
|
|
136
|
+
v
|
|
137
|
+
selected todo tools: create_todo, delete_todo
|
|
130
138
|
```
|
|
131
139
|
|
|
132
|
-
With `followModel`
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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.
|
|
137
150
|
|
|
138
|
-
|
|
139
|
-
|
|
151
|
+
## Registration lifecycle
|
|
152
|
+
|
|
153
|
+
`AgentWebMcp.register` returns:
|
|
140
154
|
|
|
141
155
|
```ts
|
|
142
|
-
|
|
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
|
|
143
159
|
```
|
|
144
160
|
|
|
145
|
-
|
|
146
|
-
descriptor. This is the registration signal, distinct from the execution signal
|
|
147
|
-
passed to `execute`, which is forwarded as `Invocation.signal` so an agent can
|
|
148
|
-
cancel a call in flight.
|
|
161
|
+
The main options are:
|
|
149
162
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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. |
|
|
171
|
+
|
|
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`.
|
|
156
175
|
|
|
157
176
|
## Errors
|
|
158
177
|
|
|
159
|
-
|
|
160
|
-
calling agent can read it and decide what to do:
|
|
178
|
+
Application refusals become tool errors the calling agent can reason about:
|
|
161
179
|
|
|
162
180
|
```text
|
|
163
181
|
No such capability: delete_todo
|
|
@@ -167,42 +185,52 @@ Invalid input for "create_todo"
|
|
|
167
185
|
Capability "create_todo" failed unexpectedly
|
|
168
186
|
```
|
|
169
187
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
written for it.
|
|
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.
|
|
174
191
|
|
|
175
192
|
## Testing without a browser
|
|
176
193
|
|
|
177
|
-
`modelContext` is the only
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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.
|
|
182
212
|
|
|
183
213
|
## Choosing an adapter
|
|
184
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
|
+
|
|
185
218
|
| Where the agent runs | Adapter |
|
|
186
219
|
| --- | --- |
|
|
187
220
|
| 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.
|
|
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) |
|
|
194
224
|
|
|
195
225
|
## Why this stays an adapter
|
|
196
226
|
|
|
197
|
-
WebMCP is experimental and
|
|
198
|
-
|
|
199
|
-
without the
|
|
200
|
-
|
|
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.
|
|
201
231
|
|
|
202
232
|
## See also
|
|
203
233
|
|
|
204
|
-
- [
|
|
205
|
-
|
|
206
|
-
- [
|
|
207
|
-
— the contract this adapter serves.
|
|
208
|
-
- [WebMCP](https://github.com/webmachinelearning/webmcp) — the proposal.
|
|
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",
|