/**
 * This file is part of the NocoBase (R) project.
 * Copyright (c) 2020-2024 NocoBase Co., Ltd.
 * Authors: NocoBase Team.
 *
 * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
 * For more information, please refer to: https://www.nocobase.com/agreement.
 */

var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var mcp_tools_exports = {};
__export(mcp_tools_exports, {
  DEFAULT_MCP_PACKAGE_PATTERNS: () => DEFAULT_MCP_PACKAGE_PATTERNS,
  collectMcpToolsFromSwagger: () => collectMcpToolsFromSwagger,
  normalizeMcpToolName: () => normalizeMcpToolName,
  normalizePackageName: () => normalizePackageName,
  normalizePackagePatterns: () => normalizePackagePatterns,
  parseResourceActionFromPath: () => parseResourceActionFromPath
});
module.exports = __toCommonJS(mcp_tools_exports);
var import_utils = require("@nocobase/utils");
var import_utils2 = require("@nocobase/utils");
var import_light_my_request = __toESM(require("light-my-request"));
var import_schema_utils = require("./schema-utils");
const SWAGGER_TARGETS = ["swagger.json", "swagger/index.json", "swagger"];
const DEFAULT_MCP_PACKAGE_PATTERNS = [];
function getSwaggerPrefixes() {
  if (process.env.NODE_ENV === "production") {
    return ["lib", "dist", "src"];
  }
  return ["src", "lib", "dist"];
}
function loadSwagger(packageName) {
  const prefixes = getSwaggerPrefixes();
  for (const prefix of prefixes) {
    for (const target of SWAGGER_TARGETS) {
      try {
        const file = `${packageName}/${prefix}/${target}`;
        const filePath = require.resolve(file);
        delete require.cache[filePath];
        return (0, import_utils.requireModule)(file);
      } catch (error) {
      }
    }
  }
  return {};
}
function normalizePackageName(packageName) {
  const normalized = packageName.trim();
  if (!normalized) {
    return "";
  }
  if (normalized.startsWith("@")) {
    return normalized;
  }
  return `@nocobase/${normalized}`;
}
function normalizePackagePatterns(packageNames = []) {
  return [...new Set(packageNames.map(normalizePackageName).filter(Boolean))];
}
function matchesPackagePattern(packageName, pattern) {
  if (pattern.endsWith("*")) {
    return packageName.startsWith(pattern.slice(0, -1));
  }
  return packageName === pattern;
}
function shouldIncludePackage(packageName, patterns) {
  const normalizedPatterns = (patterns == null ? void 0 : patterns.length) ? normalizePackagePatterns(patterns) : DEFAULT_MCP_PACKAGE_PATTERNS;
  return normalizedPatterns.some((pattern) => matchesPackagePattern(packageName, pattern));
}
function mergeSwagger(target, source) {
  return (0, import_utils2.merge)(target, source, {
    arrayMerge: (destinationArray, sourceArray) => sourceArray.concat(destinationArray)
  });
}
function resolveLocalRef(document, ref) {
  if (!ref.startsWith("#/")) {
    return void 0;
  }
  return ref.slice(2).split("/").reduce((current, segment) => current == null ? void 0 : current[segment], document);
}
function dereferenceNode(node, document, seen = /* @__PURE__ */ new Set()) {
  if (Array.isArray(node)) {
    return node.map((item) => dereferenceNode(item, document, seen));
  }
  if (!node || typeof node !== "object") {
    return node;
  }
  if (typeof node.$ref === "string") {
    if (seen.has(node.$ref)) {
      return {};
    }
    const resolved = resolveLocalRef(document, node.$ref);
    if (!resolved) {
      return node;
    }
    return dereferenceNode(resolved, document, /* @__PURE__ */ new Set([...seen, node.$ref]));
  }
  return Object.fromEntries(Object.entries(node).map(([key, value]) => [key, dereferenceNode(value, document, seen)]));
}
function prepareSwaggerForMcpTools(document) {
  const swagger = {
    ...document,
    paths: {}
  };
  for (const [path, pathItem] of Object.entries(document.paths || {})) {
    if (!pathItem) {
      continue;
    }
    const nextPathItem = {
      ...pathItem
    };
    if (Array.isArray(pathItem.parameters)) {
      nextPathItem.parameters = dereferenceNode(pathItem.parameters, document);
    }
    for (const method of ["get", "post", "put", "patch", "delete", "options", "head", "trace"]) {
      const operation = pathItem[method];
      if (!operation) {
        continue;
      }
      nextPathItem[method] = {
        ...operation,
        parameters: Array.isArray(operation.parameters) ? dereferenceNode(operation.parameters, document) : operation.parameters,
        requestBody: operation.requestBody ? dereferenceNode(operation.requestBody, document) : operation.requestBody
      };
    }
    swagger.paths[path] = nextPathItem;
  }
  return swagger;
}
function joinUrl(baseUrl, path) {
  if (!baseUrl) {
    return path;
  }
  const normalizedBase = baseUrl.replace(/\/$/, "");
  const normalizedPath = path.startsWith("/") ? path : `/${path}`;
  return `${normalizedBase}${normalizedPath}`;
}
function parseResourceActionFromPath(pathTemplate) {
  const normalizedPath = pathTemplate.replace(/^\/+/, "");
  const separatorIndex = normalizedPath.lastIndexOf(":");
  if (separatorIndex === -1) {
    return {};
  }
  const resourcePath = normalizedPath.slice(0, separatorIndex);
  const actionName = normalizedPath.slice(separatorIndex + 1);
  const resourceName = resourcePath.split("/").filter((segment) => segment && !segment.startsWith("{")).join(".");
  if (!resourceName || !actionName) {
    return {};
  }
  return {
    resourceName,
    actionName
  };
}
function buildQueryValue(value) {
  if (typeof value === "undefined") {
    return void 0;
  }
  if (value === null) {
    return null;
  }
  if (Array.isArray(value)) {
    return value;
  }
  if (typeof value === "object") {
    return JSON.stringify(value);
  }
  return value;
}
function formatToolName(name) {
  return name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[.:\-/\s]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toLowerCase().replace(/^(get|post|put|delete|patch|options|head|trace)_/, "");
}
function normalizeMcpToolName(tool) {
  const actionMeta = parseResourceActionFromPath(tool.pathTemplate);
  if (actionMeta.resourceName && actionMeta.actionName) {
    return formatToolName(`${actionMeta.resourceName}_${actionMeta.actionName}`);
  }
  return formatToolName(tool.name);
}
function buildRequest(tool, args = {}, context) {
  var _a;
  const missingPathParams = [];
  const path = tool.pathTemplate.replace(/\{([^}]+)\}/g, (_, key) => {
    const value = args[key];
    if (value === void 0 || value === null) {
      missingPathParams.push(key);
      return `{${key}}`;
    }
    return encodeURIComponent(String(value));
  });
  const query = {};
  const headers = {};
  const cookies = {};
  for (const parameter of tool.executionParameters ?? []) {
    const value = args[parameter.name];
    if (value === void 0) {
      continue;
    }
    if (parameter.in === "query") {
      query[parameter.name] = buildQueryValue(value);
      continue;
    }
    if (parameter.in === "header") {
      headers[parameter.name] = String(value);
      continue;
    }
    if (parameter.in === "cookie") {
      cookies[parameter.name] = String(value);
    }
  }
  if ((context == null ? void 0 : context.token) && !headers.authorization && !headers.Authorization) {
    headers.authorization = `Bearer ${context.token}`;
  }
  let payload = args.requestBody;
  if (payload !== void 0 && tool.requestBodyContentType && !headers["content-type"]) {
    headers["content-type"] = tool.requestBodyContentType;
  }
  if (payload !== void 0 && ((_a = tool.requestBodyContentType) == null ? void 0 : _a.includes("application/json")) && typeof payload === "string") {
    try {
      payload = JSON.parse(payload);
    } catch (error) {
    }
  }
  return {
    missingPathParams,
    method: tool.method.toUpperCase(),
    url: joinUrl(tool.baseUrl || "", path),
    query,
    headers,
    cookies,
    payload
  };
}
async function collectMcpToolsFromSwagger(options) {
  var _a;
  const { app, packagePatterns } = options;
  const plugins = await app.db.getRepository("applicationPlugins").find({
    filter: {
      enabled: true
    }
  });
  let swagger = {
    openapi: "3.0.3",
    info: {
      title: "NocoBase API - MCP",
      version: await app.version.get()
    },
    paths: {},
    servers: [
      {
        url: (((_a = app.resourcer.options) == null ? void 0 : _a.prefix) || "/").replace(/^[^/]/, "/$1")
      }
    ]
  };
  if (shouldIncludePackage("@nocobase/server", packagePatterns)) {
    swagger = mergeSwagger(swagger, loadSwagger("@nocobase/server"));
  }
  for (const plugin of plugins) {
    const packageName = plugin.get("packageName");
    if (!packageName || !shouldIncludePackage(packageName, packagePatterns)) {
      continue;
    }
    const pluginSwagger = loadSwagger(packageName);
    if (Object.keys(pluginSwagger).length === 0) {
      continue;
    }
    swagger = mergeSwagger(swagger, pluginSwagger);
  }
  swagger = prepareSwaggerForMcpTools(swagger);
  const { getToolsFromOpenApi } = await import("openapi-mcp-generator");
  const mcpTools = await getToolsFromOpenApi(swagger);
  return mcpTools.map((tool) => {
    const actionMeta = parseResourceActionFromPath(tool.pathTemplate);
    const mcpTool = {
      name: normalizeMcpToolName(tool),
      description: tool.description,
      resourceName: actionMeta.resourceName,
      actionName: actionMeta.actionName,
      path: tool.pathTemplate,
      method: tool.method.toUpperCase(),
      inputSchema: (0, import_schema_utils.sanitizeJsonSchemaForOpenAITools)(tool.inputSchema),
      call: async (args, context) => {
        const request = buildRequest(tool, args, context);
        if (request.missingPathParams.length > 0) {
          throw new Error(`Missing required path params: ${request.missingPathParams.join(", ")}`);
        }
        const response = await (0, import_light_my_request.default)(app.callback(), {
          method: request.method,
          url: request.url,
          query: request.query,
          headers: request.headers,
          cookies: request.cookies,
          payload: request.payload
        });
        const contentType = String(response.headers["content-type"] || "").toLowerCase();
        const body = contentType.includes("application/json") || contentType.includes("+json") ? (() => {
          try {
            return response.json();
          } catch (error) {
            return response.payload;
          }
        })() : response.payload;
        if (response.statusCode >= 400) {
          throw new Error(
            JSON.stringify({
              statusCode: response.statusCode,
              body
            })
          );
        }
        return app.aiManager.mcpToolsManager.postProcessToolResult(mcpTool, body, {
          args,
          callContext: context,
          response: {
            statusCode: response.statusCode,
            headers: response.headers,
            body
          }
        });
      }
    };
    return mcpTool;
  });
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  DEFAULT_MCP_PACKAGE_PATTERNS,
  collectMcpToolsFromSwagger,
  normalizeMcpToolName,
  normalizePackageName,
  normalizePackagePatterns,
  parseResourceActionFromPath
});
