@dudko.dev/agent-web 0.0.7 → 0.0.9

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 CHANGED
@@ -191,6 +191,55 @@ Browsers can only speak the **HTTP (StreamableHTTP)** transport — stdio MCP is
191
191
  Node-only. The connector lives in the `./mcp` subpath so the MCP SDK never
192
192
  enters your core bundle.
193
193
 
194
+ `connectMcpHttp` returns `{ tools, catalog, results, refreshServer, close }`.
195
+ `tools` and `catalog` are mutated **in place** by `refreshServer(name)`, so an
196
+ agent built from them picks up a server's new tool list (react to it via the
197
+ `onToolsChanged` option) without being rebuilt. Filtering is plain object work:
198
+ delete the keys you don't want before merging.
199
+
200
+ ### OAuth 2.1 + Dynamic Client Registration
201
+
202
+ A server behind OAuth needs more than a header: access tokens expire mid-run.
203
+ Pass an `authProvider` and the MCP SDK discovers the authorization server
204
+ (RFC 9728), registers this app dynamically (RFC 7591 — no client_id to
205
+ pre-provision), runs PKCE, and **refreshes the access token on any 401 and
206
+ retries the request**.
207
+
208
+ ```ts
209
+ import { BrowserOAuthProvider, connectMcpHttp, finishMcpOAuth, readOAuthCallback }
210
+ from '@dudko.dev/agent-web/mcp'
211
+
212
+ const provider = new BrowserOAuthProvider({
213
+ serverUrl: 'https://my-mcp-server.example/mcp',
214
+ redirectUrl: `${location.origin}/callback`, // must match on the way back
215
+ })
216
+
217
+ // 1. On the callback page: finish the flow before anything else.
218
+ const callback = readOAuthCallback()
219
+ if (callback) await finishMcpOAuth(provider, callback)
220
+
221
+ // 2. Connect. `needsAuthorization` means "send the user to authorize".
222
+ const mcp = await connectMcpHttp({ docs: { url: provider.serverUrl, authProvider: provider } })
223
+ if (mcp.results[0]?.needsAuthorization) {
224
+ location.href = String(provider.authorizationUrl) // a button, a popup — your call
225
+ }
226
+ ```
227
+
228
+ Tokens, the dynamic registration and the PKCE verifier are stored in the same
229
+ **encrypted IndexedDB vault** as provider API keys (`VaultOAuthStorage`; swap in
230
+ `MemoryOAuthStorage` or your own via `storage`). `redirectToAuthorization` never
231
+ navigates on its own — the SDK can demand authorization from inside a running
232
+ tool call, and evicting the user then would discard the run. The URL is recorded
233
+ on `provider.authorizationUrl` and the app decides when to use it.
234
+
235
+ Two things to check on the **server** side, both invisible until they bite:
236
+
237
+ - CORS must allow `Authorization` and expose `WWW-Authenticate` and
238
+ `mcp-session-id` (`Access-Control-Expose-Headers`). Without the first, the
239
+ browser cannot read the 401 challenge and discovery never starts.
240
+ - The authorization server's `/register`, metadata and token endpoints must be
241
+ CORS-enabled too — a browser client calls them directly.
242
+
194
243
  ## Configuration (highlights)
195
244
 
196
245
  | Option | Default | Purpose |