UNPKG

jaegis-npm-mcp-server

Version:

NPM Package Management MCP Server with 10 production-ready tools including complete 5-step security pipeline

1,153 lines (1,141 loc) 41.8 kB
#!/usr/bin/env node "use strict"; 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 __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 )); // src/index.ts var import_server = require("@modelcontextprotocol/sdk/server/index.js"); var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js"); var import_types = require("@modelcontextprotocol/sdk/types.js"); var dotenv = __toESM(require("dotenv")); var fs2 = __toESM(require("fs")); var path2 = __toESM(require("path")); var os = __toESM(require("os")); var semver = __toESM(require("semver")); var import_child_process = require("child_process"); // src/security_scan.ts var fs = __toESM(require("fs")); var path = __toESM(require("path")); var SENSITIVE_FILES = [ ".env", ".env.local", ".env.production", ".env.development", "id_rsa", "id_dsa", "id_ed25519", "*.pem", "*.key", "*.p12", "*.pfx", "npm-debug.log", "yarn-error.log", ".npmrc", ".pypirc", "secrets.json", "credentials.json" ]; var IGNORED_DIRS = [ "node_modules", ".git", ".svn", ".hg", "dist", "build", "coverage", "__pycache__", ".pytest_cache", ".mypy_cache", "logs", "cache", "tmp", "temp" ]; var SENSITIVE_PATTERNS = [ { name: "AWS Access Key", regex: /AKIA[0-9A-Z]{16}/ }, { name: "Generic Private Key", regex: /-----BEGIN PRIVATE KEY-----/ }, { name: "Generic API Key", regex: /(api_key|apikey|secret|token)[\s]*[:=][\s]*['"][a-zA-Z0-9_\-]{20,}['"]/i }, { name: "GitHub Token", regex: /gh[pousr]_[a-zA-Z0-9]{36}/ }, { name: "NPM Token", regex: /npm_[a-zA-Z0-9]{36}/ }, { name: "Slack Token", regex: /xox[baprs]-([0-9a-zA-Z]{10,48})/ } ]; async function scanDirectory(dirPath) { const issues = []; const warnings = []; try { const files = await getAllFiles(dirPath); for (const file of files) { const relativePath = path.relative(dirPath, file); const fileName = path.basename(file); if (SENSITIVE_FILES.some((pattern) => { if (pattern.startsWith("*")) return fileName.endsWith(pattern.slice(1)); return fileName === pattern; })) { issues.push(`Sensitive file found: ${relativePath}`); } try { const stats = fs.statSync(file); if (stats.size < 1024 * 1024) { const content = fs.readFileSync(file, "utf8"); if (!content.includes("\0")) { for (const pattern of SENSITIVE_PATTERNS) { if (pattern.regex.test(content)) { issues.push(`Potential ${pattern.name} found in: ${relativePath}`); } } } } } catch (err) { } } } catch (error) { issues.push(`Failed to scan directory: ${error instanceof Error ? error.message : String(error)}`); } return { passed: issues.length === 0, issues, warnings }; } async function getAllFiles(dir) { const files = []; async function traverse(currentDir) { const entries = fs.readdirSync(currentDir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(currentDir, entry.name); if (IGNORED_DIRS.includes(entry.name)) continue; if (entry.isDirectory()) { await traverse(fullPath); } else { files.push(fullPath); } } } await traverse(dir); return files; } // src/index.ts console.log = (...args) => { try { console.error(...args); } catch { } }; (function loadEnvCascade() { const homeEnv = path2.join(os.homedir() || "", ".mcp", ".env"); const localEnv = path2.resolve(process.cwd(), ".env"); for (const p of [homeEnv, localEnv]) { try { if (fs2.existsSync(p)) { dotenv.config({ path: p, override: true }); } } catch { } } })(); var hasListTools = false; var hasToolsJson = false; var hasHelp = false; var formatTable = false; function sanitizeCredential(value) { if (!value || value.length < 8) return "****"; return `${value.substring(0, 4)}****...****${value.substring(value.length - 4)}`; } function showHelp() { console.error(` NPM JAEGIS MCP Server v1.0.0 - NPM package management with 8 specialized tools USAGE: npm-mcp-server [OPTIONS] OPTIONS: --list-tools List all available tools with descriptions --tools-json Output complete MCP tools schema as JSON --format=table Use table format for tool listings (default: JSON) --help, -h Show this help message ENVIRONMENT VARIABLES: NPM_TOKEN NPM access token (required) Format: npm_* Generate at: https://www.npmjs.com/settings/tokens NPM_USERNAME NPM username (required) NPM_EMAIL NPM email (required) NPM_REGISTRY NPM registry URL (optional, default: https://registry.npmjs.org/) DEFAULT_PACKAGE Default package name (optional, default: JAEGIS-web-os) EXAMPLES: # List all tools in table format npm-mcp-server --list-tools --format=table # Get tools schema for MCP client integration npm-mcp-server --tools-json # Start the MCP server (default) npm-mcp-server QUICK SETUP: 1. Generate NPM token: https://www.npmjs.com/settings/tokens 2. Set environment variables: export NPM_TOKEN=npm_your_token_here export NPM_USERNAME=your_username export NPM_EMAIL=your_email@example.com 3. Run: npm-mcp-server For detailed documentation, see: README.md `); } function getToolsInfo() { return [ { name: "npm_package_info", category: "Package Information", description: "Get detailed information about an NPM package including metadata and dependencies", requiredParams: ["package_name"], optionalParams: ["version"], inputSchema: { type: "object", properties: { package_name: { type: "string" } } } }, { name: "npm_search_packages", category: "Package Discovery", description: "Search for NPM packages using keywords and filters", requiredParams: ["query"], optionalParams: ["limit"], inputSchema: { type: "object", properties: { query: { type: "string" } } } }, { name: "npm_publish_package", category: "Package Publishing", description: "Publish a package to NPM registry with authentication and specified configuration", requiredParams: ["package_path"], optionalParams: ["tag", "access", "additional_flags"], inputSchema: { type: "object", properties: { package_path: { type: "string" } } } }, { name: "npm_unpublish_package", category: "Package Management", description: "Unpublish a package version from NPM registry (irreversible)", requiredParams: ["package_name"], optionalParams: ["version"], inputSchema: { type: "object", properties: { package_name: { type: "string" } } } }, { name: "npm_deprecate_package", category: "Package Management", description: "Deprecate a package version with a custom message", requiredParams: ["package_name", "version", "message"], optionalParams: [], inputSchema: { type: "object", properties: { package_name: { type: "string" }, version: { type: "string" }, message: { type: "string" } } } }, { name: "npm_list_versions", category: "Package Information", description: "List all available versions of a package with release information", requiredParams: ["package_name"], optionalParams: [], inputSchema: { type: "object", properties: { package_name: { type: "string" } } } }, { name: "npm_download_stats", category: "Package Analytics", description: "Get download statistics and analytics for a package", requiredParams: ["package_name"], optionalParams: ["period"], inputSchema: { type: "object", properties: { package_name: { type: "string" } } } }, { name: "npm_validate_package", category: "Package Validation", description: "Validate package.json and check for publishing readiness", requiredParams: ["package_path"], optionalParams: [], inputSchema: { type: "object", properties: { package_path: { type: "string" } } } }, { name: "security_check", category: "Security", description: "Scan a directory for sensitive files and secrets", requiredParams: ["path"], optionalParams: [], inputSchema: { type: "object", properties: { path: { type: "string" } } } }, { name: "health_check", category: "Diagnostics", description: "Return server health, version, uptime, and dependency status", requiredParams: [], optionalParams: [], inputSchema: { type: "object", properties: {} } } ]; } function displayToolsTable(tools) { console.error("\n\u{1F4E6} NPM JAEGIS MCP Server - Tool Inventory\n"); console.error("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"); console.error("\u2502 Tool Name \u2502 Category \u2502 Required \u2502 Optional \u2502"); console.error("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524"); tools.forEach((tool) => { const name = tool.name.padEnd(35); const category = tool.category.padEnd(32); const required = tool.requiredParams.length.toString().padEnd(8); const optional = tool.optionalParams.length.toString().padEnd(8); console.error(`\u2502 ${name} \u2502 ${category} \u2502 ${required} \u2502 ${optional} \u2502`); }); console.error("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"); const categories = tools.reduce((acc, tool) => { acc[tool.category] = (acc[tool.category] || 0) + 1; return acc; }, {}); console.error("\n\u{1F4CB} Tool Summary by Category:"); Object.entries(categories).forEach(([category, count]) => { console.error(` \u2022 ${category}: ${count} tools`); }); console.error(` \u{1F3AF} Total: ${tools.length} tools available `); } function displayToolsJson(tools, includeSchema = false) { const output = { server: "npm-jaegis-mcp-server", version: "1.0.0", totalTools: tools.length, categories: tools.reduce((acc, tool) => { acc[tool.category] = (acc[tool.category] || 0) + 1; return acc; }, {}), tools: tools.map((tool) => ({ name: tool.name, category: tool.category, description: tool.description, parameters: { required: tool.requiredParams, optional: tool.optionalParams, total: tool.requiredParams.length + tool.optionalParams.length }, ...includeSchema && { inputSchema: tool.inputSchema } })) }; console.error(JSON.stringify(output, null, 2)); } if (hasHelp) { showHelp(); process.exit(0); } if (hasListTools || hasToolsJson) { const tools = getToolsInfo(); if (hasToolsJson) { displayToolsJson(tools, true); } else if (formatTable) { displayToolsTable(tools); } else { displayToolsJson(tools, false); } process.exit(0); } var NPMJAEGISMCPServer = class { server; config; constructor() { this.config = this.loadConfig(); this.validateConfiguration(); this.server = new import_server.Server( { name: "npm-jaegis-mcp-server", version: "1.0.0" }, { capabilities: { tools: {} } } ); this.setupToolHandlers(); this.setupErrorHandling(); } loadConfig() { const token = process.env.NPM_TOKEN || ""; const username = process.env.NPM_USERNAME || ""; const email = process.env.NPM_EMAIL || ""; return { token, username, email, registry: process.env.NPM_REGISTRY || "https://registry.npmjs.org/", scope: process.env.NPM_SCOPE, defaultPackage: process.env.DEFAULT_PACKAGE || "JAEGIS-web-os" }; } validateConfiguration() { if (this.config.token && !this.config.token.startsWith("npm_")) { console.warn("Warning: NPM token format may be invalid. Expected format: npm_*"); } if (!this.config.username || !this.config.email || !this.config.token) { console.warn("NPM credentials are not fully set; auth-required tools may fail until provided."); } console.error(`NPM MCP Server configured for user: ${sanitizeCredential(this.config.username || "unknown")}`); console.error(`Registry: ${this.config.registry}`); console.error(`Default package: ${this.config.defaultPackage}`); } setupToolHandlers() { this.server.setRequestHandler(import_types.ListToolsRequestSchema, async () => { return { tools: [ { name: "npm_package_info", description: "Get detailed information about an NPM package", inputSchema: { type: "object", properties: { package_name: { type: "string", description: "Name of the NPM package" }, version: { type: "string", description: "Specific version (optional, defaults to latest)" } }, required: ["package_name"] } }, { name: "npm_search_packages", description: "Search for NPM packages", inputSchema: { type: "object", properties: { query: { type: "string", description: "Search query" }, limit: { type: "number", description: "Maximum number of results (default: 20)" } }, required: ["query"] } }, { name: "npm_publish_package", description: "Publish a package to NPM registry with authentication", inputSchema: { type: "object", properties: { package_path: { type: "string", description: "Path to package directory containing package.json" }, tag: { type: "string", description: "Distribution tag (default: latest)" }, access: { type: "string", enum: ["public", "restricted"], description: "Package access level (default: public)" }, additional_flags: { type: "string", description: 'Additional npm publish flags (optional, e.g., "--dry-run")' } }, required: ["package_path"] } }, { name: "npm_unpublish_package", description: "Unpublish a package version from NPM", inputSchema: { type: "object", properties: { package_name: { type: "string", description: "Name of the package to unpublish" }, version: { type: "string", description: "Specific version to unpublish (optional, unpublishes entire package if not specified)" } }, required: ["package_name"] } }, { name: "npm_deprecate_package", description: "Deprecate a package version", inputSchema: { type: "object", properties: { package_name: { type: "string", description: "Name of the package" }, version: { type: "string", description: "Version range to deprecate" }, message: { type: "string", description: "Deprecation message" } }, required: ["package_name", "version", "message"] } }, { name: "npm_list_versions", description: "List all versions of a package", inputSchema: { type: "object", properties: { package_name: { type: "string", description: "Name of the package" } }, required: ["package_name"] } }, { name: "npm_download_stats", description: "Get download statistics for a package", inputSchema: { type: "object", properties: { package_name: { type: "string", description: "Name of the package" }, period: { type: "string", enum: ["last-day", "last-week", "last-month", "last-year"], description: "Time period for stats (default: last-month)" } }, required: ["package_name"] } }, { name: "npm_validate_package", description: "Validate package.json and check for publishing readiness", inputSchema: { type: "object", properties: { package_path: { type: "string", description: "Path to package directory" } }, required: ["package_path"] } }, { name: "health_check", description: "Return server health, version, uptime, and dependency status", inputSchema: { type: "object", properties: {} } } ] }; }); this.server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case "npm_package_info": return await this.getPackageInfo(args); case "npm_search_packages": return await this.searchPackages(args); case "npm_publish_package": return await this.publishPackage(args); case "npm_unpublish_package": return await this.unpublishPackage(args); case "npm_deprecate_package": return await this.deprecatePackage(args); case "npm_list_versions": return await this.listVersions(args); case "npm_download_stats": return await this.getDownloadStats(args); case "npm_validate_package": return await this.validatePackage(args); case "security_check": return await this.securityCheck(args); case "health_check": { const version = (() => { try { return JSON.parse(fs2.readFileSync(path2.resolve(__dirname, "..", "package.json"), "utf8")).version; } catch { return "unknown"; } })(); const uptime = process.uptime(); return { content: [{ type: "text", text: JSON.stringify({ status: "ok", version, uptime, success: true }) }] }; } default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { return { content: [ { type: "text", text: JSON.stringify({ error: error instanceof Error ? error.message : String(error), success: false }, null, 2) } ] }; } }); } setupErrorHandling() { this.server.onerror = (error) => { console.error("[NPM MCP Server Error]", error); }; process.on("SIGINT", async () => { await this.server.close(); process.exit(0); }); } // Tool implementations async getPackageInfo(args) { try { const packageName = args.package_name; if (!packageName) throw new Error("package_name is required"); const url = `${this.config.registry}${packageName}`; const response = await fetch(url); if (!response.ok) { throw new Error(`Package not found: ${packageName}`); } const data = await response.json(); const version = args.version || "latest"; const versionData = version === "latest" ? data["dist-tags"]?.latest : version; const packageData = data.versions?.[versionData] || data; return { content: [ { type: "text", text: JSON.stringify({ name: data.name, version: packageData.version, description: data.description, author: data.author, license: data.license, homepage: data.homepage, repository: data.repository, keywords: data.keywords, dependencies: packageData.dependencies, devDependencies: packageData.devDependencies, downloads: data.downloads, lastPublished: data.time?.modified, success: true }, null, 2) } ] }; } catch (error) { return { content: [ { type: "text", text: JSON.stringify({ error: error instanceof Error ? error.message : String(error), success: false }, null, 2) } ] }; } } async searchPackages(args) { try { const query = args.query; const limit = Math.min(args.limit || 20, 100); if (!query) throw new Error("query is required"); const url = `${this.config.registry}-/v1/search?text=${encodeURIComponent(query)}&size=${limit}`; const response = await fetch(url); if (!response.ok) { throw new Error("Search failed"); } const data = await response.json(); const results = data.objects?.map((obj) => ({ name: obj.package.name, version: obj.package.version, description: obj.package.description, date: obj.package.date, links: obj.package.links })) || []; return { content: [ { type: "text", text: JSON.stringify({ query, total: data.total, results, success: true }, null, 2) } ] }; } catch (error) { return { content: [ { type: "text", text: JSON.stringify({ error: error instanceof Error ? error.message : String(error), success: false }, null, 2) } ] }; } } async publishPackage(args) { try { const packagePath = args.package_path; if (!packagePath) throw new Error("package_path is required"); if (!this.config.token) throw new Error("NPM_TOKEN environment variable is required"); const packageJsonPath = path2.join(packagePath, "package.json"); if (!fs2.existsSync(packageJsonPath)) { throw new Error(`package.json not found at ${packageJsonPath}`); } const packageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf8")); const tag = args.tag || "latest"; const access = args.access || "public"; const additionalFlags = args.additional_flags || ""; const validationIssues = []; if (!packageJson.name) validationIssues.push("Missing required field: name"); if (!packageJson.version) validationIssues.push("Missing required field: version"); if (!semver.valid(packageJson.version)) validationIssues.push("Invalid version format (must be semver)"); if (validationIssues.length > 0) { throw new Error(`Package validation failed: ${validationIssues.join("; ")}`); } console.error("[Security] Step 1/5: Checking for version conflicts..."); const { checkVersionConflictAsync } = require(path2.join(__dirname, "../../../scripts/registry_manager.js")); const versionConflict = await checkVersionConflictAsync(packageJson.name, packageJson.version, "npm"); if (versionConflict) { throw new Error(`Version ${packageJson.version} already exists on NPM registry. Aborting to prevent conflicts.`); } console.error("[Security] \u2713 Version available"); console.error("[Security] Step 2/5: Checking README..."); const { smartReadmeUpdate } = require(path2.join(__dirname, "../../../scripts/professional_readme_generator.js")); await smartReadmeUpdate(packagePath, { name: packageJson.name, version: packageJson.version, description: packageJson.description, author: packageJson.author, license: packageJson.license, repository: packageJson.repository?.url || packageJson.repository, homepage: packageJson.homepage, keywords: packageJson.keywords || [], tools: [], examples: [] }, "npm"); console.error("[Security] \u2713 README validated"); console.error("[Security] Step 3/5: Deep security scan..."); const scanResult = await scanDirectory(packagePath); if (!scanResult.passed) { throw new Error(`Security check failed: ${scanResult.issues.join("; ")}`); } console.error("[Security] \u2713 No sensitive data detected"); console.error("[Security] Step 4/5: Sanitizing build artifacts..."); const { sanitizeNpmPackage } = require(path2.join(__dirname, "../../../scripts/artifact_sanitizer.js")); const sanitizationResult = await sanitizeNpmPackage(packagePath); if (sanitizationResult.success && !sanitizationResult.clean) { console.error(`[Security] \u26A0 Sanitized ${sanitizationResult.report.filesModified} files`); } console.error("[Security] \u2713 Artifacts clean"); console.error("[Security] Step 5/5: Triggering historical scan (async)..."); const { scanNpmPackageHistory, autoRemediateNpm } = require(path2.join(__dirname, "../../../scripts/npm_historical_scanner.js")); setImmediate(async () => { try { console.error(`[Historical Scan] Scanning ${packageJson.name} history...`); const historicalResults = await scanNpmPackageHistory(packageJson.name, { limit: 10 }); if (historicalResults.compromisedVersions.length > 0) { console.error(`[Historical Scan] \u26A0 Found ${historicalResults.compromisedVersions.length} compromised versions`); await autoRemediateNpm(packageJson.name, historicalResults); } else { console.error("[Historical Scan] \u2713 All historical versions clean"); } } catch (e) { console.error(`[Historical Scan] Error: ${e.message}`); } }); console.error("[Security] \u2713 Historical scan queued"); console.error("[Security] ==================== ALL CHECKS PASSED ===================="); const publishEnv = { ...process.env, NPM_TOKEN: this.config.token, NPM_CONFIG_REGISTRY: this.config.registry, NPM_CONFIG__AUTH_TOKEN: this.config.token, NPM_CONFIG_USERCONFIG: path2.join(os.homedir(), ".npmrc") }; const publishArgs = ["publish", "--access", access, "--tag", tag]; if (additionalFlags) { publishArgs.push(...additionalFlags.split(" ")); } console.error(`[NPM Publish] Executing: npm ${publishArgs.join(" ")} in ${packagePath}`); console.error(`[NPM Publish] Package: ${packageJson.name}@${packageJson.version}`); const publishOutput = await new Promise((resolve2, reject) => { const child = (0, import_child_process.spawn)("npm", publishArgs, { cwd: packagePath, env: publishEnv, stdio: ["pipe", "pipe", "pipe"] }); let output = ""; let errorOutput = ""; child.stdout.on("data", (data) => { const str = data.toString(); output += str; console.error(`[NPM] ${str}`); const urlMatch = str.match(/https:\/\/www\.npmjs\.com\/auth\/cli\/[a-f0-9-]+/); if (urlMatch) { const url = urlMatch[0]; console.error(`[NPM] Auth URL detected: ${url}`); try { const startCmd = process.platform === "win32" ? "start" : process.platform === "darwin" ? "open" : "xdg-open"; require("child_process").exec(`${startCmd} ${url}`); } catch (e) { console.error("Failed to open browser automatically"); } if (child.stdin) { child.stdin.write("\n"); } } }); child.stderr.on("data", (data) => { const str = data.toString(); errorOutput += str; console.error(`[NPM Error] ${str}`); }); child.on("close", (code) => { if (code === 0) { resolve2(output); } else { reject(new Error(`npm publish failed with code ${code}: ${errorOutput}`)); } }); child.on("error", (err) => { reject(err); }); }); console.error(`[NPM Publish] Success: ${packageJson.name}@${packageJson.version}`); return { content: [ { type: "text", text: JSON.stringify({ success: true, message: `Successfully published ${packageJson.name}@${packageJson.version} to NPM registry`, package: packageJson.name, version: packageJson.version, tag, access, registry: this.config.registry, publishOutput: publishOutput.substring(0, 500), // Limit output size timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2) } ] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`[NPM Publish Error] ${errorMessage}`); return { content: [ { type: "text", text: JSON.stringify({ success: false, error: errorMessage, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2) } ], isError: true }; } } async securityCheck(args) { try { const dirPath = args.path; if (!dirPath) throw new Error("path is required"); const result = await scanDirectory(dirPath); return { content: [ { type: "text", text: JSON.stringify({ operation: "security_check", path: dirPath, ...result, success: true }, null, 2) } ] }; } catch (error) { return { content: [ { type: "text", text: JSON.stringify({ error: error instanceof Error ? error.message : String(error), success: false }, null, 2) } ] }; } } async unpublishPackage(args) { try { const packageName = args.package_name; if (!packageName) throw new Error("package_name is required"); if (!this.config.token) throw new Error("NPM_TOKEN environment variable is required"); return { content: [ { type: "text", text: JSON.stringify({ message: `Unpublish request for ${packageName}${args.version ? `@${args.version}` : " (all versions)"}. This is a destructive operation.`, package: packageName, version: args.version || "all", warning: "Unpublishing is irreversible. Use npm unpublish command to complete.", success: true }, null, 2) } ] }; } catch (error) { return { content: [ { type: "text", text: JSON.stringify({ error: error instanceof Error ? error.message : String(error), success: false }, null, 2) } ] }; } } async deprecatePackage(args) { try { const packageName = args.package_name; const version = args.version; const message = args.message; if (!packageName || !version || !message) { throw new Error("package_name, version, and message are all required"); } if (!this.config.token) throw new Error("NPM_TOKEN environment variable is required"); return { content: [ { type: "text", text: JSON.stringify({ message: `Deprecation request for ${packageName}@${version}`, package: packageName, version, deprecationMessage: message, warning: "Use npm deprecate command to complete this action.", success: true }, null, 2) } ] }; } catch (error) { return { content: [ { type: "text", text: JSON.stringify({ error: error instanceof Error ? error.message : String(error), success: false }, null, 2) } ] }; } } async listVersions(args) { try { const packageName = args.package_name; if (!packageName) throw new Error("package_name is required"); const url = `${this.config.registry}${packageName}`; const response = await fetch(url); if (!response.ok) { throw new Error(`Package not found: ${packageName}`); } const data = await response.json(); const versions = Object.keys(data.versions || {}).sort((a, b) => { try { return semver.compare(b, a); } catch { return b.localeCompare(a); } }); return { content: [ { type: "text", text: JSON.stringify({ package: packageName, totalVersions: versions.length, versions, latest: data["dist-tags"]?.latest, success: true }, null, 2) } ] }; } catch (error) { return { content: [ { type: "text", text: JSON.stringify({ error: error instanceof Error ? error.message : String(error), success: false }, null, 2) } ] }; } } async getDownloadStats(args) { try { const packageName = args.package_name; const period = args.period || "last-month"; if (!packageName) throw new Error("package_name is required"); const url = `https://api.npmjs.org/downloads/point/${period}/${packageName}`; const response = await fetch(url); if (!response.ok) { throw new Error(`Failed to fetch download stats for ${packageName}`); } const data = await response.json(); return { content: [ { type: "text", text: JSON.stringify({ package: packageName, period, downloads: data.downloads, start: data.start, end: data.end, success: true }, null, 2) } ] }; } catch (error) { return { content: [ { type: "text", text: JSON.stringify({ error: error instanceof Error ? error.message : String(error), success: false }, null, 2) } ] }; } } async validatePackage(args) { try { const packagePath = args.package_path; if (!packagePath) throw new Error("package_path is required"); const packageJsonPath = path2.join(packagePath, "package.json"); if (!fs2.existsSync(packageJsonPath)) { throw new Error(`package.json not found at ${packageJsonPath}`); } const packageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf8")); const issues = []; if (!packageJson.name) issues.push("Missing required field: name"); if (!packageJson.version) issues.push("Missing required field: version"); if (!packageJson.description) issues.push("Missing recommended field: description"); if (!packageJson.author) issues.push("Missing recommended field: author"); if (!packageJson.license) issues.push("Missing recommended field: license"); if (!packageJson.repository) issues.push("Missing recommended field: repository"); if (packageJson.name && !/^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(packageJson.name)) { issues.push("Invalid package name format"); } if (packageJson.version && !semver.valid(packageJson.version)) { issues.push("Invalid version format (must be semver)"); } return { content: [ { type: "text", text: JSON.stringify({ package: packageJson.name, version: packageJson.version, isValid: issues.length === 0, issues, warnings: issues.length > 0 ? "Fix issues before publishing" : "Package is ready to publish", success: true }, null, 2) } ] }; } catch (error) { return { content: [ { type: "text", text: JSON.stringify({ error: error instanceof Error ? error.message : String(error), success: false }, null, 2) } ] }; } } async run() { const transport = new import_stdio.StdioServerTransport(); await this.server.connect(transport); const tools = getToolsInfo(); const categories = tools.reduce((acc, tool) => { acc[tool.category] = (acc[tool.category] || 0) + 1; return acc; }, {}); console.error("\u{1F4E6} NPM JAEGIS MCP Server (v1.0.0) running on stdio"); console.error(`\u{1F4CA} Total Tools: ${tools.length} | Categories: ${Object.keys(categories).length}`); console.error(`\u{1F527} Configured for user: ${sanitizeCredential(this.config.username)}`); console.error(`\u{1F310} Registry: ${this.config.registry}`); console.error("\u2705 Server ready with NPM package management tools"); console.error("\u{1F4A1} Use --list-tools to see all available tools"); } }; if (require.main === module) { try { const server = new NPMJAEGISMCPServer(); server.run().catch((error) => { console.error("Failed to start NPM JAEGIS MCP Server:", error.message); process.exit(1); }); } catch (error) { console.error("Failed to initialize NPM JAEGIS MCP Server:"); console.error(error instanceof Error ? error.message : String(error)); process.exit(1); } } //# sourceMappingURL=index.cjs.map