@waniwani/kit 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/dist/web.d.ts ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The widget-side API.
3
+ *
4
+ * A component gets its data from `useWidget(widget)`, typed straight off the
5
+ * widget's own `data` schema. There is no generated helpers file and no server
6
+ * type import, so a widget cannot drift from its contract.
7
+ */
8
+ import type { Infer, Shape, WidgetDefinition } from "./index.js";
9
+ export { mountView, useCallTool, useDisplayMode, useDownload, useFiles, useLayout, useOpenExternal, useRequestClose, useRequestModal, useRequestSize, useSendFollowUpMessage, useSetOpenInAppUrl, useUser, } from "skybridge/web";
10
+ /**
11
+ * Per-widget state that survives re-renders and is handed back to the host.
12
+ *
13
+ * Upstream this is `useViewState`. The name here follows the wire format
14
+ * instead: what reaches ChatGPT is `openai/widgetCSP`, `openai/widgetDescription`,
15
+ * and `window.openai.widgetState`. Holding the public vocabulary steady is also
16
+ * the point of the package — the upstream rename of widgets to views cost app
17
+ * repos nothing, and it should keep costing them nothing.
18
+ */
19
+ export { useViewState as useWidgetState } from "skybridge/web";
20
+ export { defineWidget } from "./index.js";
21
+ export type { Infer, Shape, WidgetDefinition } from "./index.js";
22
+ export type UseWidgetResult<S extends Shape> = {
23
+ /**
24
+ * The widget's data. Present as soon as the host has the tool input, which
25
+ * on most hosts is before the server responds — so render optimistically.
26
+ */
27
+ data: Infer<S> | undefined;
28
+ /** The host is still streaming the tool call. */
29
+ isLoading: boolean;
30
+ /** The server has responded and `data` is final. */
31
+ isReady: boolean;
32
+ };
33
+ /**
34
+ * Read this widget's data.
35
+ *
36
+ * @param _widget the widget contract, passed for type inference only
37
+ */
38
+ export declare function useWidget<S extends Shape>(_widget: WidgetDefinition<S>): UseWidgetResult<S>;
39
+ //# sourceMappingURL=web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../src/web.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEjE,OAAO,EACN,SAAS,EACT,WAAW,EACX,cAAc,EACd,WAAW,EACX,QAAQ,EACR,SAAS,EACT,eAAe,EACf,eAAe,EACf,eAAe,EACf,cAAc,EACd,sBAAsB,EACtB,kBAAkB,EAClB,OAAO,GACP,MAAM,eAAe,CAAC;AAEvB;;;;;;;;GAQG;AACH,OAAO,EAAE,YAAY,IAAI,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/D,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEjE,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,KAAK,IAAI;IAC9C;;;OAGG;IACH,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAC3B,iDAAiD;IACjD,SAAS,EAAE,OAAO,CAAC;IACnB,oDAAoD;IACpD,OAAO,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,KAAK,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAS3F"}
package/dist/web.js ADDED
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The widget-side API.
3
+ *
4
+ * A component gets its data from `useWidget(widget)`, typed straight off the
5
+ * widget's own `data` schema. There is no generated helpers file and no server
6
+ * type import, so a widget cannot drift from its contract.
7
+ */
8
+ import { useToolInfo } from "skybridge/web";
9
+ export { mountView, useCallTool, useDisplayMode, useDownload, useFiles, useLayout, useOpenExternal, useRequestClose, useRequestModal, useRequestSize, useSendFollowUpMessage, useSetOpenInAppUrl, useUser, } from "skybridge/web";
10
+ /**
11
+ * Per-widget state that survives re-renders and is handed back to the host.
12
+ *
13
+ * Upstream this is `useViewState`. The name here follows the wire format
14
+ * instead: what reaches ChatGPT is `openai/widgetCSP`, `openai/widgetDescription`,
15
+ * and `window.openai.widgetState`. Holding the public vocabulary steady is also
16
+ * the point of the package — the upstream rename of widgets to views cost app
17
+ * repos nothing, and it should keep costing them nothing.
18
+ */
19
+ export { useViewState as useWidgetState } from "skybridge/web";
20
+ export { defineWidget } from "./index.js";
21
+ /**
22
+ * Read this widget's data.
23
+ *
24
+ * @param _widget the widget contract, passed for type inference only
25
+ */
26
+ export function useWidget(_widget) {
27
+ const info = useToolInfo();
28
+ const data = (info.output ?? info.input);
29
+ return {
30
+ data,
31
+ isLoading: !info.isSuccess,
32
+ isReady: info.isSuccess,
33
+ };
34
+ }
35
+ //# sourceMappingURL=web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.js","sourceRoot":"","sources":["../src/web.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5C,OAAO,EACN,SAAS,EACT,WAAW,EACX,cAAc,EACd,WAAW,EACX,QAAQ,EACR,SAAS,EACT,eAAe,EACf,eAAe,EACf,eAAe,EACf,cAAc,EACd,sBAAsB,EACtB,kBAAkB,EAClB,OAAO,GACP,MAAM,eAAe,CAAC;AAEvB;;;;;;;;GAQG;AACH,OAAO,EAAE,YAAY,IAAI,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/D,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAe1C;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAkB,OAA4B;IACtE,MAAM,IAAI,GAAG,WAAW,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,CAAyB,CAAC;IAEjE,OAAO;QACN,IAAI;QACJ,SAAS,EAAE,CAAC,IAAI,CAAC,SAAS;QAC1B,OAAO,EAAE,IAAI,CAAC,SAAS;KACvB,CAAC;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,88 @@
1
+ {
2
+ "name": "@waniwani/kit",
3
+ "version": "0.1.0",
4
+ "description": "Build an MCP app as a folder: tools, widgets, flows and docs, with one CLI and one shared server runtime.",
5
+ "type": "module",
6
+ "bin": {
7
+ "waniwani": "cli/index.mjs"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "default": "./dist/index.js"
15
+ },
16
+ "./server": {
17
+ "types": "./dist/server.d.ts",
18
+ "default": "./dist/server.js"
19
+ },
20
+ "./web": {
21
+ "types": "./dist/web.d.ts",
22
+ "default": "./dist/web.js"
23
+ }
24
+ },
25
+ "//files": "`src` ships alongside `dist` on purpose: `waniwani eject` vendors the runtime as readable TypeScript, and it reads it out of the installed package.",
26
+ "//dependencies": "tsx, typescript and the @types packages are here rather than in devDependencies because the underlying framework shells out to tsc and tsx by bare name and resolves types from the app repo tree, while declaring none of them. An app repo owns no build config, so this package is the only thing that can put them there. nodemon is the fourth of that set and arrives on its own, as a peer of it.",
27
+ "files": [
28
+ "dist",
29
+ "src",
30
+ "cli"
31
+ ],
32
+ "//README.md": "Written by `scripts/sync-readme.mjs` from the repo root README, which is this package's documentation, and gitignored. npm packs any README.md it finds next to the manifest whatever `files` says.",
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "scripts": {
37
+ "build": "tsc -p tsconfig.json",
38
+ "typecheck": "tsc -p tsconfig.json --noEmit",
39
+ "prepack": "tsc -p tsconfig.json"
40
+ },
41
+ "dependencies": {
42
+ "@modelcontextprotocol/sdk": "^1.29.0",
43
+ "@tailwindcss/vite": "^4.3.3",
44
+ "@types/node": "^24.12.0",
45
+ "@types/react": "^19.2.14",
46
+ "@types/react-dom": "^19.2.3",
47
+ "@vitejs/plugin-react": "^6.0.3",
48
+ "@waniwani/sdk": "^0.19.5",
49
+ "dotenv": "^17.4.1",
50
+ "skybridge": "^1.3.5",
51
+ "tailwindcss": "^4.3.3",
52
+ "tsx": "^4.20.6",
53
+ "typescript": "^6.0.2",
54
+ "vite": "^8.1.5"
55
+ },
56
+ "devDependencies": {
57
+ "@skybridge/devtools": "^1.2.7"
58
+ },
59
+ "//optionalDependencies": "cloudflared is what `waniwani tunnel` runs, and its install step fetches a platform binary. Every other command works without it, so a CI image or a container build installing with --omit=optional skips the download and still gets a working CLI.",
60
+ "optionalDependencies": {
61
+ "cloudflared": "^0.7.1"
62
+ },
63
+ "peerDependencies": {
64
+ "react": ">=19",
65
+ "react-dom": ">=19",
66
+ "zod": "^4"
67
+ },
68
+ "//repository": "`directory` is not decoration: `npm publish --provenance` reads this field to bind the published tarball to the workflow run that built it, and the attestation is rejected if it does not resolve.",
69
+ "repository": {
70
+ "type": "git",
71
+ "url": "git+https://github.com/WaniWani-AI/kit.git",
72
+ "directory": "packages/kit"
73
+ },
74
+ "keywords": [
75
+ "mcp",
76
+ "waniwani",
77
+ "mcp-server",
78
+ "mcp-app",
79
+ "framework",
80
+ "cli",
81
+ "widgets",
82
+ "tools",
83
+ "flows",
84
+ "skybridge"
85
+ ],
86
+ "author": "Waniwani",
87
+ "license": "MIT"
88
+ }
package/src/index.ts ADDED
@@ -0,0 +1,138 @@
1
+ /**
2
+ * The Waniwani MCP authoring API.
3
+ *
4
+ * This is everything an app author imports. There is no server bootstrap, no
5
+ * transport, no MCP wiring, no build config in an app repo — the runtime owns
6
+ * all of it (see `./server.ts`) and the CLI generates the glue (see `../cli/`).
7
+ *
8
+ * An app is a folder:
9
+ *
10
+ * waniwani.config.ts defineApp({ ... })
11
+ * tools/<name>.ts export default defineTool({ ... })
12
+ * widgets/<name>/widget.ts export default defineWidget({ ... })
13
+ * widgets/<name>/ui.tsx export default function Component() { ... }
14
+ * flows/<name>.ts export default createFlow({ ... }).compile()
15
+ * docs/<slug>.md searchable knowledge
16
+ */
17
+
18
+ import type { z } from "zod";
19
+
20
+ /** A Zod object shape — `{ name: z.string() }`, not `z.object({ ... })`. */
21
+ export type Shape = z.ZodRawShape;
22
+
23
+ /** The TypeScript type a `Shape` describes. */
24
+ export type Infer<S extends Shape> = z.infer<z.ZodObject<S>>;
25
+
26
+ /**
27
+ * Behavioural hints handed to the host LLM. The runtime translates these into
28
+ * MCP `annotations` and always fills in the `title` that Claude's Connectors
29
+ * Directory requires, so an app repo cannot get that wrong.
30
+ */
31
+ export type ToolHints = {
32
+ /** The tool only reads. Defaults to `true` for widgets, `false` for tools. */
33
+ readOnly?: boolean;
34
+ /** The tool can destroy data. */
35
+ destructive?: boolean;
36
+ /** The tool reaches out to the open internet. */
37
+ openWorld?: boolean;
38
+ /** Calling twice with the same input has the same effect as calling once. */
39
+ idempotent?: boolean;
40
+ };
41
+
42
+ // ---------------------------------------------------------------- app config
43
+
44
+ export type AppConfig = {
45
+ /** MCP server name, e.g. `oney-split-payment`. */
46
+ name: string;
47
+ /** Shown to humans in connector UIs. */
48
+ title?: string;
49
+ /** Defaults to the app `package.json` version. */
50
+ version?: string;
51
+ /**
52
+ * Server-level instructions handed to the host LLM once, before any tool
53
+ * call. Tone, guardrails, what this app is for.
54
+ */
55
+ instructions?: string;
56
+ };
57
+
58
+ export function defineApp(config: AppConfig): AppConfig {
59
+ return config;
60
+ }
61
+
62
+ // ---------------------------------------------------------------------- tools
63
+
64
+ /**
65
+ * What a tool handler may return. A string becomes the model-facing text; an
66
+ * object becomes `structuredContent` plus a JSON text fallback. Returning a
67
+ * full MCP `CallToolResult` is still allowed for the rare tool that needs it.
68
+ */
69
+ export type ToolResult =
70
+ | string
71
+ | Record<string, unknown>
72
+ | { content: Array<{ type: "text"; text: string }>; structuredContent?: Record<string, unknown> };
73
+
74
+ export type ToolDefinition<S extends Shape = Shape, R extends ToolResult = ToolResult> = {
75
+ title: string;
76
+ /** LLM-facing. When to call this, and what it does. */
77
+ description: string;
78
+ input?: S;
79
+ output?: Shape;
80
+ hints?: ToolHints;
81
+ run: (input: Infer<S>) => R | Promise<R>;
82
+ };
83
+
84
+ export function defineTool<S extends Shape, R extends ToolResult>(
85
+ def: ToolDefinition<S, R>,
86
+ ): ToolDefinition<S, R> {
87
+ return def;
88
+ }
89
+
90
+ // -------------------------------------------------------------------- widgets
91
+
92
+ export type WidgetCsp = {
93
+ /** Domains the widget may `fetch()`. */
94
+ connectDomains?: string[];
95
+ /** Domains the widget may load images/fonts/scripts from. */
96
+ resourceDomains?: string[];
97
+ };
98
+
99
+ /**
100
+ * A widget contract. This file is imported by *both* the server and the
101
+ * browser bundle, so it must stay free of React and CSS — the component lives
102
+ * next to it in `ui.tsx`.
103
+ *
104
+ * `data` is the single schema for the widget: it is the tool's input schema,
105
+ * its output schema, and the type `useWidget()` hands the component. One
106
+ * schema, so server and UI cannot drift.
107
+ */
108
+ export type WidgetDefinition<S extends Shape = Shape> = {
109
+ title: string;
110
+ /** LLM-facing. When to show this widget, and how to frame it. */
111
+ description: string;
112
+ data: S;
113
+ hints?: ToolHints;
114
+ csp?: WidgetCsp;
115
+ /**
116
+ * Text handed to the model alongside the rendered widget. Use it to tell
117
+ * the model what NOT to repeat, and what to wait for.
118
+ */
119
+ llmText?: (data: Infer<S>) => string;
120
+ /**
121
+ * Optional server-side loader, for widgets whose data comes from an API
122
+ * rather than from the model. Defaults to echoing the input through.
123
+ */
124
+ load?: (input: Infer<S>) => Infer<S> | Promise<Infer<S>>;
125
+ };
126
+
127
+ export function defineWidget<S extends Shape>(def: WidgetDefinition<S>): WidgetDefinition<S> {
128
+ return def;
129
+ }
130
+
131
+ // ----------------------------------------------------------------------- docs
132
+
133
+ /** A `docs/<slug>.md` file, parsed at build time and inlined into the bundle. */
134
+ export type DocEntry = {
135
+ slug: string;
136
+ title: string;
137
+ body: string;
138
+ };
package/src/server.ts ADDED
@@ -0,0 +1,285 @@
1
+ /**
2
+ * The shared MCP runtime.
3
+ *
4
+ * Every Waniwani MCP app runs this exact code. App repos contain no server
5
+ * bootstrap, so a runtime fix is one package bump away for all of them — no
6
+ * 30-repo sweep, no per-repo verification.
7
+ *
8
+ * The server itself belongs to the distribution template, which constructs it,
9
+ * registers whatever tools it ships, and runs it. The CLI generates one file
10
+ * against that seam — `src/waniwani.ts` — which imports the app's modules and
11
+ * hands them to `registerApp()`. Nothing else.
12
+ */
13
+
14
+ import { McpServer, type ViewName } from "skybridge/server";
15
+ import { z } from "zod";
16
+ import type { DocEntry, Shape, ToolHints, WidgetCsp } from "./index.js";
17
+
18
+ /**
19
+ * The manifest holds definitions with unrelated schemas side by side, so the
20
+ * handler signatures are widened here. `never` in the parameter position
21
+ * accepts any concrete handler; the call sites cast back.
22
+ */
23
+ type AnyToolDefinition = {
24
+ title: string;
25
+ description: string;
26
+ input?: Shape;
27
+ output?: Shape;
28
+ hints?: ToolHints;
29
+ run: (input: never) => unknown;
30
+ };
31
+
32
+ type AnyWidgetDefinition = {
33
+ title: string;
34
+ description: string;
35
+ data: Shape;
36
+ hints?: ToolHints;
37
+ csp?: WidgetCsp;
38
+ llmText?: (data: never) => string;
39
+ load?: (input: never) => unknown;
40
+ };
41
+
42
+ /** A flow compiled by `createFlow(...).compile()` from `@waniwani/sdk/mcp`. */
43
+ export type CompiledFlow = {
44
+ name: string;
45
+ // biome-ignore lint/suspicious/noExplicitAny: the SDK's own tool config shape
46
+ config: any;
47
+ // biome-ignore lint/suspicious/noExplicitAny: the SDK's own handler shape
48
+ handler: any;
49
+ };
50
+
51
+ export type Manifest = {
52
+ tools: Array<{ name: string; def: AnyToolDefinition }>;
53
+ widgets: Array<{ name: string; def: AnyWidgetDefinition }>;
54
+ flows: CompiledFlow[];
55
+ docs: DocEntry[];
56
+ /**
57
+ * Origins the template's Tailwind entry loads from, read off it at build
58
+ * time. Every view imports that stylesheet, so every widget needs them.
59
+ */
60
+ styleDomains?: string[];
61
+ };
62
+
63
+ /**
64
+ * The origins a widget may load assets from: its own, plus the ones its
65
+ * stylesheet needs.
66
+ *
67
+ * A host that enforces the widget CSP drops undeclared requests silently — a
68
+ * blocked webfont is not an error, just a fallback face — so the base
69
+ * stylesheet's origins are added for every widget rather than left to each app
70
+ * to remember. An app's own `csp` is additive, never overwritten.
71
+ */
72
+ function resourceDomains(csp: WidgetCsp | undefined, styleDomains: string[]) {
73
+ const merged = [...new Set([...(csp?.resourceDomains ?? []), ...styleDomains])];
74
+ return merged.length > 0 ? merged : undefined;
75
+ }
76
+
77
+ /**
78
+ * Translate `hints` into MCP annotations. `title` is always present because
79
+ * Claude's Connectors Directory rejects tools without one.
80
+ */
81
+ function annotations(title: string, hints: ToolHints | undefined, defaults: ToolHints) {
82
+ const merged = { ...defaults, ...hints };
83
+ return {
84
+ title,
85
+ readOnlyHint: merged.readOnly ?? false,
86
+ destructiveHint: merged.destructive ?? false,
87
+ openWorldHint: merged.openWorld ?? false,
88
+ idempotentHint: merged.idempotent ?? false,
89
+ };
90
+ }
91
+
92
+ /** Normalise whatever a tool handler returned into an MCP `CallToolResult`. */
93
+ function toolResult(value: unknown) {
94
+ if (typeof value === "string") {
95
+ return { content: [{ type: "text" as const, text: value }] };
96
+ }
97
+ if (value && typeof value === "object" && "content" in value) {
98
+ return value as { content: Array<{ type: "text"; text: string }> };
99
+ }
100
+ const structuredContent = (value ?? {}) as Record<string, unknown>;
101
+ return {
102
+ structuredContent,
103
+ content: [{ type: "text" as const, text: JSON.stringify(structuredContent, null, 2) }],
104
+ };
105
+ }
106
+
107
+ /**
108
+ * Default model-facing text for a widget. Widgets render their own detail, so
109
+ * the model is told to stop narrating it — the single most common cause of a
110
+ * widget being read aloud twice.
111
+ */
112
+ function defaultWidgetText(name: string) {
113
+ return `The ${name} widget is now rendered for the user. It displays all the detail itself — do NOT list or repeat its contents in text. Acknowledge it in one short sentence, then wait for the user to interact with it.`;
114
+ }
115
+
116
+ function widgetError(name: string, error: unknown) {
117
+ const message = error instanceof Error ? error.message : String(error);
118
+ console.error(`[waniwani] widget "${name}" failed to load:`, error);
119
+ return {
120
+ isError: true as const,
121
+ content: [
122
+ {
123
+ type: "text" as const,
124
+ text: `The ${name} widget could not load its data (${message}). Tell the user something went wrong on our side and offer to try again — do not invent the data.`,
125
+ },
126
+ ],
127
+ };
128
+ }
129
+
130
+ function registerDocsTool(server: McpServer, docs: DocEntry[]) {
131
+ const corpus = docs.map((doc) => ({
132
+ ...doc,
133
+ haystack: `${doc.title}\n${doc.body}`.toLowerCase(),
134
+ }));
135
+
136
+ server.registerTool(
137
+ {
138
+ name: "search_docs",
139
+ title: "Search the documentation",
140
+ description:
141
+ "Search this product's documentation and answer general questions — pricing, eligibility, policies, how things work. Always search before answering, and answer only from what comes back. Never invent facts that are not in the results.",
142
+ inputSchema: { question: z.string().describe("The user's question, in their own words.") },
143
+ outputSchema: {
144
+ results: z.array(z.object({ slug: z.string(), title: z.string(), body: z.string() })),
145
+ },
146
+ annotations: annotations("Search the documentation", undefined, { readOnly: true }),
147
+ },
148
+ async ({ question }) => {
149
+ const terms = question
150
+ .toLowerCase()
151
+ .split(/[^a-z0-9]+/)
152
+ .filter((term) => term.length > 2);
153
+
154
+ const results = corpus
155
+ .map((doc) => ({
156
+ doc,
157
+ score: terms.reduce((sum, term) => sum + (doc.haystack.includes(term) ? 1 : 0), 0),
158
+ }))
159
+ .filter(({ score }) => score > 0)
160
+ .sort((a, b) => b.score - a.score)
161
+ .slice(0, 3)
162
+ .map(({ doc }) => ({ slug: doc.slug, title: doc.title, body: doc.body }));
163
+
164
+ if (results.length === 0) {
165
+ const text = "Nothing in the documentation covers that question.";
166
+ return { structuredContent: { results: [] }, content: [{ type: "text" as const, text }] };
167
+ }
168
+
169
+ return {
170
+ structuredContent: { results },
171
+ content: [
172
+ {
173
+ type: "text" as const,
174
+ text: results.map((r) => `## ${r.title}\n${r.body}`).join("\n\n---\n\n"),
175
+ },
176
+ ],
177
+ };
178
+ },
179
+ );
180
+ }
181
+
182
+ /**
183
+ * Register an app's tools, widgets, flows, and docs onto a server the template
184
+ * built.
185
+ *
186
+ * The template owns construction, its own tools, `withWaniwani`, and `run()`.
187
+ * This adds to that server rather than replacing it, so a tool the template
188
+ * ships reaches every app built on it — one publish, not thirty edits — and an
189
+ * app's own tools sit alongside it.
190
+ */
191
+ export async function registerApp(server: McpServer, manifest: Manifest): Promise<McpServer> {
192
+ const { tools, widgets, flows, docs, styleDomains = [] } = manifest;
193
+
194
+ // Widgets: one `data` schema drives the input schema, the structured output,
195
+ // and the type the component receives.
196
+ //
197
+ // A widget is a tool with a view attached. The view's component name is the
198
+ // widget's folder name, which is also the name of the entry the generator
199
+ // writes into `src/views/` — one name, from the filesystem, so a widget
200
+ // cannot be registered against a component that was never bundled.
201
+ for (const { name, def } of widgets) {
202
+ server.registerTool(
203
+ {
204
+ name,
205
+ title: def.title,
206
+ description: def.description,
207
+ inputSchema: def.data,
208
+ outputSchema: def.data,
209
+ annotations: annotations(def.title, def.hints, { readOnly: true }),
210
+ view: {
211
+ component: name as ViewName,
212
+ description: def.description,
213
+ csp: {
214
+ ...def.csp,
215
+ resourceDomains: resourceDomains(def.csp, styleDomains),
216
+ },
217
+ },
218
+ },
219
+ async (input) => {
220
+ try {
221
+ const data = (def.load ? await def.load(input as never) : input) as Record<
222
+ string,
223
+ unknown
224
+ >;
225
+ return {
226
+ structuredContent: data,
227
+ content: [
228
+ {
229
+ type: "text" as const,
230
+ text: def.llmText?.(data as never) ?? defaultWidgetText(name),
231
+ },
232
+ ],
233
+ };
234
+ } catch (error) {
235
+ return widgetError(name, error);
236
+ }
237
+ },
238
+ );
239
+ }
240
+
241
+ for (const { name, def } of tools) {
242
+ server.registerTool(
243
+ {
244
+ name,
245
+ title: def.title,
246
+ description: def.description,
247
+ inputSchema: def.input ?? {},
248
+ outputSchema: def.output,
249
+ annotations: annotations(def.title, def.hints, { readOnly: false }),
250
+ },
251
+ async (input) => {
252
+ try {
253
+ return toolResult(await def.run(input as never));
254
+ } catch (error) {
255
+ const message = error instanceof Error ? error.message : String(error);
256
+ console.error(`[waniwani] tool "${name}" failed:`, error);
257
+ return {
258
+ isError: true as const,
259
+ content: [
260
+ {
261
+ type: "text" as const,
262
+ text: `The ${name} tool failed (${message}). Tell the user it did not work and offer to retry — do not invent a result.`,
263
+ },
264
+ ],
265
+ };
266
+ }
267
+ },
268
+ );
269
+ }
270
+
271
+ // Flows arrive from the SDK shaped for the MCP SDK's `(name, config, handler)`
272
+ // call, which the framework replaced with a single config carrying the name.
273
+ for (const flow of flows) {
274
+ server.registerTool({ ...flow.config, name: flow.name }, flow.handler);
275
+ }
276
+
277
+ if (docs.length > 0) {
278
+ registerDocsTool(server, docs);
279
+ }
280
+
281
+ // `withWaniwani` is deliberately not called here. It wraps every registered
282
+ // handler in place, so it has to run after the last registration — which is
283
+ // the template's, not this function's. `src/server.ts` calls it.
284
+ return server;
285
+ }
package/src/web.tsx ADDED
@@ -0,0 +1,68 @@
1
+ /**
2
+ * The widget-side API.
3
+ *
4
+ * A component gets its data from `useWidget(widget)`, typed straight off the
5
+ * widget's own `data` schema. There is no generated helpers file and no server
6
+ * type import, so a widget cannot drift from its contract.
7
+ */
8
+
9
+ import { useToolInfo } from "skybridge/web";
10
+ import type { Infer, Shape, WidgetDefinition } from "./index.js";
11
+
12
+ export {
13
+ mountView,
14
+ useCallTool,
15
+ useDisplayMode,
16
+ useDownload,
17
+ useFiles,
18
+ useLayout,
19
+ useOpenExternal,
20
+ useRequestClose,
21
+ useRequestModal,
22
+ useRequestSize,
23
+ useSendFollowUpMessage,
24
+ useSetOpenInAppUrl,
25
+ useUser,
26
+ } from "skybridge/web";
27
+
28
+ /**
29
+ * Per-widget state that survives re-renders and is handed back to the host.
30
+ *
31
+ * Upstream this is `useViewState`. The name here follows the wire format
32
+ * instead: what reaches ChatGPT is `openai/widgetCSP`, `openai/widgetDescription`,
33
+ * and `window.openai.widgetState`. Holding the public vocabulary steady is also
34
+ * the point of the package — the upstream rename of widgets to views cost app
35
+ * repos nothing, and it should keep costing them nothing.
36
+ */
37
+ export { useViewState as useWidgetState } from "skybridge/web";
38
+
39
+ export { defineWidget } from "./index.js";
40
+ export type { Infer, Shape, WidgetDefinition } from "./index.js";
41
+
42
+ export type UseWidgetResult<S extends Shape> = {
43
+ /**
44
+ * The widget's data. Present as soon as the host has the tool input, which
45
+ * on most hosts is before the server responds — so render optimistically.
46
+ */
47
+ data: Infer<S> | undefined;
48
+ /** The host is still streaming the tool call. */
49
+ isLoading: boolean;
50
+ /** The server has responded and `data` is final. */
51
+ isReady: boolean;
52
+ };
53
+
54
+ /**
55
+ * Read this widget's data.
56
+ *
57
+ * @param _widget the widget contract, passed for type inference only
58
+ */
59
+ export function useWidget<S extends Shape>(_widget: WidgetDefinition<S>): UseWidgetResult<S> {
60
+ const info = useToolInfo();
61
+ const data = (info.output ?? info.input) as Infer<S> | undefined;
62
+
63
+ return {
64
+ data,
65
+ isLoading: !info.isSuccess,
66
+ isReady: info.isSuccess,
67
+ };
68
+ }