@aquera/mcp-ui-render 0.0.1

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.
Files changed (42) hide show
  1. package/CHANGELOG.md +437 -0
  2. package/CONFIGURATION.md +178 -0
  3. package/LICENSE +21 -0
  4. package/README.md +237 -0
  5. package/RELEASE-NOTES.md +77 -0
  6. package/TEST-PLAN.md +72 -0
  7. package/USAGE.md +100 -0
  8. package/package.json +52 -0
  9. package/src/controls.d.ts +1 -0
  10. package/src/controls.js +572 -0
  11. package/src/controls.js.map +1 -0
  12. package/src/elements/aq-mcp-config.d.ts +289 -0
  13. package/src/elements/aq-mcp-config.js +673 -0
  14. package/src/elements/aq-mcp-config.js.map +1 -0
  15. package/src/elements/aq-mcp-field.d.ts +92 -0
  16. package/src/elements/aq-mcp-field.js +275 -0
  17. package/src/elements/aq-mcp-field.js.map +1 -0
  18. package/src/elements/aq-mcp-section.d.ts +101 -0
  19. package/src/elements/aq-mcp-section.js +261 -0
  20. package/src/elements/aq-mcp-section.js.map +1 -0
  21. package/src/engine/status.d.ts +31 -0
  22. package/src/engine/status.js +44 -0
  23. package/src/engine/status.js.map +1 -0
  24. package/src/engine/submit.d.ts +26 -0
  25. package/src/engine/submit.js +37 -0
  26. package/src/engine/submit.js.map +1 -0
  27. package/src/engine/validate.d.ts +24 -0
  28. package/src/engine/validate.js +144 -0
  29. package/src/engine/validate.js.map +1 -0
  30. package/src/engine/values.d.ts +20 -0
  31. package/src/engine/values.js +42 -0
  32. package/src/engine/values.js.map +1 -0
  33. package/src/index.d.ts +18 -0
  34. package/src/index.js +20 -0
  35. package/src/index.js.map +1 -0
  36. package/src/mcp-ui-render.css +811 -0
  37. package/src/registry.d.ts +63 -0
  38. package/src/registry.js +39 -0
  39. package/src/registry.js.map +1 -0
  40. package/src/types.d.ts +239 -0
  41. package/src/types.js +8 -0
  42. package/src/types.js.map +1 -0
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Component registry (§9.1 extensibility): `ui-component` string → a Lit control
3
+ * renderer. New components register into the map without touching the engine;
4
+ * an unknown `ui-component` falls back to the datatype's default control, so a
5
+ * field is never dropped.
6
+ */
7
+ import type { TemplateResult } from 'lit';
8
+ import type { Datatype, Field, FieldAction, Section, Values } from './types.js';
9
+ /** What a control renderer receives from the field engine. */
10
+ export interface ControlContext {
11
+ field: Field;
12
+ value: unknown;
13
+ /** Validation message to show, or null when valid. */
14
+ error: string | null;
15
+ /** Emit a new value for this field's `attribute` (§14). */
16
+ onInput: (value: unknown) => void;
17
+ /** Invoke a `button`/action field (§5), or one entry from `field.actions` for a
18
+ * control with more than one independent action — omit the argument for the
19
+ * single-action `button` case (uses the field's own `tool`/`args`/`confirm`),
20
+ * pass the specific `FieldAction` for a multi-action toolbar. No-op for plain
21
+ * value controls that never call it at all. */
22
+ onAction?: (action?: FieldAction) => void;
23
+ /** Host-fed options for `select`/`radio` fields whose list is resolved
24
+ * dynamically via `field.tool` rather than declared statically in the
25
+ * descriptor (§9.6) — keyed by `field.attribute`. Only consulted when
26
+ * `field.options` is absent/empty; see `<aq-mcp-config>`'s `dynamicOptions`
27
+ * property and the `aq-mcp-options-request` event. */
28
+ dynamicOptions?: Record<string, unknown[]>;
29
+ /**
30
+ * The field's row is being laid out label-ABOVE-control in a narrow grid
31
+ * cell (`sectionsLayout: 'accordion'`), rather than in the roomy
32
+ * label-left/control-right row the other two layouts use. Purely advisory:
33
+ * every built-in control renders correctly either way, and a custom control
34
+ * that ignores this is not broken. The built-ins use it for one thing —
35
+ * showing "Not set" as the placeholder of an empty field that has no
36
+ * `defaultValue` to advertise instead, which is only legible in the dense
37
+ * grid where an empty box would otherwise say nothing at all.
38
+ */
39
+ dense?: boolean;
40
+ }
41
+ export type ControlRenderer = (ctx: ControlContext) => TemplateResult;
42
+ /** Register (or override) the renderer for a `ui-component`. */
43
+ export declare function registerControl(name: string, renderer: ControlRenderer): void;
44
+ /** Resolve a renderer by `ui-component`, or undefined if none is registered. */
45
+ export declare function resolveControl(uiComponent: string): ControlRenderer | undefined;
46
+ /** What a section-footer renderer receives in place of the library's default save button. */
47
+ export interface SectionFooterContext {
48
+ section: Section;
49
+ values: Values;
50
+ /** Validate + emit `aq-mcp-submit` for this section (§13/§14), same as the default button. */
51
+ onSubmit: () => void;
52
+ }
53
+ export type SectionFooterRenderer = (ctx: SectionFooterContext) => TemplateResult;
54
+ /**
55
+ * Register (or override) a custom footer for a section, keyed by `section.key`
56
+ * (e.g. `"authorization"`). Replaces the library's default "Save <label>" button
57
+ * entirely — the renderer owns the markup and must call `onSubmit` itself.
58
+ */
59
+ export declare function registerSectionFooter(key: string, renderer: SectionFooterRenderer): void;
60
+ /** Resolve a custom footer renderer by section key, or undefined for the library default. */
61
+ export declare function resolveSectionFooter(key: string): SectionFooterRenderer | undefined;
62
+ /** The fallback control name for a datatype when `ui-component` is unknown (§9.1). */
63
+ export declare function defaultControlFor(datatype: Datatype): string;
@@ -0,0 +1,39 @@
1
+ const registry = new Map();
2
+ /** Register (or override) the renderer for a `ui-component`. */
3
+ export function registerControl(name, renderer) {
4
+ registry.set(name, renderer);
5
+ }
6
+ /** Resolve a renderer by `ui-component`, or undefined if none is registered. */
7
+ export function resolveControl(uiComponent) {
8
+ return registry.get(uiComponent);
9
+ }
10
+ const footerRegistry = new Map();
11
+ /**
12
+ * Register (or override) a custom footer for a section, keyed by `section.key`
13
+ * (e.g. `"authorization"`). Replaces the library's default "Save <label>" button
14
+ * entirely — the renderer owns the markup and must call `onSubmit` itself.
15
+ */
16
+ export function registerSectionFooter(key, renderer) {
17
+ footerRegistry.set(key, renderer);
18
+ }
19
+ /** Resolve a custom footer renderer by section key, or undefined for the library default. */
20
+ export function resolveSectionFooter(key) {
21
+ return footerRegistry.get(key);
22
+ }
23
+ /** The default `ui-component` for each datatype (§4). `object` defaults to `textarea`, not
24
+ * `input` — a single line is a poor fit for JSON, and `textarea` is what `controls.ts`'s
25
+ * object-aware JSON display/parsing is built around. */
26
+ const DATATYPE_DEFAULT = {
27
+ string: 'input',
28
+ number: 'input',
29
+ boolean: 'checkbox',
30
+ array: 'select',
31
+ object: 'textarea',
32
+ file: 'file',
33
+ none: 'button',
34
+ };
35
+ /** The fallback control name for a datatype when `ui-component` is unknown (§9.1). */
36
+ export function defaultControlFor(datatype) {
37
+ return DATATYPE_DEFAULT[datatype] ?? 'input';
38
+ }
39
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../../apps/mcp-ui-render/src/registry.ts"],"names":[],"mappings":"AA4CA,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA2B,CAAC;AAEpD,gEAAgE;AAChE,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,QAAyB;IACrE,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC/B,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,cAAc,CAAC,WAAmB;IAChD,OAAO,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AACnC,CAAC;AAYD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAiC,CAAC;AAEhE;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAW,EAAE,QAA+B;IAChF,cAAc,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;AACpC,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC9C,OAAO,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACjC,CAAC;AAED;;yDAEyD;AACzD,MAAM,gBAAgB,GAA6B;IACjD,MAAM,EAAE,OAAO;IACf,MAAM,EAAE,OAAO;IACf,OAAO,EAAE,UAAU;IACnB,KAAK,EAAE,QAAQ;IACf,MAAM,EAAE,UAAU;IAClB,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,QAAQ;CACf,CAAC;AAEF,sFAAsF;AACtF,MAAM,UAAU,iBAAiB,CAAC,QAAkB;IAClD,OAAO,gBAAgB,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC;AAC/C,CAAC"}
package/src/types.d.ts ADDED
@@ -0,0 +1,239 @@
1
+ /**
2
+ * TypeScript model of the `bootstrap` tenant-configuration descriptor
3
+ * (bootstrap-tool-tenant-config.md §2–§3). `additionalProperties: true` at both
4
+ * the section and field level is honoured with index signatures so unknown keys
5
+ * survive a round-trip (§9.4).
6
+ */
7
+ /** The closed set of value types (§4). */
8
+ export type Datatype = 'string' | 'number' | 'boolean' | 'array' | 'object' | 'file' | 'none';
9
+ /** The one auth shape an application uses (§2 Section). Metadata — never submitted. */
10
+ export type AuthType = 'Basic' | 'Bearer' | 'Header' | 'URL' | 'OAuth2' | 'OAuth2-client_credentials' | 'BasicNBearer';
11
+ /** A single control descriptor (§3 $defs/field). */
12
+ export interface Field {
13
+ attribute: string;
14
+ datatype: Datatype;
15
+ /** How it renders. Open set — an unknown value falls back to the datatype default (§9.1). */
16
+ 'ui-component': string;
17
+ label?: string;
18
+ help?: string;
19
+ /**
20
+ * `select`/`radio` only. Each entry is either a plain primitive (its value IS its
21
+ * display text — the original contract) or `{ value, label }` when the submitted
22
+ * code and its human-readable display name differ, e.g.
23
+ * `{ value: 'sftpStorage', label: 'SFTP Storage' }`. `value` is always what's
24
+ * emitted/submitted; `label` (when present) is what's shown to the user.
25
+ */
26
+ options?: unknown[];
27
+ defaultValue?: unknown;
28
+ isRequired?: boolean;
29
+ hidden?: boolean;
30
+ readOnly?: boolean;
31
+ secret?: boolean;
32
+ writeOnce?: boolean;
33
+ /** ECMAScript regex sources, whole-value, AND-combined (§8). */
34
+ validation?: string[];
35
+ validationmessage?: string;
36
+ min?: number;
37
+ max?: number;
38
+ minItems?: number;
39
+ maxItems?: number;
40
+ /** `datatype: "file"` only — allowed MIME types/extensions (e.g. `["image/png", ".pdf"]`). */
41
+ accept?: string[];
42
+ /** `datatype: "file"` only — max size per file, in KB. */
43
+ maxSizeKB?: number;
44
+ requiredKeys?: string[];
45
+ dependencyAttribute?: string;
46
+ dependencyValue?: string | string[];
47
+ /**
48
+ * Compound dependency (§12 amendment, §9.7) — a field applies if `dependencyAttribute`
49
+ * matches OR any entry here matches, i.e. an OR across MULTIPLE fields' values, not just
50
+ * one. For a field gated on a single attribute, `dependencyAttribute`/`dependencyValue`
51
+ * alone is still enough — this exists for the case where two DIFFERENT fields can each
52
+ * independently make a field relevant (e.g. an FTP-server picker that applies when
53
+ * `sourceConnectionType === 'directConnection'` OR `storageType === 'sftpStorage'`, two
54
+ * unrelated fields, neither of which alone tells the whole story).
55
+ */
56
+ dependsOn?: {
57
+ attribute: string;
58
+ value: string | string[];
59
+ }[];
60
+ /** button/submitTool wiring. */
61
+ tool?: string;
62
+ args?: Record<string, unknown>;
63
+ confirm?: string;
64
+ /** e.g. "secretRef:entraid/client_secret" — a reference, never a live secret (§7). */
65
+ valueFrom?: string;
66
+ /**
67
+ * `properties-table` only — static rows declared directly in the descriptor, for
68
+ * data the connector already knows when it builds the `bootstrap` response (no
69
+ * host-side computation needed). Same "descriptor-declared default, host-supplied
70
+ * `values[attribute]` wins if present" priority `options`/`dynamicOptions` already
71
+ * have for `select`/`radio` (§9.6) — see `PropertiesTableRow`.
72
+ */
73
+ rows?: PropertiesTableRow[];
74
+ /**
75
+ * A toolbar of named actions rendered by the control itself (currently
76
+ * `properties-table` only), distinct from the single `tool`/`args`/`confirm` a
77
+ * whole `button`-datatype field already represents (§5) — for a control that
78
+ * needs MORE THAN ONE independent action (e.g. "Download"/"Edit Configuration").
79
+ * Each entry carries its own `tool`/`args`/`confirm`; `onAction` is called with
80
+ * the specific action clicked, so the host can tell which one fired. A field
81
+ * with no `actions` renders no toolbar at all — purely additive, no effect on
82
+ * the existing single-action `button` control.
83
+ *
84
+ * This shape only describes the DEFAULT rendering (icon-only `nile-icon-button`
85
+ * when `icon` is set, else a labeled `nile-button`). A host that wants different
86
+ * toolbar UI entirely — different button styling, a dropdown instead of inline
87
+ * buttons, extra chrome around the actions — doesn't need a new hook for that:
88
+ * `registerControl('properties-table', myRenderer)` (§9.1) already replaces the
89
+ * WHOLE control, toolbar included, the same escape hatch every other control in
90
+ * this library relies on for "the default doesn't fit, build your own."
91
+ */
92
+ actions?: FieldAction[];
93
+ /** Unknown keys preserved on round-trip (§9.4). */
94
+ [k: string]: unknown;
95
+ }
96
+ /**
97
+ * One entry in `Field.actions` — a named, independently-invokable action a
98
+ * control's own toolbar exposes (see `Field.actions`'s doc comment for why this
99
+ * exists alongside the single-action `button` control). `icon` (a `nile-glyph`
100
+ * name) selects an icon-only button; omit it for a labeled text button.
101
+ */
102
+ export interface FieldAction {
103
+ key: string;
104
+ label: string;
105
+ tool: string;
106
+ args?: Record<string, unknown>;
107
+ confirm?: string;
108
+ icon?: string;
109
+ }
110
+ /** An ordered group of fields (§2 Section). */
111
+ export interface Section {
112
+ key: string;
113
+ label: string;
114
+ order: number;
115
+ authType?: AuthType;
116
+ /** When set, the connector saves this section via this tool instead of the platform default (§13). */
117
+ submitTool?: string;
118
+ fields: Field[];
119
+ [k: string]: unknown;
120
+ }
121
+ /** The full response envelope (§2). */
122
+ export interface Descriptor {
123
+ /** Contract version MAJOR.MINOR — consumers switch on MAJOR only (§9.5). */
124
+ version: string;
125
+ server: {
126
+ name: string;
127
+ version: string;
128
+ [k: string]: unknown;
129
+ };
130
+ sections: Section[];
131
+ [k: string]: unknown;
132
+ }
133
+ /** Collected values keyed by `attribute` (§14). */
134
+ export type Values = Record<string, unknown>;
135
+ /**
136
+ * One probe in a `test-connection-report` value (§5, `ui-component:
137
+ * "test-connection-report"`). `SKIPPED` covers a check the host never ran —
138
+ * e.g. because an earlier config-validation check already failed — and
139
+ * renders as neutral, not a failure.
140
+ */
141
+ export interface TestConnectionCheck {
142
+ name: string;
143
+ /** `null`/omitted for a config-validation check that never called anything. */
144
+ endpoint?: string | null;
145
+ /** Open categorisation string, e.g. "auth" | "capability" | "probe" | "config". */
146
+ category?: string;
147
+ /** @deprecated superseded by `category`; still accepted for older payloads. */
148
+ type?: string;
149
+ /** For a config-validation check: the tenant-attribute name it validated. */
150
+ attribute?: string;
151
+ status: 'PASSED' | 'FAILED' | 'SKIPPED' | string;
152
+ /** For a config-validation check: was this attribute required/supplied/well-formed. */
153
+ required?: boolean;
154
+ provided?: boolean;
155
+ valid?: boolean;
156
+ httpStatus?: number;
157
+ message?: string;
158
+ errorCode?: string;
159
+ /** Shown collapsed under the check, in a "How to fix" accordion, when `status` is FAILED. */
160
+ mitigation?: string;
161
+ }
162
+ /**
163
+ * The value shape for `ui-component: "test-connection-report"` (`datatype: "none"` —
164
+ * read-only, never submitted). The host sets this into `values[attribute]` after
165
+ * running its own connection-test tool; the library only renders it.
166
+ */
167
+ export interface TestConnectionReport {
168
+ success: boolean;
169
+ connectionStatus: 'SUCCESS' | 'PARTIAL' | 'FAILED' | 'CONFIG_INVALID' | string;
170
+ /** ISO 8601 timestamp. */
171
+ testedAt: string;
172
+ summary: {
173
+ total: number;
174
+ passed: number;
175
+ failed: number;
176
+ skipped?: number;
177
+ };
178
+ checks: TestConnectionCheck[];
179
+ /** A hard failure to even run the test (distinct from an individual check failing). */
180
+ error?: string | null;
181
+ /**
182
+ * Optional business-impact sentence for the banner's second line (e.g. "Payroll
183
+ * data will not be pulled until the failed check is resolved") — supplied by the
184
+ * host, which knows what the connector's checks mean; the library never invents it.
185
+ */
186
+ impact?: string;
187
+ }
188
+ /**
189
+ * One row for `ui-component: "permissions-report"` (`datatype: "none"` — read-only,
190
+ * never submitted) — a single scope/permission the connector's credentials were
191
+ * checked against. `access` is free-form (matching the live reference's own
192
+ * "Granted" badge text) rather than a closed enum, same "open categorisation
193
+ * string" choice `TestConnectionCheck.category` already made.
194
+ */
195
+ export interface PermissionGrant {
196
+ resourceType: string;
197
+ permissionType: string;
198
+ access: 'Granted' | 'Denied' | string;
199
+ }
200
+ /**
201
+ * The value shape for `ui-component: "permissions-report"` (`datatype: "none"` —
202
+ * read-only, never submitted, same contract as `test-connection-report`). Distinct
203
+ * from `TestConnectionReport`: that shape models a list of PASS/FAIL endpoint
204
+ * probes, this one models a scope/permission GRANT table with a running count and
205
+ * an All/Success filter — the live reference's own "Credentials" tab Test
206
+ * Connection result renders exactly this shape (a "Validation Report" of
207
+ * `resourceType`/`permissionType`/`access` rows), not the endpoint-checklist shape.
208
+ * The host sets this into `values[attribute]` after running its own
209
+ * credential-validation tool; the library only renders it.
210
+ */
211
+ export interface PermissionsValidationReport {
212
+ valid: boolean;
213
+ /** e.g. "The provided credentials are valid. All permissions granted." */
214
+ message: string;
215
+ /** ISO 8601 timestamp. */
216
+ testedAt: string;
217
+ grants: PermissionGrant[];
218
+ }
219
+ /**
220
+ * One row for `ui-component: "properties-table"` (`datatype: "none"` — read-only,
221
+ * never submitted). Two ways to supply `PropertiesTableRow[]`, same priority
222
+ * `select`/`radio` already have between `options` and `dynamicOptions` (§9.6):
223
+ * - **Static, in the descriptor itself** — `field.rows`, for data the connector
224
+ * already knows when it builds the `bootstrap` response (no host computation
225
+ * needed at all).
226
+ * - **Dynamic, host-supplied** — `values[attribute]`, for data the host computes
227
+ * at render time (typically summarizing a handful of OTHER attributes' current
228
+ * values) — same "host computes, library only displays" split
229
+ * `test-connection-report` already established. Wins over `field.rows` when
230
+ * both are present, for the same reason a host-fed `dynamicOptions` entry wins
231
+ * over a field's static `options`: the host's data is necessarily more current.
232
+ */
233
+ export interface PropertiesTableRow {
234
+ property: string;
235
+ /** Rendered as-is for most types; booleans/arrays/long strings get light
236
+ * formatting (see `controls.ts`'s `formatTableValue`) — never re-interpreted
237
+ * or validated, since this is a display-only control. */
238
+ value: unknown;
239
+ }
package/src/types.js ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * TypeScript model of the `bootstrap` tenant-configuration descriptor
3
+ * (bootstrap-tool-tenant-config.md §2–§3). `additionalProperties: true` at both
4
+ * the section and field level is honoured with index signatures so unknown keys
5
+ * survive a round-trip (§9.4).
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../apps/mcp-ui-render/src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}