@kitn.ai/ui 0.28.0 → 0.29.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/README.md +1 -1
- package/dist/agent-tooling/construct/public.d.ts +15 -0
- package/dist/agent-tooling/construct/schema.d.ts +111 -0
- package/dist/construct-cli.es.js +3 -3
- package/dist/construct.d.ts +5 -0
- package/dist/construct.js +327 -0
- package/dist/core-C8fzo39E.js +1 -12
- package/dist/engine-javascript-C1x7zo1_.js +1 -141
- package/dist/mcp.es.js +1 -1
- package/package.json +16 -16
- package/bin/route.test.js +0 -58
- package/src/agent-tooling/README.md +0 -192
- package/src/agent-tooling/archetypes.ts +0 -205
- package/src/agent-tooling/catalog/README.md +0 -676
- package/src/agent-tooling/catalog/catalog-types.ts +0 -231
- package/src/agent-tooling/catalog/fabrications.ts +0 -96
- package/src/agent-tooling/catalog/invariants.ts +0 -284
- package/src/agent-tooling/catalog/labs-titles.ts +0 -114
- package/src/agent-tooling/catalog/scenarios.ts +0 -87
- package/src/agent-tooling/catalog/surfaces.ts +0 -347
- package/src/agent-tooling/construct/cli-entry.ts +0 -8
- package/src/agent-tooling/construct/cli.ts +0 -147
- package/src/agent-tooling/construct/codegen.ts +0 -1635
- package/src/agent-tooling/construct/construct.v1.schema.json +0 -319
- package/src/agent-tooling/construct/dev.ts +0 -148
- package/src/agent-tooling/construct/fixtures/demo-widget.construct.json +0 -7
- package/src/agent-tooling/construct/fixtures/ops-console.construct.json +0 -44
- package/src/agent-tooling/construct/fixtures/owner-widget.construct.json +0 -32
- package/src/agent-tooling/construct/schema.ts +0 -452
- package/src/agent-tooling/integrations/anthropic.ts +0 -442
- package/src/agent-tooling/integrations/cloudflare.ts +0 -218
- package/src/agent-tooling/integrations/langgraph.ts +0 -133
- package/src/agent-tooling/integrations/mastra.ts +0 -182
- package/src/agent-tooling/integrations/mock.ts +0 -148
- package/src/agent-tooling/integrations/ollama.ts +0 -97
- package/src/agent-tooling/integrations/openai.ts +0 -95
- package/src/agent-tooling/integrations/openrouter.ts +0 -82
- package/src/agent-tooling/integrations/pi.ts +0 -176
- package/src/agent-tooling/integrations/pydantic-ai.ts +0 -108
- package/src/agent-tooling/integrations/vercel-ai-sdk.ts +0 -398
- package/src/agent-tooling/mcp/css-raw.d.ts +0 -12
- package/src/agent-tooling/mcp/manifest.ts +0 -422
- package/src/agent-tooling/mcp/server.ts +0 -116
- package/src/agent-tooling/mcp/stdio.ts +0 -18
- package/src/agent-tooling/mcp/tools/construct.ts +0 -130
- package/src/agent-tooling/mcp/tools/debug.ts +0 -621
- package/src/agent-tooling/mcp/tools/reference.ts +0 -980
- package/src/agent-tooling/mcp/tools/scaffold.ts +0 -6708
- package/src/agent-tooling/mcp/tools/theme.ts +0 -456
- package/src/agent-tooling/mcp/tools/types.ts +0 -14
- package/src/agent-tooling/mcp/validate-args.ts +0 -141
- package/src/agent-tooling/recipes/composed-thread.ts +0 -714
- package/src/agent-tooling/recipes/index.ts +0 -22
- package/src/agent-tooling/recipes/types.ts +0 -32
- package/src/agent-tooling/registry.ts +0 -132
- package/src/agent-tooling/route-emit.ts +0 -305
- package/src/agent-tooling/types.ts +0 -390
- package/src/components/artifact-card.tsx +0 -120
- package/src/components/artifact.tsx +0 -914
- package/src/components/attachment-types.ts +0 -66
- package/src/components/attachments.tsx +0 -536
- package/src/components/audio-visualizer/audio-visualizer.voice-fixture.ts +0 -257
- package/src/components/audio-visualizer/aurora.glsl.ts +0 -330
- package/src/components/audio-visualizer/fit-scale.ts +0 -112
- package/src/components/audio-visualizer/index.tsx +0 -528
- package/src/components/audio-visualizer/labs/lab-choreography.ts +0 -187
- package/src/components/audio-visualizer/labs/lab-shaders.ts +0 -363
- package/src/components/audio-visualizer/labs/lab-visualizer.tsx +0 -102
- package/src/components/audio-visualizer/shader-canvas.tsx +0 -876
- package/src/components/audio-visualizer/sizes.ts +0 -54
- package/src/components/audio-visualizer/variant-aurora.tsx +0 -305
- package/src/components/audio-visualizer/variant-bar.tsx +0 -175
- package/src/components/audio-visualizer/variant-custom.tsx +0 -344
- package/src/components/audio-visualizer/variant-grid.tsx +0 -175
- package/src/components/audio-visualizer/variant-radial.tsx +0 -163
- package/src/components/audio-visualizer/variant-wave.tsx +0 -130
- package/src/components/audio-visualizer/wave.glsl.ts +0 -130
- package/src/components/card-fallback.tsx +0 -60
- package/src/components/card-renderer.tsx +0 -220
- package/src/components/card.tsx +0 -212
- package/src/components/chain-of-thought.tsx +0 -348
- package/src/components/chat-container.tsx +0 -89
- package/src/components/chat-scope-picker.tsx +0 -57
- package/src/components/chat-thread.tsx +0 -1014
- package/src/components/checkpoint.tsx +0 -88
- package/src/components/choice-card.tsx +0 -743
- package/src/components/coachmark.tsx +0 -249
- package/src/components/code-block.tsx +0 -181
- package/src/components/composer-dom.ts +0 -159
- package/src/components/composer-highlight.ts +0 -242
- package/src/components/composer-history.ts +0 -82
- package/src/components/composer.tsx +0 -1075
- package/src/components/confirm-card.tsx +0 -402
- package/src/components/context.tsx +0 -377
- package/src/components/conversation-item.tsx +0 -241
- package/src/components/conversation-list.tsx +0 -429
- package/src/components/conversation-panel.tsx +0 -118
- package/src/components/dismissed-stub.tsx +0 -80
- package/src/components/embed.tsx +0 -200
- package/src/components/empty.tsx +0 -177
- package/src/components/feedback-bar.tsx +0 -166
- package/src/components/file-tree.tsx +0 -530
- package/src/components/file-upload.tsx +0 -167
- package/src/components/form-widgets.tsx +0 -525
- package/src/components/form.tsx +0 -1296
- package/src/components/home-panel.tsx +0 -159
- package/src/components/image.tsx +0 -65
- package/src/components/link-preview.tsx +0 -195
- package/src/components/loader.tsx +0 -367
- package/src/components/markdown.tsx +0 -162
- package/src/components/message-skills.tsx +0 -40
- package/src/components/message.tsx +0 -765
- package/src/components/model-switcher.tsx +0 -132
- package/src/components/prompt-input.tsx +0 -188
- package/src/components/prompt-suggestion.tsx +0 -150
- package/src/components/reasoning.tsx +0 -293
- package/src/components/response-compare-types.ts +0 -152
- package/src/components/response-compare.tsx +0 -455
- package/src/components/response-stream.tsx +0 -109
- package/src/components/screen.tsx +0 -181
- package/src/components/scroll-button.tsx +0 -93
- package/src/components/source.tsx +0 -192
- package/src/components/tasks-card.tsx +0 -801
- package/src/components/text-shimmer.tsx +0 -37
- package/src/components/thinking-bar.tsx +0 -50
- package/src/components/thread.tsx +0 -228
- package/src/components/toast.tsx +0 -503
- package/src/components/tool-classify.ts +0 -26
- package/src/components/tool-types.ts +0 -49
- package/src/components/tool.tsx +0 -190
- package/src/components/use-card-resolution.ts +0 -44
- package/src/components/voice-input.tsx +0 -222
- package/src/components/voice-output.tsx +0 -242
- package/src/components/widget-tab-bar.tsx +0 -73
- package/src/components/workspace-shell.tsx +0 -285
- package/src/diagnostics/hook.ts +0 -352
- package/src/diagnostics/index.ts +0 -82
- package/src/diagnostics/report-request.ts +0 -296
- package/src/elements/agent-card.tsx +0 -67
- package/src/elements/artifact.tsx +0 -194
- package/src/elements/attachments.tsx +0 -140
- package/src/elements/audio-visualizer.tsx +0 -227
- package/src/elements/autoloader.ts +0 -89
- package/src/elements/avatar.tsx +0 -33
- package/src/elements/badge.tsx +0 -32
- package/src/elements/button.tsx +0 -149
- package/src/elements/card.tsx +0 -133
- package/src/elements/cards.tsx +0 -266
- package/src/elements/chain-of-thought.tsx +0 -141
- package/src/elements/chat-scope-picker.tsx +0 -65
- package/src/elements/chat-types.ts +0 -102
- package/src/elements/chat-workspace.tsx +0 -188
- package/src/elements/chat.tsx +0 -304
- package/src/elements/checkbox-group.tsx +0 -164
- package/src/elements/checkbox.tsx +0 -134
- package/src/elements/checkpoint.tsx +0 -47
- package/src/elements/choice.tsx +0 -100
- package/src/elements/coachmark.tsx +0 -90
- package/src/elements/code-block.tsx +0 -60
- package/src/elements/command.tsx +0 -197
- package/src/elements/compare.tsx +0 -111
- package/src/elements/compiled.css +0 -2
- package/src/elements/composer.tsx +0 -162
- package/src/elements/confirm-card.tsx +0 -76
- package/src/elements/context-meter.tsx +0 -107
- package/src/elements/conversation-item.tsx +0 -120
- package/src/elements/conversation-list.tsx +0 -203
- package/src/elements/css.ts +0 -5
- package/src/elements/default-input.tsx +0 -392
- package/src/elements/define-entry.ts +0 -12
- package/src/elements/define.tsx +0 -563
- package/src/elements/diagnostic-events.ts +0 -114
- package/src/elements/dialog.tsx +0 -145
- package/src/elements/disclosure.ts +0 -95
- package/src/elements/dock.tsx +0 -215
- package/src/elements/dropdown.tsx +0 -123
- package/src/elements/editable-label.tsx +0 -140
- package/src/elements/element-data-types.ts +0 -152
- package/src/elements/element-diagnostics.ts +0 -392
- package/src/elements/element-manifest.json +0 -354
- package/src/elements/element-nonscalar.json +0 -187
- package/src/elements/element-types.d.ts +0 -4353
- package/src/elements/embed.tsx +0 -35
- package/src/elements/empty.tsx +0 -29
- package/src/elements/feedback-bar.tsx +0 -66
- package/src/elements/file-tree.tsx +0 -62
- package/src/elements/file-upload.tsx +0 -44
- package/src/elements/form.tsx +0 -94
- package/src/elements/hover-card.tsx +0 -80
- package/src/elements/icon.tsx +0 -41
- package/src/elements/image.tsx +0 -32
- package/src/elements/input.tsx +0 -492
- package/src/elements/kbd.tsx +0 -45
- package/src/elements/link-preview.tsx +0 -34
- package/src/elements/loader.tsx +0 -25
- package/src/elements/markdown.tsx +0 -38
- package/src/elements/menu.tsx +0 -241
- package/src/elements/message-skills.tsx +0 -83
- package/src/elements/message.tsx +0 -361
- package/src/elements/model-switcher.tsx +0 -121
- package/src/elements/nav.tsx +0 -87
- package/src/elements/notice.tsx +0 -53
- package/src/elements/pane-grid.tsx +0 -111
- package/src/elements/pane-group.tsx +0 -119
- package/src/elements/pane.tsx +0 -129
- package/src/elements/popover.tsx +0 -81
- package/src/elements/progress-bar.tsx +0 -39
- package/src/elements/prompt-dock.tsx +0 -82
- package/src/elements/prompt-input.tsx +0 -240
- package/src/elements/prompt-suggestions.tsx +0 -113
- package/src/elements/radio-group.tsx +0 -129
- package/src/elements/reasoning.tsx +0 -71
- package/src/elements/register-impl.ts +0 -148
- package/src/elements/register.ts +0 -76
- package/src/elements/remote.tsx +0 -225
- package/src/elements/resizable.globals.d.ts +0 -36
- package/src/elements/resizable.tsx +0 -704
- package/src/elements/response-stream.tsx +0 -40
- package/src/elements/screen.tsx +0 -90
- package/src/elements/scroll-area.tsx +0 -33
- package/src/elements/scroll-button.tsx +0 -179
- package/src/elements/search.tsx +0 -185
- package/src/elements/segmented.tsx +0 -129
- package/src/elements/select.tsx +0 -165
- package/src/elements/separator.tsx +0 -36
- package/src/elements/setting-item.tsx +0 -50
- package/src/elements/settings-group.tsx +0 -37
- package/src/elements/skeleton.tsx +0 -45
- package/src/elements/slider.tsx +0 -171
- package/src/elements/slot-text.ts +0 -73
- package/src/elements/slots.ts +0 -787
- package/src/elements/source.tsx +0 -126
- package/src/elements/status.tsx +0 -42
- package/src/elements/styles.css +0 -328
- package/src/elements/switch.tsx +0 -118
- package/src/elements/tabs.tsx +0 -95
- package/src/elements/tasks.tsx +0 -103
- package/src/elements/text-shimmer.tsx +0 -28
- package/src/elements/thinking-bar.tsx +0 -43
- package/src/elements/thread.tsx +0 -139
- package/src/elements/toast.tsx +0 -106
- package/src/elements/tool.tsx +0 -78
- package/src/elements/tooltip.tsx +0 -85
- package/src/elements/validate-messages.ts +0 -76
- package/src/elements/voice-input.tsx +0 -100
- package/src/elements/voice-output.tsx +0 -82
- package/src/index.ts +0 -430
- package/src/primitives/audio-bands.ts +0 -174
- package/src/primitives/card-component-types.ts +0 -63
- package/src/primitives/card-contract.ts +0 -80
- package/src/primitives/card-data-types.ts +0 -361
- package/src/primitives/card-host.tsx +0 -37
- package/src/primitives/card-recovery.ts +0 -160
- package/src/primitives/card-registry.tsx +0 -78
- package/src/primitives/card-resolution.ts +0 -36
- package/src/primitives/card-routing.ts +0 -85
- package/src/primitives/card-schemas/artifact.schema.json +0 -92
- package/src/primitives/card-schemas/card-envelope.schema.json +0 -14
- package/src/primitives/card-schemas/card-event.schema.json +0 -12
- package/src/primitives/card-schemas/choice.schema.json +0 -75
- package/src/primitives/card-schemas/confirm.schema.json +0 -65
- package/src/primitives/card-schemas/embed.schema.json +0 -63
- package/src/primitives/card-schemas/form.result.schema.json +0 -7
- package/src/primitives/card-schemas/form.schema.json +0 -53
- package/src/primitives/card-schemas/link.schema.json +0 -54
- package/src/primitives/card-schemas/tasks.result.schema.json +0 -16
- package/src/primitives/card-schemas/tasks.schema.json +0 -83
- package/src/primitives/card-tags.ts +0 -61
- package/src/primitives/card-validate-cards.ts +0 -255
- package/src/primitives/card-validate-schemas.ts +0 -58
- package/src/primitives/card-validate.ts +0 -131
- package/src/primitives/chat-config.tsx +0 -103
- package/src/primitives/composer-model.ts +0 -49
- package/src/primitives/composer-triggers.ts +0 -35
- package/src/primitives/controllable.ts +0 -21
- package/src/primitives/conversation-store.ts +0 -248
- package/src/primitives/create-kai-chat.ts +0 -66
- package/src/primitives/create-tween.ts +0 -266
- package/src/primitives/embed-providers.ts +0 -254
- package/src/primitives/field-mask.ts +0 -256
- package/src/primitives/field-semantics.ts +0 -115
- package/src/primitives/highlighter.ts +0 -157
- package/src/primitives/input-mask.ts +0 -853
- package/src/primitives/link-preview.ts +0 -87
- package/src/primitives/message-feedback.ts +0 -124
- package/src/primitives/pdf-preview.ts +0 -121
- package/src/primitives/toast-store.ts +0 -338
- package/src/primitives/url-scheme-policy.ts +0 -70
- package/src/primitives/use-audio-analysis.ts +0 -340
- package/src/primitives/use-auto-resize.ts +0 -31
- package/src/primitives/use-resize-observer.ts +0 -45
- package/src/primitives/use-sequencer.ts +0 -72
- package/src/primitives/use-speech-recognition.ts +0 -146
- package/src/primitives/use-stick-to-bottom.ts +0 -75
- package/src/primitives/use-text-stream.ts +0 -143
- package/src/primitives/use-voice-recorder.ts +0 -62
- package/src/primitives/visualizer-sequences.ts +0 -350
- package/src/remote/host-embed.ts +0 -345
- package/src/remote/index.ts +0 -2
- package/src/remote/origin.ts +0 -30
- package/src/remote/provider-runtime.ts +0 -262
- package/src/remote/provider.ts +0 -2
- package/src/remote/validate.ts +0 -22
- package/src/remote/version.ts +0 -12
- package/src/remote/wire.ts +0 -48
- package/src/schemas/from-tool-call.ts +0 -151
- package/src/schemas/index.ts +0 -341
- package/src/schemas/provider-subsets.ts +0 -538
- package/src/schemas/registry.ts +0 -355
- package/src/schemas/tool-defs.ts +0 -793
- package/src/solid.ts +0 -173
- package/src/state/index.ts +0 -44
- package/src/state/messages.ts +0 -53
- package/src/state/mock.ts +0 -297
- package/src/state/parts.ts +0 -295
- package/src/state/persistence.ts +0 -180
- package/src/state/stream.ts +0 -246
- package/src/state/suggestions.ts +0 -9
- package/src/state/threads.ts +0 -142
- package/src/test-utils/fake-clock.ts +0 -88
- package/src/types.ts +0 -95
- package/src/ui/action-icons.ts +0 -53
- package/src/ui/agent-card.tsx +0 -218
- package/src/ui/avatar.tsx +0 -23
- package/src/ui/badge.tsx +0 -21
- package/src/ui/button.tsx +0 -49
- package/src/ui/card.tsx +0 -245
- package/src/ui/checkbox-group.tsx +0 -153
- package/src/ui/checkbox.tsx +0 -63
- package/src/ui/collapsible.tsx +0 -163
- package/src/ui/command.tsx +0 -103
- package/src/ui/dialog.tsx +0 -223
- package/src/ui/dock.tsx +0 -630
- package/src/ui/dropdown.tsx +0 -636
- package/src/ui/editable-label.tsx +0 -112
- package/src/ui/hover-card.tsx +0 -345
- package/src/ui/icon.tsx +0 -167
- package/src/ui/input.tsx +0 -391
- package/src/ui/kbd.tsx +0 -118
- package/src/ui/nav.tsx +0 -282
- package/src/ui/notice.tsx +0 -84
- package/src/ui/overlay.tsx +0 -277
- package/src/ui/pane-grid.tsx +0 -117
- package/src/ui/pane-group.tsx +0 -291
- package/src/ui/pane.tsx +0 -193
- package/src/ui/popover.tsx +0 -106
- package/src/ui/progress-bar.tsx +0 -83
- package/src/ui/prompt-dock.tsx +0 -145
- package/src/ui/radio.tsx +0 -150
- package/src/ui/resizable.tsx +0 -693
- package/src/ui/scroll-area.tsx +0 -26
- package/src/ui/segmented.tsx +0 -109
- package/src/ui/select.tsx +0 -168
- package/src/ui/separator.tsx +0 -10
- package/src/ui/settings-group.tsx +0 -67
- package/src/ui/skeleton.tsx +0 -73
- package/src/ui/slider.tsx +0 -178
- package/src/ui/status.tsx +0 -48
- package/src/ui/switch.tsx +0 -119
- package/src/ui/tabs.tsx +0 -168
- package/src/ui/textarea.tsx +0 -21
- package/src/ui/tooltip.tsx +0 -118
- package/src/utils/cn.ts +0 -30
- package/src/wire/chunk.ts +0 -248
- package/src/wire/consume.ts +0 -599
- package/src/wire/diagnostics.ts +0 -727
- package/src/wire/encode-probe.ts +0 -214
- package/src/wire/encode.ts +0 -947
- package/src/wire/files.ts +0 -342
- package/src/wire/formats/anthropic.ts +0 -274
- package/src/wire/formats/openai.ts +0 -228
- package/src/wire/index.ts +0 -95
- package/src/wire/media-types.ts +0 -344
- package/src/wire/read.ts +0 -396
- package/src/wire/sink-helpers.ts +0 -50
- package/src/wire/sse.ts +0 -153
|
@@ -1,422 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* manifest.ts — reads dist/custom-elements.json (a Custom Elements Manifest)
|
|
3
|
-
* and exposes helpers for the component_reference tool.
|
|
4
|
-
*
|
|
5
|
-
* Resolution strategy (dual-context) — each context is an EXACT location, never a
|
|
6
|
-
* search. See `resolveManifestPath` for why that distinction is the whole point:
|
|
7
|
-
* 1. Bundled bin: dist/mcp.es.js lives in dist/, so custom-elements.json is
|
|
8
|
-
* a sibling → ./custom-elements.json relative to import.meta.url.
|
|
9
|
-
* 2. Vitest (source): manifest.ts lives at <package>/src/agent-tooling/mcp/, so
|
|
10
|
-
* the manifest is <package>/dist/custom-elements.json and nowhere else.
|
|
11
|
-
*
|
|
12
|
-
* It also answers "which of these 80 elements has anything to do with cards", for
|
|
13
|
-
* the card contract component_reference serves. That question lives HERE rather than
|
|
14
|
-
* in reference.ts because half of it is a question about the element manifest — the
|
|
15
|
-
* kit's own `type -> tag` map, crossed against the tag list this module already owns
|
|
16
|
-
* — and because both halves of the answer must come off one place. See
|
|
17
|
-
* `cardTagForType` for the rule and for what happens when an eighth card type lands.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { readFileSync, readdirSync, existsSync } from 'node:fs';
|
|
21
|
-
import { fileURLToPath } from 'node:url';
|
|
22
|
-
import { join, dirname, resolve } from 'node:path';
|
|
23
|
-
// The package's own public entry, by the same specifier the scaffolder tells a
|
|
24
|
-
// consumer's route to use. That is now a CHOICE, and this comment used to say the
|
|
25
|
-
// opposite: the barrel re-exports src/schemas/registry.ts, which type-IMPORTED
|
|
26
|
-
// `CardComponentMap` from src/primitives/card-registry.TSX, and pulling a .tsx into
|
|
27
|
-
// this pass would mean giving tsconfig.mcp.json `jsx` + `jsxImportSource` + the DOM
|
|
28
|
-
// lib, dragging the whole Solid component tree into a Node-only project — measured
|
|
29
|
-
// as 0 errors -> 1364. Those two types moved to the DOM-free
|
|
30
|
-
// src/primitives/card-component-types.ts, so a relative import of the barrel would
|
|
31
|
-
// typecheck here today. It stays on the public specifier anyway, because emitting
|
|
32
|
-
// and consuming the same string is the point of this module. The built entry
|
|
33
|
-
// resolves to dist/schemas/index.d.ts, whose own reference to card-registry is a
|
|
34
|
-
// .d.ts that skipLibCheck skips, so the Node/no-DOM guarantee survives intact.
|
|
35
|
-
//
|
|
36
|
-
// `BUILTIN_CARD_TAGS` is the authoritative `CardEnvelope.type -> kai-* tag` map and
|
|
37
|
-
// arrives here as DATA, not a type. It is authored in src/primitives/card-tags.ts,
|
|
38
|
-
// which holds that map ALONE — no Solid below it — so it is readable from source by a
|
|
39
|
-
// Node/no-DOM project rather than only through this built entry. Before that split it
|
|
40
|
-
// shared a module with `BUILTIN_CARD_COMPONENTS` and this module re-derived it by
|
|
41
|
-
// convention instead; see `cardTagForType`.
|
|
42
|
-
//
|
|
43
|
-
// This costs a build before typecheck and before the unit suite. That is not a new
|
|
44
|
-
// dependency: resolveManifestPath() below already requires dist/custom-elements.json,
|
|
45
|
-
// and CI already runs `nx build ui` ahead of both.
|
|
46
|
-
import { BUILTIN_CARD_TAGS, cardSchemaNames } from '@kitn.ai/ui/schemas';
|
|
47
|
-
|
|
48
|
-
// ── CEM types ────────────────────────────────────────────────────────────────
|
|
49
|
-
|
|
50
|
-
export interface CemType {
|
|
51
|
-
text: string;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface CemMember {
|
|
55
|
-
kind: 'field' | 'method';
|
|
56
|
-
privacy?: 'public' | 'private' | 'protected';
|
|
57
|
-
name: string;
|
|
58
|
-
type?: CemType;
|
|
59
|
-
description?: string;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export interface CemAttribute {
|
|
63
|
-
name: string;
|
|
64
|
-
fieldName?: string;
|
|
65
|
-
type?: CemType;
|
|
66
|
-
description?: string;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export interface CemEvent {
|
|
70
|
-
name: string;
|
|
71
|
-
type?: CemType;
|
|
72
|
-
description?: string;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export interface CemCssProperty {
|
|
76
|
-
name: string;
|
|
77
|
-
description?: string;
|
|
78
|
-
default?: string;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export interface CemSlot {
|
|
82
|
-
name: string;
|
|
83
|
-
description?: string;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export interface CemCssPart {
|
|
87
|
-
name: string;
|
|
88
|
-
description?: string;
|
|
89
|
-
/** Our extension: a copy-paste styling example. */
|
|
90
|
-
recipe?: string;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export interface Declaration {
|
|
94
|
-
tagName?: string;
|
|
95
|
-
name: string;
|
|
96
|
-
kind: string;
|
|
97
|
-
description?: string;
|
|
98
|
-
members?: CemMember[];
|
|
99
|
-
attributes?: CemAttribute[];
|
|
100
|
-
events?: CemEvent[];
|
|
101
|
-
cssProperties?: CemCssProperty[];
|
|
102
|
-
slots?: CemSlot[];
|
|
103
|
-
cssParts?: CemCssPart[];
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
interface CemModule {
|
|
107
|
-
path?: string;
|
|
108
|
-
declarations?: Declaration[];
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
interface CustomElementsManifest {
|
|
112
|
-
modules: CemModule[];
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// ── Manifest resolution ───────────────────────────────────────────────────────
|
|
116
|
-
//
|
|
117
|
-
// THE MANIFEST IS THIS PACKAGE'S OWN BUILD ARTIFACT, SO IT IS ADDRESSED, NOT SEARCHED
|
|
118
|
-
// FOR. This used to walk up ten parent directories taking the first
|
|
119
|
-
// `<dir>/dist/custom-elements.json` it found, and that is a categorically weaker
|
|
120
|
-
// thing than it looks: "a custom-elements.json exists somewhere above me" and "THIS
|
|
121
|
-
// package's custom-elements.json exists" are different facts, and a walk-up cannot
|
|
122
|
-
// tell them apart. It reports success for both.
|
|
123
|
-
//
|
|
124
|
-
// It was not hypothetical. Measured from an agent git worktree at
|
|
125
|
-
// `<repo>/.claude/worktrees/<agent>/packages/ui/src/agent-tooling/mcp`, the loop
|
|
126
|
-
// climbed past the worktree's own (unbuilt) `packages/ui/dist`, out of the worktree
|
|
127
|
-
// entirely, and bound on iteration 8 to `<repo>/dist/custom-elements.json` — a
|
|
128
|
-
// leftover from the pre-monorepo layout, six weeks stale, 78 tags, and zero elements
|
|
129
|
-
// declaring `cardSchemas`. The consequence is this repo's dominant failure mode in
|
|
130
|
-
// its purest form: on an unbuilt tree the MCP manifest tests did not error, they
|
|
131
|
-
// PASSED, 16 of 17, against an artifact from a tree nobody was working in. Two
|
|
132
|
-
// checkouts could disagree about what they had tested and nothing said so.
|
|
133
|
-
//
|
|
134
|
-
// So the rule here is now: a missing manifest is a HARD FAILURE that names the path
|
|
135
|
-
// it expected. Never a fallback, never a wider search. If this throws, the answer is
|
|
136
|
-
// to build — not to let it find someone else's build.
|
|
137
|
-
|
|
138
|
-
const PACKAGE_NAME = '@kitn.ai/ui';
|
|
139
|
-
const MANIFEST_FILE = 'custom-elements.json';
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* `<package>/src/agent-tooling/mcp` -> `<package>`. A fixed, exact hop, and it is
|
|
143
|
-
* CHECKED below rather than trusted: if this module is ever moved to a different
|
|
144
|
-
* depth the derived root stops being this package and resolution throws, instead of
|
|
145
|
-
* silently addressing whatever directory happens to sit three levels up.
|
|
146
|
-
*/
|
|
147
|
-
const SOURCE_TO_PACKAGE_ROOT = ['..', '..', '..'] as const;
|
|
148
|
-
|
|
149
|
-
/** Is `root` the root of THIS package — not merely *a* directory holding a dist/? */
|
|
150
|
-
function isThisPackage(root: string): boolean {
|
|
151
|
-
const manifest = join(root, 'package.json');
|
|
152
|
-
if (!existsSync(manifest)) return false;
|
|
153
|
-
try {
|
|
154
|
-
return (JSON.parse(readFileSync(manifest, 'utf-8')) as { name?: string }).name === PACKAGE_NAME;
|
|
155
|
-
} catch {
|
|
156
|
-
return false;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Absolute path to this package's Custom Elements Manifest, or a throw naming what
|
|
162
|
-
* it looked for.
|
|
163
|
-
*
|
|
164
|
-
* `fromDir` exists for the tests and defaults to this module's own directory. It is
|
|
165
|
-
* the only way to write the check that matters: the guarantee is not "a manifest was
|
|
166
|
-
* found", it is "THIS package's manifest was found", and the two only come apart
|
|
167
|
-
* when there is a decoy above the origin. manifest.test.ts builds exactly that tree.
|
|
168
|
-
*/
|
|
169
|
-
export function resolveManifestPath(
|
|
170
|
-
fromDir: string = dirname(fileURLToPath(import.meta.url)),
|
|
171
|
-
): string {
|
|
172
|
-
// 1. Bundled bin: dist/mcp.es.js and dist/custom-elements.json are siblings.
|
|
173
|
-
// Unambiguous by construction — a sibling cannot be another checkout's artifact.
|
|
174
|
-
const sibling = join(fromDir, MANIFEST_FILE);
|
|
175
|
-
if (existsSync(sibling)) return sibling;
|
|
176
|
-
|
|
177
|
-
// 2. Source (vitest, tsx): one exact location, derived and then verified.
|
|
178
|
-
const packageRoot = resolve(fromDir, ...SOURCE_TO_PACKAGE_ROOT);
|
|
179
|
-
const expected = join(packageRoot, 'dist', MANIFEST_FILE);
|
|
180
|
-
|
|
181
|
-
if (!isThisPackage(packageRoot)) {
|
|
182
|
-
throw new Error(
|
|
183
|
-
`[${PACKAGE_NAME}] Cannot locate the Custom Elements Manifest: ${packageRoot} is not ` +
|
|
184
|
-
`the ${PACKAGE_NAME} package root, so ${expected} would not be this package's ` +
|
|
185
|
-
`manifest even if it existed.\n` +
|
|
186
|
-
`Resolved from: ${fromDir}\n` +
|
|
187
|
-
`This module must live at <package>/src/agent-tooling/mcp/ (or be bundled beside ` +
|
|
188
|
-
`${MANIFEST_FILE} in dist/). Resolution deliberately does NOT search parent ` +
|
|
189
|
-
`directories — finding some other checkout's manifest is worse than failing.`,
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (!existsSync(expected)) {
|
|
194
|
-
throw new Error(
|
|
195
|
-
`[${PACKAGE_NAME}] Missing build artifact: ${expected}\n` +
|
|
196
|
-
`Resolved from: ${fromDir}\n` +
|
|
197
|
-
`The Custom Elements Manifest is generated by the build. Run \`nx build ui\` ` +
|
|
198
|
-
`(or \`npm run build:api\` in packages/ui) and try again.\n` +
|
|
199
|
-
`Resolution deliberately does NOT search parent directories: binding to a ` +
|
|
200
|
-
`neighbouring checkout's manifest would make this succeed against stale data.`,
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return expected;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// ── Parsed manifest (module-level cache) ─────────────────────────────────────
|
|
208
|
-
|
|
209
|
-
let _manifest: CustomElementsManifest | undefined;
|
|
210
|
-
|
|
211
|
-
function getManifest(): CustomElementsManifest {
|
|
212
|
-
if (!_manifest) {
|
|
213
|
-
const path = resolveManifestPath();
|
|
214
|
-
const raw = readFileSync(path, 'utf-8');
|
|
215
|
-
_manifest = JSON.parse(raw) as CustomElementsManifest;
|
|
216
|
-
}
|
|
217
|
-
return _manifest;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function getDeclarations(): Declaration[] {
|
|
221
|
-
return getManifest().modules.flatMap((m) => m.declarations ?? []);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// ── Public API ────────────────────────────────────────────────────────────────
|
|
225
|
-
|
|
226
|
-
/** Returns the Declaration for a given custom-element tag, or undefined. */
|
|
227
|
-
export function getElement(tag: string): Declaration | undefined {
|
|
228
|
-
return getDeclarations().find((d) => d.tagName === tag);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/** Returns all custom-element tagNames, sorted alphabetically. */
|
|
232
|
-
export function listElements(): string[] {
|
|
233
|
-
return getDeclarations()
|
|
234
|
-
.filter((d) => d.tagName)
|
|
235
|
-
.map((d) => d.tagName!)
|
|
236
|
-
.sort();
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// ── Per-element entry map ─────────────────────────────────────────────────────
|
|
240
|
-
//
|
|
241
|
-
// A STATIC import, not an fs read off resolveManifestPath's dual-context pattern:
|
|
242
|
-
// element-manifest.json (unlike custom-elements.json) is never copied into dist/,
|
|
243
|
-
// so an fs read relative to this module's own URL would resolve in the vitest/source
|
|
244
|
-
// context and 404 in the bundled dist/mcp.es.js. A static import sidesteps the gap
|
|
245
|
-
// entirely — Rollup inlines the JSON at build time, so the bundled bin carries the
|
|
246
|
-
// data with no runtime file dependency. NAMED import (not the whole module), matching
|
|
247
|
-
// the same tradeoff element-diagnostics.ts already made for this file: a default
|
|
248
|
-
// import would pull `files` in too for 0 benefit here.
|
|
249
|
-
import { tags as ELEMENT_ENTRY_TAGS } from '../../elements/element-manifest.json';
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* The per-element entry basename for a tag, e.g. 'kai-chat' -> 'chat'.
|
|
253
|
-
*
|
|
254
|
-
* Read from element-manifest.json's `tags` map rather than derived by stripping
|
|
255
|
-
* the `kai-` prefix: TEN of the eighty elements do not match that derivation
|
|
256
|
-
* (`kai-conversations` -> `conversation-list`), so a derived path would emit a
|
|
257
|
-
* broken import for them.
|
|
258
|
-
*/
|
|
259
|
-
export function entryForTag(tag: string): string | undefined {
|
|
260
|
-
return (ELEMENT_ENTRY_TAGS as Record<string, string>)[tag];
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* The per-element entry basename for a tag the register-all bundle does NOT
|
|
265
|
-
* carry, e.g. 'kai-remote' -> 'remote'. `undefined` for every tag `entryForTag`
|
|
266
|
-
* already answers, and for a tag no built module registers.
|
|
267
|
-
*
|
|
268
|
-
* WHY THIS EXISTS. `entryForTag` reads element-manifest.json, which is generated
|
|
269
|
-
* from the import list in register-impl.ts — so an opt-in element is absent from
|
|
270
|
-
* it by construction, and the reference had nothing to name. It said "find the
|
|
271
|
-
* entry point in the package's published exports", which is the one place in the
|
|
272
|
-
* reference where "how to make this element exist" does not answer itself.
|
|
273
|
-
*
|
|
274
|
-
* WHY IT IS READ AND NOT DERIVED. Stripping `kai-` is wrong for ten of the eighty
|
|
275
|
-
* elements, so guessing here risks exactly the broken import this tool exists to
|
|
276
|
-
* catch. The fact is stated in vite.config.elements.ts, which adds an explicit
|
|
277
|
-
* entry for each opt-in element precisely "so `@kitn.ai/ui/elements/remote`
|
|
278
|
-
* resolves to a real dist file" — but a vite config is not shipped in the
|
|
279
|
-
* package, so the runtime reads that intent where it LANDS: dist/elements/. A
|
|
280
|
-
* candidate is a built module that element-manifest.json does not already claim
|
|
281
|
-
* (plus the two non-element entries the same build emits), and it is matched to a
|
|
282
|
-
* tag by the tag literal it registers. So the answer is the built artifact's,
|
|
283
|
-
* confirmed against the tag rather than assumed from a filename.
|
|
284
|
-
*
|
|
285
|
-
* dist/elements/ is a sibling of the manifest in both contexts — the bundled bin
|
|
286
|
-
* (dist/mcp.es.js) and vitest-over-source, which resolveManifestPath already
|
|
287
|
-
* normalises — so this needs no second resolution strategy. A missing or
|
|
288
|
-
* unreadable directory yields `undefined` rather than throwing: the caller's
|
|
289
|
-
* fallback is the honest "this is opt-in" paragraph it already prints.
|
|
290
|
-
*/
|
|
291
|
-
export function optInEntryForTag(tag: string): string | undefined {
|
|
292
|
-
if (entryForTag(tag)) return undefined;
|
|
293
|
-
return optInEntries().get(tag);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
/** Not element entries: the register-all barrel and the DOM autoloader, both
|
|
297
|
-
* emitted into dist/elements/ by the same per-element build. */
|
|
298
|
-
const NON_ELEMENT_ENTRIES = new Set(['index', 'autoloader']);
|
|
299
|
-
|
|
300
|
-
let _optInEntries: Map<string, string> | undefined;
|
|
301
|
-
|
|
302
|
-
function optInEntries(): Map<string, string> {
|
|
303
|
-
if (_optInEntries) return _optInEntries;
|
|
304
|
-
_optInEntries = new Map();
|
|
305
|
-
|
|
306
|
-
const dir = join(dirname(resolveManifestPath()), 'elements');
|
|
307
|
-
let candidates: string[];
|
|
308
|
-
try {
|
|
309
|
-
const registered = new Set(Object.values(ELEMENT_ENTRY_TAGS as Record<string, string>));
|
|
310
|
-
candidates = readdirSync(dir)
|
|
311
|
-
.filter((f) => f.endsWith('.js'))
|
|
312
|
-
.map((f) => f.slice(0, -'.js'.length))
|
|
313
|
-
.filter((base) => !registered.has(base) && !NON_ELEMENT_ENTRIES.has(base));
|
|
314
|
-
} catch {
|
|
315
|
-
return _optInEntries;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Only the tags element-manifest.json does not already answer for. Narrow on
|
|
319
|
-
// purpose: an element module may mention a tag it merely renders, and matching
|
|
320
|
-
// against the whole tag list would let that be read as a registration.
|
|
321
|
-
const unclaimed = listElements().filter((t) => !entryForTag(t));
|
|
322
|
-
|
|
323
|
-
for (const base of candidates) {
|
|
324
|
-
let code: string;
|
|
325
|
-
try {
|
|
326
|
-
code = readFileSync(join(dir, `${base}.js`), 'utf-8');
|
|
327
|
-
} catch {
|
|
328
|
-
continue;
|
|
329
|
-
}
|
|
330
|
-
for (const tag of unclaimed) {
|
|
331
|
-
if (!_optInEntries.has(tag) && code.includes(`"${tag}"`)) _optInEntries.set(tag, base);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
return _optInEntries;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// ── Which elements have anything to do with cards ────────────────────────────
|
|
339
|
-
//
|
|
340
|
-
// Two different populations, and conflating them would be the "attaches card
|
|
341
|
-
// material to everything" failure:
|
|
342
|
-
//
|
|
343
|
-
// CARD-BACKED kai-confirm, kai-choice, … — one element per CardEnvelope.type.
|
|
344
|
-
// These get a schema and a generated tool definition.
|
|
345
|
-
// CARD HOST kai-chat, kai-message, … — the elements that RENDER a thread of
|
|
346
|
-
// cards and therefore carry the `cardTypes` / `cardSchemas` props.
|
|
347
|
-
// These get the wiring note, not a schema.
|
|
348
|
-
//
|
|
349
|
-
// Neither list is written down here. The host list is derived from the manifest; the
|
|
350
|
-
// card-backed one is the kit's own map, read rather than guessed at. Either way an
|
|
351
|
-
// eighth card type is covered the day it lands rather than the day someone remembers
|
|
352
|
-
// this file.
|
|
353
|
-
|
|
354
|
-
/** Cached derivations. Same lifetime as the parsed manifest above. */
|
|
355
|
-
let _cardTags: Map<string, string> | undefined;
|
|
356
|
-
let _cardHosts: string[] | undefined;
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* `CardEnvelope.type` -> the `kai-*` element that renders it.
|
|
360
|
-
*
|
|
361
|
-
* THE MAP IS IMPORTED, NOT INFERRED. `BUILTIN_CARD_TAGS` is the same object
|
|
362
|
-
* `<kai-cards>` dispatches on in the browser, reached here through
|
|
363
|
-
* `@kitn.ai/ui/schemas`. This function used to RE-DERIVE it by convention — the tag
|
|
364
|
-
* is `kai-<t>` when that element exists, else the single element whose tag starts
|
|
365
|
-
* `kai-<t>-` — which was correct against all 80 tags on this tree, `link` ->
|
|
366
|
-
* `kai-link-preview` included, and was still a second copy of a fact the repo already
|
|
367
|
-
* held. The eighth card type is the one that would have broken it: any type whose tag
|
|
368
|
-
* is neither `kai-<t>` nor the sole `kai-<t>-*` had to be noticed by a human, and a
|
|
369
|
-
* future `kai-form-field` would have made `form` ambiguous on its own. A lookup has
|
|
370
|
-
* neither failure mode.
|
|
371
|
-
*
|
|
372
|
-
* WHY IT IS STILL CROSSED AGAINST THE MANIFEST. The map is authoritative for the
|
|
373
|
-
* ASSOCIATION; only the manifest knows what actually got registered. A tag in the map
|
|
374
|
-
* with no element behind it (a rename that missed this file) would otherwise have
|
|
375
|
-
* `component_reference` send a harness to an element that does not exist. Entries with
|
|
376
|
-
* no registered element are dropped, so the answer is a real tag or nothing.
|
|
377
|
-
*
|
|
378
|
-
* WHEN IT BREAKS, IT BREAKS LOUDLY. reference.test.ts asserts every member of
|
|
379
|
-
* `cardSchemaNames` resolves AND that what it resolves to is in `listElements()`, so a
|
|
380
|
-
* card type missing from the map, or pointed at a tag nobody registered, fails a test
|
|
381
|
-
* instead of silently losing its schema in the reference.
|
|
382
|
-
*/
|
|
383
|
-
export function cardTagForType(type: string): string | undefined {
|
|
384
|
-
if (!_cardTags) {
|
|
385
|
-
const present = new Set(listElements());
|
|
386
|
-
_cardTags = new Map(Object.entries(BUILTIN_CARD_TAGS).filter(([, tag]) => present.has(tag)));
|
|
387
|
-
}
|
|
388
|
-
return _cardTags.get(type);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
/** The inverse: which card type does this element render, if any. */
|
|
392
|
-
export function cardTypeForTag(tag: string): string | undefined {
|
|
393
|
-
for (const name of cardSchemaNames) {
|
|
394
|
-
if (cardTagForType(name) === tag) return name;
|
|
395
|
-
}
|
|
396
|
-
return undefined;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
/**
|
|
400
|
-
* The elements that host a thread of cards, derived from the manifest: an element is
|
|
401
|
-
* a card host exactly when it declares the `cardSchemas` prop.
|
|
402
|
-
*
|
|
403
|
-
* That prop is the one that carries a developer's own card schemas into the browser
|
|
404
|
-
* validator, so "declares it" and "hosts cards" are the same fact rather than two
|
|
405
|
-
* facts that can disagree. Today it selects kai-chat / kai-message / kai-thread /
|
|
406
|
-
* kai-workspace; an element that grows the prop tomorrow joins without an edit here.
|
|
407
|
-
*/
|
|
408
|
-
export function cardHostTags(): string[] {
|
|
409
|
-
if (!_cardHosts) {
|
|
410
|
-
_cardHosts = getDeclarations()
|
|
411
|
-
.filter(
|
|
412
|
-
(d) =>
|
|
413
|
-
d.tagName !== undefined &&
|
|
414
|
-
(d.members ?? []).some(
|
|
415
|
-
(m) => m.kind === 'field' && m.privacy === 'public' && m.name === 'cardSchemas',
|
|
416
|
-
),
|
|
417
|
-
)
|
|
418
|
-
.map((d) => d.tagName!)
|
|
419
|
-
.sort();
|
|
420
|
-
}
|
|
421
|
-
return _cardHosts;
|
|
422
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
-
import {
|
|
3
|
-
CallToolRequestSchema,
|
|
4
|
-
ListToolsRequestSchema,
|
|
5
|
-
} from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
-
import { createRequire } from 'node:module';
|
|
7
|
-
import { z } from 'zod';
|
|
8
|
-
import { reference } from './tools/reference';
|
|
9
|
-
import { scaffold } from './tools/scaffold';
|
|
10
|
-
import { theme } from './tools/theme';
|
|
11
|
-
import { debug } from './tools/debug';
|
|
12
|
-
import { constructTool } from './tools/construct';
|
|
13
|
-
import type { Tool } from './tools/types';
|
|
14
|
-
import { validateToolArgs } from './validate-args';
|
|
15
|
-
|
|
16
|
-
const tools: Tool[] = [reference, scaffold, theme, debug, constructTool];
|
|
17
|
-
|
|
18
|
-
const PACKAGE_NAME = '@kitn.ai/ui';
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* The `serverInfo` every MCP harness reads back on `initialize`. READ, NOT TYPED.
|
|
22
|
-
*
|
|
23
|
-
* This was a hand-typed `{ name: '@kitn.ai/ui', version: '0.15.0' }` and the kit kept
|
|
24
|
-
* releasing past it, so every harness that connected was told a version ten minors
|
|
25
|
-
* stale — the same "derive it, don't type it" failure as the CDN pins, minus their
|
|
26
|
-
* lint. Nothing here is allowed to restate the version: `server.test.ts` reads
|
|
27
|
-
* package.json off disk itself and compares.
|
|
28
|
-
*
|
|
29
|
-
* RESOLVED BY THE PACKAGE'S OWN NAME, not by path arithmetic. This module runs from
|
|
30
|
-
* two places — `<package>/src/agent-tooling/mcp/server.ts` under vitest, and inlined
|
|
31
|
-
* into `<package>/dist/mcp.es.js` for the bin — which sit at different depths, so a
|
|
32
|
-
* relative hop would need one rule per context (what manifest.ts has to do, because
|
|
33
|
-
* `dist/custom-elements.json` is a build artifact with no name to ask for). A
|
|
34
|
-
* self-reference has no depth to get wrong: Node resolves `@kitn.ai/ui/package.json`
|
|
35
|
-
* against the nearest package.json whose `name` matches, which is this package in
|
|
36
|
-
* both contexts and in an install. It also never escapes into a neighbouring
|
|
37
|
-
* checkout the way a parent-directory search can.
|
|
38
|
-
*
|
|
39
|
-
* WHY IT SURVIVES PUBLISHING. npm always ships package.json, `exports` carries
|
|
40
|
-
* `"./package.json": "./package.json"` (self-reference is restricted to the exports
|
|
41
|
-
* map, so that key is load-bearing — drop it and this throws, and the test goes red
|
|
42
|
-
* with it), and `bin/mcp.js` imports `../dist/mcp.es.js` by its own URL, so the
|
|
43
|
-
* resolver starts inside the installed package however it was launched (npx, global
|
|
44
|
-
* install, symlink). Not a JSON import: that inlines the version at BUILD time, which
|
|
45
|
-
* is a second copy that can disagree with the package.json shipped beside it, and it
|
|
46
|
-
* would need `resolveJsonModule` on the repo's browser tsc pass as well as this one.
|
|
47
|
-
*
|
|
48
|
-
* A broken resolve throws instead of falling back to a placeholder — `bin/mcp.js`
|
|
49
|
-
* turns that into a named fatal on stderr, which is the loud version. A wrong version
|
|
50
|
-
* silently announced to every harness is the quiet one.
|
|
51
|
-
*/
|
|
52
|
-
function packageIdentity(): { name: string; version: string } {
|
|
53
|
-
const specifier = `${PACKAGE_NAME}/package.json`;
|
|
54
|
-
const require = createRequire(import.meta.url);
|
|
55
|
-
const pkg = require(specifier) as { name?: string; version?: string };
|
|
56
|
-
if (pkg.name !== PACKAGE_NAME || typeof pkg.version !== 'string') {
|
|
57
|
-
throw new Error(
|
|
58
|
-
`[${PACKAGE_NAME}] Refusing to report a serverInfo that is not this package's: ` +
|
|
59
|
-
`${specifier} resolved to ${require.resolve(specifier)}, whose name is ` +
|
|
60
|
-
`${JSON.stringify(pkg.name)} and version ${JSON.stringify(pkg.version)}.`,
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
return { name: pkg.name, version: pkg.version };
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* The AI/UI MCP server. A stdio server (see ./stdio.ts) exposing the AI/UI tools
|
|
68
|
-
* to any MCP harness. Registers four tools; Tasks 2–5 fill in each handler.
|
|
69
|
-
*
|
|
70
|
-
* The returned `Server` is augmented with `__listToolsForTest()`, a test-only
|
|
71
|
-
* helper that returns the registered tool names without touching the transport.
|
|
72
|
-
*/
|
|
73
|
-
export interface AiUiServer extends Server {
|
|
74
|
-
__listToolsForTest(): string[];
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function createServer(): AiUiServer {
|
|
78
|
-
const server = new Server(packageIdentity(), { capabilities: { tools: {} } }) as AiUiServer;
|
|
79
|
-
|
|
80
|
-
const byName = new Map(tools.map((t) => [t.name, t]));
|
|
81
|
-
|
|
82
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
83
|
-
tools: tools.map((t) => ({
|
|
84
|
-
name: t.name,
|
|
85
|
-
description: t.description,
|
|
86
|
-
// The protocol requires a JSON Schema. We use zod 4's native converter
|
|
87
|
-
// (the repo is on zod 4); the SDK-bundled zod-to-json-schema only
|
|
88
|
-
// understands zod 3 and emits an empty schema for our zod 4 objects.
|
|
89
|
-
inputSchema: z.toJSONSchema(t.inputSchema) as Record<string, unknown>,
|
|
90
|
-
})),
|
|
91
|
-
}));
|
|
92
|
-
|
|
93
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
94
|
-
const tool = byName.get(request.params.name);
|
|
95
|
-
if (!tool) {
|
|
96
|
-
return {
|
|
97
|
-
isError: true,
|
|
98
|
-
content: [{ type: 'text', text: `Unknown tool: ${request.params.name}` }],
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
const args = request.params.arguments ?? {};
|
|
102
|
-
// Enforce the schema each tool ADVERTISES (additionalProperties: false +
|
|
103
|
-
// required) — one path for all four tools. Without this, a wrong key was
|
|
104
|
-
// silently ignored and the handler answered a different question (see
|
|
105
|
-
// validate-args.ts for the observed failure).
|
|
106
|
-
const problem = validateToolArgs(tool.name, tool.inputSchema, args);
|
|
107
|
-
if (problem) {
|
|
108
|
-
return { isError: true, content: [{ type: 'text', text: problem }] };
|
|
109
|
-
}
|
|
110
|
-
return tool.handler(args);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
server.__listToolsForTest = () => tools.map((t) => t.name);
|
|
114
|
-
|
|
115
|
-
return server;
|
|
116
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
2
|
-
import { createServer } from './server';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Build entry for the AI/UI MCP server. Connects the server (./server.ts) to a
|
|
6
|
-
* stdio transport so any MCP harness (Claude Code, Codex, …) can drive it over
|
|
7
|
-
* stdin/stdout. Compiled to dist/mcp.es.js by vite.config.mcp.ts and launched by
|
|
8
|
-
* bin/mcp.js, which handles fatal errors / exit (this file stays free of Node
|
|
9
|
-
* globals so it typechecks under the repo's vite/client-only tsconfig).
|
|
10
|
-
*/
|
|
11
|
-
export async function startStdioServer(): Promise<void> {
|
|
12
|
-
const server = createServer();
|
|
13
|
-
const transport = new StdioServerTransport();
|
|
14
|
-
await server.connect(transport);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Auto-start when imported as the entry (the only consumer is bin/mcp.js).
|
|
18
|
-
startStdioServer();
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import type { Tool } from './types';
|
|
3
|
-
import { validateConstruct, CONSTRUCT_SCHEMA_URL } from '../../construct/schema';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* construct — turn-by-turn authoring of a kitn construct (one JSON file → one
|
|
7
|
-
* web component). Stateless by design: the HARNESS owns the construct file;
|
|
8
|
-
* every call carries the full construct for THIS turn — that is what makes
|
|
9
|
-
* turn 40 safe, there is no server-side draft to corrupt.
|
|
10
|
-
*
|
|
11
|
-
* Two entry shapes: `intent` alone returns a STARTER construct plus the
|
|
12
|
-
* real-choice questions the intent leaves open (menu-honesty: never ask a
|
|
13
|
-
* question the intent already answers). `construct` (optionally with
|
|
14
|
-
* `intent`) validates — rejection is a NORMAL, non-error result, not a thrown
|
|
15
|
-
* error, because an invalid turn does not change the file: the previous good
|
|
16
|
-
* construct still stands.
|
|
17
|
-
*
|
|
18
|
-
* NOTE on the advertised schema: `Tool.inputSchema` is a plain `z.ZodObject`,
|
|
19
|
-
* and both `server.ts` (`z.toJSONSchema`) and `validate-args.ts`
|
|
20
|
-
* (`schema.shape`) need that exact shape — a `.superRefine` wrapper is a
|
|
21
|
-
* `ZodEffects`-like type with no `.shape`, so it can't be `tool.inputSchema`.
|
|
22
|
-
* The advertised schema therefore stays a plain `.strict()` object; the
|
|
23
|
-
* "at least one of intent/construct" rule is enforced here in the handler
|
|
24
|
-
* with the same message the skeleton's superRefine would have produced.
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
const inputSchema = z
|
|
28
|
-
.object({
|
|
29
|
-
intent: z
|
|
30
|
-
.string()
|
|
31
|
-
.min(1)
|
|
32
|
-
.optional()
|
|
33
|
-
.describe('What the author wants, in their words. Alone: returns a starter construct.'),
|
|
34
|
-
construct: z
|
|
35
|
-
.record(z.string(), z.unknown())
|
|
36
|
-
.optional()
|
|
37
|
-
.describe('The FULL construct JSON for this turn. The harness owns the file; send all of it every turn.'),
|
|
38
|
-
})
|
|
39
|
-
.strict();
|
|
40
|
-
|
|
41
|
-
function starterFor(intent: string) {
|
|
42
|
-
// Derive what the intent already implies; ask only what it leaves open.
|
|
43
|
-
const impliesWidget = /widget|embed|bubble|corner|launcher/i.test(intent);
|
|
44
|
-
const name = 'my-chat'; // real-choice: always ask for the tag name (it is theirs)
|
|
45
|
-
return {
|
|
46
|
-
construct: {
|
|
47
|
-
$schema: CONSTRUCT_SCHEMA_URL,
|
|
48
|
-
name,
|
|
49
|
-
layout: 'widget',
|
|
50
|
-
provider: { mode: 'mock' },
|
|
51
|
-
},
|
|
52
|
-
stated: impliesWidget ? ['layout: widget (implied by your request)'] : [],
|
|
53
|
-
questions: [
|
|
54
|
-
`What should the element tag be? (kebab-case, e.g. "acme-support"; using "${name}" until you say)`,
|
|
55
|
-
...(impliesWidget ? [] : ['Layout: widget, fullscreen, aside, split, or custom?']),
|
|
56
|
-
],
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export const constructTool: Tool = {
|
|
61
|
-
name: 'construct',
|
|
62
|
-
description:
|
|
63
|
-
'Author a kitn construct (one JSON file → one web component) turn by turn. ' +
|
|
64
|
-
'Send the full construct each turn; invalid turns come back REJECTED with per-path reasons and the previous good construct stands. ' +
|
|
65
|
-
`Schema: ${CONSTRUCT_SCHEMA_URL}. Preview with "kai dev <file>".`,
|
|
66
|
-
inputSchema,
|
|
67
|
-
handler: async (args) => {
|
|
68
|
-
const { intent, construct } = args as { intent?: string; construct?: Record<string, unknown> };
|
|
69
|
-
|
|
70
|
-
if (!intent && !construct) {
|
|
71
|
-
return {
|
|
72
|
-
isError: true,
|
|
73
|
-
content: [{ type: 'text', text: 'construct: pass intent, construct, or both' }],
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (!construct) {
|
|
78
|
-
const s = starterFor(intent ?? '');
|
|
79
|
-
return {
|
|
80
|
-
content: [
|
|
81
|
-
{
|
|
82
|
-
type: 'text',
|
|
83
|
-
text: [
|
|
84
|
-
'STARTER construct (mock provider — keyless, previews immediately):',
|
|
85
|
-
'```json',
|
|
86
|
-
JSON.stringify(s.construct, null, 2),
|
|
87
|
-
'```',
|
|
88
|
-
...s.stated,
|
|
89
|
-
...s.questions,
|
|
90
|
-
].join('\n'),
|
|
91
|
-
},
|
|
92
|
-
],
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const out = validateConstruct(construct);
|
|
97
|
-
if (!out.ok) {
|
|
98
|
-
return {
|
|
99
|
-
content: [
|
|
100
|
-
{
|
|
101
|
-
type: 'text',
|
|
102
|
-
text: [
|
|
103
|
-
'REJECTED — this turn does not change the file; the previous good construct stands.',
|
|
104
|
-
...out.problems.map((p) => ` ${p.path || '(root)'}: ${p.message}`),
|
|
105
|
-
].join('\n'),
|
|
106
|
-
},
|
|
107
|
-
],
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const c = out.construct;
|
|
112
|
-
return {
|
|
113
|
-
content: [
|
|
114
|
-
{
|
|
115
|
-
type: 'text',
|
|
116
|
-
text: [
|
|
117
|
-
`VALID: <${c.name}> (${c.layout}, ${c.provider.mode}).`,
|
|
118
|
-
'```json',
|
|
119
|
-
JSON.stringify(c, null, 2),
|
|
120
|
-
'```',
|
|
121
|
-
`Preview: kai dev <file>. Compile: kai compile <file>.`,
|
|
122
|
-
...(c.provider.mode === 'endpoint'
|
|
123
|
-
? ['Backend route: use the scaffold tool — it emits a compiling route for your framework and wire.']
|
|
124
|
-
: []),
|
|
125
|
-
].join('\n'),
|
|
126
|
-
},
|
|
127
|
-
],
|
|
128
|
-
};
|
|
129
|
-
},
|
|
130
|
-
};
|