-
-
Notifications
You must be signed in to change notification settings - Fork 100
feat(devtools-webmcp): add a development-only WebMCP tool registry #531
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
22530b5
feat(devtools-webmcp): add a development-only WebMCP tool registry
AlemTuzlak 74981df
chore: format webmcp files
AlemTuzlak c3be0ba
fix: sort example dependencies for sherif
AlemTuzlak 5f6a05d
fix(devtools-webmcp): wrap execute with a signal and drop JSDoc params
AlemTuzlak File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| '@tanstack/devtools-webmcp': patch | ||
| --- | ||
|
|
||
| Add `@tanstack/devtools-webmcp` so a library can register development-only WebMCP tools. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| --- | ||
| title: WebMCP Tools | ||
| id: webmcp-tools | ||
| --- | ||
|
|
||
| An agent in the browser cannot see the internals of your library. WebMCP tools give that agent a way to read those internals during development. | ||
|
|
||
| Call `registerDevtoolsTools` from `@tanstack/devtools-webmcp`. | ||
|
|
||
| ## Register the Tools | ||
|
|
||
| 1. Install `@tanstack/devtools-webmcp`. | ||
|
|
||
| ```sh | ||
| npm install @tanstack/devtools-webmcp | ||
| ``` | ||
|
|
||
| The package has no runtime dependencies. | ||
|
|
||
| 2. Paste this call into your library. | ||
|
|
||
| ```ts | ||
| import { registerDevtoolsTools } from '@tanstack/devtools-webmcp' | ||
|
|
||
| const stop = registerDevtoolsTools({ | ||
| pluginId: 'tanstack.query', | ||
| instanceId: 'main', | ||
| tools: [ | ||
| { | ||
| name: 'getQueryCache', | ||
| description: 'Return a JSON summary of the query cache.', | ||
| inputSchema: { | ||
| type: 'object', | ||
| properties: { | ||
| queryHash: { type: 'string' }, | ||
| }, | ||
| }, | ||
| execute: (input) => summarize(client, input), | ||
| }, | ||
| ], | ||
| }) | ||
|
|
||
| stop() | ||
| ``` | ||
|
|
||
| `summarize` and `client` are your own library code. | ||
|
|
||
| 3. If the tools must stay, do not call `stop()`. | ||
| 4. When the tools must leave the page, call `stop()`. | ||
|
|
||
| ## Names | ||
|
|
||
| `pluginId` is required. `instanceId` is optional. | ||
|
|
||
| An empty `instanceId` means the same thing as no `instanceId`. An empty `instanceId` is legal. | ||
|
|
||
| The browser tool name is `pluginId.name`. When `instanceId` is a non-empty string, the browser tool name is `pluginId.instanceId.name`. | ||
|
|
||
| In the sample, the browser tool name is `tanstack.query.main.getQueryCache`. | ||
|
|
||
| A part is `pluginId` or `name`. When `instanceId` is a non-empty string, that value is also a part. | ||
|
|
||
| Each part is a non-empty string. Each part uses only ASCII letters (`A-Z` and `a-z`), digits, `_`, `-`, and `.`. | ||
|
|
||
| The full tool name has 1 to 128 characters. | ||
|
|
||
| ## Tool Fields | ||
|
|
||
| A tool needs `name`, `description`, and `execute`. | ||
|
|
||
| - `name`: the last part of the browser tool name. | ||
| - `description`: the text that the agent reads. | ||
| - `title`: an optional string. | ||
| - `inputSchema`: an optional JSON Schema. | ||
| - `execute`: the function that returns the data. | ||
|
|
||
| `TInput` defaults to `any`. This package does not read `inputSchema` into TypeScript types. | ||
|
|
||
| `execute` receives the input and `{ signal }`. The `signal` is an `AbortSignal`. | ||
|
|
||
| The return value is data that `JSON.stringify` can serialize. The helper sends that value to the browser with no change. | ||
|
|
||
| The helper sets `annotations.debugging` to `true`. | ||
|
|
||
| When you set one of these hints, the helper sends that hint with the tool. | ||
|
|
||
| - `readOnlyHint` | ||
| - `untrustedContentHint` | ||
| - `consequentialHint` | ||
|
|
||
| These hints do not change `debugging`. | ||
|
|
||
| ## Development and Production | ||
|
|
||
| Use the root import for development. | ||
|
|
||
| ```ts | ||
| import { registerDevtoolsTools } from '@tanstack/devtools-webmcp' | ||
| ``` | ||
|
|
||
| When `process.env.NODE_ENV` is `'development'`, the root import is the real helper. In every other environment, the root import is a no-op. An unset `NODE_ENV` is a no-op too. | ||
|
|
||
| Bundlers remove the real helper. The tool objects stay in the library bundle. The no-op does not call the browser. The no-op does not keep the tools object. | ||
|
|
||
| If the tools must stay registered in production, import `@tanstack/devtools-webmcp/production`. | ||
|
|
||
| ```ts | ||
| import { registerDevtoolsTools } from '@tanstack/devtools-webmcp/production' | ||
| ``` | ||
|
|
||
| The import `@tanstack/devtools-webmcp/production` is always the real helper. The `registerDevtoolsTools` call stays the same. Read [Production](./production) for the production build. | ||
|
|
||
| ## Browser Lookup | ||
|
|
||
| When `registerTool` is a function on `document.modelContext`, the helper uses `document.modelContext`. | ||
|
|
||
| When that function is not on `document.modelContext`, and `navigator.modelContext.registerTool` is a function, the helper uses `navigator.modelContext`. | ||
|
|
||
| When neither object has that function, the call returns a cleanup function. The call does not throw. The helper writes nothing to the console on this path. | ||
|
|
||
| These environments take this path: | ||
|
|
||
| - server render | ||
| - Node | ||
| - browser with no WebMCP | ||
|
|
||
| ## Replace and Remove | ||
|
|
||
| One `AbortController` is for the whole list. The helper passes that signal to each `registerTool` call. The helper passes the same signal to `execute` as `{ signal }`. | ||
|
|
||
| Cleanup aborts that `AbortController` and the `signal` in `execute`. Then the browser removes the tools. | ||
|
|
||
| The call returns `stop` at once. The call does not return the `registerTool` promise. | ||
|
|
||
| A second call with the same `pluginId` and `instanceId` replaces the previous list. That call aborts the previous controller. Then the previous `stop` function does nothing. | ||
|
|
||
| A hot reload calls the helper a second time with the same `pluginId` and `instanceId`. | ||
|
|
||
| An empty `tools` array removes the previous tools for that `pluginId` and `instanceId`. The helper registers no tools on that call. | ||
|
|
||
| ## Errors | ||
|
|
||
| The helper writes one `console.error` for each error. The helper does not throw. The helper logs a rejected `registerTool` promise. When the helper aborts the signal, a rejection from that `registerTool` call writes no `console.error`. | ||
|
|
||
| - If `pluginId` or `instanceId` is illegal, the helper registers nothing and the previous registration stays. An empty `instanceId` is legal. | ||
| - If a tool `name` is illegal, or the full name is longer than 128 characters, the helper skips that tool. Legal tools in the same list still register. | ||
| - If the list repeats a full name, the browser rejects the second tool. The helper logs that error, and the first tool stays registered. | ||
| - If other code owns that full name, the browser rejects that tool. The helper logs the error and skips that tool. | ||
| - If `JSON.stringify` cannot serialize `inputSchema`, the browser rejects that tool. The helper logs the error and continues with the other tools. | ||
|
|
||
| ## Result | ||
|
|
||
| These tools do not appear in a devtools panel. | ||
|
|
||
| The agent can call `tanstack.query.main.getQueryCache`. The tool returns the value from your `summarize` function. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Keep disabled-button colors visible on hover and in the dark theme.
The existing hover selectors outrank this selector. The later dark-theme button rule also overrides it. As a result, disabled Run tool and Stop registration buttons can take enabled-button colors. Match the existing button selector’s scope, and exclude disabled buttons from both hover rules. (w3.org)
🤖 Prompt for AI Agents