mcp-use 1.9.1-canary.0 → 1.10.0-canary.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/README.md +9 -6
- package/dist/.tsbuildinfo +1 -1
- package/dist/{chunk-MUZ5WYE3.js → chunk-BFFS67JY.js} +1 -1
- package/dist/{chunk-D22NUQTL.js → chunk-HRWL2M2I.js} +184 -0
- package/dist/{chunk-5URNFWCQ.js → chunk-LWVK6RXA.js} +8 -3
- package/dist/{chunk-KHTTBIRP.js → chunk-Q3PFK7Y4.js} +138 -1
- package/dist/{context-storage-TXQ4DVSS.js → context-storage-NA4MHWOZ.js} +3 -1
- package/dist/index.cjs +184 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/src/browser.cjs +184 -0
- package/dist/src/browser.js +1 -1
- package/dist/src/react/index.cjs +184 -0
- package/dist/src/react/index.js +2 -2
- package/dist/src/server/context-storage.d.ts +8 -1
- package/dist/src/server/context-storage.d.ts.map +1 -1
- package/dist/src/server/endpoints/mount-mcp.d.ts.map +1 -1
- package/dist/src/server/index.cjs +595 -63
- package/dist/src/server/index.d.ts +3 -3
- package/dist/src/server/index.d.ts.map +1 -1
- package/dist/src/server/index.js +459 -67
- package/dist/src/server/mcp-server.d.ts +49 -9
- package/dist/src/server/mcp-server.d.ts.map +1 -1
- package/dist/src/server/oauth/providers.d.ts +27 -9
- package/dist/src/server/oauth/providers.d.ts.map +1 -1
- package/dist/src/server/prompts/index.d.ts.map +1 -1
- package/dist/src/server/resources/index.d.ts +43 -23
- package/dist/src/server/resources/index.d.ts.map +1 -1
- package/dist/src/server/resources/subscriptions.d.ts +54 -0
- package/dist/src/server/resources/subscriptions.d.ts.map +1 -0
- package/dist/src/server/sessions/session-manager.d.ts +9 -1
- package/dist/src/server/sessions/session-manager.d.ts.map +1 -1
- package/dist/src/server/tools/tool-execution-helpers.d.ts +30 -17
- package/dist/src/server/tools/tool-execution-helpers.d.ts.map +1 -1
- package/dist/src/server/tools/tool-registration.d.ts.map +1 -1
- package/dist/src/server/types/common.d.ts +24 -8
- package/dist/src/server/types/common.d.ts.map +1 -1
- package/dist/src/server/types/index.d.ts +3 -3
- package/dist/src/server/types/index.d.ts.map +1 -1
- package/dist/src/server/types/prompt.d.ts +2 -1
- package/dist/src/server/types/prompt.d.ts.map +1 -1
- package/dist/src/server/types/resource.d.ts +53 -8
- package/dist/src/server/types/resource.d.ts.map +1 -1
- package/dist/src/server/types/tool-context.d.ts +131 -0
- package/dist/src/server/types/tool-context.d.ts.map +1 -1
- package/dist/src/server/types/tool.d.ts +1 -1
- package/dist/src/server/types/tool.d.ts.map +1 -1
- package/dist/src/server/utils/response-helpers.d.ts +48 -4
- package/dist/src/server/utils/response-helpers.d.ts.map +1 -1
- package/dist/src/server/widgets/index.d.ts +2 -2
- package/dist/src/server/widgets/ui-resource-registration.d.ts +2 -2
- package/dist/src/session.d.ts +337 -2
- package/dist/src/session.d.ts.map +1 -1
- package/dist/{tool-execution-helpers-IVUDHXMK.js → tool-execution-helpers-RRMGLAHR.js} +7 -1
- package/package.json +3 -3
package/dist/src/server/index.js
CHANGED
|
@@ -2,11 +2,12 @@ import {
|
|
|
2
2
|
getRequestContext,
|
|
3
3
|
hasRequestContext,
|
|
4
4
|
runWithContext
|
|
5
|
-
} from "../../chunk-
|
|
5
|
+
} from "../../chunk-LWVK6RXA.js";
|
|
6
6
|
import {
|
|
7
7
|
createEnhancedContext,
|
|
8
|
-
findSessionContext
|
|
9
|
-
|
|
8
|
+
findSessionContext,
|
|
9
|
+
isValidLogLevel
|
|
10
|
+
} from "../../chunk-Q3PFK7Y4.js";
|
|
10
11
|
import {
|
|
11
12
|
convertToolResultToResourceResult
|
|
12
13
|
} from "../../chunk-362PI25Z.js";
|
|
@@ -31,6 +32,8 @@ import {
|
|
|
31
32
|
McpServer as OfficialMcpServer,
|
|
32
33
|
ResourceTemplate as ResourceTemplate2
|
|
33
34
|
} from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
35
|
+
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
|
|
36
|
+
import { z as z2 } from "zod";
|
|
34
37
|
|
|
35
38
|
// src/server/utils/response-helpers.ts
|
|
36
39
|
function text(content) {
|
|
@@ -63,7 +66,109 @@ function image(data, mimeType = "image/png") {
|
|
|
63
66
|
};
|
|
64
67
|
}
|
|
65
68
|
__name(image, "image");
|
|
66
|
-
function
|
|
69
|
+
function getAudioMimeType(filename) {
|
|
70
|
+
const ext = filename.split(".").pop()?.toLowerCase();
|
|
71
|
+
switch (ext) {
|
|
72
|
+
case "wav":
|
|
73
|
+
return "audio/wav";
|
|
74
|
+
case "mp3":
|
|
75
|
+
return "audio/mpeg";
|
|
76
|
+
case "ogg":
|
|
77
|
+
return "audio/ogg";
|
|
78
|
+
case "m4a":
|
|
79
|
+
return "audio/mp4";
|
|
80
|
+
case "webm":
|
|
81
|
+
return "audio/webm";
|
|
82
|
+
case "flac":
|
|
83
|
+
return "audio/flac";
|
|
84
|
+
case "aac":
|
|
85
|
+
return "audio/aac";
|
|
86
|
+
default:
|
|
87
|
+
return "audio/wav";
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
__name(getAudioMimeType, "getAudioMimeType");
|
|
91
|
+
function arrayBufferToBase64(buffer) {
|
|
92
|
+
if (isDeno) {
|
|
93
|
+
const bytes = new Uint8Array(buffer);
|
|
94
|
+
let binary2 = "";
|
|
95
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
96
|
+
binary2 += String.fromCharCode(bytes[i]);
|
|
97
|
+
}
|
|
98
|
+
return btoa(binary2);
|
|
99
|
+
} else {
|
|
100
|
+
return Buffer.from(buffer).toString("base64");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
__name(arrayBufferToBase64, "arrayBufferToBase64");
|
|
104
|
+
function audio(dataOrPath, mimeType) {
|
|
105
|
+
const isFilePath = dataOrPath.includes("/") || dataOrPath.includes("\\") || dataOrPath.includes(".");
|
|
106
|
+
if (isFilePath && dataOrPath.length < 1e3) {
|
|
107
|
+
return (async () => {
|
|
108
|
+
const buffer = await fsHelpers.readFile(dataOrPath);
|
|
109
|
+
const base64Data = arrayBufferToBase64(buffer);
|
|
110
|
+
const inferredMimeType = mimeType || getAudioMimeType(dataOrPath);
|
|
111
|
+
return {
|
|
112
|
+
content: [
|
|
113
|
+
{
|
|
114
|
+
type: "audio",
|
|
115
|
+
data: base64Data,
|
|
116
|
+
mimeType: inferredMimeType
|
|
117
|
+
}
|
|
118
|
+
],
|
|
119
|
+
_meta: {
|
|
120
|
+
mimeType: inferredMimeType,
|
|
121
|
+
isAudio: true
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
})();
|
|
125
|
+
}
|
|
126
|
+
const finalMimeType = mimeType || "audio/wav";
|
|
127
|
+
return {
|
|
128
|
+
content: [
|
|
129
|
+
{
|
|
130
|
+
type: "audio",
|
|
131
|
+
data: dataOrPath,
|
|
132
|
+
mimeType: finalMimeType
|
|
133
|
+
}
|
|
134
|
+
],
|
|
135
|
+
_meta: {
|
|
136
|
+
mimeType: finalMimeType,
|
|
137
|
+
isAudio: true
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
__name(audio, "audio");
|
|
142
|
+
function resource(uri, mimeTypeOrContent, text2) {
|
|
143
|
+
if (typeof mimeTypeOrContent === "object" && mimeTypeOrContent !== null && "content" in mimeTypeOrContent) {
|
|
144
|
+
const contentResult = mimeTypeOrContent;
|
|
145
|
+
let extractedText;
|
|
146
|
+
let extractedMimeType;
|
|
147
|
+
if (contentResult._meta && typeof contentResult._meta === "object") {
|
|
148
|
+
const meta = contentResult._meta;
|
|
149
|
+
if (meta.mimeType && typeof meta.mimeType === "string") {
|
|
150
|
+
extractedMimeType = meta.mimeType;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (contentResult.content && contentResult.content.length > 0) {
|
|
154
|
+
const firstContent = contentResult.content[0];
|
|
155
|
+
if (firstContent.type === "text" && "text" in firstContent) {
|
|
156
|
+
extractedText = firstContent.text;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const resourceContent2 = {
|
|
160
|
+
type: "resource",
|
|
161
|
+
resource: {
|
|
162
|
+
uri,
|
|
163
|
+
...extractedMimeType && { mimeType: extractedMimeType },
|
|
164
|
+
...extractedText && { text: extractedText }
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
return {
|
|
168
|
+
content: [resourceContent2]
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
const mimeType = mimeTypeOrContent;
|
|
67
172
|
const resourceContent = {
|
|
68
173
|
type: "resource",
|
|
69
174
|
resource: {
|
|
@@ -338,7 +443,7 @@ function parseTemplateUri(template, uri) {
|
|
|
338
443
|
const params = {};
|
|
339
444
|
let regexPattern = template.replace(/[.*+?^$()[\]\\|]/g, "\\$&");
|
|
340
445
|
const paramNames = [];
|
|
341
|
-
regexPattern = regexPattern.replace(
|
|
446
|
+
regexPattern = regexPattern.replace(/\{([^}]+)\}/g, (_, paramName) => {
|
|
342
447
|
paramNames.push(paramName);
|
|
343
448
|
return "([^/]+)";
|
|
344
449
|
});
|
|
@@ -1762,7 +1867,7 @@ function toolRegistration(toolDefinition, callback) {
|
|
|
1762
1867
|
const initialRequestContext = getRequestContext();
|
|
1763
1868
|
const extraProgressToken = extra?._meta?.progressToken;
|
|
1764
1869
|
const extraSendNotification = extra?.sendNotification;
|
|
1765
|
-
const { requestContext, progressToken, sendNotification: sendNotification2 } = findSessionContext(
|
|
1870
|
+
const { requestContext, session, progressToken, sendNotification: sendNotification2 } = findSessionContext(
|
|
1766
1871
|
this.sessions,
|
|
1767
1872
|
initialRequestContext,
|
|
1768
1873
|
extraProgressToken,
|
|
@@ -1773,7 +1878,9 @@ function toolRegistration(toolDefinition, callback) {
|
|
|
1773
1878
|
this.createMessage.bind(this),
|
|
1774
1879
|
this.server.server.elicitInput.bind(this.server.server),
|
|
1775
1880
|
progressToken,
|
|
1776
|
-
sendNotification2
|
|
1881
|
+
sendNotification2,
|
|
1882
|
+
session?.logLevel,
|
|
1883
|
+
session?.clientCapabilities
|
|
1777
1884
|
);
|
|
1778
1885
|
const executeCallback = /* @__PURE__ */ __name(async () => {
|
|
1779
1886
|
if (actualCallback.length >= 2) {
|
|
@@ -1794,6 +1901,154 @@ __name(toolRegistration, "toolRegistration");
|
|
|
1794
1901
|
|
|
1795
1902
|
// src/server/resources/index.ts
|
|
1796
1903
|
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
1904
|
+
|
|
1905
|
+
// src/server/resources/subscriptions.ts
|
|
1906
|
+
import {
|
|
1907
|
+
SubscribeRequestSchema,
|
|
1908
|
+
UnsubscribeRequestSchema
|
|
1909
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
1910
|
+
var ResourceSubscriptionManager = class {
|
|
1911
|
+
static {
|
|
1912
|
+
__name(this, "ResourceSubscriptionManager");
|
|
1913
|
+
}
|
|
1914
|
+
/**
|
|
1915
|
+
* Tracks resource subscriptions per session
|
|
1916
|
+
* Map structure: uri -> Set<sessionId>
|
|
1917
|
+
*/
|
|
1918
|
+
subscriptions = /* @__PURE__ */ new Map();
|
|
1919
|
+
/**
|
|
1920
|
+
* Register subscription handlers with an MCP server instance
|
|
1921
|
+
*
|
|
1922
|
+
* @param server - The native MCP server instance
|
|
1923
|
+
* @param sessions - Map of active sessions
|
|
1924
|
+
*/
|
|
1925
|
+
registerHandlers(server, sessions) {
|
|
1926
|
+
server.server.setRequestHandler(
|
|
1927
|
+
SubscribeRequestSchema,
|
|
1928
|
+
async (request) => {
|
|
1929
|
+
const { uri } = request.params;
|
|
1930
|
+
const sessionId = this.getSessionIdFromContext(sessions, server);
|
|
1931
|
+
if (!sessionId) {
|
|
1932
|
+
console.warn(
|
|
1933
|
+
`[MCP] Could not determine session ID for resource subscription to ${uri}`
|
|
1934
|
+
);
|
|
1935
|
+
return {};
|
|
1936
|
+
}
|
|
1937
|
+
if (!this.subscriptions.has(uri)) {
|
|
1938
|
+
this.subscriptions.set(uri, /* @__PURE__ */ new Set());
|
|
1939
|
+
}
|
|
1940
|
+
this.subscriptions.get(uri).add(sessionId);
|
|
1941
|
+
console.log(
|
|
1942
|
+
`[MCP] Session ${sessionId} subscribed to resource: ${uri}`
|
|
1943
|
+
);
|
|
1944
|
+
return {};
|
|
1945
|
+
}
|
|
1946
|
+
);
|
|
1947
|
+
server.server.setRequestHandler(
|
|
1948
|
+
UnsubscribeRequestSchema,
|
|
1949
|
+
async (request) => {
|
|
1950
|
+
const { uri } = request.params;
|
|
1951
|
+
const sessionId = this.getSessionIdFromContext(sessions, server);
|
|
1952
|
+
if (!sessionId) {
|
|
1953
|
+
console.warn(
|
|
1954
|
+
`[MCP] Could not determine session ID for resource unsubscribe from ${uri}`
|
|
1955
|
+
);
|
|
1956
|
+
return {};
|
|
1957
|
+
}
|
|
1958
|
+
const subscribers = this.subscriptions.get(uri);
|
|
1959
|
+
if (subscribers) {
|
|
1960
|
+
subscribers.delete(sessionId);
|
|
1961
|
+
if (subscribers.size === 0) {
|
|
1962
|
+
this.subscriptions.delete(uri);
|
|
1963
|
+
}
|
|
1964
|
+
console.log(
|
|
1965
|
+
`[MCP] Session ${sessionId} unsubscribed from resource: ${uri}`
|
|
1966
|
+
);
|
|
1967
|
+
}
|
|
1968
|
+
return {};
|
|
1969
|
+
}
|
|
1970
|
+
);
|
|
1971
|
+
}
|
|
1972
|
+
/**
|
|
1973
|
+
* Get session ID from request context or sessions map
|
|
1974
|
+
*
|
|
1975
|
+
* @param sessions - Map of active sessions
|
|
1976
|
+
* @param server - The server instance to match against
|
|
1977
|
+
* @returns The session ID, or undefined if not found
|
|
1978
|
+
*/
|
|
1979
|
+
getSessionIdFromContext(sessions, server) {
|
|
1980
|
+
const requestContext = getRequestContext();
|
|
1981
|
+
let sessionId;
|
|
1982
|
+
if (requestContext) {
|
|
1983
|
+
sessionId = requestContext.req.header("mcp-session-id");
|
|
1984
|
+
}
|
|
1985
|
+
if (!sessionId) {
|
|
1986
|
+
for (const [sid, session] of sessions.entries()) {
|
|
1987
|
+
if (session.server === server) {
|
|
1988
|
+
sessionId = sid;
|
|
1989
|
+
break;
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
return sessionId;
|
|
1994
|
+
}
|
|
1995
|
+
/**
|
|
1996
|
+
* Notify subscribed clients that a resource has been updated
|
|
1997
|
+
*
|
|
1998
|
+
* This method sends a `notifications/resources/updated` notification to all
|
|
1999
|
+
* sessions that have subscribed to the specified resource URI.
|
|
2000
|
+
*
|
|
2001
|
+
* @param uri - The URI of the resource that changed
|
|
2002
|
+
* @param sessions - Map of active sessions
|
|
2003
|
+
* @returns Promise that resolves when all notifications have been sent
|
|
2004
|
+
*/
|
|
2005
|
+
async notifyResourceUpdated(uri, sessions) {
|
|
2006
|
+
const subscribers = this.subscriptions.get(uri);
|
|
2007
|
+
if (!subscribers || subscribers.size === 0) {
|
|
2008
|
+
return;
|
|
2009
|
+
}
|
|
2010
|
+
console.log(
|
|
2011
|
+
`[MCP] Notifying ${subscribers.size} subscriber(s) of resource update: ${uri}`
|
|
2012
|
+
);
|
|
2013
|
+
for (const sessionId of subscribers) {
|
|
2014
|
+
const session = sessions.get(sessionId);
|
|
2015
|
+
if (session?.server) {
|
|
2016
|
+
try {
|
|
2017
|
+
await session.server.server.sendResourceUpdated({ uri });
|
|
2018
|
+
console.log(
|
|
2019
|
+
`[MCP] Sent resource update notification to session ${sessionId}`
|
|
2020
|
+
);
|
|
2021
|
+
} catch (error2) {
|
|
2022
|
+
console.error(
|
|
2023
|
+
`[MCP] Failed to send resource update notification to session ${sessionId}:`,
|
|
2024
|
+
error2
|
|
2025
|
+
);
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
/**
|
|
2031
|
+
* Clean up resource subscriptions for a closed session
|
|
2032
|
+
*
|
|
2033
|
+
* This method is called automatically when a session is closed to remove
|
|
2034
|
+
* all resource subscriptions associated with that session.
|
|
2035
|
+
*
|
|
2036
|
+
* @param sessionId - The session ID to clean up
|
|
2037
|
+
*/
|
|
2038
|
+
cleanupSession(sessionId) {
|
|
2039
|
+
for (const [uri, subscribers] of this.subscriptions) {
|
|
2040
|
+
subscribers.delete(sessionId);
|
|
2041
|
+
if (subscribers.size === 0) {
|
|
2042
|
+
this.subscriptions.delete(uri);
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
console.log(
|
|
2046
|
+
`[MCP] Cleaned up resource subscriptions for session ${sessionId}`
|
|
2047
|
+
);
|
|
2048
|
+
}
|
|
2049
|
+
};
|
|
2050
|
+
|
|
2051
|
+
// src/server/resources/index.ts
|
|
1797
2052
|
function registerResource(resourceDefinition, callback) {
|
|
1798
2053
|
const actualCallback = callback || resourceDefinition.readCallback;
|
|
1799
2054
|
if (!actualCallback) {
|
|
@@ -1803,8 +2058,8 @@ function registerResource(resourceDefinition, callback) {
|
|
|
1803
2058
|
}
|
|
1804
2059
|
const explicitMimeType = resourceDefinition.mimeType;
|
|
1805
2060
|
const wrappedCallback = /* @__PURE__ */ __name(async () => {
|
|
1806
|
-
const { getRequestContext: getRequestContext2, runWithContext: runWithContext2 } = await import("../../context-storage-
|
|
1807
|
-
const { findSessionContext: findSessionContext2 } = await import("../../tool-execution-helpers-
|
|
2061
|
+
const { getRequestContext: getRequestContext2, runWithContext: runWithContext2 } = await import("../../context-storage-NA4MHWOZ.js");
|
|
2062
|
+
const { findSessionContext: findSessionContext2 } = await import("../../tool-execution-helpers-RRMGLAHR.js");
|
|
1808
2063
|
const initialRequestContext = getRequestContext2();
|
|
1809
2064
|
const sessions = this.sessions || /* @__PURE__ */ new Map();
|
|
1810
2065
|
const { requestContext } = findSessionContext2(
|
|
@@ -1852,24 +2107,25 @@ function registerResourceTemplate(resourceTemplateDefinition, callback) {
|
|
|
1852
2107
|
`Resource template '${resourceTemplateDefinition.name}' must have either a readCallback property or a callback parameter`
|
|
1853
2108
|
);
|
|
1854
2109
|
}
|
|
1855
|
-
const
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
2110
|
+
const isFlatStructure = "uriTemplate" in resourceTemplateDefinition;
|
|
2111
|
+
const uriTemplate = isFlatStructure ? resourceTemplateDefinition.uriTemplate : resourceTemplateDefinition.resourceTemplate.uriTemplate;
|
|
2112
|
+
const mimeType = isFlatStructure ? resourceTemplateDefinition.mimeType : resourceTemplateDefinition.resourceTemplate.mimeType;
|
|
2113
|
+
const templateDescription = isFlatStructure ? void 0 : resourceTemplateDefinition.resourceTemplate.description;
|
|
2114
|
+
const template = new ResourceTemplate(uriTemplate, {
|
|
2115
|
+
list: void 0,
|
|
2116
|
+
// Optional: callback to list all matching resources
|
|
2117
|
+
complete: void 0
|
|
2118
|
+
// Optional: callback for auto-completion
|
|
2119
|
+
});
|
|
1864
2120
|
const metadata = {};
|
|
1865
2121
|
if (resourceTemplateDefinition.title) {
|
|
1866
2122
|
metadata.title = resourceTemplateDefinition.title;
|
|
1867
2123
|
}
|
|
1868
|
-
if (resourceTemplateDefinition.description ||
|
|
1869
|
-
metadata.description = resourceTemplateDefinition.description ||
|
|
2124
|
+
if (resourceTemplateDefinition.description || templateDescription) {
|
|
2125
|
+
metadata.description = resourceTemplateDefinition.description || templateDescription;
|
|
1870
2126
|
}
|
|
1871
|
-
if (
|
|
1872
|
-
metadata.mimeType =
|
|
2127
|
+
if (mimeType) {
|
|
2128
|
+
metadata.mimeType = mimeType;
|
|
1873
2129
|
}
|
|
1874
2130
|
if (resourceTemplateDefinition.annotations) {
|
|
1875
2131
|
metadata.annotations = resourceTemplateDefinition.annotations;
|
|
@@ -1879,12 +2135,9 @@ function registerResourceTemplate(resourceTemplateDefinition, callback) {
|
|
|
1879
2135
|
template,
|
|
1880
2136
|
metadata,
|
|
1881
2137
|
async (uri) => {
|
|
1882
|
-
const params = this.parseTemplateUri(
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
);
|
|
1886
|
-
const { getRequestContext: getRequestContext2, runWithContext: runWithContext2 } = await import("../../context-storage-TXQ4DVSS.js");
|
|
1887
|
-
const { findSessionContext: findSessionContext2 } = await import("../../tool-execution-helpers-IVUDHXMK.js");
|
|
2138
|
+
const params = this.parseTemplateUri(uriTemplate, uri.toString());
|
|
2139
|
+
const { getRequestContext: getRequestContext2, runWithContext: runWithContext2 } = await import("../../context-storage-NA4MHWOZ.js");
|
|
2140
|
+
const { findSessionContext: findSessionContext2 } = await import("../../tool-execution-helpers-RRMGLAHR.js");
|
|
1888
2141
|
const initialRequestContext = getRequestContext2();
|
|
1889
2142
|
const sessions = this.sessions || /* @__PURE__ */ new Map();
|
|
1890
2143
|
const { requestContext } = findSessionContext2(
|
|
@@ -1897,8 +2150,12 @@ function registerResourceTemplate(resourceTemplateDefinition, callback) {
|
|
|
1897
2150
|
const executeCallback = /* @__PURE__ */ __name(async () => {
|
|
1898
2151
|
if (actualCallback.length >= 3) {
|
|
1899
2152
|
return await actualCallback(uri, params, enhancedContext);
|
|
2153
|
+
} else if (actualCallback.length === 2) {
|
|
2154
|
+
return await actualCallback(uri, params);
|
|
2155
|
+
} else if (actualCallback.length === 1) {
|
|
2156
|
+
return await actualCallback(uri);
|
|
1900
2157
|
}
|
|
1901
|
-
return await actualCallback(
|
|
2158
|
+
return await actualCallback();
|
|
1902
2159
|
}, "executeCallback");
|
|
1903
2160
|
const result = requestContext ? await runWithContext2(requestContext, executeCallback) : await executeCallback();
|
|
1904
2161
|
if ("contents" in result && Array.isArray(result.contents)) {
|
|
@@ -1928,12 +2185,14 @@ function registerPrompt(promptDefinition, callback) {
|
|
|
1928
2185
|
argsSchema = this.convertZodSchemaToParams(
|
|
1929
2186
|
promptDefinition.schema
|
|
1930
2187
|
);
|
|
2188
|
+
} else if (promptDefinition.args && promptDefinition.args.length > 0) {
|
|
2189
|
+
argsSchema = this.createParamsSchema(promptDefinition.args);
|
|
1931
2190
|
} else {
|
|
1932
|
-
argsSchema =
|
|
2191
|
+
argsSchema = void 0;
|
|
1933
2192
|
}
|
|
1934
2193
|
const wrappedCallback = /* @__PURE__ */ __name(async (params, extra) => {
|
|
1935
|
-
const { getRequestContext: getRequestContext2, runWithContext: runWithContext2 } = await import("../../context-storage-
|
|
1936
|
-
const { findSessionContext: findSessionContext2 } = await import("../../tool-execution-helpers-
|
|
2194
|
+
const { getRequestContext: getRequestContext2, runWithContext: runWithContext2 } = await import("../../context-storage-NA4MHWOZ.js");
|
|
2195
|
+
const { findSessionContext: findSessionContext2 } = await import("../../tool-execution-helpers-RRMGLAHR.js");
|
|
1937
2196
|
const initialRequestContext = getRequestContext2();
|
|
1938
2197
|
const sessions = this.sessions || /* @__PURE__ */ new Map();
|
|
1939
2198
|
const { requestContext } = findSessionContext2(
|
|
@@ -2204,7 +2463,7 @@ async function sendNotificationToSession2(sessionId, method, params) {
|
|
|
2204
2463
|
__name(sendNotificationToSession2, "sendNotificationToSession");
|
|
2205
2464
|
|
|
2206
2465
|
// src/server/sessions/session-manager.ts
|
|
2207
|
-
function startIdleCleanup(sessions, idleTimeoutMs) {
|
|
2466
|
+
function startIdleCleanup(sessions, idleTimeoutMs, mcpServerInstance) {
|
|
2208
2467
|
if (idleTimeoutMs <= 0) {
|
|
2209
2468
|
return void 0;
|
|
2210
2469
|
}
|
|
@@ -2222,6 +2481,7 @@ function startIdleCleanup(sessions, idleTimeoutMs) {
|
|
|
2222
2481
|
);
|
|
2223
2482
|
for (const sessionId of expiredSessions) {
|
|
2224
2483
|
sessions.delete(sessionId);
|
|
2484
|
+
mcpServerInstance?.cleanupSessionSubscriptions?.(sessionId);
|
|
2225
2485
|
}
|
|
2226
2486
|
}
|
|
2227
2487
|
}, 6e4);
|
|
@@ -2235,7 +2495,11 @@ async function mountMcp(app, mcpServerInstance, sessions, config, isProductionMo
|
|
|
2235
2495
|
const transports = /* @__PURE__ */ new Map();
|
|
2236
2496
|
let idleCleanupInterval;
|
|
2237
2497
|
if (idleTimeoutMs > 0) {
|
|
2238
|
-
idleCleanupInterval = startIdleCleanup(
|
|
2498
|
+
idleCleanupInterval = startIdleCleanup(
|
|
2499
|
+
sessions,
|
|
2500
|
+
idleTimeoutMs,
|
|
2501
|
+
mcpServerInstance
|
|
2502
|
+
);
|
|
2239
2503
|
}
|
|
2240
2504
|
const handleRequest = /* @__PURE__ */ __name(async (c) => {
|
|
2241
2505
|
const sessionId = c.req.header("mcp-session-id");
|
|
@@ -2262,11 +2526,23 @@ async function mountMcp(app, mcpServerInstance, sessions, config, isProductionMo
|
|
|
2262
2526
|
context: c,
|
|
2263
2527
|
honoContext: c
|
|
2264
2528
|
});
|
|
2529
|
+
server.server.oninitialized = () => {
|
|
2530
|
+
const clientCapabilities = server.server.getClientCapabilities();
|
|
2531
|
+
if (clientCapabilities && sessions.has(sid)) {
|
|
2532
|
+
const session = sessions.get(sid);
|
|
2533
|
+
session.clientCapabilities = clientCapabilities;
|
|
2534
|
+
console.log(
|
|
2535
|
+
`[MCP] Captured client capabilities for session ${sid}:`,
|
|
2536
|
+
Object.keys(clientCapabilities)
|
|
2537
|
+
);
|
|
2538
|
+
}
|
|
2539
|
+
};
|
|
2265
2540
|
}, "onsessioninitialized"),
|
|
2266
2541
|
onsessionclosed: /* @__PURE__ */ __name((sid) => {
|
|
2267
2542
|
console.log(`[MCP] Session closed: ${sid}`);
|
|
2268
2543
|
transports.delete(sid);
|
|
2269
2544
|
sessions.delete(sid);
|
|
2545
|
+
mcpServerInstance.cleanupSessionSubscriptions?.(sid);
|
|
2270
2546
|
}, "onsessionclosed")
|
|
2271
2547
|
});
|
|
2272
2548
|
await server.connect(transport);
|
|
@@ -2557,9 +2833,9 @@ async function setupOAuthForServer(app, oauthProvider, baseUrl, state) {
|
|
|
2557
2833
|
__name(setupOAuthForServer, "setupOAuthForServer");
|
|
2558
2834
|
|
|
2559
2835
|
// src/server/mcp-server.ts
|
|
2560
|
-
var
|
|
2836
|
+
var MCPServer = class {
|
|
2561
2837
|
static {
|
|
2562
|
-
__name(this, "
|
|
2838
|
+
__name(this, "MCPServer");
|
|
2563
2839
|
}
|
|
2564
2840
|
/**
|
|
2565
2841
|
* Native MCP server instance from @modelcontextprotocol/sdk
|
|
@@ -2602,6 +2878,22 @@ var McpServer = class {
|
|
|
2602
2878
|
resources: /* @__PURE__ */ new Map(),
|
|
2603
2879
|
resourceTemplates: /* @__PURE__ */ new Map()
|
|
2604
2880
|
};
|
|
2881
|
+
/**
|
|
2882
|
+
* Resource subscription manager for tracking and notifying resource updates
|
|
2883
|
+
*/
|
|
2884
|
+
subscriptionManager = new ResourceSubscriptionManager();
|
|
2885
|
+
/**
|
|
2886
|
+
* Clean up resource subscriptions for a closed session
|
|
2887
|
+
*
|
|
2888
|
+
* This method is called automatically when a session is closed to remove
|
|
2889
|
+
* all resource subscriptions associated with that session.
|
|
2890
|
+
*
|
|
2891
|
+
* @param sessionId - The session ID to clean up
|
|
2892
|
+
* @internal
|
|
2893
|
+
*/
|
|
2894
|
+
cleanupSessionSubscriptions(sessionId) {
|
|
2895
|
+
this.subscriptionManager.cleanupSession(sessionId);
|
|
2896
|
+
}
|
|
2605
2897
|
/**
|
|
2606
2898
|
* Creates a new MCP server instance with Hono integration
|
|
2607
2899
|
*
|
|
@@ -2610,16 +2902,27 @@ var McpServer = class {
|
|
|
2610
2902
|
* access to Hono methods while preserving MCP server functionality.
|
|
2611
2903
|
*
|
|
2612
2904
|
* @param config - Server configuration including name, version, and description
|
|
2613
|
-
* @returns A proxied
|
|
2905
|
+
* @returns A proxied MCPServer instance that supports both MCP and Hono methods
|
|
2614
2906
|
*/
|
|
2615
2907
|
constructor(config) {
|
|
2616
2908
|
this.config = config;
|
|
2617
2909
|
this.serverHost = config.host || "localhost";
|
|
2618
2910
|
this.serverBaseUrl = config.baseUrl;
|
|
2619
|
-
this.nativeServer = new OfficialMcpServer(
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2911
|
+
this.nativeServer = new OfficialMcpServer(
|
|
2912
|
+
{
|
|
2913
|
+
name: config.name,
|
|
2914
|
+
version: config.version
|
|
2915
|
+
},
|
|
2916
|
+
{
|
|
2917
|
+
capabilities: {
|
|
2918
|
+
logging: {},
|
|
2919
|
+
resources: {
|
|
2920
|
+
subscribe: true,
|
|
2921
|
+
listChanged: true
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2925
|
+
);
|
|
2623
2926
|
this.app = createHonoApp(requestLogger);
|
|
2624
2927
|
this.oauthConfig = config.oauth;
|
|
2625
2928
|
this.wrapRegistrationMethods();
|
|
@@ -2674,10 +2977,17 @@ var McpServer = class {
|
|
|
2674
2977
|
* This is called for each initialize request to create an isolated server.
|
|
2675
2978
|
*/
|
|
2676
2979
|
getServerForSession() {
|
|
2677
|
-
const newServer = new OfficialMcpServer(
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2980
|
+
const newServer = new OfficialMcpServer(
|
|
2981
|
+
{
|
|
2982
|
+
name: this.config.name,
|
|
2983
|
+
version: this.config.version
|
|
2984
|
+
},
|
|
2985
|
+
{
|
|
2986
|
+
capabilities: {
|
|
2987
|
+
logging: {}
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
);
|
|
2681
2991
|
for (const [name, recipe] of this.registrationRecipes.tools) {
|
|
2682
2992
|
const { config, handler: actualCallback } = recipe;
|
|
2683
2993
|
let inputSchema;
|
|
@@ -2692,12 +3002,21 @@ var McpServer = class {
|
|
|
2692
3002
|
const initialRequestContext = getRequestContext();
|
|
2693
3003
|
const extraProgressToken = extra?._meta?.progressToken;
|
|
2694
3004
|
const extraSendNotification = extra?.sendNotification;
|
|
2695
|
-
const { requestContext, progressToken, sendNotification: sendNotification2 } = findSessionContext(
|
|
3005
|
+
const { requestContext, session, progressToken, sendNotification: sendNotification2 } = findSessionContext(
|
|
2696
3006
|
this.sessions,
|
|
2697
3007
|
initialRequestContext,
|
|
2698
3008
|
extraProgressToken,
|
|
2699
3009
|
extraSendNotification
|
|
2700
3010
|
);
|
|
3011
|
+
let sessionId;
|
|
3012
|
+
if (session) {
|
|
3013
|
+
for (const [id, s] of this.sessions.entries()) {
|
|
3014
|
+
if (s === session) {
|
|
3015
|
+
sessionId = id;
|
|
3016
|
+
break;
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
3019
|
+
}
|
|
2701
3020
|
const createMessageWithLogging = /* @__PURE__ */ __name(async (params2, options) => {
|
|
2702
3021
|
console.log("[createMessage] About to call server.createMessage");
|
|
2703
3022
|
console.log("[createMessage] Has server:", !!newServer);
|
|
@@ -2723,7 +3042,11 @@ var McpServer = class {
|
|
|
2723
3042
|
createMessageWithLogging,
|
|
2724
3043
|
newServer.server.elicitInput.bind(newServer.server),
|
|
2725
3044
|
progressToken,
|
|
2726
|
-
sendNotification2
|
|
3045
|
+
sendNotification2,
|
|
3046
|
+
session?.logLevel,
|
|
3047
|
+
session?.clientCapabilities,
|
|
3048
|
+
sessionId,
|
|
3049
|
+
this.sessions
|
|
2727
3050
|
);
|
|
2728
3051
|
const executeCallback = /* @__PURE__ */ __name(async () => {
|
|
2729
3052
|
if (actualCallback.length >= 2) {
|
|
@@ -2753,8 +3076,10 @@ var McpServer = class {
|
|
|
2753
3076
|
let argsSchema;
|
|
2754
3077
|
if (config.schema) {
|
|
2755
3078
|
argsSchema = this.convertZodSchemaToParams(config.schema);
|
|
3079
|
+
} else if (config.args && config.args.length > 0) {
|
|
3080
|
+
argsSchema = this.createParamsSchema(config.args);
|
|
2756
3081
|
} else {
|
|
2757
|
-
argsSchema =
|
|
3082
|
+
argsSchema = void 0;
|
|
2758
3083
|
}
|
|
2759
3084
|
const wrappedHandler = /* @__PURE__ */ __name(async (params) => {
|
|
2760
3085
|
const result = await handler(params);
|
|
@@ -2797,22 +3122,23 @@ var McpServer = class {
|
|
|
2797
3122
|
}
|
|
2798
3123
|
for (const [_name, recipe] of this.registrationRecipes.resourceTemplates) {
|
|
2799
3124
|
const { config, handler } = recipe;
|
|
2800
|
-
const
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
3125
|
+
const isFlatStructure = "uriTemplate" in config;
|
|
3126
|
+
const uriTemplate = isFlatStructure ? config.uriTemplate : config.resourceTemplate.uriTemplate;
|
|
3127
|
+
const mimeType = isFlatStructure ? config.mimeType : config.resourceTemplate.mimeType;
|
|
3128
|
+
const templateDescription = isFlatStructure ? void 0 : config.resourceTemplate.description;
|
|
3129
|
+
const template = new ResourceTemplate2(uriTemplate, {
|
|
3130
|
+
list: void 0,
|
|
3131
|
+
complete: void 0
|
|
3132
|
+
});
|
|
2807
3133
|
const metadata = {};
|
|
2808
3134
|
if (config.title) {
|
|
2809
3135
|
metadata.title = config.title;
|
|
2810
3136
|
}
|
|
2811
|
-
if (config.description ||
|
|
2812
|
-
metadata.description = config.description ||
|
|
3137
|
+
if (config.description || templateDescription) {
|
|
3138
|
+
metadata.description = config.description || templateDescription;
|
|
2813
3139
|
}
|
|
2814
|
-
if (
|
|
2815
|
-
metadata.mimeType =
|
|
3140
|
+
if (mimeType) {
|
|
3141
|
+
metadata.mimeType = mimeType;
|
|
2816
3142
|
}
|
|
2817
3143
|
if (config.annotations) {
|
|
2818
3144
|
metadata.annotations = config.annotations;
|
|
@@ -2822,10 +3148,7 @@ var McpServer = class {
|
|
|
2822
3148
|
template,
|
|
2823
3149
|
metadata,
|
|
2824
3150
|
async (uri) => {
|
|
2825
|
-
const params = this.parseTemplateUri(
|
|
2826
|
-
config.resourceTemplate.uriTemplate,
|
|
2827
|
-
uri.toString()
|
|
2828
|
-
);
|
|
3151
|
+
const params = this.parseTemplateUri(uriTemplate, uri.toString());
|
|
2829
3152
|
const result = await handler(uri, params);
|
|
2830
3153
|
if ("contents" in result && Array.isArray(result.contents)) {
|
|
2831
3154
|
return result;
|
|
@@ -2835,6 +3158,50 @@ var McpServer = class {
|
|
|
2835
3158
|
}
|
|
2836
3159
|
);
|
|
2837
3160
|
}
|
|
3161
|
+
newServer.server.setRequestHandler(
|
|
3162
|
+
z2.object({ method: z2.literal("logging/setLevel") }).passthrough(),
|
|
3163
|
+
async (request) => {
|
|
3164
|
+
const level = request.params?.level;
|
|
3165
|
+
if (!level) {
|
|
3166
|
+
throw new McpError(
|
|
3167
|
+
ErrorCode.InvalidParams,
|
|
3168
|
+
"Missing 'level' parameter"
|
|
3169
|
+
);
|
|
3170
|
+
}
|
|
3171
|
+
if (!isValidLogLevel(level)) {
|
|
3172
|
+
throw new McpError(
|
|
3173
|
+
ErrorCode.InvalidParams,
|
|
3174
|
+
`Invalid log level '${level}'. Must be one of: debug, info, notice, warning, error, critical, alert, emergency`
|
|
3175
|
+
);
|
|
3176
|
+
}
|
|
3177
|
+
const requestContext = getRequestContext();
|
|
3178
|
+
if (requestContext) {
|
|
3179
|
+
const sessionId = requestContext.req.header("mcp-session-id");
|
|
3180
|
+
if (sessionId && this.sessions.has(sessionId)) {
|
|
3181
|
+
const session = this.sessions.get(sessionId);
|
|
3182
|
+
session.logLevel = level;
|
|
3183
|
+
console.log(
|
|
3184
|
+
`[MCP] Set log level to '${level}' for session ${sessionId}`
|
|
3185
|
+
);
|
|
3186
|
+
return {};
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
for (const [sessionId, session] of this.sessions.entries()) {
|
|
3190
|
+
if (session.server === newServer) {
|
|
3191
|
+
session.logLevel = level;
|
|
3192
|
+
console.log(
|
|
3193
|
+
`[MCP] Set log level to '${level}' for session ${sessionId}`
|
|
3194
|
+
);
|
|
3195
|
+
return {};
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
console.warn(
|
|
3199
|
+
"[MCP] Could not find session for logging/setLevel request"
|
|
3200
|
+
);
|
|
3201
|
+
throw new McpError(ErrorCode.InternalError, "Could not find session");
|
|
3202
|
+
}
|
|
3203
|
+
);
|
|
3204
|
+
this.subscriptionManager.registerHandlers(newServer, this.sessions);
|
|
2838
3205
|
return newServer;
|
|
2839
3206
|
}
|
|
2840
3207
|
/**
|
|
@@ -2864,6 +3231,24 @@ var McpServer = class {
|
|
|
2864
3231
|
getActiveSessions = getActiveSessions;
|
|
2865
3232
|
sendNotification = sendNotification;
|
|
2866
3233
|
sendNotificationToSession = sendNotificationToSession2;
|
|
3234
|
+
/**
|
|
3235
|
+
* Notify subscribed clients that a resource has been updated
|
|
3236
|
+
*
|
|
3237
|
+
* This method sends a `notifications/resources/updated` notification to all
|
|
3238
|
+
* sessions that have subscribed to the specified resource URI.
|
|
3239
|
+
*
|
|
3240
|
+
* @param uri - The URI of the resource that changed
|
|
3241
|
+
* @returns Promise that resolves when all notifications have been sent
|
|
3242
|
+
*
|
|
3243
|
+
* @example
|
|
3244
|
+
* ```typescript
|
|
3245
|
+
* // After updating a resource, notify subscribers
|
|
3246
|
+
* await server.notifyResourceUpdated("file:///path/to/resource.txt");
|
|
3247
|
+
* ```
|
|
3248
|
+
*/
|
|
3249
|
+
async notifyResourceUpdated(uri) {
|
|
3250
|
+
return this.subscriptionManager.notifyResourceUpdated(uri, this.sessions);
|
|
3251
|
+
}
|
|
2867
3252
|
uiResource = uiResourceRegistration;
|
|
2868
3253
|
/**
|
|
2869
3254
|
* Mount MCP server endpoints at /mcp and /sse
|
|
@@ -2889,7 +3274,7 @@ var McpServer = class {
|
|
|
2889
3274
|
const result = await mountMcp(
|
|
2890
3275
|
this.app,
|
|
2891
3276
|
this,
|
|
2892
|
-
// Pass the
|
|
3277
|
+
// Pass the MCPServer instance so mountMcp can call getServerForSession()
|
|
2893
3278
|
this.sessions,
|
|
2894
3279
|
this.config,
|
|
2895
3280
|
isProductionMode()
|
|
@@ -2947,6 +3332,11 @@ var McpServer = class {
|
|
|
2947
3332
|
if (hostEnv) {
|
|
2948
3333
|
this.serverHost = hostEnv;
|
|
2949
3334
|
}
|
|
3335
|
+
this.serverBaseUrl = getServerBaseUrl(
|
|
3336
|
+
this.serverBaseUrl,
|
|
3337
|
+
this.serverHost,
|
|
3338
|
+
this.serverPort
|
|
3339
|
+
);
|
|
2950
3340
|
if (this.oauthConfig && !this.oauthSetupState.complete) {
|
|
2951
3341
|
await setupOAuthForServer(
|
|
2952
3342
|
this.app,
|
|
@@ -2984,7 +3374,7 @@ var McpServer = class {
|
|
|
2984
3374
|
* @example
|
|
2985
3375
|
* ```typescript
|
|
2986
3376
|
* // For Supabase Edge Functions (handles path rewriting automatically)
|
|
2987
|
-
* const server =
|
|
3377
|
+
* const server = new MCPServer({ name: 'my-server', version: '1.0.0' });
|
|
2988
3378
|
* server.tool({ ... });
|
|
2989
3379
|
* const handler = await server.getHandler({ provider: 'supabase' });
|
|
2990
3380
|
* Deno.serve(handler);
|
|
@@ -2993,7 +3383,7 @@ var McpServer = class {
|
|
|
2993
3383
|
* @example
|
|
2994
3384
|
* ```typescript
|
|
2995
3385
|
* // For Cloudflare Workers
|
|
2996
|
-
* const server =
|
|
3386
|
+
* const server = new MCPServer({ name: 'my-server', version: '1.0.0' });
|
|
2997
3387
|
* server.tool({ ... });
|
|
2998
3388
|
* const handler = await server.getHandler();
|
|
2999
3389
|
* export default { fetch: handler };
|
|
@@ -3071,7 +3461,7 @@ var McpServer = class {
|
|
|
3071
3461
|
}
|
|
3072
3462
|
};
|
|
3073
3463
|
function createMCPServer(name, config = {}) {
|
|
3074
|
-
const instance = new
|
|
3464
|
+
const instance = new MCPServer({
|
|
3075
3465
|
name,
|
|
3076
3466
|
version: config.version || "1.0.0",
|
|
3077
3467
|
description: config.description,
|
|
@@ -3674,9 +4064,11 @@ function requireAnyScope(needed) {
|
|
|
3674
4064
|
}
|
|
3675
4065
|
__name(requireAnyScope, "requireAnyScope");
|
|
3676
4066
|
export {
|
|
4067
|
+
MCPServer,
|
|
3677
4068
|
adaptConnectMiddleware,
|
|
3678
4069
|
adaptMiddleware,
|
|
3679
4070
|
array,
|
|
4071
|
+
audio,
|
|
3680
4072
|
binary,
|
|
3681
4073
|
buildWidgetUrl,
|
|
3682
4074
|
createExternalUrlResource,
|