7.5 KiB
WebMCP prototype
Registration
The deck workspace exposes nine React-owned tools on / and /deck.
usewebmcp handles native browser registration and cleanup. Tools become
available after saved decks load. Connection discovery may still be pending;
list_connections then returns connections: null.
Registration uses document.modelContext; there is no polyfill or external
MCP transport. Unsupported browsers retain the manual interface. All server
operations use the same access controls and persistence rules as the UI.
Workspace tools
| Tool | Input | Behavior |
|---|---|---|
list_connections |
{} |
Return connection IDs, platforms, origins, account IDs, display names, and states; never credentials |
list_decks |
{} |
Return saved decks and this tab's temporary views, activeDeckId, and storageError |
get_deck |
Optional deckId |
Read the named or active view, including persisted and saved revision |
set_deck |
Optional deckId and expectedRevision, required title and columns |
Without an ID, create a temporary view; with an ID, replace and activate that existing view |
save_deck |
deckId |
Explicitly persist a temporary view; an already saved deck is unchanged |
select_deck |
deckId |
Activate a view and update the URL; other tabs remain unchanged |
delete_deck |
deckId, optional expectedRevision |
Discard a temporary view or delete a saved deck for all devices |
Read tools return the workspace's current client snapshot, not a fresh server
request. Saved definitions refresh on focus and while visible, except during
editing. Replacing or deleting a saved deck requires expectedRevision from
the definition being edited. A stale revision fails without overwriting the
server. Use the UI's reload action after a conflict when a fresh definition
is needed.
set_deck accepts at most six columns, each with a title, connectionId from
discovery, and a source. Connections must be connected and match the source
platform. Optional column IDs preserve identity; omitted IDs are generated.
Include every retained column: omitted columns are removed, and an empty array
clears the view. A supplied deck ID must already exist.
Twitter sources support search, user, and list. platform defaults to
twitter; search requires query and defaults product to Latest and
following to false. User/list targets accept handles or profile URLs and
numeric list IDs or list URLs, respectively.
Mastodon requires platform: "mastodon" and supports search (query),
user (handle or instance-local account ID), list (account-local numeric
list ID), and hashtag (tag without #). It does not accept Twitter ranking
or following controls. Search availability and coverage depend on the
instance; an empty successful result does not prove full-text support.
Mutation results and persistence
set_deck returns deck, its actual persisted flag, and
posts: "loading-asynchronously". A new view exists only in tab memory until
save_deck succeeds. Temporary views disappear on reload or navigation,
including OAuth redirects. Saving keeps the ID, allowing an identical create
request to be retried without duplicate decks. Failed saves keep temporary
content; rejected updates do not replace the accepted saved definition.
Successful mutations close unsaved editor forms. Deleting the active view selects a remaining one; deleting the last opens an empty temporary view. There is no tool-level deletion undo. Column requests may fail independently after a definition is applied or saved. Existing browser decks are imported only through the explicit UI import action, not through a tool side effect.
Column tools
get_column_posts accepts columnId, offset (default 0, nonnegative integer),
and limit (default 20, integer 1–50). It reads already loaded posts without a
network request. Only columns mounted in the active view are available;
select the view and allow it to render first.
load_more_column accepts columnId and fetches or retries one continuation
using the column's connection. Wait for initial loading or refresh to finish.
Concurrent pagination joins the existing request. If the view or column
changes during pagination, execution fails rather than returning results under
the changed identity. At the end, no posts are appended and hasMore is false.
Both return:
column: ID, title, connection ID, and sourcestatus:loading,ready, orerror, withloadinganderrordetailsposts: normalized records with original URLs, text, author, and available media, quotes, content warnings, or boost informationloadedCount,offset, andnextOffsetfor slices of deduplicated postshasMore: whether the feed has an upstream continuation
Continuation returns up to 20 newly appended posts. Use nextOffset with
get_column_posts for additional already loaded records; use
load_more_column for another upstream page. These are cache snapshots, not
archived evidence.
Errors and annotations
Results are JSON in an MCP text content block. Execution failures set
isError: true with code, message, and retryable: false. Schema and
connection-validation failures use invalid-input; other execution failures
use tool-error. Column snapshots expose upstream errors separately in their
error field. Empty successful queries are not errors.
list_connections, list_decks, get_deck, and get_column_posts have
readOnlyHint: true. delete_deck has destructiveHint: true; tools returning
external posts mark them untrusted. Annotations describe behavior and do not
grant authorization. No tool posts to or modifies an SNS account.
Browser setup and verification
nix develop -c pnpm test
nix develop -c pnpm typecheck
nix develop -c pnpm test:e2e e2e/integrations/webmcp.test.ts
E2E tests enable Chromium WebMCP/testing flags and invoke actual registered
tools through navigator.modelContextTesting. Mock relay responses pass
through real server functions and isolated database state.
For interactive testing, enable chrome://flags/#enable-webmcp-testing,
restart Chrome, and use Model Context Tool Inspector. Reload after changing
browser support. Native WebMCP requires a secure context; use the configured
Tailscale Serve HTTPS origin for remote access. Development also requires
allowing its exact hostname through __VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS.
The app's owner check still applies; direct localhost access does not supply
Serve identity. See runtime access configuration.
No production origin-trial token or external MCP-client bridge is configured. Browser cancellation does not guarantee cancellation of a shared feed request.