mcp-use
Version:
Opinionated MCP Framework for TypeScript (@modelcontextprotocol/sdk compatible) - Build MCP Agents, Clients and Servers with support for ChatGPT Apps, Code Mode, OAuth, Notifications, Sampling, Observability and more.
1,617 lines (1,605 loc) • 177 kB
JavaScript
import {
getRequestContext,
hasRequestContext,
runWithContext
} from "../../chunk-LWVK6RXA.js";
import {
createEnhancedContext,
findSessionContext,
isValidLogLevel
} from "../../chunk-YV6CCR64.js";
import {
convertToolResultToResourceResult
} from "../../chunk-362PI25Z.js";
import {
convertToolResultToPromptResult
} from "../../chunk-2EYAMIT3.js";
import {
createRequest,
sendNotificationToAll,
sendNotificationToSession
} from "../../chunk-UWWLWLS2.js";
import "../../chunk-KUEVOU4M.js";
import {
Telemetry,
VERSION,
fsHelpers,
generateUUID,
getCwd,
getEnv,
getPackageVersion,
isDeno,
pathHelpers
} from "../../chunk-CN263ZGG.js";
import "../../chunk-FRUZDWXH.js";
import {
__name
} from "../../chunk-3GQAWCBQ.js";
// src/server/mcp-server.ts
import {
McpServer as OfficialMcpServer,
ResourceTemplate as ResourceTemplate2
} from "@mcp-use/modelcontextprotocol-sdk/server/mcp.js";
import {
McpError,
ErrorCode
} from "@mcp-use/modelcontextprotocol-sdk/types.js";
import { z as z3 } from "zod";
// src/server/utils/response-helpers.ts
function text(content) {
return {
content: [
{
type: "text",
text: content
}
],
_meta: {
mimeType: "text/plain"
}
};
}
__name(text, "text");
function image(data, mimeType = "image/png") {
return {
content: [
{
type: "image",
data,
mimeType
}
],
_meta: {
mimeType,
isImage: true
}
};
}
__name(image, "image");
function getAudioMimeType(filename) {
const ext = filename.split(".").pop()?.toLowerCase();
switch (ext) {
case "wav":
return "audio/wav";
case "mp3":
return "audio/mpeg";
case "ogg":
return "audio/ogg";
case "m4a":
return "audio/mp4";
case "webm":
return "audio/webm";
case "flac":
return "audio/flac";
case "aac":
return "audio/aac";
default:
return "audio/wav";
}
}
__name(getAudioMimeType, "getAudioMimeType");
function arrayBufferToBase64(buffer) {
if (isDeno) {
const bytes = new Uint8Array(buffer);
let binary2 = "";
for (let i = 0; i < bytes.length; i++) {
binary2 += String.fromCharCode(bytes[i]);
}
return btoa(binary2);
} else {
return Buffer.from(buffer).toString("base64");
}
}
__name(arrayBufferToBase64, "arrayBufferToBase64");
function audio(dataOrPath, mimeType) {
const isFilePath = dataOrPath.includes("/") || dataOrPath.includes("\\") || dataOrPath.includes(".");
if (isFilePath && dataOrPath.length < 1e3) {
return (async () => {
const buffer = await fsHelpers.readFile(dataOrPath);
const base64Data = arrayBufferToBase64(buffer);
const inferredMimeType = mimeType || getAudioMimeType(dataOrPath);
return {
content: [
{
type: "audio",
data: base64Data,
mimeType: inferredMimeType
}
],
_meta: {
mimeType: inferredMimeType,
isAudio: true
}
};
})();
}
const finalMimeType = mimeType || "audio/wav";
return {
content: [
{
type: "audio",
data: dataOrPath,
mimeType: finalMimeType
}
],
_meta: {
mimeType: finalMimeType,
isAudio: true
}
};
}
__name(audio, "audio");
function resource(uri, mimeTypeOrContent, text2) {
if (typeof mimeTypeOrContent === "object" && mimeTypeOrContent !== null && "content" in mimeTypeOrContent) {
const contentResult = mimeTypeOrContent;
let extractedText;
let extractedMimeType;
if (contentResult._meta && typeof contentResult._meta === "object") {
const meta = contentResult._meta;
if (meta.mimeType && typeof meta.mimeType === "string") {
extractedMimeType = meta.mimeType;
}
}
if (contentResult.content && contentResult.content.length > 0) {
const firstContent = contentResult.content[0];
if (firstContent.type === "text" && "text" in firstContent) {
extractedText = firstContent.text;
}
}
const resourceContent2 = {
type: "resource",
resource: {
uri,
...extractedMimeType && { mimeType: extractedMimeType },
...extractedText && { text: extractedText }
}
};
return {
content: [resourceContent2]
};
}
const mimeType = mimeTypeOrContent;
const resourceContent = {
type: "resource",
resource: {
uri,
...mimeType && { mimeType },
...text2 && { text: text2 }
}
};
return {
content: [resourceContent]
};
}
__name(resource, "resource");
function error(message) {
return {
isError: true,
content: [
{
type: "text",
text: message
}
]
};
}
__name(error, "error");
function object(data) {
return Array.isArray(data) ? array(data) : {
content: [
{
type: "text",
text: JSON.stringify(data, null, 2)
}
],
structuredContent: data,
_meta: {
mimeType: "application/json"
}
};
}
__name(object, "object");
function array(data) {
return {
content: [
{
type: "text",
text: JSON.stringify(data, null, 2)
}
],
structuredContent: { data }
};
}
__name(array, "array");
function html(content) {
return {
content: [
{
type: "text",
text: content
}
],
_meta: {
mimeType: "text/html"
}
};
}
__name(html, "html");
function markdown(content) {
return {
content: [
{
type: "text",
text: content
}
],
_meta: {
mimeType: "text/markdown"
}
};
}
__name(markdown, "markdown");
function xml(content) {
return {
content: [
{
type: "text",
text: content
}
],
_meta: {
mimeType: "text/xml"
}
};
}
__name(xml, "xml");
function css(content) {
return {
content: [
{
type: "text",
text: content
}
],
_meta: {
mimeType: "text/css"
}
};
}
__name(css, "css");
function javascript(content) {
return {
content: [
{
type: "text",
text: content
}
],
_meta: {
mimeType: "text/javascript"
}
};
}
__name(javascript, "javascript");
function binary(base64Data, mimeType) {
return {
content: [
{
type: "text",
text: base64Data
}
],
_meta: {
mimeType,
isBinary: true
}
};
}
__name(binary, "binary");
function widget(config) {
const props = config.props || config.data || {};
const { output, message } = config;
const finalContent = message ? [{ type: "text", text: message }] : Array.isArray(output?.content) && output.content.length > 0 ? output.content : [{ type: "text", text: "" }];
const meta = {
...output?._meta || {},
"mcp-use/props": props
};
const result = {
content: finalContent,
_meta: meta
};
if (output?.structuredContent) {
result.structuredContent = output.structuredContent;
} else if (Object.keys(props).length > 0) {
result.structuredContent = props;
}
return result;
}
__name(widget, "widget");
function mix(...results) {
const structuredContent = results.find((result) => result.structuredContent) && results.filter((result) => result.structuredContent).map((result) => result.structuredContent).reduce(
(acc, result) => {
return { ...acc, ...result };
},
{}
);
const _meta = results.find((result) => result._meta) && results.filter((result) => result._meta).map((result) => result._meta).reduce(
(acc, result) => {
return { ...acc, ...result };
},
{}
);
return {
content: results.flatMap((result) => result.content),
...structuredContent && { structuredContent },
..._meta && { _meta }
};
}
__name(mix, "mix");
// src/server/utils/server-helpers.ts
import { Hono } from "hono";
import { cors } from "hono/cors";
function getDefaultCorsOptions() {
return {
origin: "*",
allowMethods: ["GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"],
allowHeaders: [
"Content-Type",
"Accept",
"Authorization",
"mcp-protocol-version",
"mcp-session-id",
"X-Proxy-Token",
"X-Target-URL"
],
// Expose mcp-session-id so browser clients can read it from responses
exposeHeaders: ["mcp-session-id"]
};
}
__name(getDefaultCorsOptions, "getDefaultCorsOptions");
function createHonoApp(requestLogger2) {
const app = new Hono();
app.use("*", cors(getDefaultCorsOptions()));
app.use("*", requestLogger2);
return app;
}
__name(createHonoApp, "createHonoApp");
function getServerBaseUrl(serverBaseUrl, serverHost, serverPort) {
if (serverBaseUrl) {
return serverBaseUrl;
}
const mcpUrl = getEnv("MCP_URL");
if (mcpUrl) {
return mcpUrl;
}
return `http://${serverHost}:${serverPort}`;
}
__name(getServerBaseUrl, "getServerBaseUrl");
function getCSPUrls() {
const cspUrlsEnv = getEnv("CSP_URLS");
if (!cspUrlsEnv) {
console.log("[CSP] No CSP_URLS environment variable found");
return [];
}
const urls = cspUrlsEnv.split(",").map((url) => url.trim()).filter((url) => url.length > 0);
console.log("[CSP] Parsed CSP URLs:", urls);
return urls;
}
__name(getCSPUrls, "getCSPUrls");
function logRegisteredItems(registeredTools, registeredPrompts, registeredResources) {
console.log("\n\u{1F4CB} Server exposes:");
console.log(` Tools: ${registeredTools.length}`);
if (registeredTools.length > 0) {
registeredTools.forEach((name) => {
console.log(` - ${name}`);
});
}
console.log(` Prompts: ${registeredPrompts.length}`);
if (registeredPrompts.length > 0) {
registeredPrompts.forEach((name) => {
console.log(` - ${name}`);
});
}
console.log(` Resources: ${registeredResources.length}`);
if (registeredResources.length > 0) {
registeredResources.forEach((name) => {
console.log(` - ${name}`);
});
}
console.log("");
}
__name(logRegisteredItems, "logRegisteredItems");
function parseTemplateUri(template, uri) {
const params = {};
let regexPattern = template.replace(/[.*+?^$()[\]\\|]/g, "\\$&");
const paramNames = [];
regexPattern = regexPattern.replace(/\{([^}]+)\}/g, (_, paramName) => {
paramNames.push(paramName);
return "([^/]+)";
});
const regex = new RegExp(`^${regexPattern}$`);
const match = uri.match(regex);
if (match) {
paramNames.forEach((paramName, index) => {
params[paramName] = match[index + 1];
});
}
return params;
}
__name(parseTemplateUri, "parseTemplateUri");
// src/server/utils/server-lifecycle.ts
function isProductionMode() {
return getEnv("NODE_ENV") === "production";
}
__name(isProductionMode, "isProductionMode");
function getDenoCorsHeaders() {
return {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type"
};
}
__name(getDenoCorsHeaders, "getDenoCorsHeaders");
function applyDenoCorsHeaders(response) {
const corsHeaders = getDenoCorsHeaders();
const newHeaders = new Headers(response.headers);
Object.entries(corsHeaders).forEach(([key, value]) => {
newHeaders.set(key, value);
});
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHeaders
});
}
__name(applyDenoCorsHeaders, "applyDenoCorsHeaders");
function createSupabasePathRewriter() {
return (pathname) => {
let newPathname = pathname;
const functionsMatch = pathname.match(/^\/functions\/v1\/[^/]+(\/.*)?$/);
if (functionsMatch) {
newPathname = functionsMatch[1] || "/";
} else {
const functionNameMatch = pathname.match(/^\/([^/]+)(\/.*)?$/);
if (functionNameMatch && functionNameMatch[2]) {
newPathname = functionNameMatch[2] || "/";
}
}
return newPathname;
};
}
__name(createSupabasePathRewriter, "createSupabasePathRewriter");
function rewriteSupabaseRequest(req) {
const url = new URL(req.url);
const pathname = url.pathname;
const rewriter = createSupabasePathRewriter();
const newPathname = rewriter(pathname);
if (newPathname !== pathname) {
const newUrl = new URL(newPathname + url.search, url.origin);
return new Request(newUrl, {
method: req.method,
headers: req.headers,
body: req.body,
redirect: req.redirect
});
}
return req;
}
__name(rewriteSupabaseRequest, "rewriteSupabaseRequest");
async function startServer(app, port, host, options) {
if (isDeno) {
const corsHeaders = getDenoCorsHeaders();
globalThis.Deno.serve(
{ port, hostname: host },
async (req) => {
if (req.method === "OPTIONS") {
return new Response("ok", { headers: corsHeaders });
}
let finalReq = req;
if (options?.onDenoRequest) {
finalReq = await options.onDenoRequest(req);
}
let response = await app.fetch(finalReq);
if (options?.onDenoResponse) {
response = await options.onDenoResponse(response);
} else {
response = applyDenoCorsHeaders(response);
}
return response;
}
);
console.log(`[SERVER] Listening`);
} else {
const { serve } = await import("@hono/node-server");
serve(
{
fetch: app.fetch,
port,
hostname: host
},
(_info) => {
console.log(`[SERVER] Listening on http://${host}:${port}`);
console.log(
`[MCP] Endpoints: http://${host}:${port}/mcp and http://${host}:${port}/sse`
);
}
);
}
}
__name(startServer, "startServer");
// src/server/connect-adapter.ts
function isExpressMiddleware(middleware) {
if (!middleware || typeof middleware !== "function") {
return false;
}
const paramCount = middleware.length;
if (paramCount === 3 || paramCount === 4) {
return true;
}
if (paramCount === 2) {
const fnString = middleware.toString();
const expressPatterns = [
/\bres\.(send|json|status|end|redirect|render|sendFile|download)\b/,
/\breq\.(body|params|query|cookies|session)\b/,
/\breq\.get\s*\(/,
/\bres\.set\s*\(/
];
const hasExpressPattern = expressPatterns.some(
(pattern) => pattern.test(fnString)
);
if (hasExpressPattern) {
return true;
}
return false;
}
return false;
}
__name(isExpressMiddleware, "isExpressMiddleware");
async function adaptMiddleware(middleware, middlewarePath = "*") {
if (isExpressMiddleware(middleware)) {
return adaptConnectMiddleware(middleware, middlewarePath);
}
return middleware;
}
__name(adaptMiddleware, "adaptMiddleware");
async function adaptConnectMiddleware(connectMiddleware, middlewarePath) {
let createRequest2;
let createResponse;
try {
const { createRequire } = await import("module");
const { pathToFileURL } = await import("url");
const userProjectRequire = createRequire(
pathToFileURL(
// Use process.cwd() since this is a runtime utility that should work from user's project
process.cwd() + "/package.json"
).href
);
const httpMocksPath = userProjectRequire.resolve("node-mocks-http");
const httpMocks = await import(httpMocksPath);
createRequest2 = httpMocks.createRequest;
createResponse = httpMocks.createResponse;
} catch (error2) {
throw new Error(
"\u274C Widget middleware dependencies not installed!\n\nTo use Connect middleware adapters with MCP widgets, you need to install:\n\n npm install node-mocks-http\n # or\n pnpm add node-mocks-http\n\nThis dependency is automatically included in projects created with 'create-mcp-use-app'."
);
}
let normalizedPath = middlewarePath;
if (normalizedPath.endsWith("*")) {
normalizedPath = normalizedPath.slice(0, -1);
}
if (normalizedPath.endsWith("/")) {
normalizedPath = normalizedPath.slice(0, -1);
}
const honoMiddleware = /* @__PURE__ */ __name(async (c, next) => {
const request = c.req.raw;
const parsedURL = new URL(request.url, "http://localhost");
const query = {};
for (const [key, value] of parsedURL.searchParams.entries()) {
query[key] = value;
}
let middlewarePathname = parsedURL.pathname;
if (normalizedPath && middlewarePathname.startsWith(normalizedPath)) {
middlewarePathname = middlewarePathname.substring(normalizedPath.length);
if (middlewarePathname === "") {
middlewarePathname = "/";
} else if (!middlewarePathname.startsWith("/")) {
middlewarePathname = "/" + middlewarePathname;
}
}
const mockRequest = createRequest2({
method: request.method.toUpperCase(),
url: middlewarePathname + parsedURL.search,
headers: request.headers && typeof request.headers.entries === "function" ? Object.fromEntries(request.headers.entries()) : request.headers,
query,
...request.body && { body: request.body }
});
const mockResponse = createResponse();
let responseResolved = false;
const res = await new Promise((resolve) => {
const originalEnd = mockResponse.end.bind(mockResponse);
mockResponse.end = (...args) => {
const result = originalEnd(...args);
if (!responseResolved && mockResponse.writableEnded) {
responseResolved = true;
const statusCode = mockResponse.statusCode;
const noBodyStatuses = [204, 304];
const responseBody = noBodyStatuses.includes(statusCode) ? null : mockResponse._getData() || mockResponse._getBuffer() || null;
const connectResponse = new Response(responseBody, {
status: statusCode,
statusText: mockResponse.statusMessage,
headers: mockResponse.getHeaders()
});
resolve(connectResponse);
}
return result;
};
connectMiddleware(mockRequest, mockResponse, () => {
if (!responseResolved && !mockResponse.writableEnded) {
responseResolved = true;
const statusCode = mockResponse.statusCode;
const noBodyStatuses = [204, 304];
const responseBody = noBodyStatuses.includes(statusCode) ? null : mockResponse._getData() || mockResponse._getBuffer() || null;
const preparedHeaders = c.newResponse(null, 204, {}).headers;
for (const key of [...preparedHeaders.keys()]) {
if (preparedHeaders.has(key)) {
c.header(key, void 0);
}
if (c.res && c.res.headers.has(key)) {
c.res.headers.delete(key);
}
}
const connectHeaders = mockResponse.getHeaders();
for (const [key, value] of Object.entries(connectHeaders)) {
if (value !== void 0) {
c.header(
key,
Array.isArray(value) ? value.join(", ") : String(value)
);
}
}
c.status(statusCode);
if (noBodyStatuses.includes(statusCode)) {
resolve(c.newResponse(null, statusCode));
} else if (responseBody) {
resolve(c.body(responseBody));
} else {
resolve(void 0);
}
}
});
});
if (res) {
c.res = res;
return res;
}
await next();
}, "honoMiddleware");
return honoMiddleware;
}
__name(adaptConnectMiddleware, "adaptConnectMiddleware");
// src/server/utils/hono-proxy.ts
function createHonoProxy(target, app) {
return new Proxy(target, {
get(target2, prop) {
if (prop === "use") {
return async (...args) => {
const hasPath = typeof args[0] === "string";
const path = hasPath ? args[0] : "*";
const handlers = hasPath ? args.slice(1) : args;
const adaptedHandlers = handlers.map((handler) => {
if (isExpressMiddleware(handler)) {
return { __isExpressMiddleware: true, handler, path };
}
return handler;
});
const hasExpressMiddleware = adaptedHandlers.some(
(h) => h.__isExpressMiddleware
);
if (hasExpressMiddleware) {
await Promise.all(
adaptedHandlers.map(async (h) => {
if (h.__isExpressMiddleware) {
const adapted = await adaptConnectMiddleware(
h.handler,
h.path
);
if (hasPath) {
app.use(path, adapted);
} else {
app.use(adapted);
}
} else {
if (hasPath) {
app.use(path, h);
} else {
app.use(h);
}
}
})
);
return target2;
}
return app.use(...args);
};
}
if (prop in target2) {
return target2[prop];
}
const value = app[prop];
return typeof value === "function" ? value.bind(app) : value;
}
});
}
__name(createHonoProxy, "createHonoProxy");
// src/server/widgets/mcp-ui-adapter.ts
import { createUIResource } from "@mcp-ui/server";
function buildWidgetUrl(widget2, props, config) {
const url = new URL(
`/mcp-use/widgets/${widget2}`,
`${config.baseUrl}:${config.port}`
);
if (props && Object.keys(props).length > 0) {
url.searchParams.set("props", JSON.stringify(props));
}
return url.toString();
}
__name(buildWidgetUrl, "buildWidgetUrl");
async function createExternalUrlResource(uri, iframeUrl, encoding = "text", adapters, metadata) {
return await createUIResource({
uri,
content: { type: "externalUrl", iframeUrl },
encoding,
adapters,
metadata
});
}
__name(createExternalUrlResource, "createExternalUrlResource");
async function createRawHtmlResource(uri, htmlString, encoding = "text", adapters, metadata) {
return await createUIResource({
uri,
content: { type: "rawHtml", htmlString },
encoding,
adapters,
metadata
});
}
__name(createRawHtmlResource, "createRawHtmlResource");
async function createRemoteDomResource(uri, script, framework = "react", encoding = "text", adapters, metadata) {
return await createUIResource({
uri,
content: { type: "remoteDom", script, framework },
encoding,
adapters,
metadata
});
}
__name(createRemoteDomResource, "createRemoteDomResource");
function createAppsSdkResource(uri, htmlTemplate, metadata) {
const resource2 = {
uri,
mimeType: "text/html+skybridge",
text: htmlTemplate
};
if (metadata && Object.keys(metadata).length > 0) {
resource2._meta = metadata;
}
return {
type: "resource",
resource: resource2
};
}
__name(createAppsSdkResource, "createAppsSdkResource");
async function createUIResourceFromDefinition(definition, params, config) {
const buildIdPart = config.buildId ? `-${config.buildId}` : "";
const uri = definition.type === "appsSdk" ? `ui://widget/${definition.name}${buildIdPart}.html` : `ui://widget/${definition.name}${buildIdPart}`;
const encoding = definition.encoding || "text";
switch (definition.type) {
case "externalUrl": {
const widgetUrl = buildWidgetUrl(definition.widget, params, config);
return await createExternalUrlResource(
uri,
widgetUrl,
encoding,
definition.adapters,
definition.appsSdkMetadata
);
}
case "rawHtml": {
return await createRawHtmlResource(
uri,
definition.htmlContent,
encoding,
definition.adapters,
definition.appsSdkMetadata
);
}
case "remoteDom": {
const framework = definition.framework || "react";
return await createRemoteDomResource(
uri,
definition.script,
framework,
encoding,
definition.adapters,
definition.appsSdkMetadata
);
}
case "appsSdk": {
return createAppsSdkResource(
uri,
definition.htmlTemplate,
definition.appsSdkMetadata
);
}
default: {
const _exhaustive = definition;
throw new Error(`Unknown UI resource type: ${_exhaustive.type}`);
}
}
}
__name(createUIResourceFromDefinition, "createUIResourceFromDefinition");
// src/server/widgets/widget-helpers.ts
function generateWidgetUri(widgetName, buildId, extension = "", suffix = "") {
const parts = [widgetName];
if (buildId) {
parts.push(buildId);
}
if (suffix) {
parts.push(suffix);
}
return `ui://widget/${parts.join("-")}${extension}`;
}
__name(generateWidgetUri, "generateWidgetUri");
function convertPropsToInputs(props) {
if (!props) return [];
return Object.entries(props).map(([name, prop]) => ({
name,
type: prop.type,
description: prop.description,
required: prop.required,
default: prop.default
}));
}
__name(convertPropsToInputs, "convertPropsToInputs");
function applyDefaultProps(props) {
if (!props) return {};
const defaults = {};
for (const [key, prop] of Object.entries(props)) {
if (prop.default !== void 0) {
defaults[key] = prop.default;
}
}
return defaults;
}
__name(applyDefaultProps, "applyDefaultProps");
async function readBuildManifest() {
try {
const manifestPath = pathHelpers.join(
isDeno ? "." : getCwd(),
"dist",
"mcp-use.json"
);
const content = await fsHelpers.readFileSync(manifestPath, "utf8");
return JSON.parse(content);
} catch {
return null;
}
}
__name(readBuildManifest, "readBuildManifest");
function getContentType(filename) {
const ext = filename.split(".").pop()?.toLowerCase();
switch (ext) {
case "js":
return "application/javascript";
case "css":
return "text/css";
case "png":
return "image/png";
case "jpg":
case "jpeg":
return "image/jpeg";
case "svg":
return "image/svg+xml";
case "gif":
return "image/gif";
case "webp":
return "image/webp";
case "ico":
return "image/x-icon";
case "woff":
return "font/woff";
case "woff2":
return "font/woff2";
case "ttf":
return "font/ttf";
case "otf":
return "font/otf";
case "json":
return "application/json";
case "pdf":
return "application/pdf";
default:
return "application/octet-stream";
}
}
__name(getContentType, "getContentType");
function processWidgetHtml(html2, widgetName, baseUrl) {
let processedHtml = html2;
if (baseUrl && processedHtml) {
let htmlWithoutComments = processedHtml;
let prevHtmlWithoutComments;
do {
prevHtmlWithoutComments = htmlWithoutComments;
htmlWithoutComments = htmlWithoutComments.replace(/<!--[\s\S]*?-->/g, "");
} while (prevHtmlWithoutComments !== htmlWithoutComments);
const baseTagRegex = /<base\s+[^>]*\/?>/i;
if (baseTagRegex.test(htmlWithoutComments)) {
const actualBaseTagMatch = processedHtml.match(/<base\s+[^>]*\/?>/i);
if (actualBaseTagMatch) {
processedHtml = processedHtml.replace(
actualBaseTagMatch[0],
`<base href="${baseUrl}" />`
);
}
} else {
const headTagRegex = /<head[^>]*>/i;
if (headTagRegex.test(processedHtml)) {
processedHtml = processedHtml.replace(
headTagRegex,
(match) => `${match}
<base href="${baseUrl}" />`
);
}
}
processedHtml = processedHtml.replace(
/src="\/mcp-use\/widgets\/([^"]+)"/g,
`src="${baseUrl}/mcp-use/widgets/$1"`
);
processedHtml = processedHtml.replace(
/href="\/mcp-use\/widgets\/([^"]+)"/g,
`href="${baseUrl}/mcp-use/widgets/$1"`
);
processedHtml = processedHtml.replace(
/<head[^>]*>/i,
`<head>
<script>window.__getFile = (filename) => { return "${baseUrl}/mcp-use/widgets/${widgetName}/"+filename }; window.__mcpPublicUrl = "${baseUrl}/mcp-use/public";</script>`
);
}
return processedHtml;
}
__name(processWidgetHtml, "processWidgetHtml");
function createWidgetRegistration(widgetName, metadata, html2, serverConfig, isDev = false) {
const props = metadata.props || metadata.inputs || metadata.schema || {};
const description = metadata.description || `Widget: ${widgetName}`;
const title = metadata.title || widgetName;
const exposeAsTool = metadata.exposeAsTool !== void 0 ? metadata.exposeAsTool : true;
const mcp_connect_domain = serverConfig.serverBaseUrl ? new URL(serverConfig.serverBaseUrl || "").origin : null;
return {
name: widgetName,
title,
description,
type: "appsSdk",
props,
_meta: {
"mcp-use/widget": {
name: widgetName,
title,
description,
type: "appsSdk",
props,
html: html2,
dev: isDev,
exposeAsTool
},
...metadata._meta || {}
},
htmlTemplate: html2,
appsSdkMetadata: {
"openai/widgetDescription": description,
"openai/toolInvocation/invoking": `Loading ${widgetName}...`,
"openai/toolInvocation/invoked": `${widgetName} ready`,
"openai/widgetAccessible": true,
"openai/resultCanProduceWidget": true,
...metadata.appsSdkMetadata || {},
"openai/widgetCSP": {
connect_domains: [
// always also add the base url of the server
...mcp_connect_domain ? [mcp_connect_domain] : [],
...metadata.appsSdkMetadata?.["openai/widgetCSP"]?.connect_domains || []
],
resource_domains: [
"https://*.oaistatic.com",
"https://*.oaiusercontent.com",
...isDev ? [] : ["https://*.openai.com"],
// always also add the base url of the server
...mcp_connect_domain ? [mcp_connect_domain] : [],
// add additional CSP URLs from environment variable
...serverConfig.cspUrls,
...metadata.appsSdkMetadata?.["openai/widgetCSP"]?.resource_domains || []
]
}
}
};
}
__name(createWidgetRegistration, "createWidgetRegistration");
async function createWidgetUIResource(definition, params, serverConfig) {
let configBaseUrl = `http://${serverConfig.serverHost}`;
let configPort = serverConfig.serverPort || 3e3;
if (serverConfig.serverBaseUrl) {
try {
const url = new URL(serverConfig.serverBaseUrl);
configBaseUrl = `${url.protocol}//${url.hostname}`;
configPort = url.port || (url.protocol === "https:" ? 443 : 80);
} catch (e) {
console.warn("Failed to parse baseUrl, falling back to host:port", e);
}
}
const urlConfig = {
baseUrl: configBaseUrl,
port: configPort,
buildId: serverConfig.buildId
};
const uiResource = await createUIResourceFromDefinition(
definition,
params,
urlConfig
);
if (definition._meta && Object.keys(definition._meta).length > 0) {
uiResource.resource._meta = {
...uiResource.resource._meta,
...definition._meta
};
}
return uiResource;
}
__name(createWidgetUIResource, "createWidgetUIResource");
function ensureWidgetMetadata(metadata, widgetName, widgetDescription) {
const result = { ...metadata };
if (!result.description) {
result.description = widgetDescription || `Widget: ${widgetName}`;
}
return result;
}
__name(ensureWidgetMetadata, "ensureWidgetMetadata");
async function readWidgetHtml(filePath, widgetName) {
try {
return await fsHelpers.readFileSync(filePath, "utf8");
} catch (error2) {
console.error(
`[WIDGET] Failed to read html template for widget ${widgetName}:`,
error2
);
return "";
}
}
__name(readWidgetHtml, "readWidgetHtml");
async function registerWidgetFromTemplate(widgetName, htmlPath, metadata, serverConfig, registerWidget, isDev = false) {
let html2 = await readWidgetHtml(htmlPath, widgetName);
if (!html2) {
return;
}
html2 = processWidgetHtml(html2, widgetName, serverConfig.serverBaseUrl);
const processedMetadata = ensureWidgetMetadata(metadata, widgetName);
const widgetRegistration = createWidgetRegistration(
widgetName,
processedMetadata,
html2,
serverConfig,
isDev
);
registerWidget(widgetRegistration);
}
__name(registerWidgetFromTemplate, "registerWidgetFromTemplate");
function setupPublicRoutes(app, useDistDirectory = false) {
app.get("/mcp-use/public/*", async (c) => {
const filePath = c.req.path.replace("/mcp-use/public/", "");
const basePath = useDistDirectory ? "dist/public" : "public";
const fullPath = pathHelpers.join(getCwd(), basePath, filePath);
try {
if (await fsHelpers.existsSync(fullPath)) {
const content = await fsHelpers.readFile(fullPath);
const contentType = getContentType(filePath);
return new Response(content, {
status: 200,
headers: { "Content-Type": contentType }
});
}
return c.notFound();
} catch {
return c.notFound();
}
});
}
__name(setupPublicRoutes, "setupPublicRoutes");
function setupFaviconRoute(app, faviconPath, useDistDirectory = false) {
if (!faviconPath) {
return;
}
app.get("/favicon.ico", async (c) => {
const basePath = useDistDirectory ? "dist/public" : "public";
const fullPath = pathHelpers.join(getCwd(), basePath, faviconPath);
try {
if (await fsHelpers.existsSync(fullPath)) {
const content = await fsHelpers.readFile(fullPath);
const contentType = getContentType(faviconPath);
return new Response(content, {
status: 200,
headers: {
"Content-Type": contentType,
"Cache-Control": "public, max-age=31536000"
// Cache for 1 year
}
});
}
return c.notFound();
} catch {
return c.notFound();
}
});
}
__name(setupFaviconRoute, "setupFaviconRoute");
// src/server/widgets/mount-widgets-dev.ts
var TMP_MCP_USE_DIR = ".mcp-use";
async function mountWidgetsDev(app, serverConfig, registerWidget, options) {
const { promises: fs } = await import("fs");
const baseRoute = options?.baseRoute || "/mcp-use/widgets";
const resourcesDir = options?.resourcesDir || "resources";
const srcDir = pathHelpers.join(getCwd(), resourcesDir);
try {
await fs.access(srcDir);
} catch (error2) {
console.log(
`[WIDGETS] No ${resourcesDir}/ directory found - skipping widget serving`
);
return;
}
const entries = [];
try {
const files = await fs.readdir(srcDir, { withFileTypes: true });
for (const dirent of files) {
if (dirent.name.startsWith("._") || dirent.name.startsWith(".DS_Store")) {
continue;
}
if (dirent.isFile() && (dirent.name.endsWith(".tsx") || dirent.name.endsWith(".ts"))) {
entries.push({
name: dirent.name.replace(/\.tsx?$/, ""),
path: pathHelpers.join(srcDir, dirent.name)
});
} else if (dirent.isDirectory()) {
const widgetPath = pathHelpers.join(srcDir, dirent.name, "widget.tsx");
try {
await fs.access(widgetPath);
entries.push({
name: dirent.name,
path: widgetPath
});
} catch {
}
}
}
} catch (error2) {
console.log(`[WIDGETS] No widgets found in ${resourcesDir}/ directory`);
return;
}
if (entries.length === 0) {
console.log(`[WIDGETS] No widgets found in ${resourcesDir}/ directory`);
return;
}
const tempDir = pathHelpers.join(getCwd(), TMP_MCP_USE_DIR);
try {
await fs.access(tempDir);
const currentWidgetNames = new Set(entries.map((e) => e.name));
const existingDirs = await fs.readdir(tempDir, { withFileTypes: true });
for (const dirent of existingDirs) {
if (dirent.isDirectory() && !currentWidgetNames.has(dirent.name)) {
const staleDir = pathHelpers.join(tempDir, dirent.name);
await fs.rm(staleDir, { recursive: true, force: true });
console.log(`[WIDGETS] Cleaned up stale widget: ${dirent.name}`);
}
}
} catch {
}
await fs.mkdir(tempDir, { recursive: true }).catch(() => {
});
let createServer;
let react;
let tailwindcss;
try {
const { createRequire } = await import("module");
const { pathToFileURL } = await import("url");
const userProjectRequire = createRequire(
pathToFileURL(pathHelpers.join(getCwd(), "package.json")).href
);
const vitePath = userProjectRequire.resolve("vite");
const reactPluginPath = userProjectRequire.resolve("@vitejs/plugin-react");
const tailwindPath = userProjectRequire.resolve("@tailwindcss/vite");
const viteModule = await import(vitePath);
createServer = viteModule.createServer;
const reactModule = await import(reactPluginPath);
react = reactModule.default;
const tailwindModule = await import(tailwindPath);
tailwindcss = tailwindModule.default;
} catch (error2) {
throw new Error(
"\u274C Widget dependencies not installed!\n\nTo use MCP widgets with resources folder, you need to install the required dependencies:\n\n npm install vite @vitejs/plugin-react @tailwindcss/vite\n # or\n pnpm add vite @vitejs/plugin-react @tailwindcss/vite\n\nThese dependencies are automatically included in projects created with 'create-mcp-use-app'.\nFor production, pre-build your widgets using 'mcp-use build'."
);
}
const widgets = entries.map((entry) => {
return {
name: entry.name,
description: `Widget: ${entry.name}`,
entry: entry.path
};
});
for (const widget2 of widgets) {
const widgetTempDir = pathHelpers.join(tempDir, widget2.name);
await fs.mkdir(widgetTempDir, { recursive: true });
const resourcesPath = pathHelpers.join(getCwd(), resourcesDir);
const relativeResourcesPath = pathHelpers.relative(widgetTempDir, resourcesPath).replace(/\\/g, "/");
const mcpUsePath = pathHelpers.join(getCwd(), "node_modules", "mcp-use");
const relativeMcpUsePath = pathHelpers.relative(widgetTempDir, mcpUsePath).replace(/\\/g, "/");
const cssContent = `@import "tailwindcss";
/* Configure Tailwind to scan the resources directory and mcp-use package */
@source "${relativeResourcesPath}";
@source "${relativeMcpUsePath}/**/*.{ts,tsx,js,jsx}";
`;
await fs.writeFile(
pathHelpers.join(widgetTempDir, "styles.css"),
cssContent,
"utf8"
);
const entryContent = `import React from 'react'
import { createRoot } from 'react-dom/client'
import './styles.css'
import Component from '${widget2.entry}'
const container = document.getElementById('widget-root')
if (container && Component) {
const root = createRoot(container)
root.render(<Component />)
}
`;
const htmlContent = `<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>${widget2.name} Widget</title>${serverConfig.favicon ? `
<link rel="icon" href="/mcp-use/public/${serverConfig.favicon}" />` : ""}
</head>
<body>
<div id="widget-root"></div>
<script type="module" src="${baseRoute}/${widget2.name}/entry.tsx"></script>
</body>
</html>`;
await fs.writeFile(
pathHelpers.join(widgetTempDir, "entry.tsx"),
entryContent,
"utf8"
);
await fs.writeFile(
pathHelpers.join(widgetTempDir, "index.html"),
htmlContent,
"utf8"
);
}
const serverOrigin = serverConfig.serverBaseUrl;
console.log(
`[WIDGETS] Serving ${entries.length} widget(s) with shared Vite dev server and HMR`
);
const ssrCssPlugin = {
name: "ssr-css-handler",
enforce: "pre",
resolveId(id, importer, options2) {
if (options2 && options2.ssr === true && (id.endsWith(".css") || id.endsWith(".module.css"))) {
return "\0ssr-css:" + id;
}
return null;
},
load(id, options2) {
if (options2 && options2.ssr === true && id.startsWith("\0ssr-css:")) {
return "export default {}";
}
return null;
}
};
const watchResourcesPlugin = {
name: "watch-resources",
configureServer(server) {
const resourcesPath = pathHelpers.join(getCwd(), resourcesDir);
server.watcher.add(resourcesPath);
console.log(`[WIDGETS] Watching resources directory: ${resourcesPath}`);
server.watcher.on("unlink", async (filePath) => {
const relativePath = pathHelpers.relative(resourcesPath, filePath);
if ((relativePath.endsWith(".tsx") || relativePath.endsWith(".ts")) && !relativePath.includes("/")) {
const widgetName = relativePath.replace(/\.tsx?$/, "");
const widgetDir = pathHelpers.join(tempDir, widgetName);
try {
await fs.access(widgetDir);
await fs.rm(widgetDir, { recursive: true, force: true });
console.log(
`[WIDGETS] Cleaned up stale widget (file removed): ${widgetName}`
);
} catch {
}
} else if (relativePath.endsWith("widget.tsx")) {
const parts = relativePath.split("/");
if (parts.length === 2) {
const widgetName = parts[0];
const widgetDir = pathHelpers.join(tempDir, widgetName);
try {
await fs.access(widgetDir);
await fs.rm(widgetDir, { recursive: true, force: true });
console.log(
`[WIDGETS] Cleaned up stale widget (file removed): ${widgetName}`
);
} catch {
}
}
}
});
server.watcher.on("unlinkDir", async (dirPath) => {
const relativePath = pathHelpers.relative(resourcesPath, dirPath);
if (relativePath && !relativePath.includes("/")) {
const widgetName = relativePath;
const widgetDir = pathHelpers.join(tempDir, widgetName);
try {
await fs.access(widgetDir);
await fs.rm(widgetDir, { recursive: true, force: true });
console.log(
`[WIDGETS] Cleaned up stale widget (directory removed): ${widgetName}`
);
} catch {
}
}
});
}
};
const nodeStubsPlugin = {
name: "node-stubs",
enforce: "pre",
resolveId(id) {
if (id === "posthog-node" || id.startsWith("posthog-node/")) {
return "\0virtual:posthog-node-stub";
}
return null;
},
load(id) {
if (id === "\0virtual:posthog-node-stub") {
return `
export class PostHog {
constructor() {}
capture() {}
identify() {}
alias() {}
flush() { return Promise.resolve(); }
shutdown() { return Promise.resolve(); }
}
export default PostHog;
`;
}
return null;
}
};
const viteServer = await createServer({
root: tempDir,
base: baseRoute + "/",
plugins: [
nodeStubsPlugin,
ssrCssPlugin,
watchResourcesPlugin,
tailwindcss(),
react()
],
resolve: {
alias: {
"@": pathHelpers.join(getCwd(), resourcesDir)
}
},
server: {
middlewareMode: true,
origin: serverOrigin,
watch: {
// Watch the resources directory for HMR to work
// This ensures changes to widget source files trigger hot reload
ignored: ["**/node_modules/**", "**/.git/**"],
// Include the resources directory in watch list
// Vite will watch files imported from outside root
usePolling: false
}
},
// Explicitly tell Vite to watch files outside root
// This is needed because widget entry files import from resources directory
optimizeDeps: {
// Exclude Node.js-only packages from browser bundling
// posthog-node is for server-side telemetry and doesn't work in browser
exclude: ["posthog-node"]
},
ssr: {
// Force Vite to transform these packages in SSR instead of using external requires
noExternal: ["@openai/apps-sdk-ui", "react-router"],
// Mark Node.js-only packages as external in SSR mode
external: ["posthog-node"]
},
define: {
// Define process.env for SSR context
"process.env.NODE_ENV": JSON.stringify(
process.env.NODE_ENV || "development"
),
"import.meta.env.DEV": true,
"import.meta.env.PROD": false,
"import.meta.env.MODE": JSON.stringify("development"),
"import.meta.env.SSR": true
}
});
app.use(`${baseRoute}/*`, async (c, next) => {
const url = new URL(c.req.url);
const pathname = url.pathname;
const widgetMatch = pathname.replace(baseRoute, "").match(/^\/([^/]+)/);
if (widgetMatch) {
const widgetName = widgetMatch[1];
const widget2 = widgets.find((w) => w.name === widgetName);
if (widget2) {
const relativePath = pathname.replace(baseRoute, "");
if (relativePath === `/${widgetName}` || relativePath === `/${widgetName}/`) {
const newUrl = new URL(c.req.url);
newUrl.pathname = `${baseRoute}/${widgetName}/index.html`;
const newRequest = new Request(newUrl.toString(), c.req.raw);
Object.defineProperty(c, "req", {
value: {
...c.req,
url: newUrl.toString(),
raw: newRequest
},
writable: false,
configurable: true
});
}
}
}
await next();
});
const viteMiddleware = await adaptConnectMiddleware(
viteServer.middlewares,
`${baseRoute}/*`
);
app.use(`${baseRoute}/*`, viteMiddleware);
setupPublicRoutes(app, false);
setupFaviconRoute(app, serverConfig.favicon, false);
app.use(`${baseRoute}/*`, async (c) => {
const url = new URL(c.req.url);
const isAsset = url.pathname.match(
/\.(js|css|png|jpg|jpeg|svg|json|ico|woff2?|tsx?)$/i
);
const message = isAsset ? "Widget asset not found" : "Widget not found";
return c.text(message, 404);
});
widgets.forEach((widget2) => {
console.log(
`[WIDGET] ${widget2.name} mounted at ${baseRoute}/${widget2.name}`
);
});
for (const widget2 of widgets) {
let metadata = {};
try {
const mod = await viteServer.ssrLoadModule(widget2.entry);
if (mod.widgetMetadata) {
metadata = mod.widgetMetadata;
const schemaField = metadata.props || metadata.inputs;
if (schemaField) {
try {
metadata.props = schemaField;
if (!metadata.inputs) {
metadata.inputs = schemaField;
}
} catch (error2) {
console.warn(
`[WIDGET] Failed to extract schema for ${widget2.name}:`,
error2
);
}
}
}
} catch (error2) {
console.warn(
`[WIDGET] Failed to load metadata for ${widget2.name}:`,
error2
);
}
await registerWidgetFromTemplate(
widget2.name,
pathHelpers.join(tempDir, widget2.name, "index.html"),
metadata.description ? metadata : { ...metadata, description: widget2.description },
serverConfig,
registerWidget,
true
// isDev
);
}
}
__name(mountWidgetsDev, "mountWidgetsDev");
// src/server/widgets/mount-widgets-production.ts
async function mountWidgetsProduction(app, serverConfig, registerWidget, options) {
const baseRoute = options?.baseRoute || "/mcp-use/widgets";
const widgetsDir = pathHelpers.join(
isDeno ? "." : getCwd(),
"dist",
"resources",
"widgets"
);
console.log("widgetsDir", widgetsDir);
const manifestPath = "./dist/mcp-use.json";
let widgets = [];
let widgetsMetadata = {};
try {
const manifestContent = await fsHelpers.readFileSync(manifestPath, "utf8");
const manifest = JSON.parse(manifestContent);
if (manifest.buildId && typeof manifest.buildId === "string") {
serverConfig.buildId = manifest.buildId;
console.log(`[WIDGETS] Build ID: ${manifest.buildId}`);
}
if (manifest.widgets && typeof manifest.widgets === "object" && !Array.isArray(manifest.widgets)) {
widgets = Object.keys(manifest.widgets);
widgetsMetadata = manifest.widgets;
console.log(`[WIDGETS] Loaded ${widgets.length} widget(s) from manifest`);
} else if (manifest.widgets && Array.isArray(manifest.widgets)) {
widgets = manifest.widgets;
console.log(
`[WIDGETS] Loaded ${widgets.length} widget(s) from manifest (legacy format)`
);
} else {
console.log("[WIDGETS] No widgets found in manifest");
}
} catch (error2) {
console.log(
"[WIDGETS] Could not read manifest file, falling back to directory listing:",
error2
);
try {
const allEntries = await fsHelpers.readdirSync(widgetsDir);
for (const name of allEntries) {
const widgetPath = pathHelpers.join(widgetsDir, name);
const indexPath = pathHelpers.join(widgetPath, "index.html");
if (await fsHelpers.existsSync(indexPath)) {
widgets.push(name);
}
}
} catch (dirError) {
console.log("[WIDGETS] Directory listing also failed:", dirError);
}
}
if (widgets.length === 0) {
console.log("[WIDGETS] No built widgets found");
return;
}
console.log(
`[WIDGETS] Serving ${widgets.length} pre-built widget(s) from dist/resources/widgets/`
);
for (const widgetName of widgets) {
const widgetPath = pathHelpers.join(widgetsDir, widgetName);
const indexPath = pathHelpers.join(widgetPath, "index.html");
const metadata = widgetsMetadata[widgetName] || {};
const mcp_connect_domain = serverConfig.serverBaseUrl ? new URL(serverConfig.serverBaseUrl || "").origin : null;
console.log("[CSP] mcp_connect_domain", mcp_connect_domain);
console.log("[CSP] cspUrls", serverConfig.cspUrls);
console.log("[CSP] metadata.appsSdkMetadata", metadata.appsSdkMetadata);
console.log("[CSP] metadata._meta", metadata._meta);
await registerWidgetFromT