mcp-adr-analysis-server
Version:
MCP server for analyzing Architectural Decision Records and project architecture
272 lines • 9.16 kB
JavaScript
/**
* Resource Versioning and Metadata Tracking
*
* Provides version control for MCP resources, enabling:
* - Semantic versioning (MAJOR.MINOR.PATCH)
* - Backward compatibility checking
* - Version migration support
* - Metadata tracking (creation, updates, changelog)
*/
/**
* Version comparison result
*/
export var VersionComparisonResult;
(function (VersionComparisonResult) {
VersionComparisonResult["MAJOR_BREAKING"] = "major_breaking";
VersionComparisonResult["MINOR_COMPATIBLE"] = "minor_compatible";
VersionComparisonResult["PATCH_COMPATIBLE"] = "patch_compatible";
VersionComparisonResult["EQUAL"] = "equal";
VersionComparisonResult["OLDER"] = "older";
})(VersionComparisonResult || (VersionComparisonResult = {}));
/**
* Parse semantic version string to object
*/
export function parseVersion(versionString) {
const match = versionString.match(/^(\d+)\.(\d+)\.(\d+)$/);
if (!match) {
throw new Error(`Invalid semantic version: ${versionString}`);
}
return {
major: parseInt(match[1], 10),
minor: parseInt(match[2], 10),
patch: parseInt(match[3], 10),
};
}
/**
* Convert semantic version object to string
*/
export function versionToString(version) {
return `${version.major}.${version.minor}.${version.patch}`;
}
/**
* Compare two semantic versions
* @returns VersionComparisonResult indicating relationship
*/
export function compareVersions(current, requested) {
const currentV = parseVersion(current);
const requestedV = parseVersion(requested);
// Equal versions
if (currentV.major === requestedV.major &&
currentV.minor === requestedV.minor &&
currentV.patch === requestedV.patch) {
return VersionComparisonResult.EQUAL;
}
// Current is older than requested
if (currentV.major < requestedV.major ||
(currentV.major === requestedV.major && currentV.minor < requestedV.minor) ||
(currentV.major === requestedV.major &&
currentV.minor === requestedV.minor &&
currentV.patch < requestedV.patch)) {
return VersionComparisonResult.OLDER;
}
// Current is newer - check compatibility
if (currentV.major > requestedV.major) {
return VersionComparisonResult.MAJOR_BREAKING;
}
if (currentV.minor > requestedV.minor) {
return VersionComparisonResult.MINOR_COMPATIBLE;
}
return VersionComparisonResult.PATCH_COMPATIBLE;
}
/**
* Check if two versions are compatible (same major version)
*/
export function isCompatible(version1, version2) {
const v1 = parseVersion(version1);
const v2 = parseVersion(version2);
return v1.major === v2.major;
}
/**
* Increment version based on change type
*/
export function incrementVersion(current, changeType) {
const version = parseVersion(current);
switch (changeType) {
case 'major':
return versionToString({ major: version.major + 1, minor: 0, patch: 0 });
case 'minor':
return versionToString({ major: version.major, minor: version.minor + 1, patch: 0 });
case 'patch':
return versionToString({ major: version.major, minor: version.minor, patch: version.patch + 1 });
default:
throw new Error(`Invalid change type: ${changeType}`);
}
}
/**
* Create initial resource metadata
*/
export function createResourceMetadata(version = '1.0.0') {
const now = new Date().toISOString();
return {
version,
createdAt: now,
updatedAt: now,
schemaVersion: version,
};
}
/**
* Update resource metadata with new version
*/
export function updateResourceMetadata(current, newVersion, changes, breakingChanges) {
const now = new Date().toISOString();
const changelogEntry = {
version: newVersion,
date: now,
changes,
};
// Only add breakingChanges if provided
if (breakingChanges) {
changelogEntry.breakingChanges = breakingChanges;
}
return {
...current,
version: newVersion,
updatedAt: now,
changelog: [...(current.changelog || []), changelogEntry],
};
}
/**
* Mark resource as deprecated
*/
export function deprecateResource(metadata, reason, migration, removedIn) {
const deprecationNotice = {
deprecatedIn: metadata.version,
reason,
migration,
};
// Only add removedIn if provided
if (removedIn) {
deprecationNotice.removedIn = removedIn;
}
return {
...metadata,
deprecationNotice,
};
}
/**
* Check if resource is deprecated
*/
export function isDeprecated(metadata) {
return metadata.deprecationNotice !== undefined;
}
/**
* Extract version from URI search parameters
*/
export function extractVersionFromParams(searchParams) {
if (!searchParams) {
return null;
}
return searchParams.get('version');
}
/**
* Validate requested version against available versions
*/
export function validateVersionRequest(requested, current, supportedVersions) {
// No version requested - use current
if (!requested) {
return { valid: true, version: current };
}
// Check if requested version is supported
if (!supportedVersions.includes(requested)) {
return {
valid: false,
version: current,
warning: `Version ${requested} not supported. Using current version ${current}. Supported: ${supportedVersions.join(', ')}`,
};
}
// Check compatibility
const comparison = compareVersions(current, requested);
if (comparison === VersionComparisonResult.MAJOR_BREAKING) {
return {
valid: true,
version: requested,
warning: `Warning: Version ${requested} has breaking changes from current ${current}`,
};
}
return { valid: true, version: requested };
}
/**
* Generate version compatibility matrix
*/
export function generateCompatibilityMatrix(versions) {
const matrix = new Map();
for (const version of versions) {
const compatible = versions.filter((v) => isCompatible(version, v));
matrix.set(version, compatible);
}
return matrix;
}
/**
* Get changelog for specific version range
*/
export function getChangelogForRange(metadata, fromVersion, toVersion) {
if (!metadata.changelog) {
return [];
}
const from = parseVersion(fromVersion);
const to = parseVersion(toVersion);
return metadata.changelog.filter((entry) => {
const entryVersion = parseVersion(entry.version);
// Version is within range (exclusive of fromVersion, inclusive of toVersion)
const isAfterFrom = entryVersion.major > from.major ||
(entryVersion.major === from.major && entryVersion.minor > from.minor) ||
(entryVersion.major === from.major &&
entryVersion.minor === from.minor &&
entryVersion.patch > from.patch);
const isBeforeOrEqualTo = entryVersion.major < to.major ||
(entryVersion.major === to.major && entryVersion.minor < to.minor) ||
(entryVersion.major === to.major &&
entryVersion.minor === to.minor &&
entryVersion.patch <= to.patch);
return isAfterFrom && isBeforeOrEqualTo;
});
}
/**
* Extract breaking changes from changelog
*/
export function getBreakingChanges(metadata) {
if (!metadata.changelog) {
return [];
}
return metadata.changelog.filter((entry) => entry.breakingChanges && entry.breakingChanges.length > 0);
}
/**
* Format metadata for display
*/
export function formatMetadata(metadata) {
const lines = [
`Version: ${metadata.version}`,
`Schema: ${metadata.schemaVersion}`,
`Created: ${metadata.createdAt}`,
`Updated: ${metadata.updatedAt}`,
];
if (metadata.deprecationNotice) {
lines.push('');
lines.push('⚠️ DEPRECATED');
lines.push(`Deprecated in: ${metadata.deprecationNotice.deprecatedIn}`);
if (metadata.deprecationNotice.removedIn) {
lines.push(`Will be removed in: ${metadata.deprecationNotice.removedIn}`);
}
lines.push(`Reason: ${metadata.deprecationNotice.reason}`);
lines.push(`Migration: ${metadata.deprecationNotice.migration}`);
}
if (metadata.changelog && metadata.changelog.length > 0) {
lines.push('');
lines.push('Recent Changes:');
const recentChanges = metadata.changelog.slice(-3).reverse();
for (const entry of recentChanges) {
lines.push(` v${entry.version} (${entry.date})`);
for (const change of entry.changes) {
lines.push(` - ${change}`);
}
if (entry.breakingChanges && entry.breakingChanges.length > 0) {
lines.push(' ⚠️ Breaking Changes:');
for (const breaking of entry.breakingChanges) {
lines.push(` - ${breaking}`);
}
}
}
}
return lines.join('\n');
}
//# sourceMappingURL=resource-versioning.js.map