@wangkanai/devops-mcp
Version:
Dynamic Azure DevOps MCP Server for directory-based environment switching
188 lines • 7.24 kB
JavaScript
;
/**
* Local Configuration Loader
* Reads .azure-devops.json files from repository directories
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.LocalConfigLoader = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
class LocalConfigLoader {
/**
* Load Azure DevOps configuration from local file in current directory
*/
static loadLocalConfig(directory) {
const workingDir = directory || process.cwd();
const configPath = path.join(workingDir, this.CONFIG_FILE_NAME);
try {
if (!fs.existsSync(configPath)) {
console.log(`No local Azure DevOps config found in ${workingDir}`);
return null;
}
const configData = fs.readFileSync(configPath, 'utf8');
const localConfig = JSON.parse(configData);
// Validate required fields
if (!localConfig.organizationUrl || !localConfig.project || !localConfig.pat) {
throw new Error('Missing required fields: organizationUrl, project, or pat');
}
// Convert to internal config format
const config = {
organizationUrl: localConfig.organizationUrl,
project: localConfig.project,
pat: localConfig.pat
};
if (process.env.DEBUG === 'true') {
console.debug(`Loaded Azure DevOps config from ${configPath}`);
console.debug(`Organization: ${config.organizationUrl}`);
console.debug(`Project: ${config.project}`);
}
return config;
}
catch (error) {
console.error(`Failed to load local config from ${configPath}:`, error);
return null;
}
}
/**
* Check if local configuration file exists in directory
*/
static hasLocalConfig(directory) {
const workingDir = directory || process.cwd();
const configPath = path.join(workingDir, this.CONFIG_FILE_NAME);
return fs.existsSync(configPath);
}
/**
* Search for configuration file in current directory and parent directories
*/
static findLocalConfig(startDirectory) {
let currentDir = startDirectory || process.cwd();
const rootDir = path.parse(currentDir).root;
while (currentDir !== rootDir) {
const config = this.loadLocalConfig(currentDir);
if (config) {
return config;
}
// Move to parent directory
const parentDir = path.dirname(currentDir);
if (parentDir === currentDir) {
break; // Reached root
}
currentDir = parentDir;
}
console.log('No Azure DevOps configuration found in current directory tree');
return null;
}
/**
* Type guard to check if a string is a valid key of LocalAzureDevOpsConfig
*/
static isRequiredField(field) {
return ['organizationUrl', 'project', 'pat'].includes(field);
}
/**
* Check if hostname is a valid Azure DevOps domain
*/
static isValidAzureDevOpsHostname(hostname) {
// Support dev.azure.com, visualstudio.com, and custom domains
return hostname === 'dev.azure.com' ||
hostname.endsWith('.visualstudio.com') ||
hostname.endsWith('.dev.azure.com');
}
/**
* Validate local configuration structure
*/
static validateLocalConfig(config) {
const requiredFields = ['organizationUrl', 'project', 'pat'];
for (const field of requiredFields) {
if (this.isRequiredField(field) && (!config[field] || typeof config[field] !== 'string')) {
console.error(`Missing or invalid required field: ${field}`);
return false;
}
}
// Validate organization URL format
try {
const url = new URL(config.organizationUrl);
if (!this.isValidAzureDevOpsHostname(url.hostname)) {
console.error('Invalid organization URL: hostname is not recognized as a valid Azure DevOps domain');
return false;
}
}
catch {
console.error('Invalid organization URL format');
return false;
}
return true;
}
/**
* Create example configuration file
*/
static createExampleConfig(directory) {
const workingDir = directory || process.cwd();
const configPath = path.join(workingDir, this.CONFIG_FILE_NAME);
const exampleConfig = {
organizationUrl: "https://dev.azure.com/your-org",
project: "YourProject",
pat: "your-pat-token-here",
description: "Azure DevOps configuration for this repository",
settings: {
timeout: 30000,
retries: 3,
apiVersion: "7.1"
},
tools: {
workItems: true,
repositories: true,
builds: true,
pullRequests: true,
pipelines: true
},
meta: {
configVersion: "1.0",
lastUpdated: new Date().toISOString().split('T')[0],
createdBy: "devops-enhanced-mcp"
}
};
try {
fs.writeFileSync(configPath, JSON.stringify(exampleConfig, null, 2));
console.log(`Created example configuration at ${configPath}`);
}
catch (error) {
console.error(`Failed to create example configuration:`, error);
}
}
}
exports.LocalConfigLoader = LocalConfigLoader;
LocalConfigLoader.CONFIG_FILE_NAME = '.azure-devops.json';
//# sourceMappingURL=local-config-loader.js.map