mcp-adr-analysis-server
Version:
MCP server for analyzing Architectural Decision Records and project architecture
96 lines • 2.98 kB
JavaScript
/**
* Resource Router - URI routing infrastructure for templated resources
* Supports parameterized URIs like adr://adr/{id} and adr://research/{topic}
*/
import { URL } from 'url';
import { McpAdrError } from '../types/index.js';
/**
* URI Router for templated resources
* Matches URI patterns and extracts parameters
*/
export class ResourceRouter {
routes = [];
/**
* Register a route pattern with its handler
*/
register(pattern, handler, description) {
// Ensure pattern starts with /
const normalizedPattern = pattern.startsWith('/') ? pattern : `/${pattern}`;
const registration = {
pattern: normalizedPattern,
handler,
};
if (description !== undefined) {
registration.description = description;
}
this.routes.push(registration);
}
/**
* Route a URI to its handler and execute
*/
async route(uri) {
const url = new URL(uri);
const path = url.pathname;
// Try to match pattern
for (const route of this.routes) {
if (this.matchPattern(route.pattern, path)) {
const params = this.extractParams(route.pattern, path);
return await route.handler(params, url.searchParams);
}
}
throw new McpAdrError(`No route found for: ${uri}`, 'RESOURCE_NOT_FOUND');
}
/**
* Check if a pattern matches a path
*/
matchPattern(pattern, path) {
// Convert pattern to regex: /adr/{id} -> /adr/([^/]+)
const regex = pattern.replace(/\{[^}]+\}/g, '([^/]+)');
return new RegExp(`^${regex}$`).test(path);
}
/**
* Extract parameters from path based on pattern
*/
extractParams(pattern, path) {
const paramNames = pattern.match(/\{([^}]+)\}/g)?.map(p => p.slice(1, -1)) || [];
const regex = pattern.replace(/\{[^}]+\}/g, '([^/]+)');
const matches = path.match(new RegExp(`^${regex}$`));
const params = {};
if (matches) {
paramNames.forEach((name, i) => {
const value = matches[i + 1];
if (value !== undefined) {
params[name] = decodeURIComponent(value);
}
});
}
return params;
}
/**
* Get all registered routes
*/
getRoutes() {
return [...this.routes];
}
/**
* Check if a URI can be routed
*/
canRoute(uri) {
try {
const url = new URL(uri);
const path = url.pathname;
for (const route of this.routes) {
if (this.matchPattern(route.pattern, path)) {
return true;
}
}
return false;
}
catch {
return false;
}
}
}
// Singleton instance
export const resourceRouter = new ResourceRouter();
//# sourceMappingURL=resource-router.js.map