Skip to main content
Glama

mcp-adr-analysis-server

by tosin2013
resource-versioning.ts10.1 kB
/** * 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) */ import { URLSearchParams } from 'url'; /** * Semantic version structure */ export interface SemanticVersion { major: number; minor: number; patch: number; } /** * Resource metadata for tracking changes */ export interface ResourceMetadata { version: string; // Semantic version string (e.g., "1.2.3") createdAt: string; // ISO 8601 timestamp updatedAt: string; // ISO 8601 timestamp schemaVersion: string; // Schema compatibility version changelog?: ChangelogEntry[]; // Optional change history deprecationNotice?: DeprecationNotice; // Optional deprecation info } /** * Changelog entry for tracking changes */ export interface ChangelogEntry { version: string; date: string; changes: string[]; breakingChanges?: string[]; author?: string; } /** * Deprecation notice for resources */ export interface DeprecationNotice { deprecatedIn: string; // Version when deprecated removedIn?: string; // Version when removed (if known) reason: string; migration: string; // Migration guide } /** * Version comparison result */ export enum VersionComparisonResult { MAJOR_BREAKING = 'major_breaking', MINOR_COMPATIBLE = 'minor_compatible', PATCH_COMPATIBLE = 'patch_compatible', EQUAL = 'equal', OLDER = 'older', } /** * Parse semantic version string to object */ export function parseVersion(versionString: string): SemanticVersion { 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: SemanticVersion): string { return `${version.major}.${version.minor}.${version.patch}`; } /** * Compare two semantic versions * @returns VersionComparisonResult indicating relationship */ export function compareVersions( current: string, requested: string ): VersionComparisonResult { 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: string, version2: string): boolean { const v1 = parseVersion(version1); const v2 = parseVersion(version2); return v1.major === v2.major; } /** * Increment version based on change type */ export function incrementVersion( current: string, changeType: 'major' | 'minor' | 'patch' ): string { 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: string = '1.0.0'): ResourceMetadata { const now = new Date().toISOString(); return { version, createdAt: now, updatedAt: now, schemaVersion: version, }; } /** * Update resource metadata with new version */ export function updateResourceMetadata( current: ResourceMetadata, newVersion: string, changes: string[], breakingChanges?: string[] ): ResourceMetadata { const now = new Date().toISOString(); const changelogEntry: 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: ResourceMetadata, reason: string, migration: string, removedIn?: string ): ResourceMetadata { const deprecationNotice: 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: ResourceMetadata): boolean { return metadata.deprecationNotice !== undefined; } /** * Extract version from URI search parameters */ export function extractVersionFromParams(searchParams?: URLSearchParams): string | null { if (!searchParams) { return null; } return searchParams.get('version'); } /** * Validate requested version against available versions */ export function validateVersionRequest( requested: string | null, current: string, supportedVersions: string[] ): { valid: boolean; version: string; warning?: string; } { // 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: string[]): Map<string, string[]> { const matrix = new Map<string, string[]>(); 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: ResourceMetadata, fromVersion: string, toVersion: string ): ChangelogEntry[] { 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: ResourceMetadata): ChangelogEntry[] { if (!metadata.changelog) { return []; } return metadata.changelog.filter( (entry) => entry.breakingChanges && entry.breakingChanges.length > 0 ); } /** * Format metadata for display */ export function formatMetadata(metadata: ResourceMetadata): string { 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'); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tosin2013/mcp-adr-analysis-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server