@happyvertical/smrt-mcp-openai 0.0.0-stage → 0.51.38

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/AGENTS.md ADDED
@@ -0,0 +1,11 @@
1
+ # Optional OpenAI navigation adapter
2
+
3
+ `src/index.ts` composes existing principal-authorized app-MCP workflows. Settings
4
+ persistence, atomic assignment and tenant checks stay in the owning application
5
+ handler. The adapter validates wire contracts; it never authenticates or retries
6
+ writes. `src/client.ts` uses only the portable browser bridge and bounded inert
7
+ host context. No SDK v1 helper, second server, or OpenAI types in portable APIs.
8
+
9
+ Pinned contract, fallbacks, package gates and external-host evidence limitations
10
+ are in README.md; behavior/threat rows are in TEST-DESIGN.md. Run build, typecheck,
11
+ test, test:e2e and verify:pack. Synthetic hosts do not prove OpenAI host support.
package/CLAUDE.md ADDED
@@ -0,0 +1 @@
1
+ @AGENTS.md
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright <2025> <Happy Vertical Corporation>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,176 @@
1
- # Temporary Holding Version
1
+ # @happyvertical/smrt-mcp-openai
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Optional navigation, settings, context, messages and mentions for s-m-r-t MCP Apps. Ordinary s-m-r-t apps do not
4
+ import this package. The `./client` entry is browser-only and depends on the
5
+ portable `@happyvertical/smrt-mcp-apps` bridge; the root entry composes existing
6
+ app-MCP workflows without creating another server or authorization policy.
7
+
8
+ ## Contract and scope
9
+
10
+ This package implements the navigation/settings and context/messages/mentions children of [#3202](https://github.com/happyvertical/smrt/issues/3202)
11
+ under the [M0 integration standard](../../docs/content/architecture/mcp-apps-integration.md):
12
+
13
+ - Global and thread entrypoint metadata on idempotent read UI tools accepting `{}`.
14
+ - Structured primitive settings and permission-filtered layout actions; modern
15
+ discovery advertises `capabilities.extensions['openai/settings']` only when
16
+ both settings tools are in the request's authorized catalog.
17
+ - Resource display-mode metadata and portable display requests with inline fallback.
18
+ - Validated native/web app links and observed host deep links resolved through an
19
+ existing domain-authorized read tool.
20
+ - Manifest onboarding declaration; packaged skill/assets verification belongs to M7.
21
+
22
+ Exact baseline: [OpenAI extension spec e314720](https://github.com/openai/mcp-extensions/blob/e314720a0daac326217d1f123fcf51647868fa9f/docs/spec.md),
23
+ revalidated as upstream main on 2026-09-30 UTC, and the [official extension guide](https://developers.openai.com/plugins/build/extensions)
24
+ and [UI guide](https://developers.openai.com/plugins/build/chatgpt-ui). The SDK v1
25
+ OpenAI helper is deliberately not a dependency. Server transport uses native
26
+ scoped MCP SDK 2.0.0 / protocol `2026-07-28`; browser Apps uses `2026-01-26`.
27
+
28
+ Only implemented features are declared. Files/forms remain separate planned M6 children. Settings support booleans, strings/enums/length bounds,
29
+ numbers/integers/range/multiple bounds. Arbitrary regex patterns are explicitly
30
+ rejected to avoid running unbounded patterns; applications can use an enum or
31
+ an existing domain validator. No silent schema downgrade occurs. Metadata is
32
+ bounded to 64 KiB, depth 16; settings to 64 fields, strings to 4096 characters,
33
+ routes to 2048 characters. Unknown fields/types fail closed.
34
+
35
+ ## Server composition
36
+
37
+ Use `withOpenAiEntrypoints(existingReadWorkflow, ['global', 'thread'])` and pass
38
+ the result to the existing `createMcpAppServer({ workflowTools })`. It retains the
39
+ handler and portable `_meta.ui.resourceUri`. Initial tool results still render
40
+ without duplicate entrypoint calls. Give thread views distinct descriptive titles.
41
+
42
+ `bindOpenAiSettings({ schema, read, update, server: () => appServer })` wraps two
43
+ existing workflow definitions. Add its `workflows` to that server, then mount it
44
+ with `extensions: ({ tools }) => settings.extensions(tools)`. The callback runs
45
+ on `server/discover` with the same verified principal and filtered catalog used
46
+ for tool discovery. Neither annotations nor extension metadata grant permission.
47
+
48
+ Read handlers return `structuredContent: { schema, values, layout? }`, with a
49
+ value for every property. Update handlers receive `{ set: { changedField: value } }`
50
+ and return `structuredContent: { values }`. Updates are absolute-value assignments,
51
+ not toggles or increments. The binding verifies their declared read/write effects,
52
+ requires idempotent assignment, validates inputs/outputs, and never retries.
53
+
54
+ **Owning handlers must authorize resource ownership, recheck active tenant and
55
+ membership, and persist patches atomically using their existing executor and
56
+ revision policy.** The adapter does not provide persistence, transaction guards,
57
+ or domain approval. The host wire contract has no revision parameter; do not add
58
+ an invented field. Workflows requiring a human review/revision token should keep
59
+ that flow in the application, rather than force it into native settings. Tests
60
+ exercise an owning SQLite transaction, guarded update, rollback and retries.
61
+
62
+ Pass `openAiDisplayMetadata({ availableDisplayModes: ['inline', 'fullscreen'],
63
+ preferredDisplayMode: 'fullscreen' })` as a declared resource's `metadata`.
64
+ Resource security metadata and digest remain owned by app-MCP. `pip` is not part
65
+ of this pinned OpenAI display contract. Omitted fields preserve upstream defaults;
66
+ preferences remain hints.
67
+
68
+ `openAiOnboardingDeclaration('./skills/setup/SKILL.md')` returns only
69
+ `{ extensions: { 'com.openai': { onboardingSkill: ... } } }`. Merge that into the
70
+ plugin manifest; it does not install or execute the skill.
71
+
72
+ ## Browser navigation and fallbacks
73
+
74
+ Import `./client`, pass the existing `McpAppBridge` to
75
+ `observeOpenAiNavigation({ bridge, resolveTool, onResult, onFallback })`, and call
76
+ the returned disposer when unmounting. It consumes bounded `rawHostContext` from
77
+ the bridge's validated origin/source and lifecycle, recognizes only
78
+ `openai/deepLink: { url }`, and calls the designated server tool with `{ url }`.
79
+ The domain tool must reject guessed or cross-tenant objects. Server applications
80
+ can use `resolveOpenAiNavigationTarget` to dispatch only an authorized read tool.
81
+
82
+ Repeated identical routes do not duplicate calls; new routes cancel old requests
83
+ and ignore stale replies. URLs are app-relative, fragment-free, traversal-free,
84
+ and reject protocol-relative, double-encoded and control-character forms. Treat
85
+ query parameters as untrusted input, never as identity or approval. The literal
86
+ query delimiter is separated before pathname decoding, so encoded question marks
87
+ cannot hide traversal segments. Traversal-like text in query values remains legal.
88
+
89
+ Only a rejected tool call or an `isError` result selects the `denied` fallback.
90
+ Consumer callback exceptions are not authorization failures and never retry a
91
+ fallback. Callbacks should handle their own rendering errors; uncaught exceptions
92
+ in asynchronous result/fallback callbacks surface as unhandled promise rejections.
93
+
94
+ `createOpenAiAppLink` emits pinned desktop (`codex`), mobile (`chatgpt`) or web
95
+ formats with separately encoded plugin/tool/path components. `openAiNavigationLink`
96
+ requires explicit `'present'` support to choose a native link; `'absent'`, unknown
97
+ or malformed support uses a configured HTTPS application URL. This support input
98
+ is a deployment/observed-host decision, **not an invented MCP capability field**.
99
+ Entrypoints/onboarding similarly have no client advertisement in the pinned spec;
100
+ keep ordinary tool/setup navigation available. Native structured settings are
101
+ optional: the same settings tools and application settings page remain usable.
102
+
103
+ `requestOpenAiDisplayMode` uses negotiated portable modes; missing/unknown modes
104
+ or a rejected request return `'inline'`. The UI must actually retain its inline
105
+ layout and render an ordinary link when navigation cannot be resolved.
106
+
107
+ ## Context, messages and composer mentions
108
+
109
+ Browser consumers import `updateOpenAiModelContext` from `./context` and
110
+ `sendOpenAiMessage` from `./messages`. Server consumers import
111
+ `withOpenAiMentionSearch` and `resolveOpenAiMentionSelection` from `./mentions`.
112
+ These subpaths share the existing portable bridge and authorized app-MCP server.
113
+
114
+ `updateOpenAiModelContext({ bridge, value, native })` replaces context with
115
+ `{ text: { text, title?, thumbnail? }, structuredContent? }`. Text is nonempty and
116
+ at most 16384 characters; titles are limited to 512. Set `background: true` on the text value to emit assistant-only audience
117
+ metadata; background is a presentation
118
+ hint, **not a privacy boundary**: the model/provider still receives the context.
119
+ Only send data authorized for that recipient. Negotiated `openai/modelContext`
120
+ uses the supplied native callback. Missing support or native failure falls back
121
+ to portable text and structured content, without native metadata. Abort/disposal
122
+ prevents a late fallback.
123
+
124
+ `sendOpenAiMessage({ bridge, value, native })` accepts text and `target: 'active'`
125
+ (the default) or `'new'`. Active conversations can use portable text messages.
126
+ New conversations require negotiated `openai/message` and a native callback;
127
+ missing or malformed support rejects before any message reaches the active
128
+ conversation. Unknown targets/modalities fail closed and sends are not retried.
129
+
130
+ `withOpenAiMentionSearch(existingReadWorkflow)` declares app-only composer search
131
+ metadata and a `{ query }` input (at most 256 characters, including empty search).
132
+ Its owning handler returns `structuredContent: { items }`, with at most 25
133
+ `{ type: 'resource', resourceUri, title, subtitle?, icons? }` entries. Handles
134
+ are nonempty opaque strings of at most 2048 characters; titles/subtitles are
135
+ nonempty and bounded to 512. Each of at most eight icons requires `src` (2048),
136
+ with optional `mimeType` (128), up to eight nonempty `sizes` strings (32 each),
137
+ and `theme: 'light' | 'dark'`. Unknown fields and malformed values reject.
138
+ The published output schema describes these same closed objects and limits.
139
+
140
+ Search results are discovery hints, never authorization grants. Pass the exact
141
+ returned `resourceUri` as the existing selection workflow's argument through
142
+ `resolveOpenAiMentionSelection({ server, tool, arguments, principal })`. The
143
+ application supplies the trusted, freshly resolved principal; app-MCP policy
144
+ rechecks actor, active tenant, scopes and membership on every call, and the
145
+ owning workflow rechecks handle ownership. Never derive identity from a handle,
146
+ browser metadata or search-time permission. Guesses, cross-tenant calls and
147
+ revocation after search must not select private data. This helper adds no
148
+ selection handler, identity provider, persistence or authorization policy.
149
+
150
+ ## Validation and evidence
151
+
152
+ Run `pnpm install`, then the documented dependency build and package gates:
153
+
154
+ ```sh
155
+ pnpm exec turbo build --filter=@happyvertical/smrt-mcp-openai...
156
+ pnpm --filter @happyvertical/smrt-mcp-openai typecheck
157
+ pnpm --filter @happyvertical/smrt-mcp-openai test
158
+ pnpm --filter @happyvertical/smrt-mcp-openai test:e2e
159
+ pnpm --filter @happyvertical/smrt-mcp-openai verify:pack
160
+ ```
161
+
162
+ The behavior/threat matrix is in [TEST-DESIGN.md](TEST-DESIGN.md). Protocol tests
163
+ use the actual SDK client and HTTP mount, including real loopback JWKS/MCP sockets
164
+ through the public M2 auth gateway with token, actor, tenant and revocation denials; the browser suite uses a real Chromium
165
+ sandboxed iframe with no-network CSP and built browser output. Packed exports
166
+ are checked for declaration and browser/server boundaries. Existing app-MCP full
167
+ suite/typecheck/pack and portable bridge gates also apply to integration changes.
168
+ The coordinator schedules all root gates and independent review.
169
+
170
+ **No actual OpenAI host compatibility is claimed.** The synthetic host does not
171
+ prove desktop, Work web, iOS, Android or account rollout support. No configured
172
+ OpenAI host connection or deployable registered synthetic plugin was provided;
173
+ record exact host/build/platform, auth profile, asset revision and observed
174
+ navigation/settings outcomes before claiming supported-host acceptance. The
175
+ upstream support matrix is expected support only; web refers to Work, excluding
176
+ classic ChatGPT. Actual host acceptance remains open for M8.
@@ -0,0 +1,27 @@
1
+ import type { McpAppBridge, ToolResult } from '@happyvertical/smrt-mcp-apps';
2
+ import { type OpenAiDisplayMode } from './validation.js';
3
+ export { appRelativeUrl, OPENAI_EXTENSIONS_REVISION } from './validation.js';
4
+ export type CapabilityState = 'present' | 'absent' | 'unknown';
5
+ /** Deployment/host-observation input, not an invented MCP wire capability. */
6
+ export declare function capabilityState(value: unknown): CapabilityState;
7
+ export interface OpenAiAppLink {
8
+ pluginId: string;
9
+ toolName: string;
10
+ path?: string;
11
+ marketplace?: string;
12
+ platform: 'desktop' | 'mobile' | 'web';
13
+ }
14
+ export declare function createOpenAiAppLink(options: OpenAiAppLink): string;
15
+ /** No host-brand inference: absent/unverified support yields an ordinary application link. */
16
+ export declare function openAiNavigationLink(capability: unknown, link: OpenAiAppLink, fallbackUrl: string): string;
17
+ /** Display negotiation remains portable; unknown modes and host failures retain inline UI. */
18
+ export declare function requestOpenAiDisplayMode(bridge: McpAppBridge, requested: unknown): Promise<OpenAiDisplayMode>;
19
+ export declare function readOpenAiDeepLink(rawHostContext: unknown): string | undefined;
20
+ /** Observe inert host context, then request a domain-authorized read; never route to arbitrary URLs. */
21
+ export declare function observeOpenAiNavigation(options: {
22
+ bridge: McpAppBridge;
23
+ resolveTool: string;
24
+ onResult(result: ToolResult, url: string): void;
25
+ onFallback(reason: 'unavailable' | 'invalid' | 'denied'): void;
26
+ }): () => void;
27
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC7E,OAAO,EAKL,KAAK,iBAAiB,EAIvB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,cAAc,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAC7E,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;AAC/D,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CAE/D;AACD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,SAAS,GAAG,QAAQ,GAAG,KAAK,CAAC;CACxC;AACD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAwBlE;AAOD,8FAA8F;AAC9F,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,OAAO,EACnB,IAAI,EAAE,aAAa,EACnB,WAAW,EAAE,MAAM,GAClB,MAAM,CAKR;AACD,8FAA8F;AAC9F,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,YAAY,EACpB,SAAS,EAAE,OAAO,GACjB,OAAO,CAAC,iBAAiB,CAAC,CAkB5B;AACD,wBAAgB,kBAAkB,CAChC,cAAc,EAAE,OAAO,GACtB,MAAM,GAAG,SAAS,CAOpB;AACD,wGAAwG;AACxG,wBAAgB,uBAAuB,CAAC,OAAO,EAAE;IAC/C,MAAM,EAAE,YAAY,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,UAAU,CAAC,MAAM,EAAE,aAAa,GAAG,SAAS,GAAG,QAAQ,GAAG,IAAI,CAAC;CAChE,GAAG,MAAM,IAAI,CAkDb"}
package/dist/client.js ADDED
@@ -0,0 +1,125 @@
1
+ import { appRelativeUrl, displayMode, json, keys, record, text, toolName, } from './validation.js';
2
+ export { appRelativeUrl, OPENAI_EXTENSIONS_REVISION } from './validation.js';
3
+ /** Deployment/host-observation input, not an invented MCP wire capability. */
4
+ export function capabilityState(value) {
5
+ return value === 'present' || value === 'absent' ? value : 'unknown';
6
+ }
7
+ export function createOpenAiAppLink(options) {
8
+ json(options);
9
+ keys(record(options), [
10
+ 'pluginId',
11
+ 'toolName',
12
+ 'path',
13
+ 'marketplace',
14
+ 'platform',
15
+ ]);
16
+ const plugin = encodeURIComponent(text(options.pluginId, 256));
17
+ const tool = encodeURIComponent(text(options.toolName, 128));
18
+ const path = appRelativeUrl(options.path ?? '/');
19
+ const marketplace = options.marketplace === undefined
20
+ ? ''
21
+ : `@${encodeURIComponent(text(options.marketplace, 128))}`;
22
+ if (options.platform === 'web') {
23
+ if (marketplace)
24
+ throw new TypeError('Web links require a published ChatGPT plugin');
25
+ return `https://chatgpt.com/plugins/${plugin}/app/${tool}?path=${encodeURIComponent(path)}`;
26
+ }
27
+ if (options.platform !== 'desktop' && options.platform !== 'mobile')
28
+ throw new TypeError('Unsupported link platform');
29
+ return `${options.platform === 'desktop' ? 'codex' : 'chatgpt'}://plugins/${plugin}${marketplace}/app/${tool}?path=${encodeURIComponent(path)}`;
30
+ }
31
+ function appUrl(value) {
32
+ const parsed = new URL(text(value, 2048));
33
+ if (parsed.protocol !== 'https:' || parsed.username || parsed.password)
34
+ throw new TypeError('Expected credential-free HTTPS application URL');
35
+ return parsed.href;
36
+ }
37
+ /** No host-brand inference: absent/unverified support yields an ordinary application link. */
38
+ export function openAiNavigationLink(capability, link, fallbackUrl) {
39
+ const fallback = appUrl(fallbackUrl);
40
+ return capabilityState(capability) === 'present'
41
+ ? createOpenAiAppLink(link)
42
+ : fallback;
43
+ }
44
+ /** Display negotiation remains portable; unknown modes and host failures retain inline UI. */
45
+ export async function requestOpenAiDisplayMode(bridge, requested) {
46
+ let mode;
47
+ try {
48
+ mode = displayMode(requested);
49
+ }
50
+ catch {
51
+ return 'inline';
52
+ }
53
+ const snapshot = bridge.snapshot;
54
+ if (snapshot.state !== 'ready' ||
55
+ !(snapshot.hostContext.availableDisplayModes ?? ['inline']).includes(mode))
56
+ return 'inline';
57
+ try {
58
+ return displayMode(await bridge.requestDisplayMode(mode));
59
+ }
60
+ catch {
61
+ return 'inline';
62
+ }
63
+ }
64
+ export function readOpenAiDeepLink(rawHostContext) {
65
+ json(rawHostContext);
66
+ const context = record(rawHostContext);
67
+ if (context['openai/deepLink'] === undefined)
68
+ return undefined;
69
+ const link = record(context['openai/deepLink']);
70
+ keys(link, ['url']);
71
+ return appRelativeUrl(link.url);
72
+ }
73
+ /** Observe inert host context, then request a domain-authorized read; never route to arbitrary URLs. */
74
+ export function observeOpenAiNavigation(options) {
75
+ const name = toolName(options.resolveTool);
76
+ let last;
77
+ let generation = 0;
78
+ let disposed = false;
79
+ let pending;
80
+ const unsubscribe = options.bridge.subscribe((snapshot) => {
81
+ if (disposed || snapshot.state !== 'ready')
82
+ return;
83
+ let url;
84
+ try {
85
+ url = readOpenAiDeepLink(snapshot.rawHostContext);
86
+ }
87
+ catch {
88
+ last = undefined;
89
+ generation++;
90
+ pending?.abort();
91
+ options.onFallback('invalid');
92
+ return;
93
+ }
94
+ if (url === undefined || url === last)
95
+ return;
96
+ last = url;
97
+ const current = ++generation;
98
+ pending?.abort();
99
+ if (!snapshot.hostCapabilities.serverTools) {
100
+ options.onFallback('unavailable');
101
+ return;
102
+ }
103
+ pending = new AbortController();
104
+ void options.bridge.callTool(name, { url }, pending.signal).then((result) => {
105
+ if (disposed || current !== generation || options.bridge.signal.aborted)
106
+ return;
107
+ if (result.isError)
108
+ options.onFallback('denied');
109
+ else
110
+ options.onResult(result, url);
111
+ }, () => {
112
+ if (!disposed &&
113
+ current === generation &&
114
+ !options.bridge.signal.aborted)
115
+ options.onFallback('denied');
116
+ });
117
+ });
118
+ return () => {
119
+ disposed = true;
120
+ generation++;
121
+ pending?.abort();
122
+ unsubscribe();
123
+ };
124
+ }
125
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import type { McpAppBridge } from '@happyvertical/smrt-mcp-apps';
2
+ export declare const OPENAI_MODEL_CONTEXT_CAPABILITY = "openai/modelContext";
3
+ export interface OpenAiIcon {
4
+ src: string;
5
+ mimeType?: string;
6
+ sizes?: string[];
7
+ theme?: 'light' | 'dark';
8
+ }
9
+ export interface OpenAiContextText {
10
+ text: string;
11
+ title?: string;
12
+ thumbnail?: OpenAiIcon;
13
+ /** Hidden from the person, never hidden from the model or provider. */
14
+ background?: boolean;
15
+ }
16
+ export interface OpenAiModelContextUpdate {
17
+ content?: Array<Record<string, unknown>>;
18
+ structuredContent?: Record<string, unknown>;
19
+ }
20
+ /**
21
+ * Builds only the pinned OpenAI extension fields. `_meta` crosses the provider
22
+ * boundary, so `background` is a visibility choice and never a privacy grant.
23
+ */
24
+ export declare function openAiContextText(value: OpenAiContextText): Record<string, unknown>;
25
+ /** Validates a bounded, idempotent replacement for this app instance's context. */
26
+ export declare function openAiModelContextUpdate(value: {
27
+ text?: OpenAiContextText;
28
+ structuredContent?: Record<string, unknown>;
29
+ }): OpenAiModelContextUpdate;
30
+ /** Capability detection is explicit; unknown/malformed data cannot enable native metadata. */
31
+ export declare function hasOpenAiModelContext(rawCapabilities: unknown): boolean;
32
+ /**
33
+ * Sends a portable text/structured fallback through the existing bridge when
34
+ * native support was not negotiated. Native callers receive the exact wire
35
+ * payload and may transport title/thumbnail/background without leaking them
36
+ * into portable public types.
37
+ */
38
+ export declare function updateOpenAiModelContext(options: {
39
+ bridge: McpAppBridge;
40
+ value: {
41
+ text?: OpenAiContextText;
42
+ structuredContent?: Record<string, unknown>;
43
+ };
44
+ native?(params: OpenAiModelContextUpdate, signal?: AbortSignal): Promise<void>;
45
+ signal?: AbortSignal;
46
+ }): Promise<'native' | 'portable'>;
47
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAGjE,eAAO,MAAM,+BAA+B,wBAAwB,CAAC;AAErE,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB,uEAAuE;IACvE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACzC,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC7C;AAqBD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,iBAAiB,GACvB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAmBzB;AAED,mFAAmF;AACnF,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,IAAI,CAAC,EAAE,iBAAiB,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC7C,GAAG,wBAAwB,CAc3B;AAED,8FAA8F;AAC9F,wBAAgB,qBAAqB,CAAC,eAAe,EAAE,OAAO,GAAG,OAAO,CAcvE;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAAC,OAAO,EAAE;IACtD,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE;QACL,IAAI,CAAC,EAAE,iBAAiB,CAAC;QACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAC7C,CAAC;IACF,MAAM,CAAC,CACL,MAAM,EAAE,wBAAwB,EAChC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,QAAQ,GAAG,UAAU,CAAC,CA0BjC"}
@@ -0,0 +1,110 @@
1
+ import { json, keys, record, text } from './validation.js';
2
+ export const OPENAI_MODEL_CONTEXT_CAPABILITY = 'openai/modelContext';
3
+ function icon(value) {
4
+ json(value);
5
+ const input = record(value);
6
+ keys(input, ['src', 'mimeType', 'sizes', 'theme']);
7
+ const result = { src: text(input.src, 2048) };
8
+ if (input.mimeType !== undefined)
9
+ result.mimeType = text(input.mimeType, 128);
10
+ if (input.sizes !== undefined) {
11
+ if (!Array.isArray(input.sizes) || input.sizes.length > 8)
12
+ throw new TypeError('Invalid icon sizes');
13
+ result.sizes = input.sizes.map((size) => text(size, 32));
14
+ }
15
+ if (input.theme !== undefined) {
16
+ if (input.theme !== 'light' && input.theme !== 'dark')
17
+ throw new TypeError('Invalid icon theme');
18
+ result.theme = input.theme;
19
+ }
20
+ return result;
21
+ }
22
+ /**
23
+ * Builds only the pinned OpenAI extension fields. `_meta` crosses the provider
24
+ * boundary, so `background` is a visibility choice and never a privacy grant.
25
+ */
26
+ export function openAiContextText(value) {
27
+ json(value);
28
+ const input = record(value);
29
+ keys(input, ['text', 'title', 'thumbnail', 'background']);
30
+ const result = {
31
+ type: 'text',
32
+ text: text(input.text, 16384),
33
+ };
34
+ const meta = {};
35
+ if (input.title !== undefined)
36
+ meta['openai/title'] = text(input.title, 512);
37
+ if (input.thumbnail !== undefined)
38
+ meta['openai/thumbnail'] = icon(input.thumbnail);
39
+ if (Object.keys(meta).length)
40
+ result._meta = meta;
41
+ if (input.background !== undefined) {
42
+ if (typeof input.background !== 'boolean')
43
+ throw new TypeError('Expected background boolean');
44
+ if (input.background)
45
+ result.annotations = { audience: ['assistant'] };
46
+ }
47
+ return result;
48
+ }
49
+ /** Validates a bounded, idempotent replacement for this app instance's context. */
50
+ export function openAiModelContextUpdate(value) {
51
+ json(value);
52
+ const input = record(value);
53
+ keys(input, ['text', 'structuredContent']);
54
+ if (input.text === undefined && input.structuredContent === undefined)
55
+ throw new TypeError('Model context requires content or structuredContent');
56
+ return {
57
+ ...(input.text === undefined
58
+ ? {}
59
+ : { content: [openAiContextText(input.text)] }),
60
+ ...(input.structuredContent === undefined
61
+ ? {}
62
+ : { structuredContent: record(input.structuredContent) }),
63
+ };
64
+ }
65
+ /** Capability detection is explicit; unknown/malformed data cannot enable native metadata. */
66
+ export function hasOpenAiModelContext(rawCapabilities) {
67
+ try {
68
+ json(rawCapabilities);
69
+ const capabilities = record(rawCapabilities);
70
+ const experimental = capabilities.experimental;
71
+ return (experimental !== undefined &&
72
+ record(experimental)[OPENAI_MODEL_CONTEXT_CAPABILITY] !== undefined &&
73
+ record(record(experimental)[OPENAI_MODEL_CONTEXT_CAPABILITY]) !==
74
+ undefined);
75
+ }
76
+ catch {
77
+ return false;
78
+ }
79
+ }
80
+ /**
81
+ * Sends a portable text/structured fallback through the existing bridge when
82
+ * native support was not negotiated. Native callers receive the exact wire
83
+ * payload and may transport title/thumbnail/background without leaking them
84
+ * into portable public types.
85
+ */
86
+ export async function updateOpenAiModelContext(options) {
87
+ const params = openAiModelContextUpdate(options.value);
88
+ if (options.native &&
89
+ hasOpenAiModelContext(options.bridge.snapshot.rawHostCapabilities)) {
90
+ try {
91
+ await options.native(params, options.signal);
92
+ return 'native';
93
+ }
94
+ catch (error) {
95
+ // An aborted or disposed request must not be revived on the portable path.
96
+ if (options.signal?.aborted)
97
+ throw error;
98
+ }
99
+ }
100
+ await options.bridge.updateModelContext({
101
+ ...(options.value.text === undefined
102
+ ? {}
103
+ : { text: options.value.text.text }),
104
+ ...(params.structuredContent === undefined
105
+ ? {}
106
+ : { structuredContent: params.structuredContent }),
107
+ }, options.signal);
108
+ return 'portable';
109
+ }
110
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import { type McpAppPrincipal, type McpAppServer, type McpWorkflowToolDefinition } from '@happyvertical/smrt-app-mcp';
2
+ import type { MCPTool, ToolJsonSchema } from '@happyvertical/smrt-core/generators/mcp';
3
+ import { type SettingsSchema } from './settings.js';
4
+ import { type OpenAiDisplayMode } from './validation.js';
5
+ export type { SettingSchema, SettingsGroup, SettingsReadResult, SettingsSchema, } from './settings.js';
6
+ export { validateSettingsRead, validateSettingsSchema, validateSettingsValues, } from './settings.js';
7
+ export type { OpenAiDisplayMode } from './validation.js';
8
+ export { appRelativeUrl, OPENAI_EXTENSIONS_REVISION } from './validation.js';
9
+ export declare const EMPTY_ARGUMENTS_SCHEMA: ToolJsonSchema;
10
+ /** Annotate an existing authorized read workflow; no alternative dispatch path. */
11
+ export declare function withOpenAiEntrypoints(definition: McpWorkflowToolDefinition, entrypoints: readonly ('global' | 'thread')[]): McpWorkflowToolDefinition;
12
+ /** Resource content metadata (not tool metadata). Omitted fields preserve upstream defaults. */
13
+ export declare function openAiDisplayMetadata(options?: {
14
+ availableDisplayModes?: OpenAiDisplayMode[];
15
+ preferredDisplayMode?: OpenAiDisplayMode;
16
+ }): Record<string, unknown>;
17
+ /** Manifest declaration only. M7 validates the referenced packaged asset. */
18
+ export declare function openAiOnboardingDeclaration(skill: string): {
19
+ extensions: {
20
+ 'com.openai': {
21
+ onboardingSkill: string;
22
+ };
23
+ };
24
+ };
25
+ export interface OpenAiSettingsBinding {
26
+ readTool: string;
27
+ updateTool: string;
28
+ workflows: [McpWorkflowToolDefinition, McpWorkflowToolDefinition];
29
+ /** Pass to the app-MCP protocol extension callback; input must be its authorized catalog. */
30
+ extensions(tools: readonly MCPTool[]): Record<string, Record<string, unknown>>;
31
+ }
32
+ /** Wrap existing read/atomic-assignment workflows. Their execution and authorization remain app-owned. */
33
+ export declare function bindOpenAiSettings(options: {
34
+ schema: SettingsSchema;
35
+ read: McpWorkflowToolDefinition;
36
+ update: McpWorkflowToolDefinition;
37
+ /** Resolve the same server after composition, for authorized layout tool references. */
38
+ server: () => McpAppServer;
39
+ }): OpenAiSettingsBinding;
40
+ /** Resolve untrusted route data through the existing server policy and domain read handler. */
41
+ export declare function resolveOpenAiNavigationTarget(options: {
42
+ server: McpAppServer;
43
+ tool: string;
44
+ url: string;
45
+ principal: McpAppPrincipal | null;
46
+ }): Promise<import("@happyvertical/smrt-core").MCPResponse>;
47
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,yBAAyB,EAC/B,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACf,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,KAAK,cAAc,EAIpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAKL,KAAK,iBAAiB,EAIvB,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EACV,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAC7E,eAAO,MAAM,sBAAsB,EAAE,cAIpC,CAAC;AAyBF,mFAAmF;AACnF,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,yBAAyB,EACrC,WAAW,EAAE,SAAS,CAAC,QAAQ,GAAG,QAAQ,CAAC,EAAE,GAC5C,yBAAyB,CA0B3B;AACD,gGAAgG;AAChG,wBAAgB,qBAAqB,CACnC,OAAO,GAAE;IACP,qBAAqB,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC5C,oBAAoB,CAAC,EAAE,iBAAiB,CAAC;CACrC,GACL,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAsBzB;AACD,6EAA6E;AAC7E,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,MAAM,GAAG;IAC1D,UAAU,EAAE;QAAE,YAAY,EAAE;YAAE,eAAe,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CAC3D,CAKA;AACD,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,CAAC,yBAAyB,EAAE,yBAAyB,CAAC,CAAC;IAClE,6FAA6F;IAC7F,UAAU,CACR,KAAK,EAAE,SAAS,OAAO,EAAE,GACxB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC5C;AACD,0GAA0G;AAC1G,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,yBAAyB,CAAC;IAChC,MAAM,EAAE,yBAAyB,CAAC;IAClC,wFAAwF;IACxF,MAAM,EAAE,MAAM,YAAY,CAAC;CAC5B,GAAG,qBAAqB,CAqGxB;AACD,+FAA+F;AAC/F,wBAAsB,6BAA6B,CAAC,OAAO,EAAE;IAC3D,MAAM,EAAE,YAAY,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;CACnC,2DAcA"}