@napster-corp/webmcp-toolkit 1.0.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/LICENSE +21 -0
- package/README.md +531 -0
- package/bin/webmcp-toolkit.mjs +81 -0
- package/dist/debug.d.ts +5 -0
- package/dist/debug.d.ts.map +1 -0
- package/dist/debug.js +26 -0
- package/dist/debug.js.map +1 -0
- package/dist/dev-panel.d.ts +22 -0
- package/dist/dev-panel.d.ts.map +1 -0
- package/dist/dev-panel.js +1046 -0
- package/dist/dev-panel.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/model-context.d.ts +13 -0
- package/dist/model-context.d.ts.map +1 -0
- package/dist/model-context.js +28 -0
- package/dist/model-context.js.map +1 -0
- package/dist/resources.d.ts +15 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +179 -0
- package/dist/resources.js.map +1 -0
- package/dist/tiers.d.ts +31 -0
- package/dist/tiers.d.ts.map +1 -0
- package/dist/tiers.js +107 -0
- package/dist/tiers.js.map +1 -0
- package/dist/types.d.ts +145 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/hooks/post-commit +17 -0
- package/package.json +86 -0
- package/skills/add-edge-mcp-dev-panel/SKILL.md +206 -0
- package/skills/plan-capabilities-and-state/SKILL.md +168 -0
- package/skills/setup-edge-mcp/SKILL.md +546 -0
- package/skills/sync-webmcp-tools/SKILL.md +26 -0
- package/src/debug.ts +26 -0
- package/src/dev-panel.ts +1318 -0
- package/src/index.ts +66 -0
- package/src/model-context.ts +31 -0
- package/src/resources.ts +207 -0
- package/src/tiers.ts +132 -0
- package/src/types.ts +177 -0
- package/tools/generate-capabilities.mjs +266 -0
- package/tools/install-hook.mjs +81 -0
- package/tools/runners/anthropic.mjs +75 -0
- package/tools/runners/copilot.mjs +63 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard WebMCP tool annotations. The spec's `annotations` object carries
|
|
3
|
+
* exactly two hints — there is intentionally no field for richer safety tiers
|
|
4
|
+
* (see {@link SideEffect} / {@link StatefulToolDescriptor}).
|
|
5
|
+
*/
|
|
6
|
+
export interface ToolAnnotations {
|
|
7
|
+
/** True if the tool only reads state and never mutates it. */
|
|
8
|
+
readOnlyHint?: boolean;
|
|
9
|
+
/** True if the tool's output may contain untrusted / third-party content. */
|
|
10
|
+
untrustedContentHint?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/** A single content item returned from a tool's `execute` callback. */
|
|
13
|
+
export interface ToolContent {
|
|
14
|
+
type: string;
|
|
15
|
+
text?: string;
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
/** Standard WebMCP tool result shape: `{ content: [{ type: 'text', text }] }`. */
|
|
19
|
+
export interface ToolResult {
|
|
20
|
+
content: ToolContent[];
|
|
21
|
+
}
|
|
22
|
+
/** Standard `document.modelContext.registerTool(...)` descriptor. */
|
|
23
|
+
export interface ToolDescriptor {
|
|
24
|
+
/** Domain-named identifier, e.g. 'cart.add'. */
|
|
25
|
+
name: string;
|
|
26
|
+
/** Optional human-readable label (top-level, not inside annotations). */
|
|
27
|
+
title?: string;
|
|
28
|
+
/** One-sentence description — the agent reads this to decide WHEN to call. */
|
|
29
|
+
description: string;
|
|
30
|
+
/** JSON Schema for the arguments. */
|
|
31
|
+
inputSchema?: Record<string, unknown>;
|
|
32
|
+
/** Standard hints. */
|
|
33
|
+
annotations?: ToolAnnotations;
|
|
34
|
+
/** Invokes the app's real operation and returns standard content. */
|
|
35
|
+
execute: (input: Record<string, unknown>) => ToolResult | Promise<ToolResult>;
|
|
36
|
+
}
|
|
37
|
+
/** Tool metadata as returned by `document.modelContext.getTools()` (read layer). */
|
|
38
|
+
export interface ToolInfo {
|
|
39
|
+
name: string;
|
|
40
|
+
description: string;
|
|
41
|
+
inputSchema?: Record<string, unknown>;
|
|
42
|
+
title?: string;
|
|
43
|
+
origin?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The standard `document.modelContext` object (an `EventTarget`). We type only
|
|
47
|
+
* the members the toolkit and Web SDK touch. `getTools()` / `executeTool()` are
|
|
48
|
+
* not yet in the formal WebIDL but are implemented by the pinned polyfill and
|
|
49
|
+
* by Chrome's native implementation; `toolchange` fires (a bare `Event`, no
|
|
50
|
+
* detail) whenever the tool list changes.
|
|
51
|
+
*/
|
|
52
|
+
export interface ModelContext extends EventTarget {
|
|
53
|
+
registerTool(tool: ToolDescriptor, options?: {
|
|
54
|
+
signal?: AbortSignal;
|
|
55
|
+
}): void | Promise<void>;
|
|
56
|
+
getTools(): Promise<ToolInfo[]>;
|
|
57
|
+
executeTool(tool: ToolInfo, inputArgsJson: string, options?: {
|
|
58
|
+
signal?: AbortSignal;
|
|
59
|
+
}): Promise<string | null>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Side-effect tier — governs how carefully a consumer commits a tool. The
|
|
63
|
+
* standard `annotations` object cannot carry this (the polyfill drops custom
|
|
64
|
+
* annotation keys from `getTools()`), so the tier is stored in a toolkit-side
|
|
65
|
+
* registry keyed by tool name and read back via {@link TierMeta}.
|
|
66
|
+
*/
|
|
67
|
+
export type SideEffect = 'read' | 'reversible' | 'irreversible';
|
|
68
|
+
/**
|
|
69
|
+
* Optional superset of {@link ToolDescriptor} for tools that want safety
|
|
70
|
+
* gating. Calling `registerStatefulTool` registers a STANDARD tool (so any
|
|
71
|
+
* WebMCP agent can use it) and additionally records its tier. Plain
|
|
72
|
+
* `document.modelContext.registerTool` still works and is treated as
|
|
73
|
+
* `reversible` (announce-then-run) by consumers.
|
|
74
|
+
*/
|
|
75
|
+
export interface StatefulToolDescriptor extends Omit<ToolDescriptor, 'annotations'> {
|
|
76
|
+
/** Governance tier — defaults to 'reversible' when omitted. */
|
|
77
|
+
napsterTier?: SideEffect;
|
|
78
|
+
/** True if re-running with the same args is safe to repeat. */
|
|
79
|
+
idempotent?: boolean;
|
|
80
|
+
/** True if output may contain untrusted content → sets `untrustedContentHint`. */
|
|
81
|
+
untrustedContent?: boolean;
|
|
82
|
+
/** Manual annotation override; merged last (rarely needed). */
|
|
83
|
+
annotations?: ToolAnnotations;
|
|
84
|
+
}
|
|
85
|
+
/** Tier metadata exposed to consumers (Web SDK / dev panel) for gating. */
|
|
86
|
+
export interface TierMeta {
|
|
87
|
+
tier: SideEffect;
|
|
88
|
+
idempotent: boolean;
|
|
89
|
+
/** True when the consumer must get explicit user confirmation (tier === 'irreversible'). */
|
|
90
|
+
requiresConfirmation: boolean;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* A live-state resource the agent can PERCEIVE, modeled on MCP resources.
|
|
94
|
+
*
|
|
95
|
+
* Add a resource only for state that changes out-of-band — state the user edits
|
|
96
|
+
* by hand, or state that moves server-side. If a tool already returns the
|
|
97
|
+
* answer, do NOT add a resource that mirrors it. The genuine value here is
|
|
98
|
+
* *push* (live) state; pure pull state is better modeled as a read-only tool.
|
|
99
|
+
*/
|
|
100
|
+
export interface ResourceDescriptor<T = unknown> {
|
|
101
|
+
/** URI identity, mirrors MCP, e.g. 'state://cart'. */
|
|
102
|
+
uri: string;
|
|
103
|
+
/** Logical name, e.g. 'cart'. Required (mirrors MCP `Resource.name`). */
|
|
104
|
+
name: string;
|
|
105
|
+
/** Optional human description. */
|
|
106
|
+
description?: string;
|
|
107
|
+
/** Optional MIME type of the read value. */
|
|
108
|
+
mimeType?: string;
|
|
109
|
+
/** Returns the current, serializable value. Cheap, side-effect-free. */
|
|
110
|
+
get: () => T | Promise<T>;
|
|
111
|
+
/** Optional push source. Fires onChange on mutation; returns an unsubscribe. */
|
|
112
|
+
subscribe?: (onChange: () => void) => () => void;
|
|
113
|
+
}
|
|
114
|
+
/** Resource metadata as listed by `getResources()` (mirrors `resources/list`). */
|
|
115
|
+
export interface ResourceInfo {
|
|
116
|
+
uri: string;
|
|
117
|
+
name: string;
|
|
118
|
+
description?: string;
|
|
119
|
+
mimeType?: string;
|
|
120
|
+
}
|
|
121
|
+
/** Payload of the `resourceupdated` event / `subscribeResource` handler. */
|
|
122
|
+
export interface ResourceUpdate {
|
|
123
|
+
uri: string;
|
|
124
|
+
value: unknown;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* The additive surface installed onto `document.modelContext` by the toolkit.
|
|
128
|
+
* Method names mirror the MCP resource methods (`resources/list`/`read`/
|
|
129
|
+
* `subscribe`). Consumed by the Napster agent over our own path — not
|
|
130
|
+
* interoperable with third-party WebMCP agents until the standard formalizes
|
|
131
|
+
* resources.
|
|
132
|
+
*/
|
|
133
|
+
export interface ResourceExtension {
|
|
134
|
+
/** Producer-side: register a live-state resource. Returns an unregister fn. */
|
|
135
|
+
registerResource<T = unknown>(resource: ResourceDescriptor<T>): () => void;
|
|
136
|
+
/** Consumer-side: list resources (mirrors `resources/list`). */
|
|
137
|
+
getResources(): ResourceInfo[];
|
|
138
|
+
/** Consumer-side: read one resource's current value (mirrors `resources/read`). */
|
|
139
|
+
readResource<T = unknown>(uri: string): Promise<T | undefined>;
|
|
140
|
+
/** Consumer-side: subscribe to one resource; returns an unsubscribe. */
|
|
141
|
+
subscribeResource(uri: string, handler: (update: ResourceUpdate) => void): () => void;
|
|
142
|
+
}
|
|
143
|
+
/** `document.modelContext` augmented with the toolkit's resource extension. */
|
|
144
|
+
export type ModelContextWithResources = ModelContext & ResourceExtension;
|
|
145
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Public types for the Napster WebMCP Toolkit.
|
|
2
|
+
//
|
|
3
|
+
// The toolkit re-bases on the WebMCP standard (`document.modelContext`). The
|
|
4
|
+
// website developer writes STANDARD `registerTool` calls — these types describe
|
|
5
|
+
// the standard surface we rely on, plus the two Napster extensions that live
|
|
6
|
+
// OFF the standard call site: safety tiers (`registerStatefulTool`) and live
|
|
7
|
+
// state (the MCP-shaped resource extension).
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,EAAE;AACF,6EAA6E;AAC7E,gFAAgF;AAChF,6EAA6E;AAC7E,6EAA6E;AAC7E,6CAA6C"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# WebMCP Toolkit — regenerate the tools file when the commit message opts in.
|
|
2
|
+
#
|
|
3
|
+
# Opt-in only: this runs the analysis agent ONLY when the just-made commit
|
|
4
|
+
# message contains the marker `[webmcp-toolkit]`. Every other commit is untouched.
|
|
5
|
+
# Output lands as UNCOMMITTED changes to src/webmcp/tools.ts for you to review
|
|
6
|
+
# and commit separately (a post-commit hook cannot amend the commit).
|
|
7
|
+
#
|
|
8
|
+
# Installed/updated by `webmcp-toolkit install-hook`. Do not edit between the markers.
|
|
9
|
+
if git log -1 --pretty=%B | grep -q '\[webmcp-toolkit\]'; then
|
|
10
|
+
echo "[webmcp-toolkit] commit marker found — regenerating tools…"
|
|
11
|
+
if [ -x "node_modules/.bin/webmcp-toolkit" ]; then
|
|
12
|
+
node_modules/.bin/webmcp-toolkit generate || echo "[webmcp-toolkit] generate failed (non-fatal)"
|
|
13
|
+
else
|
|
14
|
+
npx --no-install webmcp-toolkit generate || echo "[webmcp-toolkit] generate failed (non-fatal)"
|
|
15
|
+
fi
|
|
16
|
+
echo "[webmcp-toolkit] done — review the changes to src/webmcp/tools.ts and commit them."
|
|
17
|
+
fi
|
package/package.json
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@napster-corp/webmcp-toolkit",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Napster WebMCP Toolkit — polyfills the WebMCP standard (document.modelContext) cross-browser and adds live state, safety tiers, and dev tooling. Write standard registerTool calls; any compatible agent (including Napster's) can operate your app.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"browser": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./dev-panel": {
|
|
16
|
+
"types": "./dist/dev-panel.d.ts",
|
|
17
|
+
"import": "./dist/dev-panel.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"bin": {
|
|
21
|
+
"webmcp-toolkit": "./bin/webmcp-toolkit.mjs"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"src",
|
|
26
|
+
"skills",
|
|
27
|
+
"bin",
|
|
28
|
+
"tools",
|
|
29
|
+
"hooks",
|
|
30
|
+
"README.md",
|
|
31
|
+
"LICENSE"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsc",
|
|
35
|
+
"clean": "rm -rf dist",
|
|
36
|
+
"test": "tsx --test tests/*.test.ts",
|
|
37
|
+
"generate": "node ./bin/webmcp-toolkit.mjs generate",
|
|
38
|
+
"install-hook": "node ./bin/webmcp-toolkit.mjs install-hook",
|
|
39
|
+
"prepublishOnly": "npm run clean && npm run build && npm test"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"webmcp",
|
|
43
|
+
"model-context",
|
|
44
|
+
"document.modelContext",
|
|
45
|
+
"agent",
|
|
46
|
+
"ai-agent",
|
|
47
|
+
"mcp",
|
|
48
|
+
"polyfill",
|
|
49
|
+
"in-browser",
|
|
50
|
+
"tools"
|
|
51
|
+
],
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"repository": {
|
|
54
|
+
"type": "git",
|
|
55
|
+
"url": "https://github.com/napster-corp/webmcp-toolkit.git"
|
|
56
|
+
},
|
|
57
|
+
"homepage": "https://github.com/napster-corp/webmcp-toolkit#readme",
|
|
58
|
+
"bugs": {
|
|
59
|
+
"url": "https://github.com/napster-corp/webmcp-toolkit/issues"
|
|
60
|
+
},
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=18"
|
|
63
|
+
},
|
|
64
|
+
"publishConfig": {
|
|
65
|
+
"access": "public"
|
|
66
|
+
},
|
|
67
|
+
"sideEffects": [
|
|
68
|
+
"./dist/index.js",
|
|
69
|
+
"./src/index.ts"
|
|
70
|
+
],
|
|
71
|
+
"dependencies": {
|
|
72
|
+
"@mcp-b/webmcp-polyfill": "3.0.0"
|
|
73
|
+
},
|
|
74
|
+
"peerDependencies": {
|
|
75
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.0"
|
|
76
|
+
},
|
|
77
|
+
"peerDependenciesMeta": {
|
|
78
|
+
"@anthropic-ai/claude-agent-sdk": {
|
|
79
|
+
"optional": true
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"devDependencies": {
|
|
83
|
+
"tsx": "^4.19.0",
|
|
84
|
+
"typescript": "^5.4.0"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: add-edge-mcp-dev-panel
|
|
3
|
+
description: Install a small, opt-in, dev-only floating panel for testing the WebMCP surface by hand. The panel lists every registered tool with a form for its arguments (rendered from the tool's `inputSchema`), every live-state resource with a live JSON view, and an event log of every tool execution and every resource update. Use when the developer says "add a dev panel", "install the webmcp dev panel", "I want a UI for testing the bridge", "add a way to test the agent bridge without typing in the console", or accepts the offer at the end of `setup-edge-mcp`. Mounts only in dev mode and is excluded from production bundles via a sub-path import. This skill is OPT-IN: it does nothing the bridge itself needs to function. If the developer prefers the browser console, their existing test suite, or no testing scaffold at all, do not run this skill.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# add-edge-mcp-dev-panel
|
|
7
|
+
|
|
8
|
+
Install an opt-in, dev-only floating panel that mounts inside the app's own runtime and gives the developer a UI for exercising the WebMCP surface by hand: pick a tool, fill in its arguments using fields rendered from the tool's `inputSchema`, click Run, watch the resource updates scroll by in an event log. The panel is a development convenience, not part of the bridge — the WebMCP surface works the same with or without it.
|
|
9
|
+
|
|
10
|
+
The panel reads `document.modelContext` itself — the standard surface the `@napster-corp/webmcp-toolkit` polyfill installs on import. It does **not** take any instance; there is no bridge object to hand it.
|
|
11
|
+
|
|
12
|
+
## When to use this skill
|
|
13
|
+
|
|
14
|
+
Run this skill when **all** of these are true:
|
|
15
|
+
|
|
16
|
+
- The WebMCP toolkit is already set up in the target app (the toolkit is imported so its polyfill installs `document.modelContext`, and tools and live-state resources are registered).
|
|
17
|
+
- The developer wants a UI-driven way to exercise the bridge, instead of typing into DevTools or writing tests in their existing suite.
|
|
18
|
+
- The developer has accepted the panel — they were offered it at the end of `setup-edge-mcp`, or asked for it directly.
|
|
19
|
+
|
|
20
|
+
If the toolkit isn't set up yet, stop and route the developer to `setup-edge-mcp` first. If the developer hasn't been asked whether they want the panel, ask before installing — this is opt-in scaffolding, not a default.
|
|
21
|
+
|
|
22
|
+
## 0. Confirm prerequisites
|
|
23
|
+
|
|
24
|
+
Before any changes:
|
|
25
|
+
|
|
26
|
+
- Verify `@napster-corp/webmcp-toolkit` is listed in the app's `package.json`. If not, the bridge isn't installed and `setup-edge-mcp` is the right place to start.
|
|
27
|
+
- Locate the `src/webmcp/` folder — `src/webmcp/index.ts` is where the toolkit is imported and tools are registered, with `tools.ts` and `resources.ts` alongside it. If the app uses a different location (`src/lib/webmcp/`, `app/lib/webmcp/`, etc.), `grep -r '@napster-corp/webmcp-toolkit' src/` confirms the path.
|
|
28
|
+
- Identify how the app expresses "dev mode" — `import.meta.env.DEV` (Vite/Astro), `process.env.NODE_ENV === 'development'` (Next.js/Webpack/CRA), `__DEV__` (some custom setups). Match the app's existing convention.
|
|
29
|
+
|
|
30
|
+
If any of these can't be found, stop and ask the developer in plain language. Don't guess.
|
|
31
|
+
|
|
32
|
+
The app's entry point (`src/main.ts` etc.) is where the panel is mounted, behind a dev-mode guard. It already imports the toolkit setup module from the setup step; this skill adds the dev-panel mount alongside it.
|
|
33
|
+
|
|
34
|
+
## 1. Install
|
|
35
|
+
|
|
36
|
+
The dev panel ships as a sub-path import of the same `@napster-corp/webmcp-toolkit` package. Nothing new to install — the toolkit package is already in `package.json`. The skill makes two changes:
|
|
37
|
+
|
|
38
|
+
1. Adds a new file: `src/webmcp/dev-panel.ts`.
|
|
39
|
+
2. Adds one import and one call to the app's entry point, behind a dev-mode guard.
|
|
40
|
+
|
|
41
|
+
### Create `src/webmcp/dev-panel.ts`
|
|
42
|
+
|
|
43
|
+
A small wrapper that handles the dev-mode guard and the dynamic import. The dynamic `import()` is what keeps the panel's UI code out of the production bundle — a *static* import would pull the panel into the production chunk even if the if-guard skipped the call.
|
|
44
|
+
|
|
45
|
+
`installDevPanel` takes **no instance argument** — it reads `document.modelContext` itself (the toolkit's polyfill installs that surface on import). It accepts an options-only object and returns an `uninstall()` function.
|
|
46
|
+
|
|
47
|
+
**Pick the dev-mode flag that matches the app's bundler — this is not optional, the wrong flag silently fails:**
|
|
48
|
+
|
|
49
|
+
- **Vite, Astro, Nuxt, SvelteKit** → `import.meta.env.DEV`
|
|
50
|
+
- **Next.js, Webpack, Create React App, Remix** → `process.env.NODE_ENV === 'development'`
|
|
51
|
+
- **Some custom setups** → `__DEV__` (define-replaced at build)
|
|
52
|
+
|
|
53
|
+
How to tell: search the existing codebase for one of these patterns. Use whichever one already appears in the app's own code. If none appear and the framework isn't listed above, ask the developer.
|
|
54
|
+
|
|
55
|
+
**Vite-style (the most common):**
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
// src/webmcp/dev-panel.ts
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Mount the dev panel in dev mode. No-op in production.
|
|
62
|
+
*
|
|
63
|
+
* The dynamic import is required: it tree-shakes the panel's UI code from the
|
|
64
|
+
* production bundle. A static `import` at the top of this file would ship that
|
|
65
|
+
* code to production regardless of the dev-mode guard.
|
|
66
|
+
*
|
|
67
|
+
* `installDevPanel` takes no instance — it reads `document.modelContext`,
|
|
68
|
+
* which the toolkit's polyfill installs on import.
|
|
69
|
+
*/
|
|
70
|
+
export function setupDevPanel(): void {
|
|
71
|
+
if (!import.meta.env.DEV) return;
|
|
72
|
+
void import('@napster-corp/webmcp-toolkit/dev-panel').then(({ installDevPanel }) => {
|
|
73
|
+
installDevPanel();
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Next.js / Webpack / CRA-style (just swap the guard):**
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
// src/webmcp/dev-panel.ts
|
|
82
|
+
|
|
83
|
+
export function setupDevPanel(): void {
|
|
84
|
+
if (process.env.NODE_ENV !== 'development') return;
|
|
85
|
+
void import('@napster-corp/webmcp-toolkit/dev-panel').then(({ installDevPanel }) => {
|
|
86
|
+
installDevPanel();
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The rest of the file is identical. Pick one. Don't introduce a second convention into the codebase.
|
|
92
|
+
|
|
93
|
+
### Mount it from the app entry point
|
|
94
|
+
|
|
95
|
+
Add one import and one call to the app's entry point (`src/main.ts` etc.), after the toolkit setup module is imported so the polyfill and registrations are in place first. `setupDevPanel()` self-guards on dev mode, so the call is safe to leave in unconditionally.
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
// src/main.ts
|
|
99
|
+
import './webmcp'; // toolkit setup: installs the surface + registers tools
|
|
100
|
+
import { setupDevPanel } from './webmcp/dev-panel'; // ← added by this skill
|
|
101
|
+
|
|
102
|
+
setupDevPanel(); // ← added by this skill, no-op outside dev mode
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
That's the entire install. No new dependencies. The panel mounts on next dev-mode reload.
|
|
106
|
+
|
|
107
|
+
### What `installDevPanel` actually does
|
|
108
|
+
|
|
109
|
+
When the dev-mode dynamic import resolves and `installDevPanel()` runs, it:
|
|
110
|
+
|
|
111
|
+
- Reads `document.modelContext.getTools()` and (via the resource extension) `document.modelContext.getResources()` to render the initial UI.
|
|
112
|
+
- Subscribes to the `resourceupdated` event to keep the resource view and event log live.
|
|
113
|
+
- Attaches a keyboard shortcut listener so the panel can be toggled without clicking through DevTools.
|
|
114
|
+
|
|
115
|
+
It returns an `uninstall()` function. The developer doesn't need to call it normally; HMR re-runs the bootstrap and the panel re-mounts cleanly.
|
|
116
|
+
|
|
117
|
+
## 2. Where it mounts and how to open it
|
|
118
|
+
|
|
119
|
+
The panel is a fixed-position floating div in the bottom-right of the viewport, hidden by default to keep the dev experience uncluttered. **Toggle with `Cmd+Shift+E`** (or `Ctrl+Shift+E` on Windows/Linux). A small circular "E" toggle button also appears in the same corner when the panel is closed, so the developer doesn't need to remember the shortcut.
|
|
120
|
+
|
|
121
|
+
`installDevPanel({ shortcut?, startOpen? })` accepts two optional settings (options-only, no instance argument); edit `src/webmcp/dev-panel.ts` to pass them through if the customer wants non-default behavior:
|
|
122
|
+
|
|
123
|
+
- **`shortcut`** — the keyboard combo (default `'Cmd+Shift+E'`, `Ctrl+Shift+E` on non-Mac). Accepts `Cmd`, `Ctrl`, `Shift`, `Alt`, `Meta` modifiers plus a single key.
|
|
124
|
+
- **`startOpen`** — `true` to mount the panel open instead of hidden (default `false`). Useful for the first run-through so the developer sees it immediately.
|
|
125
|
+
|
|
126
|
+
The panel never auto-opens unless `startOpen: true` is passed — the developer chooses when to look at it. This keeps the rest of the dev experience unchanged.
|
|
127
|
+
|
|
128
|
+
## 3. What the panel shows
|
|
129
|
+
|
|
130
|
+
Three sections, top to bottom:
|
|
131
|
+
|
|
132
|
+
### State
|
|
133
|
+
|
|
134
|
+
A list of every registered live-state resource (read via the resource extension's `getResources()` / `readResource()`), each with:
|
|
135
|
+
|
|
136
|
+
- The resource's name (or URI).
|
|
137
|
+
- The current value, rendered as collapsible JSON. Updates live as the `resourceupdated` event fires.
|
|
138
|
+
- A timestamp of the most recent update.
|
|
139
|
+
- A "Refresh" button that calls `document.modelContext.readResource(uri)` and rerenders, in case the developer wants to force a pull.
|
|
140
|
+
|
|
141
|
+
If no resources are registered, the section shows an empty-state explaining that the gate ("out-of-band state only") may have excluded everything legitimately.
|
|
142
|
+
|
|
143
|
+
### Tools
|
|
144
|
+
|
|
145
|
+
A list of every registered tool, sorted alphabetically by name, grouped by safety tier (read → reversible → irreversible). Each tool shows:
|
|
146
|
+
|
|
147
|
+
- Name, description, safety tier (color-coded), and idempotency flag.
|
|
148
|
+
- A form with one field per argument, **rendered from the tool's `inputSchema`**. The field types follow the schema:
|
|
149
|
+
- `type: 'string'` → text input. With `enum`, becomes a select.
|
|
150
|
+
- `type: 'integer'` / `'number'` → number input.
|
|
151
|
+
- `type: 'boolean'` → checkbox.
|
|
152
|
+
- `type: 'object'` → nested fieldset (recursive).
|
|
153
|
+
- `type: 'array'` of primitives → repeating row.
|
|
154
|
+
- Anything more exotic (anyOf, allOf, $ref) → falls back to a JSON textarea for that field, with a small note.
|
|
155
|
+
- Required fields are asterisked. JSON Schema `description` becomes hint text under the field.
|
|
156
|
+
- A "Run" button that calls `document.modelContext.executeTool(toolInfo, JSON.stringify(formArgs))` and shows the result inline.
|
|
157
|
+
- The most recent execution result, with success/error state and timing.
|
|
158
|
+
|
|
159
|
+
**Last-used args are remembered in `localStorage`**, keyed by tool name. The next time the developer opens the panel, the form is pre-filled with the last values they used. This is how the developer ends up with their own per-app sample args without any new API surface — they just use the panel.
|
|
160
|
+
|
|
161
|
+
### Event log
|
|
162
|
+
|
|
163
|
+
A chronological list (newest at top) of every event since the panel mounted:
|
|
164
|
+
|
|
165
|
+
- `CALL <name>` with the args object — emitted before `executeTool`.
|
|
166
|
+
- `RESULT <name>` with the return — emitted after.
|
|
167
|
+
- `STATE <name>` with the new value — emitted on every `resourceupdated` event.
|
|
168
|
+
|
|
169
|
+
Each entry timestamps to the millisecond. The log holds the last 200 entries by default; older entries roll off.
|
|
170
|
+
|
|
171
|
+
## 4. What this skill does NOT do
|
|
172
|
+
|
|
173
|
+
- **Run scripted scenarios.** The panel is interactive only. It doesn't know what a meaningful workflow looks like in any given app, so it doesn't guess. The developer drives.
|
|
174
|
+
- **Generate default argument values.** The first time the developer runs a tool, the form is empty; they fill it in. From then on, the panel remembers their last values. The skill never invents test data.
|
|
175
|
+
- **Mock the app's underlying state.** The panel is a thin UI over the live surface in the live app. If a tool requires a real product ID, the developer has to provide a real product ID. Mocking is the test suite's job, not the panel's.
|
|
176
|
+
- **Validate args before running.** Validation is governed by how the tool's `inputSchema` is enforced by the toolkit. If validation is enabled, the panel surfaces the validator's errors when `executeTool` returns an error result. If not, args pass through unchecked, same as for any other consumer.
|
|
177
|
+
- **Replace the app's existing test suite.** For ongoing regression coverage, write tests in Vitest/Jest as usual. The panel is for ad-hoc exploration during development.
|
|
178
|
+
- **Mount in production.** The dynamic `import('@napster-corp/webmcp-toolkit/dev-panel')` is gated behind a dev-mode check. If the developer's bundler doesn't tree-shake conditionals correctly (rare), the panel still does nothing in production because the dev-mode check fails — but the safer path is to keep the dynamic import behind the same flag.
|
|
179
|
+
|
|
180
|
+
## 5. Sign off
|
|
181
|
+
|
|
182
|
+
Walk the developer through:
|
|
183
|
+
|
|
184
|
+
- The two changes involved: `src/webmcp/dev-panel.ts` (new) and the entry point (one import + one guarded call added).
|
|
185
|
+
- The keyboard shortcut for toggling the panel.
|
|
186
|
+
- That last-used args are remembered in `localStorage` (so closing/reopening doesn't lose state).
|
|
187
|
+
- That the panel only renders when the dev-mode flag is true; the production bundle is unchanged.
|
|
188
|
+
- A one-minute demo path: open the app in dev, press the shortcut, run one `read` tool, watch the resource view update if a related live-state resource is registered.
|
|
189
|
+
|
|
190
|
+
Then prompt them to keep going: "Anything missing? Wrong shortcut? The shortcut is configurable — let me know if you want to tweak it."
|
|
191
|
+
|
|
192
|
+
## Failure modes worth flagging
|
|
193
|
+
|
|
194
|
+
If the developer reports any of these, here's the diagnosis:
|
|
195
|
+
|
|
196
|
+
- **"The panel doesn't appear when I press the shortcut."** `setupDevPanel()` probably didn't run. Confirm the call line is present in the entry point and that the dev-mode flag (`import.meta.env.DEV` or equivalent) actually evaluates true in the customer's bundler. Look for the panel's own `console.info('[webmcp dev panel] mounted')` message — if it's missing, the dynamic import never resolved.
|
|
197
|
+
|
|
198
|
+
- **"The tool list is empty."** `setupDevPanel()` ran before the toolkit's tool registrations completed. The order matters: import the toolkit setup module (which installs `document.modelContext` and registers tools) first, mount the dev panel last. Fix the order.
|
|
199
|
+
|
|
200
|
+
- **"My form field shows a JSON textarea instead of a real input."** The schema for that field uses a JSON Schema construct the panel doesn't render natively (anyOf, allOf, $ref, complex conditionals). Either simplify the schema, or accept the JSON textarea as the fallback.
|
|
201
|
+
|
|
202
|
+
- **"The panel rendered in my production build."** Either the dev-mode flag is wrong (it evaluates true in production), or the `import()` in `src/webmcp/dev-panel.ts` was changed from dynamic to static at some point. Static imports get included in the bundle even when the call site is dead code; the dynamic form is required for tree-shaking.
|
|
203
|
+
|
|
204
|
+
## What's next
|
|
205
|
+
|
|
206
|
+
The dev panel is a tool, not a permanent fixture. Once the bridge is stable and the app has CI tests covering the tools, the developer can leave it installed (it costs nothing in production) or remove the bootstrap entirely. There's no follow-up skill — the next interesting step is connecting an actual agent vendor (Napster's Omniagent or any other WebMCP-compatible SDK), and that's handled by that vendor's own skills.
|