feat: prototype WebMCP reader tools
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
# WebMCP prototype
|
||||
|
||||
## Scope and design
|
||||
|
||||
Expose the existing intentional reader to agents through three React-owned
|
||||
tools. Search remains available across routes. Feed tools are registered only
|
||||
while a valid timeline, search, or conversation is open. Returning home or to
|
||||
an empty search/list page removes the feed tools. Unsupported browsers do not
|
||||
register tools or fetch posts automatically.
|
||||
|
||||
`usewebmcp` 5.1.0 owns browser registration and cleanup. No polyfill is
|
||||
initialized. The root search tool navigates through TanStack Router and awaits
|
||||
the same query options as the visible feed. Completed data is reused through
|
||||
`ensureInfiniteQueryData`; an invalidated or in-progress query is awaited
|
||||
through `fetchInfiniteQuery`, including refreshes after a profile change.
|
||||
Reading the feed uses its current React Query result. Continuation and the
|
||||
scroll observer use `fetchNextPage({ cancelRefetch: false })` to join an
|
||||
existing request. A feed change during continuation returns an error instead
|
||||
of reporting old results as belonging to the new feed.
|
||||
|
||||
## Tool contracts
|
||||
|
||||
### `search_posts`
|
||||
|
||||
Search X, show the criteria and results in the UI, and return the first slice
|
||||
after retrieval. Searches use the currently selected relay profile.
|
||||
|
||||
Inputs match the existing search controls:
|
||||
|
||||
| Field | Default | Meaning |
|
||||
| --- | --- | --- |
|
||||
| `q` | empty | Search text, including raw X search syntax |
|
||||
| `from` | empty | Author handle, optionally prefixed with `@` |
|
||||
| `since` | empty | Inclusive `YYYY-MM-DD` date using X search semantics |
|
||||
| `until` | empty | Exclusive `YYYY-MM-DD` date, later than `since` |
|
||||
| `lang` | `all` | `all`, `ja`, or `en` |
|
||||
| `content` | `all` | `all`, `images`, `videos`, or `links` |
|
||||
| `excludeReplies`, `excludeReposts` | `false` | Exclusions |
|
||||
| `product` | `Latest` | `Latest` or `Top` |
|
||||
| `following` | `false` | Restrict to followed accounts |
|
||||
|
||||
Supply `q` or `from`. Unknown properties and invalid inputs fail before
|
||||
navigation. Existing query construction validates dates, handles, and the
|
||||
512-character compiled-query limit. A concurrent search fails with an
|
||||
actionable message. Navigating elsewhere during a search prevents a stale
|
||||
success response.
|
||||
|
||||
### `get_loaded_posts`
|
||||
|
||||
Return a slice of the current feed without a network request. Accepts
|
||||
`offset` (default 0, nonnegative integer) and `limit` (default 20, integer
|
||||
1–50). Conversation results place the selected post first, once.
|
||||
|
||||
The response includes `status` (`loading`, `ready`, or `error`), `loading`,
|
||||
and `error` information alongside the common result. This is a snapshot of
|
||||
all loaded posts, not just posts inside the viewport.
|
||||
|
||||
### `load_more_posts`
|
||||
|
||||
Accepts `{}`. Wait for one continuation, append it to the UI, and return up to
|
||||
20 newly appended posts. An in-progress scroll request is shared. Initial
|
||||
loading or a feed refresh must finish first. A failed continuation can be retried explicitly.
|
||||
At the end of the feed the result is an empty `posts` array and `hasMore: false`.
|
||||
|
||||
## Results and errors
|
||||
|
||||
Successful tool results contain JSON in an MCP text content block:
|
||||
|
||||
- `request`: active feed kind and normalized criteria.
|
||||
- `posts`: `id`, `author` (username/name), `text`, `textTruncated`, optional
|
||||
`createdAt`, and the original X `url`.
|
||||
- `loadedCount`: number of deduplicated, loaded posts.
|
||||
- `offset`: start of this slice within the loaded feed.
|
||||
- `nextOffset`: next unread offset within the already loaded feed, or `null`.
|
||||
- `hasMore`: whether the last loaded page has an upstream continuation.
|
||||
|
||||
Use `get_loaded_posts` with `nextOffset` for loaded posts outside a returned
|
||||
slice; use `load_more_posts` for an upstream continuation. Scrolling can load
|
||||
more posts independently, so these fields describe a snapshot. Post text is
|
||||
capped at 2,000 characters per post and explicitly marked when truncated;
|
||||
the source URL remains available. Media, quote bodies, and article previews
|
||||
are not included in this initial text-oriented tool response.
|
||||
|
||||
Failures use `isError: true` and JSON containing `code`, `message`, and
|
||||
`retryable`. Existing relay error details are preserved. A successful empty
|
||||
search is not an error.
|
||||
|
||||
All tools mark returned external content with `untrustedContentHint: true`.
|
||||
Only `get_loaded_posts` has `readOnlyHint: true`: search and continuation
|
||||
change local UI state. No tool performs X write actions. Annotations are
|
||||
metadata, not authorization controls.
|
||||
|
||||
## Verification and limits
|
||||
|
||||
```sh
|
||||
nix develop -c pnpm test
|
||||
nix develop -c pnpm typecheck
|
||||
nix develop -c pnpm test:e2e e2e/integrations/webmcp.test.ts
|
||||
```
|
||||
|
||||
The E2E file enables native Chromium WebMCP/testing flags and uses
|
||||
`navigator.modelContextTesting` to invoke actual registered tools. The
|
||||
standalone mock relay supplies deterministic data through the production
|
||||
server-function boundary. Tests do not contact X or a personal relay.
|
||||
|
||||
For interactive testing, enable Chrome's WebMCP testing flag, restart, run
|
||||
`nix develop -c pnpm dev`, and open the local site with Model Context Tool
|
||||
Inspector. Registration uses `document.modelContext`; availability is checked
|
||||
at mount, so reload after changing browser support. No production origin-trial
|
||||
token or external MCP-client bridge is configured by this prototype.
|
||||
|
||||
The hook does not forward the browser's execution AbortSignal to application
|
||||
callbacks. Browser cancellation therefore does not guarantee cancellation of
|
||||
the shared read request. Tools do detect navigation changes before returning
|
||||
their asynchronous results. Agent task-selection quality still needs manual
|
||||
evaluation with the consuming agent; deterministic browser tests verify the
|
||||
tool contracts and UI behavior.
|
||||
|
||||
Design references: [Chrome best practices](https://developer.chrome.com/docs/ai/webmcp/best-practices),
|
||||
[workflow design](https://developer.chrome.com/docs/ai/webmcp/build-tools),
|
||||
and [usewebmcp](https://github.com/WebMCP-org/npm-packages/tree/main/packages/usewebmcp).
|
||||
Reference in New Issue
Block a user