@mcpmesh/sdk 0.9.11 → 1.0.0-beta.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.
- package/dist/__tests__/claude-handler.test.js +119 -71
- package/dist/__tests__/claude-handler.test.js.map +1 -1
- package/dist/__tests__/llm-agent-media.test.d.ts +8 -0
- package/dist/__tests__/llm-agent-media.test.d.ts.map +1 -0
- package/dist/__tests__/llm-agent-media.test.js +167 -0
- package/dist/__tests__/llm-agent-media.test.js.map +1 -0
- package/dist/__tests__/media-helpers.test.d.ts +7 -0
- package/dist/__tests__/media-helpers.test.d.ts.map +1 -0
- package/dist/__tests__/media-helpers.test.js +60 -0
- package/dist/__tests__/media-helpers.test.js.map +1 -0
- package/dist/__tests__/media-param.test.d.ts +7 -0
- package/dist/__tests__/media-param.test.d.ts.map +1 -0
- package/dist/__tests__/media-param.test.js +104 -0
- package/dist/__tests__/media-param.test.js.map +1 -0
- package/dist/__tests__/media-resolver.test.d.ts +8 -0
- package/dist/__tests__/media-resolver.test.d.ts.map +1 -0
- package/dist/__tests__/media-resolver.test.js +298 -0
- package/dist/__tests__/media-resolver.test.js.map +1 -0
- package/dist/__tests__/media-result.test.d.ts +5 -0
- package/dist/__tests__/media-result.test.d.ts.map +1 -0
- package/dist/__tests__/media-result.test.js +180 -0
- package/dist/__tests__/media-result.test.js.map +1 -0
- package/dist/__tests__/media-store.test.d.ts +7 -0
- package/dist/__tests__/media-store.test.d.ts.map +1 -0
- package/dist/__tests__/media-store.test.js +102 -0
- package/dist/__tests__/media-store.test.js.map +1 -0
- package/dist/__tests__/proxy-resource-link.test.d.ts +8 -0
- package/dist/__tests__/proxy-resource-link.test.d.ts.map +1 -0
- package/dist/__tests__/proxy-resource-link.test.js +175 -0
- package/dist/__tests__/proxy-resource-link.test.js.map +1 -0
- package/dist/agent.d.ts +1 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +76 -10
- package/dist/agent.js.map +1 -1
- package/dist/api-runtime.d.ts.map +1 -1
- package/dist/api-runtime.js +7 -1
- package/dist/api-runtime.js.map +1 -1
- package/dist/config.d.ts +17 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +19 -0
- package/dist/config.js.map +1 -1
- package/dist/debug.d.ts +1 -1
- package/dist/debug.d.ts.map +1 -1
- package/dist/express.d.ts.map +1 -1
- package/dist/express.js +33 -18
- package/dist/express.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/llm-agent.d.ts +2 -0
- package/dist/llm-agent.d.ts.map +1 -1
- package/dist/llm-agent.js +62 -13
- package/dist/llm-agent.js.map +1 -1
- package/dist/llm-provider.d.ts +55 -16
- package/dist/llm-provider.d.ts.map +1 -1
- package/dist/llm-provider.js +321 -32
- package/dist/llm-provider.js.map +1 -1
- package/dist/media/index.d.ts +104 -0
- package/dist/media/index.d.ts.map +1 -0
- package/dist/media/index.js +135 -0
- package/dist/media/index.js.map +1 -0
- package/dist/media/media-store.d.ts +75 -0
- package/dist/media/media-store.d.ts.map +1 -0
- package/dist/media/media-store.js +217 -0
- package/dist/media/media-store.js.map +1 -0
- package/dist/media/resolver.d.ts +67 -0
- package/dist/media/resolver.d.ts.map +1 -0
- package/dist/media/resolver.js +311 -0
- package/dist/media/resolver.js.map +1 -0
- package/dist/media-param.d.ts +17 -0
- package/dist/media-param.d.ts.map +1 -0
- package/dist/media-param.js +32 -0
- package/dist/media-param.js.map +1 -0
- package/dist/provider-handlers/claude-handler.d.ts +21 -26
- package/dist/provider-handlers/claude-handler.d.ts.map +1 -1
- package/dist/provider-handlers/claude-handler.js +65 -45
- package/dist/provider-handlers/claude-handler.js.map +1 -1
- package/dist/provider-handlers/provider-handler.d.ts.map +1 -1
- package/dist/provider-handlers/provider-handler.js +5 -4
- package/dist/provider-handlers/provider-handler.js.map +1 -1
- package/dist/proxy.d.ts +29 -0
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +66 -21
- package/dist/proxy.js.map +1 -1
- package/dist/tls-config.d.ts +31 -0
- package/dist/tls-config.d.ts.map +1 -0
- package/dist/tls-config.js +102 -0
- package/dist/tls-config.js.map +1 -0
- package/dist/types.d.ts +63 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +33 -1
- package/dist/types.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Media module — public API for multimodal content storage and helpers.
|
|
3
|
+
*/
|
|
4
|
+
export { type MediaStore, LocalMediaStore, S3MediaStore, getMediaStore, guessMimeType, _resetMediaStore, } from "./media-store.js";
|
|
5
|
+
export { formatForOpenai, resolveResourceLinks, resolveResourceLinksForToolMessage, resolveMediaAsUserMessage, hasResourceLink, TOOL_IMAGE_UNSUPPORTED_VENDORS, type ResolvedContent, } from "./resolver.js";
|
|
6
|
+
import { type ResolvedContent } from "./resolver.js";
|
|
7
|
+
import type { ResourceLink } from "fastmcp";
|
|
8
|
+
/**
|
|
9
|
+
* Upload binary data to the configured media store.
|
|
10
|
+
*
|
|
11
|
+
* @returns URI pointing to the stored blob (e.g. `file://...` or `s3://...`).
|
|
12
|
+
*/
|
|
13
|
+
export declare function uploadMedia(data: Buffer, filename: string, mimeType: string): Promise<string>;
|
|
14
|
+
/**
|
|
15
|
+
* Build an MCP `resource_link` content item.
|
|
16
|
+
*
|
|
17
|
+
* This is the standard way for tools to return references to binary media
|
|
18
|
+
* (images, audio, PDFs, etc.) rather than embedding them inline.
|
|
19
|
+
*
|
|
20
|
+
* Returns a proper MCP ResourceLink that FastMCP sends as a `resource_link`
|
|
21
|
+
* content type in the MCP protocol response (not serialized as JSON text).
|
|
22
|
+
*/
|
|
23
|
+
export declare function mediaResult(uri: string, name: string, mimeType: string, description?: string, size?: number): ResourceLink;
|
|
24
|
+
/**
|
|
25
|
+
* Convenience class: upload bytes and return a ResourceLink in one step.
|
|
26
|
+
*
|
|
27
|
+
* Usage:
|
|
28
|
+
* const link = await new MediaResult(pngBytes, "chart.png", "image/png").toResourceLink();
|
|
29
|
+
* // or use the standalone function:
|
|
30
|
+
* const link = await createMediaResult(pngBytes, "chart.png", "image/png");
|
|
31
|
+
*/
|
|
32
|
+
export declare class MediaResult {
|
|
33
|
+
readonly data: Buffer;
|
|
34
|
+
readonly filename: string;
|
|
35
|
+
readonly mimeType: string;
|
|
36
|
+
readonly name?: string | undefined;
|
|
37
|
+
readonly description?: string | undefined;
|
|
38
|
+
constructor(data: Buffer, filename: string, mimeType: string, name?: string | undefined, description?: string | undefined);
|
|
39
|
+
toResourceLink(): Promise<ResourceLink>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Upload bytes and return a ResourceLink in one step.
|
|
43
|
+
*/
|
|
44
|
+
export declare function createMediaResult(data: Buffer, filename: string, mimeType: string, name?: string, description?: string): Promise<ResourceLink>;
|
|
45
|
+
/**
|
|
46
|
+
* Result of saving an upload to MediaStore.
|
|
47
|
+
*/
|
|
48
|
+
export interface MediaUploadResult {
|
|
49
|
+
uri: string;
|
|
50
|
+
name: string;
|
|
51
|
+
mimeType: string;
|
|
52
|
+
size: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Save a file upload (e.g., from multer) to MediaStore and return the URI.
|
|
56
|
+
*
|
|
57
|
+
* Accepts objects with buffer/originalname/mimetype properties (multer format)
|
|
58
|
+
* or any object with data/name/mimeType properties.
|
|
59
|
+
*/
|
|
60
|
+
export declare function saveUpload(file: {
|
|
61
|
+
buffer: Buffer;
|
|
62
|
+
originalname: string;
|
|
63
|
+
mimetype: string;
|
|
64
|
+
} | {
|
|
65
|
+
data: Buffer;
|
|
66
|
+
name: string;
|
|
67
|
+
mimeType: string;
|
|
68
|
+
}, options?: {
|
|
69
|
+
filename?: string;
|
|
70
|
+
mimeType?: string;
|
|
71
|
+
}): Promise<string>;
|
|
72
|
+
/**
|
|
73
|
+
* Save a file upload and return full metadata.
|
|
74
|
+
*/
|
|
75
|
+
export declare function saveUploadResult(file: {
|
|
76
|
+
buffer: Buffer;
|
|
77
|
+
originalname: string;
|
|
78
|
+
mimetype: string;
|
|
79
|
+
} | {
|
|
80
|
+
data: Buffer;
|
|
81
|
+
name: string;
|
|
82
|
+
mimeType: string;
|
|
83
|
+
}, options?: {
|
|
84
|
+
filename?: string;
|
|
85
|
+
mimeType?: string;
|
|
86
|
+
}): Promise<MediaUploadResult>;
|
|
87
|
+
/**
|
|
88
|
+
* Resolve an array of media inputs to OpenAI-compatible image_url content parts.
|
|
89
|
+
*
|
|
90
|
+
* Each item is either:
|
|
91
|
+
* - A URI string (resolved via MediaStore.fetch())
|
|
92
|
+
* - An inline `{ data: Buffer; mimeType: string }` object
|
|
93
|
+
*
|
|
94
|
+
* Returns an array of ResolvedContent parts in OpenAI image_url format,
|
|
95
|
+
* which Vercel AI SDK converts to each vendor's native format.
|
|
96
|
+
*
|
|
97
|
+
* @param media - Array of URI strings or inline media objects
|
|
98
|
+
* @returns Array of OpenAI-compatible image_url content parts
|
|
99
|
+
*/
|
|
100
|
+
export declare function resolveMediaInputs(media: Array<string | {
|
|
101
|
+
data: Buffer;
|
|
102
|
+
mimeType: string;
|
|
103
|
+
}>): Promise<ResolvedContent[]>;
|
|
104
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/media/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Media module — public API for multimodal content storage and helpers.
|
|
3
|
+
*/
|
|
4
|
+
export { LocalMediaStore, S3MediaStore, getMediaStore, guessMimeType, _resetMediaStore, } from "./media-store.js";
|
|
5
|
+
export { formatForOpenai, resolveResourceLinks, resolveResourceLinksForToolMessage, resolveMediaAsUserMessage, hasResourceLink, TOOL_IMAGE_UNSUPPORTED_VENDORS, } from "./resolver.js";
|
|
6
|
+
import { getMediaStore } from "./media-store.js";
|
|
7
|
+
import { formatForOpenai } from "./resolver.js";
|
|
8
|
+
/**
|
|
9
|
+
* Upload binary data to the configured media store.
|
|
10
|
+
*
|
|
11
|
+
* @returns URI pointing to the stored blob (e.g. `file://...` or `s3://...`).
|
|
12
|
+
*/
|
|
13
|
+
export async function uploadMedia(data, filename, mimeType) {
|
|
14
|
+
const store = getMediaStore();
|
|
15
|
+
return store.upload(data, filename, mimeType);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Build an MCP `resource_link` content item.
|
|
19
|
+
*
|
|
20
|
+
* This is the standard way for tools to return references to binary media
|
|
21
|
+
* (images, audio, PDFs, etc.) rather than embedding them inline.
|
|
22
|
+
*
|
|
23
|
+
* Returns a proper MCP ResourceLink that FastMCP sends as a `resource_link`
|
|
24
|
+
* content type in the MCP protocol response (not serialized as JSON text).
|
|
25
|
+
*/
|
|
26
|
+
export function mediaResult(uri, name, mimeType, description, size) {
|
|
27
|
+
const result = { type: "resource_link", uri, name, mimeType };
|
|
28
|
+
if (description !== undefined)
|
|
29
|
+
result.description = description;
|
|
30
|
+
if (size !== undefined)
|
|
31
|
+
result._meta = { size };
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// MediaResult class — upload + ResourceLink in one step
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
/**
|
|
38
|
+
* Convenience class: upload bytes and return a ResourceLink in one step.
|
|
39
|
+
*
|
|
40
|
+
* Usage:
|
|
41
|
+
* const link = await new MediaResult(pngBytes, "chart.png", "image/png").toResourceLink();
|
|
42
|
+
* // or use the standalone function:
|
|
43
|
+
* const link = await createMediaResult(pngBytes, "chart.png", "image/png");
|
|
44
|
+
*/
|
|
45
|
+
export class MediaResult {
|
|
46
|
+
data;
|
|
47
|
+
filename;
|
|
48
|
+
mimeType;
|
|
49
|
+
name;
|
|
50
|
+
description;
|
|
51
|
+
constructor(data, filename, mimeType, name, description) {
|
|
52
|
+
this.data = data;
|
|
53
|
+
this.filename = filename;
|
|
54
|
+
this.mimeType = mimeType;
|
|
55
|
+
this.name = name;
|
|
56
|
+
this.description = description;
|
|
57
|
+
}
|
|
58
|
+
async toResourceLink() {
|
|
59
|
+
const uri = await uploadMedia(this.data, this.filename, this.mimeType);
|
|
60
|
+
return mediaResult(uri, this.name ?? this.filename, this.mimeType, this.description, this.data.length);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Upload bytes and return a ResourceLink in one step.
|
|
65
|
+
*/
|
|
66
|
+
export async function createMediaResult(data, filename, mimeType, name, description) {
|
|
67
|
+
return new MediaResult(data, filename, mimeType, name, description).toResourceLink();
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Save a file upload (e.g., from multer) to MediaStore and return the URI.
|
|
71
|
+
*
|
|
72
|
+
* Accepts objects with buffer/originalname/mimetype properties (multer format)
|
|
73
|
+
* or any object with data/name/mimeType properties.
|
|
74
|
+
*/
|
|
75
|
+
export async function saveUpload(file, options) {
|
|
76
|
+
const data = 'buffer' in file ? file.buffer : file.data;
|
|
77
|
+
const filename = options?.filename ?? ('originalname' in file ? file.originalname : file.name);
|
|
78
|
+
const mime = options?.mimeType ?? ('mimetype' in file ? file.mimetype : file.mimeType);
|
|
79
|
+
const store = getMediaStore();
|
|
80
|
+
return store.upload(data, filename, mime);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Save a file upload and return full metadata.
|
|
84
|
+
*/
|
|
85
|
+
export async function saveUploadResult(file, options) {
|
|
86
|
+
const data = 'buffer' in file ? file.buffer : file.data;
|
|
87
|
+
const filename = options?.filename ?? ('originalname' in file ? file.originalname : file.name);
|
|
88
|
+
const mime = options?.mimeType ?? ('mimetype' in file ? file.mimetype : file.mimeType);
|
|
89
|
+
const store = getMediaStore();
|
|
90
|
+
const uri = await store.upload(data, filename, mime);
|
|
91
|
+
return { uri, name: filename, mimeType: mime, size: data.length };
|
|
92
|
+
}
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
// resolveMediaInputs — resolve media URIs/buffers to OpenAI-compatible parts
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
/**
|
|
97
|
+
* Resolve an array of media inputs to OpenAI-compatible image_url content parts.
|
|
98
|
+
*
|
|
99
|
+
* Each item is either:
|
|
100
|
+
* - A URI string (resolved via MediaStore.fetch())
|
|
101
|
+
* - An inline `{ data: Buffer; mimeType: string }` object
|
|
102
|
+
*
|
|
103
|
+
* Returns an array of ResolvedContent parts in OpenAI image_url format,
|
|
104
|
+
* which Vercel AI SDK converts to each vendor's native format.
|
|
105
|
+
*
|
|
106
|
+
* @param media - Array of URI strings or inline media objects
|
|
107
|
+
* @returns Array of OpenAI-compatible image_url content parts
|
|
108
|
+
*/
|
|
109
|
+
export async function resolveMediaInputs(media) {
|
|
110
|
+
const store = getMediaStore();
|
|
111
|
+
const parts = [];
|
|
112
|
+
for (const item of media) {
|
|
113
|
+
try {
|
|
114
|
+
let data;
|
|
115
|
+
let mimeType;
|
|
116
|
+
if (typeof item === "string") {
|
|
117
|
+
const result = await store.fetch(item);
|
|
118
|
+
data = result.data;
|
|
119
|
+
mimeType = result.mimeType;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
data = item.data;
|
|
123
|
+
mimeType = item.mimeType;
|
|
124
|
+
}
|
|
125
|
+
const b64 = data.toString("base64");
|
|
126
|
+
parts.push(formatForOpenai(b64, mimeType));
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
const label = typeof item === "string" ? item : "(inline media)";
|
|
130
|
+
console.error(`[media] Failed to resolve ${label}, skipping:`, err);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return parts;
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/media/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MediaStore abstraction for multimodal content storage.
|
|
3
|
+
*
|
|
4
|
+
* Provides pluggable storage backends (local filesystem, S3) for
|
|
5
|
+
* binary media referenced by resource_link content items.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Storage backend for media blobs.
|
|
9
|
+
*/
|
|
10
|
+
export interface MediaStore {
|
|
11
|
+
/** Upload data and return a URI for later retrieval. */
|
|
12
|
+
upload(data: Buffer, filename: string, mimeType: string): Promise<string>;
|
|
13
|
+
/** Fetch previously stored data by URI. */
|
|
14
|
+
fetch(uri: string): Promise<{
|
|
15
|
+
data: Buffer;
|
|
16
|
+
mimeType: string;
|
|
17
|
+
}>;
|
|
18
|
+
/** Check whether a URI exists in the store. */
|
|
19
|
+
exists(uri: string): Promise<boolean>;
|
|
20
|
+
}
|
|
21
|
+
/** Guess MIME type from a filename extension. Falls back to application/octet-stream. */
|
|
22
|
+
export declare function guessMimeType(filename: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Local filesystem media store.
|
|
25
|
+
*
|
|
26
|
+
* Stores files under a configurable base path and returns `file://` URIs.
|
|
27
|
+
*/
|
|
28
|
+
export declare class LocalMediaStore implements MediaStore {
|
|
29
|
+
private readonly basePath;
|
|
30
|
+
private readonly prefix;
|
|
31
|
+
constructor(basePath?: string, prefix?: string);
|
|
32
|
+
private validatePath;
|
|
33
|
+
upload(data: Buffer, filename: string, _mimeType: string): Promise<string>;
|
|
34
|
+
fetch(uri: string): Promise<{
|
|
35
|
+
data: Buffer;
|
|
36
|
+
mimeType: string;
|
|
37
|
+
}>;
|
|
38
|
+
exists(uri: string): Promise<boolean>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* S3-compatible media store.
|
|
42
|
+
*
|
|
43
|
+
* Uses lazy imports for @aws-sdk/client-s3 so it is only required
|
|
44
|
+
* when storage is configured as "s3".
|
|
45
|
+
*/
|
|
46
|
+
export declare class S3MediaStore implements MediaStore {
|
|
47
|
+
private readonly bucket;
|
|
48
|
+
private readonly endpoint;
|
|
49
|
+
private readonly prefix;
|
|
50
|
+
private _client;
|
|
51
|
+
constructor(bucket?: string, endpoint?: string, prefix?: string);
|
|
52
|
+
private getClient;
|
|
53
|
+
private loadS3Module;
|
|
54
|
+
upload(data: Buffer, filename: string, mimeType: string): Promise<string>;
|
|
55
|
+
fetch(uri: string): Promise<{
|
|
56
|
+
data: Buffer;
|
|
57
|
+
mimeType: string;
|
|
58
|
+
}>;
|
|
59
|
+
exists(uri: string): Promise<boolean>;
|
|
60
|
+
private parseUri;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get (or create) the singleton MediaStore based on configuration.
|
|
64
|
+
*
|
|
65
|
+
* Reads `media_storage` config key (env: `MCP_MESH_MEDIA_STORAGE`).
|
|
66
|
+
* - `"local"` (default) -> LocalMediaStore
|
|
67
|
+
* - `"s3"` -> S3MediaStore
|
|
68
|
+
*/
|
|
69
|
+
export declare function getMediaStore(): MediaStore;
|
|
70
|
+
/**
|
|
71
|
+
* Reset the singleton (mainly for testing).
|
|
72
|
+
* @internal
|
|
73
|
+
*/
|
|
74
|
+
export declare function _resetMediaStore(): void;
|
|
75
|
+
//# sourceMappingURL=media-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-store.d.ts","sourceRoot":"","sources":["../../src/media/media-store.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,wDAAwD;IACxD,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE1E,2CAA2C;IAC3C,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAEhE,+CAA+C;IAC/C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACvC;AAuBD,yFAAyF;AACzF,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGtD;AAED;;;;GAIG;AACH,qBAAa,eAAgB,YAAW,UAAU;IAChD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;YAMhC,YAAY;IAQpB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAc1E,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAW/D,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAY5C;AAED;;;;;GAKG;AACH,qBAAa,YAAa,YAAW,UAAU;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAIhC,OAAO,CAAC,OAAO,CAAa;gBAEhB,MAAM,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;YAQjD,SAAS;YAmBT,YAAY;IAKpB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiBzE,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAkB/D,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAqB3C,OAAO,CAAC,QAAQ;CAejB;AAQD;;;;;;GAMG;AACH,wBAAgB,aAAa,IAAI,UAAU,CAe1C;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MediaStore abstraction for multimodal content storage.
|
|
3
|
+
*
|
|
4
|
+
* Provides pluggable storage backends (local filesystem, S3) for
|
|
5
|
+
* binary media referenced by resource_link content items.
|
|
6
|
+
*/
|
|
7
|
+
import { resolveMediaConfig } from "../config.js";
|
|
8
|
+
/** Simple extension-to-MIME mapping for common types. */
|
|
9
|
+
const MIME_MAP = {
|
|
10
|
+
".png": "image/png",
|
|
11
|
+
".jpg": "image/jpeg",
|
|
12
|
+
".jpeg": "image/jpeg",
|
|
13
|
+
".gif": "image/gif",
|
|
14
|
+
".webp": "image/webp",
|
|
15
|
+
".svg": "image/svg+xml",
|
|
16
|
+
".mp3": "audio/mpeg",
|
|
17
|
+
".wav": "audio/wav",
|
|
18
|
+
".mp4": "video/mp4",
|
|
19
|
+
".webm": "video/webm",
|
|
20
|
+
".pdf": "application/pdf",
|
|
21
|
+
".json": "application/json",
|
|
22
|
+
".txt": "text/plain",
|
|
23
|
+
".html": "text/html",
|
|
24
|
+
".css": "text/css",
|
|
25
|
+
".csv": "text/csv",
|
|
26
|
+
".md": "text/markdown",
|
|
27
|
+
};
|
|
28
|
+
/** Guess MIME type from a filename extension. Falls back to application/octet-stream. */
|
|
29
|
+
export function guessMimeType(filename) {
|
|
30
|
+
const ext = filename.slice(filename.lastIndexOf(".")).toLowerCase();
|
|
31
|
+
return MIME_MAP[ext] ?? "application/octet-stream";
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Local filesystem media store.
|
|
35
|
+
*
|
|
36
|
+
* Stores files under a configurable base path and returns `file://` URIs.
|
|
37
|
+
*/
|
|
38
|
+
export class LocalMediaStore {
|
|
39
|
+
basePath;
|
|
40
|
+
prefix;
|
|
41
|
+
constructor(basePath, prefix) {
|
|
42
|
+
const cfg = resolveMediaConfig();
|
|
43
|
+
this.basePath = basePath ?? cfg.storagePath;
|
|
44
|
+
this.prefix = prefix ?? cfg.storagePrefix;
|
|
45
|
+
}
|
|
46
|
+
async validatePath(filePath) {
|
|
47
|
+
const { resolve } = await import("node:path");
|
|
48
|
+
const resolved = resolve(filePath);
|
|
49
|
+
if (!resolved.startsWith(resolve(this.basePath))) {
|
|
50
|
+
throw new Error(`Invalid filename (path traversal): ${filePath}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async upload(data, filename, _mimeType) {
|
|
54
|
+
const { mkdir, writeFile } = await import("node:fs/promises");
|
|
55
|
+
const { join } = await import("node:path");
|
|
56
|
+
const dir = join(this.basePath, this.prefix);
|
|
57
|
+
await mkdir(dir, { recursive: true });
|
|
58
|
+
const filePath = join(dir, filename);
|
|
59
|
+
await this.validatePath(filePath);
|
|
60
|
+
await writeFile(filePath, data);
|
|
61
|
+
return `file://${filePath}`;
|
|
62
|
+
}
|
|
63
|
+
async fetch(uri) {
|
|
64
|
+
const { readFile } = await import("node:fs/promises");
|
|
65
|
+
const filePath = uri.startsWith("file://") ? uri.slice(7) : uri;
|
|
66
|
+
await this.validatePath(filePath);
|
|
67
|
+
const data = await readFile(filePath);
|
|
68
|
+
const mimeType = guessMimeType(filePath);
|
|
69
|
+
return { data: Buffer.from(data), mimeType };
|
|
70
|
+
}
|
|
71
|
+
async exists(uri) {
|
|
72
|
+
const { access } = await import("node:fs/promises");
|
|
73
|
+
const filePath = uri.startsWith("file://") ? uri.slice(7) : uri;
|
|
74
|
+
try {
|
|
75
|
+
await this.validatePath(filePath);
|
|
76
|
+
await access(filePath);
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* S3-compatible media store.
|
|
86
|
+
*
|
|
87
|
+
* Uses lazy imports for @aws-sdk/client-s3 so it is only required
|
|
88
|
+
* when storage is configured as "s3".
|
|
89
|
+
*/
|
|
90
|
+
export class S3MediaStore {
|
|
91
|
+
bucket;
|
|
92
|
+
endpoint;
|
|
93
|
+
prefix;
|
|
94
|
+
// Lazily initialised S3 client
|
|
95
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
96
|
+
_client = null;
|
|
97
|
+
constructor(bucket, endpoint, prefix) {
|
|
98
|
+
const cfg = resolveMediaConfig();
|
|
99
|
+
this.bucket = bucket ?? cfg.storageBucket ?? "mcp-mesh-media";
|
|
100
|
+
this.endpoint = endpoint ?? cfg.storageEndpoint;
|
|
101
|
+
this.prefix = prefix ?? cfg.storagePrefix;
|
|
102
|
+
}
|
|
103
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
104
|
+
async getClient() {
|
|
105
|
+
if (this._client)
|
|
106
|
+
return this._client;
|
|
107
|
+
// Lazy import - @aws-sdk/client-s3 is NOT a required dependency.
|
|
108
|
+
// Uses variable to prevent TypeScript from resolving the module at compile time.
|
|
109
|
+
const s3ModuleName = "@aws-sdk/client-s3";
|
|
110
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
111
|
+
const s3Module = await import(/* webpackIgnore: true */ s3ModuleName);
|
|
112
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
113
|
+
const opts = {};
|
|
114
|
+
if (this.endpoint) {
|
|
115
|
+
opts.endpoint = this.endpoint;
|
|
116
|
+
opts.forcePathStyle = true;
|
|
117
|
+
}
|
|
118
|
+
this._client = new s3Module.S3Client(opts);
|
|
119
|
+
return this._client;
|
|
120
|
+
}
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
122
|
+
async loadS3Module() {
|
|
123
|
+
const name = "@aws-sdk/client-s3";
|
|
124
|
+
return import(/* webpackIgnore: true */ name);
|
|
125
|
+
}
|
|
126
|
+
async upload(data, filename, mimeType) {
|
|
127
|
+
const s3Module = await this.loadS3Module();
|
|
128
|
+
const client = await this.getClient();
|
|
129
|
+
const key = `${this.prefix}${filename}`;
|
|
130
|
+
await client.send(new s3Module.PutObjectCommand({
|
|
131
|
+
Bucket: this.bucket,
|
|
132
|
+
Key: key,
|
|
133
|
+
Body: data,
|
|
134
|
+
ContentType: mimeType,
|
|
135
|
+
}));
|
|
136
|
+
return `s3://${this.bucket}/${key}`;
|
|
137
|
+
}
|
|
138
|
+
async fetch(uri) {
|
|
139
|
+
const s3Module = await this.loadS3Module();
|
|
140
|
+
const client = await this.getClient();
|
|
141
|
+
const { bucket, key } = this.parseUri(uri);
|
|
142
|
+
const response = await client.send(new s3Module.GetObjectCommand({ Bucket: bucket, Key: key }));
|
|
143
|
+
const body = await response.Body?.transformToByteArray();
|
|
144
|
+
if (!body)
|
|
145
|
+
throw new Error(`Empty body for ${uri}`);
|
|
146
|
+
return {
|
|
147
|
+
data: Buffer.from(body),
|
|
148
|
+
mimeType: response.ContentType ?? guessMimeType(key),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
async exists(uri) {
|
|
152
|
+
const s3Module = await this.loadS3Module();
|
|
153
|
+
const client = await this.getClient();
|
|
154
|
+
const { bucket, key } = this.parseUri(uri);
|
|
155
|
+
try {
|
|
156
|
+
await client.send(new s3Module.HeadObjectCommand({ Bucket: bucket, Key: key }));
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
// Only treat "not found" as false; re-throw real errors
|
|
161
|
+
if (err instanceof Error &&
|
|
162
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
163
|
+
(err.name === "NotFound" || err.$metadata?.httpStatusCode === 404)) {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
throw err;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
parseUri(uri) {
|
|
170
|
+
if (uri.startsWith("s3://")) {
|
|
171
|
+
const withoutScheme = uri.slice(5);
|
|
172
|
+
const slashIdx = withoutScheme.indexOf("/");
|
|
173
|
+
if (slashIdx === -1) {
|
|
174
|
+
return { bucket: withoutScheme, key: "" };
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
bucket: withoutScheme.slice(0, slashIdx),
|
|
178
|
+
key: withoutScheme.slice(slashIdx + 1),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
// Assume key-only, use configured bucket
|
|
182
|
+
return { bucket: this.bucket, key: uri };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
// Singleton factory
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
let _instance = null;
|
|
189
|
+
/**
|
|
190
|
+
* Get (or create) the singleton MediaStore based on configuration.
|
|
191
|
+
*
|
|
192
|
+
* Reads `media_storage` config key (env: `MCP_MESH_MEDIA_STORAGE`).
|
|
193
|
+
* - `"local"` (default) -> LocalMediaStore
|
|
194
|
+
* - `"s3"` -> S3MediaStore
|
|
195
|
+
*/
|
|
196
|
+
export function getMediaStore() {
|
|
197
|
+
if (_instance)
|
|
198
|
+
return _instance;
|
|
199
|
+
const cfg = resolveMediaConfig();
|
|
200
|
+
switch (cfg.storage) {
|
|
201
|
+
case "s3":
|
|
202
|
+
_instance = new S3MediaStore();
|
|
203
|
+
break;
|
|
204
|
+
default:
|
|
205
|
+
_instance = new LocalMediaStore();
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
return _instance;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Reset the singleton (mainly for testing).
|
|
212
|
+
* @internal
|
|
213
|
+
*/
|
|
214
|
+
export function _resetMediaStore() {
|
|
215
|
+
_instance = null;
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=media-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-store.js","sourceRoot":"","sources":["../../src/media/media-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve resource_link URIs to provider-native multimodal content.
|
|
3
|
+
*
|
|
4
|
+
* When an LLM provider calls a tool that returns a resource_link (e.g., an image URI),
|
|
5
|
+
* the LLM currently sees just a JSON string. This module resolves the URI to base64
|
|
6
|
+
* media in provider-native format so the LLM can actually "see" the image.
|
|
7
|
+
*
|
|
8
|
+
* Based on Python's resolver:
|
|
9
|
+
* src/runtime/python/_mcp_mesh/media/resolver.py
|
|
10
|
+
*/
|
|
11
|
+
export interface ResolvedContent {
|
|
12
|
+
type: string;
|
|
13
|
+
[key: string]: unknown;
|
|
14
|
+
}
|
|
15
|
+
export declare function formatForOpenai(b64: string, mimeType: string): ResolvedContent;
|
|
16
|
+
/**
|
|
17
|
+
* Scan tool result for resource_link items and resolve to provider-native format.
|
|
18
|
+
*
|
|
19
|
+
* Returns a list of content parts for the LLM message.
|
|
20
|
+
* - For image resource_links: fetches bytes, base64 encodes, formats per vendor
|
|
21
|
+
* - For non-image or non-resource_link: wraps in text content
|
|
22
|
+
*
|
|
23
|
+
* @param toolResult - The raw tool result (could be object, string, etc.)
|
|
24
|
+
* @param vendor - One of "anthropic", "openai", "gemini", "google"
|
|
25
|
+
* @returns List of content dicts suitable for provider-specific message content arrays
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveResourceLinks(toolResult: unknown, vendor: string): Promise<ResolvedContent[]>;
|
|
28
|
+
/** Vendors that do NOT support images in tool/function result messages. */
|
|
29
|
+
export declare const TOOL_IMAGE_UNSUPPORTED_VENDORS: Set<string>;
|
|
30
|
+
/**
|
|
31
|
+
* Resolve resource_links for inclusion in a tool result message.
|
|
32
|
+
*
|
|
33
|
+
* For vendors that support images in tool messages (Anthropic/Claude),
|
|
34
|
+
* returns the full multimodal content (text + image parts).
|
|
35
|
+
*
|
|
36
|
+
* For vendors that do NOT support images in tool messages (OpenAI, Gemini),
|
|
37
|
+
* returns text-only content with descriptive placeholders. The actual image
|
|
38
|
+
* should be injected via a separate user message using
|
|
39
|
+
* `resolveMediaAsUserMessage()`.
|
|
40
|
+
*
|
|
41
|
+
* @param toolResult - Raw tool result
|
|
42
|
+
* @param vendor - e.g. "anthropic", "openai", "gemini"
|
|
43
|
+
* @returns Content parts suitable for a tool result message
|
|
44
|
+
*/
|
|
45
|
+
export declare function resolveResourceLinksForToolMessage(toolResult: unknown, vendor: string): Promise<ResolvedContent[]>;
|
|
46
|
+
/**
|
|
47
|
+
* Return a user message containing resolved images from a tool result.
|
|
48
|
+
*
|
|
49
|
+
* For vendors that do NOT support images in tool messages (OpenAI, Gemini),
|
|
50
|
+
* resolves resource_link items and packages the image parts into a
|
|
51
|
+
* `role: "user"` message that can be appended after the tool result message.
|
|
52
|
+
*
|
|
53
|
+
* For vendors that support images in tool messages (Anthropic), returns null.
|
|
54
|
+
*
|
|
55
|
+
* @param toolResult - Raw tool result
|
|
56
|
+
* @param vendor - e.g. "anthropic", "openai", "gemini"
|
|
57
|
+
* @returns A user message object with image content, or null
|
|
58
|
+
*/
|
|
59
|
+
export declare function resolveMediaAsUserMessage(toolResult: unknown, vendor: string): Promise<Record<string, unknown> | null>;
|
|
60
|
+
/**
|
|
61
|
+
* Quick check whether a tool result contains any resource_link items.
|
|
62
|
+
*
|
|
63
|
+
* This is a lightweight synchronous check used to decide whether the
|
|
64
|
+
* more expensive async resolution is needed.
|
|
65
|
+
*/
|
|
66
|
+
export declare function hasResourceLink(toolResult: unknown): boolean;
|
|
67
|
+
//# sourceMappingURL=resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/media/resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAsBH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAaD,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,eAAe,CAQ9E;AAwID;;;;;;;;;;GAUG;AACH,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,OAAO,EACnB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,eAAe,EAAE,CAAC,CA+D5B;AAED,2EAA2E;AAC3E,eAAO,MAAM,8BAA8B,aAA0C,CAAC;AAEtF;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,kCAAkC,CACtD,UAAU,EAAE,OAAO,EACnB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,eAAe,EAAE,CAAC,CAY5B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,OAAO,EACnB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAoBzC;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,OAAO,GAAG,OAAO,CAoB5D"}
|