open-mcp-app 0.1.0 → 0.1.2
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/core/index.d.ts +1171 -0
- package/dist/core/index.js +10910 -0
- package/dist/core/index.js.map +1 -0
- package/dist/react/index.d.ts +196 -0
- package/dist/react/index.js +10857 -0
- package/dist/react/index.js.map +1 -0
- package/dist/server/index.d.ts +863 -0
- package/dist/server/index.js +15075 -0
- package/dist/server/index.js.map +1 -0
- package/dist/vite/index.d.ts +69 -0
- package/dist/vite/index.js +362 -0
- package/dist/vite/index.js.map +1 -0
- package/package.json +70 -16
- package/LICENSE +0 -22
- package/README.md +0 -36
- package/index.js +0 -363
package/index.js
DELETED
|
@@ -1,363 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Ensures a value is a non-empty string so that the SDK fails fast with a
|
|
5
|
-
* consistent error message. This keeps downstream network errors from masking
|
|
6
|
-
* configuration mistakes and makes SDK usage easier to debug.
|
|
7
|
-
*
|
|
8
|
-
* @param {Object} args
|
|
9
|
-
* @param {unknown} args.value
|
|
10
|
-
* @param {string} args.name
|
|
11
|
-
* @returns {string}
|
|
12
|
-
*/
|
|
13
|
-
const assertNonEmptyString = ({ value, name } = {}) => {
|
|
14
|
-
if (typeof value !== 'string' || value.trim().length === 0) {
|
|
15
|
-
throw new McpAppsSdkError({
|
|
16
|
-
message: `${name} must be a non-empty string.`,
|
|
17
|
-
code: 'MCP_APPS_INVALID_ARGUMENT',
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return value;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Normalizes a base URL by removing trailing slashes so that URL construction is
|
|
26
|
-
* predictable. This avoids subtle double-slash URLs when callers pass a base
|
|
27
|
-
* URL like "https://api.example.com/".
|
|
28
|
-
*
|
|
29
|
-
* @param {Object} args
|
|
30
|
-
* @param {string} args.baseUrl
|
|
31
|
-
* @returns {string}
|
|
32
|
-
*/
|
|
33
|
-
const normalizeBaseUrl = ({ baseUrl } = {}) => {
|
|
34
|
-
const url = assertNonEmptyString({ value: baseUrl, name: 'baseUrl' });
|
|
35
|
-
return url.replace(/\/+$/, '');
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Builds a request URL from a base URL, a path, and an optional query object.
|
|
40
|
-
* Centralizing URL construction ensures consistent encoding rules across the
|
|
41
|
-
* SDK and reduces the chance of introducing subtle request bugs.
|
|
42
|
-
*
|
|
43
|
-
* @param {Object} args
|
|
44
|
-
* @param {string} args.baseUrl
|
|
45
|
-
* @param {string} args.path
|
|
46
|
-
* @param {Object<string, string | number | boolean | null | undefined>} [args.query]
|
|
47
|
-
* @returns {string}
|
|
48
|
-
*/
|
|
49
|
-
const buildUrl = ({ baseUrl, path, query } = {}) => {
|
|
50
|
-
const normalizedBaseUrl = normalizeBaseUrl({ baseUrl });
|
|
51
|
-
const normalizedPath = assertNonEmptyString({ value: path, name: 'path' }).startsWith('/')
|
|
52
|
-
? path
|
|
53
|
-
: `/${path}`;
|
|
54
|
-
|
|
55
|
-
const url = new URL(`${normalizedBaseUrl}${normalizedPath}`);
|
|
56
|
-
|
|
57
|
-
if (query && typeof query === 'object') {
|
|
58
|
-
for (const [key, value] of Object.entries(query)) {
|
|
59
|
-
if (value === undefined || value === null) continue;
|
|
60
|
-
url.searchParams.set(key, String(value));
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return url.toString();
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* A base error type for the SDK so that callers can reliably distinguish
|
|
69
|
-
* expected SDK failures from other runtime errors.
|
|
70
|
-
*/
|
|
71
|
-
class McpAppsSdkError extends Error {
|
|
72
|
-
/**
|
|
73
|
-
* Creates a new SDK error instance.
|
|
74
|
-
*
|
|
75
|
-
* @param {Object} args
|
|
76
|
-
* @param {string} args.message
|
|
77
|
-
* @param {string} [args.code]
|
|
78
|
-
* @param {unknown} [args.cause]
|
|
79
|
-
*/
|
|
80
|
-
constructor({ message, code, cause } = {}) {
|
|
81
|
-
super(message);
|
|
82
|
-
this.name = 'McpAppsSdkError';
|
|
83
|
-
this.code = code || 'MCP_APPS_SDK_ERROR';
|
|
84
|
-
|
|
85
|
-
if (cause !== undefined) {
|
|
86
|
-
this.cause = cause;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* An error thrown when an HTTP request completes but returns an unsuccessful
|
|
93
|
-
* status code. Surfacing the URL, method, status, and response body makes it
|
|
94
|
-
* straightforward to debug API failures without requiring extra logging.
|
|
95
|
-
*/
|
|
96
|
-
class McpAppsRequestError extends McpAppsSdkError {
|
|
97
|
-
/**
|
|
98
|
-
* Creates a request error for a failed HTTP response.
|
|
99
|
-
*
|
|
100
|
-
* @param {Object} args
|
|
101
|
-
* @param {string} args.message
|
|
102
|
-
* @param {string} args.method
|
|
103
|
-
* @param {string} args.url
|
|
104
|
-
* @param {number} args.status
|
|
105
|
-
* @param {unknown} [args.body]
|
|
106
|
-
*/
|
|
107
|
-
constructor({ message, method, url, status, body } = {}) {
|
|
108
|
-
super({ message, code: 'MCP_APPS_REQUEST_FAILED' });
|
|
109
|
-
this.name = 'McpAppsRequestError';
|
|
110
|
-
this.method = method;
|
|
111
|
-
this.url = url;
|
|
112
|
-
this.status = status;
|
|
113
|
-
this.body = body;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Creates a new `McpAppsClient` instance.
|
|
119
|
-
*
|
|
120
|
-
* This factory exists so consumers can create clients without `new`, which is
|
|
121
|
-
* a common ergonomic pattern for SDKs and helps keep usage consistent across
|
|
122
|
-
* codebases.
|
|
123
|
-
*
|
|
124
|
-
* @param {Object} args
|
|
125
|
-
* @param {string} args.baseUrl
|
|
126
|
-
* @param {string} [args.apiKey]
|
|
127
|
-
* @param {(input: string, init?: Object) => Promise<Response>} [args.fetchFn]
|
|
128
|
-
* @param {string} [args.userAgent]
|
|
129
|
-
* @returns {McpAppsClient}
|
|
130
|
-
*/
|
|
131
|
-
const createMcpAppsClient = ({ baseUrl, apiKey, fetchFn, userAgent } = {}) =>
|
|
132
|
-
new McpAppsClient({ baseUrl, apiKey, fetchFn, userAgent });
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* A small, opinionated client that demonstrates the shape of an open MCP Apps SDK.
|
|
136
|
-
*
|
|
137
|
-
* The class stays intentionally lightweight: it provides a generic `request`
|
|
138
|
-
* primitive plus a few convenience methods that map to typical discovery and
|
|
139
|
-
* invocation workflows. The goal is to provide stable call signatures and
|
|
140
|
-
* error shapes even while the backing API evolves.
|
|
141
|
-
*/
|
|
142
|
-
class McpAppsClient {
|
|
143
|
-
/**
|
|
144
|
-
* Creates a new client instance.
|
|
145
|
-
*
|
|
146
|
-
* @param {Object} args
|
|
147
|
-
* @param {string} args.baseUrl
|
|
148
|
-
* @param {string} [args.apiKey]
|
|
149
|
-
* @param {(input: string, init?: Object) => Promise<Response>} [args.fetchFn]
|
|
150
|
-
* @param {string} [args.userAgent]
|
|
151
|
-
*/
|
|
152
|
-
constructor({ baseUrl, apiKey, fetchFn, userAgent } = {}) {
|
|
153
|
-
this._baseUrl = normalizeBaseUrl({ baseUrl });
|
|
154
|
-
this._apiKey = apiKey;
|
|
155
|
-
this._fetchFn = fetchFn;
|
|
156
|
-
this._userAgent = userAgent;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Updates client configuration at runtime.
|
|
161
|
-
*
|
|
162
|
-
* This exists so applications can construct a client early and later inject
|
|
163
|
-
* credentials or a custom fetch implementation without needing to pass the
|
|
164
|
-
* client instance through additional layers.
|
|
165
|
-
*
|
|
166
|
-
* @param {Object} args
|
|
167
|
-
* @param {string} [args.baseUrl]
|
|
168
|
-
* @param {string} [args.apiKey]
|
|
169
|
-
* @param {(input: string, init?: Object) => Promise<Response>} [args.fetchFn]
|
|
170
|
-
* @param {string} [args.userAgent]
|
|
171
|
-
* @returns {void}
|
|
172
|
-
*/
|
|
173
|
-
configure = ({ baseUrl, apiKey, fetchFn, userAgent } = {}) => {
|
|
174
|
-
if (baseUrl !== undefined) this._baseUrl = normalizeBaseUrl({ baseUrl });
|
|
175
|
-
if (apiKey !== undefined) this._apiKey = apiKey;
|
|
176
|
-
if (fetchFn !== undefined) this._fetchFn = fetchFn;
|
|
177
|
-
if (userAgent !== undefined) this._userAgent = userAgent;
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Issues an HTTP request and returns the parsed response body.
|
|
182
|
-
*
|
|
183
|
-
* The method is designed to be the lowest-level primitive in this SDK:
|
|
184
|
-
* higher-level helpers call into `request` so that retry, logging, custom
|
|
185
|
-
* headers, and transport behaviors can be added in one place.
|
|
186
|
-
*
|
|
187
|
-
* @param {Object} args
|
|
188
|
-
* @param {string} [args.method]
|
|
189
|
-
* @param {string} args.path
|
|
190
|
-
* @param {Object<string, string | number | boolean | null | undefined>} [args.query]
|
|
191
|
-
* @param {Object<string, string>} [args.headers]
|
|
192
|
-
* @param {unknown} [args.body]
|
|
193
|
-
* @param {AbortSignal} [args.signal]
|
|
194
|
-
* @returns {Promise<unknown>}
|
|
195
|
-
*/
|
|
196
|
-
request = async ({ method, path, query, headers, body, signal } = {}) => {
|
|
197
|
-
const url = buildUrl({ baseUrl: this._baseUrl, path, query });
|
|
198
|
-
|
|
199
|
-
const fetchFn = this._fetchFn || globalThis.fetch;
|
|
200
|
-
if (typeof fetchFn !== 'function') {
|
|
201
|
-
throw new McpAppsSdkError({
|
|
202
|
-
message:
|
|
203
|
-
'No fetch implementation is available. Provide fetchFn or use Node.js 18+ where fetch is built in.',
|
|
204
|
-
code: 'MCP_APPS_MISSING_FETCH',
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const normalizedMethod = (method || 'GET').toUpperCase();
|
|
209
|
-
|
|
210
|
-
const requestHeaders = {
|
|
211
|
-
...(headers || {}),
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
if (this._userAgent && !requestHeaders['user-agent'] && !requestHeaders['User-Agent']) {
|
|
215
|
-
requestHeaders['user-agent'] = this._userAgent;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if (this._apiKey && !requestHeaders.authorization && !requestHeaders.Authorization) {
|
|
219
|
-
requestHeaders.authorization = `Bearer ${this._apiKey}`;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/** @type {Object} */
|
|
223
|
-
const init = { method: normalizedMethod, headers: requestHeaders, signal };
|
|
224
|
-
|
|
225
|
-
if (body !== undefined) {
|
|
226
|
-
if (!requestHeaders['content-type'] && !requestHeaders['Content-Type']) {
|
|
227
|
-
requestHeaders['content-type'] = 'application/json';
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
init.body =
|
|
231
|
-
typeof body === 'string' || Buffer.isBuffer(body) ? body : JSON.stringify(body);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const response = await fetchFn(url, init);
|
|
235
|
-
|
|
236
|
-
const contentType = response.headers?.get?.('content-type') || '';
|
|
237
|
-
const isJson = contentType.toLowerCase().includes('application/json');
|
|
238
|
-
|
|
239
|
-
const responseBody = isJson
|
|
240
|
-
? await response.json().catch(() => undefined)
|
|
241
|
-
: await response.text();
|
|
242
|
-
|
|
243
|
-
if (!response.ok) {
|
|
244
|
-
throw new McpAppsRequestError({
|
|
245
|
-
message: `Request failed with status ${response.status}.`,
|
|
246
|
-
method: normalizedMethod,
|
|
247
|
-
url,
|
|
248
|
-
status: response.status,
|
|
249
|
-
body: responseBody,
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
return responseBody;
|
|
254
|
-
};
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Lists apps accessible to the current principal from the "open apps" surface.
|
|
258
|
-
*
|
|
259
|
-
* This method exists as a convenience helper for discovery flows. Routing
|
|
260
|
-
* through a dedicated open path keeps the intention clear for callers and
|
|
261
|
-
* allows backend policies to evolve independently from non-open app surfaces.
|
|
262
|
-
*
|
|
263
|
-
* @param {Object} args
|
|
264
|
-
* @param {AbortSignal} [args.signal]
|
|
265
|
-
* @returns {Promise<unknown>}
|
|
266
|
-
*/
|
|
267
|
-
listApps = async ({ signal } = {}) =>
|
|
268
|
-
this.request({ method: 'GET', path: '/open/apps', signal });
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Fetches a single open app by id.
|
|
272
|
-
*
|
|
273
|
-
* Explicitly requiring `appId` keeps the call signature self-documenting and
|
|
274
|
-
* avoids ambiguous positional parameters.
|
|
275
|
-
*
|
|
276
|
-
* @param {Object} args
|
|
277
|
-
* @param {string} args.appId
|
|
278
|
-
* @param {AbortSignal} [args.signal]
|
|
279
|
-
* @returns {Promise<unknown>}
|
|
280
|
-
*/
|
|
281
|
-
getApp = async ({ appId, signal } = {}) => {
|
|
282
|
-
const id = assertNonEmptyString({ value: appId, name: 'appId' });
|
|
283
|
-
return this.request({ method: 'GET', path: `/open/apps/${encodeURIComponent(id)}`, signal });
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Fetches an open app's manifest.
|
|
288
|
-
*
|
|
289
|
-
* Manifests are commonly used to describe capabilities (tools, input schema,
|
|
290
|
-
* output schema) without requiring execution. Exposing this as a separate
|
|
291
|
-
* call keeps discovery lightweight and avoids overloading `getApp`.
|
|
292
|
-
*
|
|
293
|
-
* @param {Object} args
|
|
294
|
-
* @param {string} args.appId
|
|
295
|
-
* @param {AbortSignal} [args.signal]
|
|
296
|
-
* @returns {Promise<unknown>}
|
|
297
|
-
*/
|
|
298
|
-
getAppManifest = async ({ appId, signal } = {}) => {
|
|
299
|
-
const id = assertNonEmptyString({ value: appId, name: 'appId' });
|
|
300
|
-
return this.request({
|
|
301
|
-
method: 'GET',
|
|
302
|
-
path: `/open/apps/${encodeURIComponent(id)}/manifest`,
|
|
303
|
-
signal,
|
|
304
|
-
});
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* Runs an open app with an input payload.
|
|
309
|
-
*
|
|
310
|
-
* Many MCP-style "apps" are orchestrations that accept structured input and
|
|
311
|
-
* return structured output. This method provides a stable entry point for
|
|
312
|
-
* that interaction while keeping transport details inside `request`.
|
|
313
|
-
*
|
|
314
|
-
* @param {Object} args
|
|
315
|
-
* @param {string} args.appId
|
|
316
|
-
* @param {unknown} [args.input]
|
|
317
|
-
* @param {AbortSignal} [args.signal]
|
|
318
|
-
* @returns {Promise<unknown>}
|
|
319
|
-
*/
|
|
320
|
-
runApp = async ({ appId, input, signal } = {}) => {
|
|
321
|
-
const id = assertNonEmptyString({ value: appId, name: 'appId' });
|
|
322
|
-
return this.request({
|
|
323
|
-
method: 'POST',
|
|
324
|
-
path: `/open/apps/${encodeURIComponent(id)}/run`,
|
|
325
|
-
body: { input },
|
|
326
|
-
signal,
|
|
327
|
-
});
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* Invokes a tool exposed by an open app.
|
|
332
|
-
*
|
|
333
|
-
* Tools are a core MCP primitive. This method intentionally accepts `args`
|
|
334
|
-
* as an object so that tool invocation stays forward-compatible when tool
|
|
335
|
-
* schemas evolve.
|
|
336
|
-
*
|
|
337
|
-
* @param {Object} args
|
|
338
|
-
* @param {string} args.appId
|
|
339
|
-
* @param {string} args.toolName
|
|
340
|
-
* @param {Object<string, unknown>} [args.args]
|
|
341
|
-
* @param {AbortSignal} [args.signal]
|
|
342
|
-
* @returns {Promise<unknown>}
|
|
343
|
-
*/
|
|
344
|
-
invokeTool = async ({ appId, toolName, args, signal } = {}) => {
|
|
345
|
-
const id = assertNonEmptyString({ value: appId, name: 'appId' });
|
|
346
|
-
const tool = assertNonEmptyString({ value: toolName, name: 'toolName' });
|
|
347
|
-
|
|
348
|
-
return this.request({
|
|
349
|
-
method: 'POST',
|
|
350
|
-
path: `/open/apps/${encodeURIComponent(id)}/tools/${encodeURIComponent(tool)}`,
|
|
351
|
-
body: { args: args || {} },
|
|
352
|
-
signal,
|
|
353
|
-
});
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
module.exports = {
|
|
358
|
-
McpAppsSdkError,
|
|
359
|
-
McpAppsRequestError,
|
|
360
|
-
McpAppsClient,
|
|
361
|
-
createMcpAppsClient,
|
|
362
|
-
};
|
|
363
|
-
|