@happyvertical/smrt-mcp-openai 0.0.0-stage → 0.51.39
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 +11 -0
- package/CLAUDE.md +1 -0
- package/FILES.md +77 -0
- package/FORMS-TEST-DESIGN.md +42 -0
- package/FORMS.md +112 -0
- package/LICENSE +7 -0
- package/README.md +175 -2
- package/dist/client.d.ts +27 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +125 -0
- package/dist/client.js.map +1 -0
- package/dist/context.d.ts +47 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +110 -0
- package/dist/context.js.map +1 -0
- package/dist/file-contracts.d.ts +34 -0
- package/dist/file-contracts.d.ts.map +1 -0
- package/dist/file-contracts.js +98 -0
- package/dist/file-contracts.js.map +1 -0
- package/dist/files-server.d.ts +6 -0
- package/dist/files-server.d.ts.map +1 -0
- package/dist/files-server.js +45 -0
- package/dist/files-server.js.map +1 -0
- package/dist/files.d.ts +38 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +288 -0
- package/dist/files.js.map +1 -0
- package/dist/forms-server.d.ts +53 -0
- package/dist/forms-server.d.ts.map +1 -0
- package/dist/forms-server.js +115 -0
- package/dist/forms-server.js.map +1 -0
- package/dist/forms-view.d.ts +14 -0
- package/dist/forms-view.d.ts.map +1 -0
- package/dist/forms-view.js +213 -0
- package/dist/forms-view.js.map +1 -0
- package/dist/forms.d.ts +118 -0
- package/dist/forms.d.ts.map +1 -0
- package/dist/forms.js +389 -0
- package/dist/forms.js.map +1 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +205 -0
- package/dist/index.js.map +1 -0
- package/dist/mentions.d.ts +30 -0
- package/dist/mentions.d.ts.map +1 -0
- package/dist/mentions.js +149 -0
- package/dist/mentions.js.map +1 -0
- package/dist/messages.d.ts +19 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +47 -0
- package/dist/messages.js.map +1 -0
- package/dist/settings.d.ts +45 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +158 -0
- package/dist/settings.js.map +1 -0
- package/dist/validation.d.ts +15 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +121 -0
- package/dist/validation.js.map +1 -0
- package/package.json +96 -3
|
@@ -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"}
|
package/dist/context.js
ADDED
|
@@ -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,34 @@
|
|
|
1
|
+
export interface OpenAiFileInput {
|
|
2
|
+
file: {
|
|
3
|
+
name: string;
|
|
4
|
+
resourceUri: string;
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
export type FileContent = {
|
|
8
|
+
text: string;
|
|
9
|
+
blob?: never;
|
|
10
|
+
} | {
|
|
11
|
+
blob: string;
|
|
12
|
+
text?: never;
|
|
13
|
+
};
|
|
14
|
+
export type FileWriteResult = {
|
|
15
|
+
outcome: 'saved' | 'conflict';
|
|
16
|
+
etag: string;
|
|
17
|
+
} | {
|
|
18
|
+
outcome: 'too-large';
|
|
19
|
+
maxBytes: number;
|
|
20
|
+
};
|
|
21
|
+
export interface FileRead {
|
|
22
|
+
uri: string;
|
|
23
|
+
mimeType: string;
|
|
24
|
+
content: FileContent;
|
|
25
|
+
writable: boolean;
|
|
26
|
+
etag?: string;
|
|
27
|
+
}
|
|
28
|
+
/** The URI is retained verbatim and is never decoded into a filesystem path. */
|
|
29
|
+
export declare function validateFileInput(value: unknown): OpenAiFileInput;
|
|
30
|
+
export declare function fileExtensions(value: readonly string[]): string[];
|
|
31
|
+
export declare function fileContent(value: unknown, maxBytes: number): FileContent;
|
|
32
|
+
export declare function fileRead(value: unknown, uri: string, maxBytes: number, mimeTypes: readonly string[]): FileRead;
|
|
33
|
+
export declare function fileWriteResult(value: unknown): FileWriteResult;
|
|
34
|
+
//# sourceMappingURL=file-contracts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-contracts.d.ts","sourceRoot":"","sources":["../src/file-contracts.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AACD,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,KAAK,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC;AACnC,MAAM,MAAM,eAAe,GACvB;IAAE,OAAO,EAAE,OAAO,GAAG,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAC/C,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,WAAW,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AACD,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CA0BjE;AACD,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAiBjE;AACD,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW,CAwBzE;AACD,wBAAgB,QAAQ,CACtB,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,SAAS,MAAM,EAAE,GAC3B,QAAQ,CA8BV;AACD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CAY/D"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { json, keys, record, text } from './validation.js';
|
|
2
|
+
/** The URI is retained verbatim and is never decoded into a filesystem path. */
|
|
3
|
+
export function validateFileInput(value) {
|
|
4
|
+
json(value);
|
|
5
|
+
const root = record(value);
|
|
6
|
+
keys(root, ['file']);
|
|
7
|
+
const file = record(root.file);
|
|
8
|
+
keys(file, ['name', 'resourceUri']);
|
|
9
|
+
const name = text(file.name, 255);
|
|
10
|
+
if (/[\\/]/.test(name) ||
|
|
11
|
+
[...name].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127) ||
|
|
12
|
+
name === '.' ||
|
|
13
|
+
name === '..')
|
|
14
|
+
throw new TypeError('Expected filename without path');
|
|
15
|
+
const uri = text(file.resourceUri, 2048);
|
|
16
|
+
// Conservative local policy: opaque host/provider URI, never a URL or file path.
|
|
17
|
+
if (!/^[a-z][a-z0-9+.-]*:/i.test(uri) ||
|
|
18
|
+
/^(file|https?|data|javascript):/i.test(uri) ||
|
|
19
|
+
/^[a-z]:/i.test(uri) ||
|
|
20
|
+
/[\s\\]/.test(uri))
|
|
21
|
+
throw new TypeError('Expected opaque resource URI');
|
|
22
|
+
return { file: { name, resourceUri: uri } };
|
|
23
|
+
}
|
|
24
|
+
export function fileExtensions(value) {
|
|
25
|
+
if (!Array.isArray(value) ||
|
|
26
|
+
!value.length ||
|
|
27
|
+
value.length > 32 ||
|
|
28
|
+
new Set(value.map((extension) => typeof extension === 'string' ? extension.toLowerCase() : extension)).size !== value.length)
|
|
29
|
+
throw new TypeError('Expected bounded unique file extensions');
|
|
30
|
+
return value.map((extension) => {
|
|
31
|
+
if (!/^\.[a-z0-9][a-z0-9._-]{0,31}$/i.test(extension))
|
|
32
|
+
throw new TypeError('Expected file extension');
|
|
33
|
+
return extension.toLowerCase();
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
export function fileContent(value, maxBytes) {
|
|
37
|
+
json(value);
|
|
38
|
+
const content = record(value);
|
|
39
|
+
keys(content, ['text', 'blob']);
|
|
40
|
+
let bytes;
|
|
41
|
+
if (typeof content.text === 'string' && !Object.hasOwn(content, 'blob')) {
|
|
42
|
+
bytes = new TextEncoder().encode(content.text).length;
|
|
43
|
+
}
|
|
44
|
+
else if (typeof content.blob === 'string' &&
|
|
45
|
+
!Object.hasOwn(content, 'text')) {
|
|
46
|
+
const blob = content.blob;
|
|
47
|
+
if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(blob))
|
|
48
|
+
throw new TypeError('Expected base64 content');
|
|
49
|
+
bytes =
|
|
50
|
+
(blob.length / 4) * 3 -
|
|
51
|
+
(blob.endsWith('==') ? 2 : blob.endsWith('=') ? 1 : 0);
|
|
52
|
+
}
|
|
53
|
+
else
|
|
54
|
+
throw new TypeError('Expected exactly one representation');
|
|
55
|
+
if (bytes > maxBytes)
|
|
56
|
+
throw new TypeError('File exceeds byte limit');
|
|
57
|
+
return structuredClone(content);
|
|
58
|
+
}
|
|
59
|
+
export function fileRead(value, uri, maxBytes, mimeTypes) {
|
|
60
|
+
const root = record(value);
|
|
61
|
+
if (!Array.isArray(root.contents) || root.contents.length !== 1)
|
|
62
|
+
throw new TypeError('Expected one granted resource');
|
|
63
|
+
const item = record(root.contents[0]);
|
|
64
|
+
if (item.uri !== uri)
|
|
65
|
+
throw new TypeError('Resource URI mismatch');
|
|
66
|
+
const mimeType = text(item.mimeType, 128);
|
|
67
|
+
if (!mimeTypes.includes(mimeType))
|
|
68
|
+
throw new TypeError('File MIME denied');
|
|
69
|
+
const content = fileContent({
|
|
70
|
+
...(Object.hasOwn(item, 'text') ? { text: item.text } : {}),
|
|
71
|
+
...(Object.hasOwn(item, 'blob') ? { blob: item.blob } : {}),
|
|
72
|
+
}, maxBytes);
|
|
73
|
+
const metadata = item._meta === undefined
|
|
74
|
+
? {}
|
|
75
|
+
: record(record(item._meta)['openai/resource'] ?? {});
|
|
76
|
+
if (metadata.writable !== undefined && typeof metadata.writable !== 'boolean')
|
|
77
|
+
throw new TypeError('Invalid writable metadata');
|
|
78
|
+
const etag = metadata.etag === undefined ? undefined : text(metadata.etag, 1024);
|
|
79
|
+
return {
|
|
80
|
+
uri,
|
|
81
|
+
mimeType,
|
|
82
|
+
content,
|
|
83
|
+
writable: metadata.writable === true,
|
|
84
|
+
...(etag ? { etag } : {}),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
export function fileWriteResult(value) {
|
|
88
|
+
json(value);
|
|
89
|
+
const result = record(value);
|
|
90
|
+
if (result.outcome === 'saved' || result.outcome === 'conflict')
|
|
91
|
+
return { outcome: result.outcome, etag: text(result.etag, 1024) };
|
|
92
|
+
if (result.outcome === 'too-large' &&
|
|
93
|
+
Number.isSafeInteger(result.maxBytes) &&
|
|
94
|
+
Number(result.maxBytes) >= 0)
|
|
95
|
+
return { outcome: 'too-large', maxBytes: Number(result.maxBytes) };
|
|
96
|
+
throw new TypeError('Invalid file write result');
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=file-contracts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-contracts.js","sourceRoot":"","sources":["../src/file-contracts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type McpWorkflowToolDefinition } from '@happyvertical/smrt-app-mcp';
|
|
2
|
+
export type { OpenAiFileInput } from './file-contracts.js';
|
|
3
|
+
export { validateFileInput } from './file-contracts.js';
|
|
4
|
+
/** Wrap an existing authorized provider/view workflow. Input is a reference, never a grant. */
|
|
5
|
+
export declare function withOpenAiFileEntrypoint(definition: McpWorkflowToolDefinition, extensions: readonly string[]): McpWorkflowToolDefinition;
|
|
6
|
+
//# sourceMappingURL=files-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files-server.d.ts","sourceRoot":"","sources":["../src/files-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,yBAAyB,EAC/B,MAAM,6BAA6B,CAAC;AAGrC,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,+FAA+F;AAC/F,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,yBAAyB,EACrC,UAAU,EAAE,SAAS,MAAM,EAAE,GAC5B,yBAAyB,CA6C3B"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createMcpWorkflowTool, } from '@happyvertical/smrt-app-mcp';
|
|
2
|
+
import { fileExtensions, validateFileInput } from './file-contracts.js';
|
|
3
|
+
export { validateFileInput } from './file-contracts.js';
|
|
4
|
+
/** Wrap an existing authorized provider/view workflow. Input is a reference, never a grant. */
|
|
5
|
+
export function withOpenAiFileEntrypoint(definition, extensions) {
|
|
6
|
+
createMcpWorkflowTool(definition);
|
|
7
|
+
const allowed = fileExtensions(extensions);
|
|
8
|
+
if (!definition.ui?.resourceUri ||
|
|
9
|
+
definition.effect !== 'read' ||
|
|
10
|
+
!definition.idempotent)
|
|
11
|
+
throw new TypeError('File entrypoint requires idempotent read UI workflow');
|
|
12
|
+
if (definition.metadata && Object.hasOwn(definition.metadata, 'openai/ui'))
|
|
13
|
+
throw new TypeError('File entrypoint metadata already declared');
|
|
14
|
+
return {
|
|
15
|
+
...definition,
|
|
16
|
+
inputSchema: {
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
file: {
|
|
20
|
+
type: 'object',
|
|
21
|
+
properties: {
|
|
22
|
+
name: { type: 'string' },
|
|
23
|
+
resourceUri: { type: 'string' },
|
|
24
|
+
},
|
|
25
|
+
required: ['name', 'resourceUri'],
|
|
26
|
+
additionalProperties: false,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
required: ['file'],
|
|
30
|
+
additionalProperties: false,
|
|
31
|
+
},
|
|
32
|
+
metadata: {
|
|
33
|
+
...structuredClone(definition.metadata ?? {}),
|
|
34
|
+
'openai/ui': { entrypoints: [{ type: 'file', extensions: allowed }] },
|
|
35
|
+
},
|
|
36
|
+
execute(context) {
|
|
37
|
+
const input = validateFileInput(context.arguments);
|
|
38
|
+
if (!allowed.some((extension) => input.file.name.toLowerCase().endsWith(extension)))
|
|
39
|
+
throw new TypeError('File extension denied');
|
|
40
|
+
// Existing handler must resolve ownership/tenant from context.principal, never metadata.
|
|
41
|
+
return definition.execute({ ...context, arguments: { ...input } });
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=files-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files-server.js","sourceRoot":"","sources":["../src/files-server.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,GAEtB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,+FAA+F;AAC/F,MAAM,UAAU,wBAAwB,CACtC,UAAqC,EACrC,UAA6B;IAE7B,qBAAqB,CAAC,UAAU,CAAC,CAAC;IAClC,MAAM,OAAO,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IAC3C,IACE,CAAC,UAAU,CAAC,EAAE,EAAE,WAAW;QAC3B,UAAU,CAAC,MAAM,KAAK,MAAM;QAC5B,CAAC,UAAU,CAAC,UAAU;QAEtB,MAAM,IAAI,SAAS,CAAC,sDAAsD,CAAC,CAAC;IAC9E,IAAI,UAAU,CAAC,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,WAAW,CAAC;QACxE,MAAM,IAAI,SAAS,CAAC,2CAA2C,CAAC,CAAC;IACnE,OAAO;QACL,GAAG,UAAU;QACb,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,IAAI,EAAE;oBACJ,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE;wBACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;wBACxB,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;qBAChC;oBACD,QAAQ,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC;oBACjC,oBAAoB,EAAE,KAAK;iBAC5B;aACF;YACD,QAAQ,EAAE,CAAC,MAAM,CAAC;YAClB,oBAAoB,EAAE,KAAK;SAC5B;QACD,QAAQ,EAAE;YACR,GAAG,eAAe,CAAC,UAAU,CAAC,QAAQ,IAAI,EAAE,CAAC;YAC7C,WAAW,EAAE,EAAE,WAAW,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,EAAE;SACtE;QACD,OAAO,CAAC,OAAO;YACb,MAAM,KAAK,GAAG,iBAAiB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACnD,IACE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAC1B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAClD;gBAED,MAAM,IAAI,SAAS,CAAC,uBAAuB,CAAC,CAAC;YAC/C,yFAAyF;YACzF,OAAO,UAAU,CAAC,OAAO,CAAC,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,EAAE,GAAG,KAAK,EAAE,EAAE,CAAC,CAAC;QACrE,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/files.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { McpAppBridge, ToolResult } from '@happyvertical/smrt-mcp-apps';
|
|
2
|
+
import { type FileContent, type FileRead, type FileWriteResult, type OpenAiFileInput } from './file-contracts.js';
|
|
3
|
+
export type { FileContent, FileRead, FileWriteResult, OpenAiFileInput, } from './file-contracts.js';
|
|
4
|
+
export { validateFileInput } from './file-contracts.js';
|
|
5
|
+
export type FileOperation = 'read' | 'write' | 'subscribe' | 'open';
|
|
6
|
+
export interface OpenAiFileOptions {
|
|
7
|
+
bridge: McpAppBridge;
|
|
8
|
+
input: OpenAiFileInput;
|
|
9
|
+
/** Existing app workflow: fresh principal/owner/tenant/grant check on every call. */
|
|
10
|
+
authorityTool: string;
|
|
11
|
+
/** Ordinary import/open/download workflow, used when native capability is absent. */
|
|
12
|
+
fallbackTool: string;
|
|
13
|
+
/** Application policy, capped below the portable bridge's JSON envelope limit. */
|
|
14
|
+
maxBytes: number;
|
|
15
|
+
mimeTypes: readonly string[];
|
|
16
|
+
}
|
|
17
|
+
/** One initial tool-input grant, one lifetime; create a new session after reconnect/input change. */
|
|
18
|
+
export declare class OpenAiFileSession {
|
|
19
|
+
#private;
|
|
20
|
+
constructor(options: OpenAiFileOptions);
|
|
21
|
+
get native(): boolean;
|
|
22
|
+
get signal(): AbortSignal;
|
|
23
|
+
fallback(operation: FileOperation): Promise<ToolResult>;
|
|
24
|
+
read(representation?: 'text' | 'blob'): Promise<FileRead | ToolResult>;
|
|
25
|
+
write(content: FileContent): Promise<FileWriteResult | ToolResult>;
|
|
26
|
+
subscribe(onRead: (file: FileRead) => void, onError: (error: unknown) => void): Promise<void>;
|
|
27
|
+
dispose(): void;
|
|
28
|
+
}
|
|
29
|
+
/** User action calls this after the existing provider selects and authorizes an exact path. */
|
|
30
|
+
export declare function openOpenAiFile(options: {
|
|
31
|
+
bridge: McpAppBridge;
|
|
32
|
+
resolveTool: string;
|
|
33
|
+
arguments: Record<string, unknown>;
|
|
34
|
+
allowedPaths: readonly string[];
|
|
35
|
+
/** Explicit application confirmation UI; false cancels without resolving or opening. */
|
|
36
|
+
confirm(): Promise<boolean>;
|
|
37
|
+
}): Promise<ToolResult | undefined>;
|
|
38
|
+
//# sourceMappingURL=files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC7E,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,eAAe,EAIpB,KAAK,eAAe,EAErB,MAAM,qBAAqB,CAAC;AAG7B,YAAY,EACV,WAAW,EACX,QAAQ,EACR,eAAe,EACf,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAKxD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,OAAO,GAAG,WAAW,GAAG,MAAM,CAAC;AACpE,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,eAAe,CAAC;IACvB,qFAAqF;IACrF,aAAa,EAAE,MAAM,CAAC;IACtB,qFAAqF;IACrF,YAAY,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9B;AAeD,qGAAqG;AACrG,qBAAa,iBAAiB;;gBAYhB,OAAO,EAAE,iBAAiB;IAkDtC,IAAI,MAAM,IAAI,OAAO,CAEpB;IACD,IAAI,MAAM,IAAI,WAAW,CAExB;IAkBK,QAAQ,CAAC,SAAS,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC;IAWvD,IAAI,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,UAAU,CAAC;IAiCtE,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,GAAG,UAAU,CAAC;IAkClE,SAAS,CACb,MAAM,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,EAChC,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAChC,OAAO,CAAC,IAAI,CAAC;IAwEhB,OAAO,IAAI,IAAI;CAsBhB;AAED,+FAA+F;AAC/F,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAC5C,MAAM,EAAE,YAAY,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,wFAAwF;IACxF,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7B,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CA6BlC"}
|