uri-patterns-resource.ts•6.28 kB
import { Result, ok } from 'neverthrow';
import { UriPatternsResource, UriPatternInfo } from '../schemas/database';
import { ResourcePatterns, ResourcePattern } from '../server/resource-patterns';
import { PATTERN_IDS } from '../constants/uri-patterns';
/**
* Convert a ResourcePattern to UriPatternInfo format
*/
function convertResourcePatternToUriPatternInfo(
pattern: ResourcePattern
): UriPatternInfo {
const uriPattern = convertUriPatternToSnakeCase(pattern.uriPattern);
const parameters = extractParametersFromUriPattern(uriPattern);
const examples = generateExamplesForPattern(pattern);
return {
id: pattern.id,
uri: uriPattern,
description: convertDescriptionForDisplay(
pattern.id,
pattern.descriptionPattern
),
examples,
parameters,
};
}
/**
* Create the URI patterns resource entry itself
*/
function createUriPatternsResourceEntry(): UriPatternInfo {
return {
id: PATTERN_IDS.URI_PATTERNS,
uri: 'db://uri-patterns',
description:
'List of all available URI patterns supported by the MCP server',
examples: ['db://uri-patterns'],
parameters: [],
};
}
/**
* Handles the db://uri-patterns MCP resource
* Returns a list of all available URI patterns supported by the server
*
* @returns Result containing URI patterns resource or error
*/
export const handleUriPatternsResource = async (): Promise<
Result<UriPatternsResource, Error>
> => {
const allPatterns = ResourcePatterns.getAllPatterns();
// Convert ResourcePattern objects to UriPatternInfo format
const patterns: UriPatternInfo[] = allPatterns.map(
convertResourcePatternToUriPatternInfo
);
// Add the URI patterns resource itself
patterns.push(createUriPatternsResourceEntry());
// Sort patterns by ID for consistent ordering
patterns.sort((a, b) => a.id.localeCompare(b.id));
return ok({ patterns });
};
/**
* Description mappings for consistent display text
*/
const DESCRIPTION_OVERRIDES: Record<string, string> = {
[PATTERN_IDS.SCHEMA_LIST]:
'Complete list of all available database schemas with metadata including schema names, table counts, and version information.',
[PATTERN_IDS.SCHEMA_TABLES]:
'Comprehensive list of all tables within the {schema_name} schema, including table metadata, row counts, and basic structure information.',
[PATTERN_IDS.SCHEMA_INFO]:
'Information about the {schema_name} schema. This URI redirects to db://schemas/{schema_name}/tables.',
[PATTERN_IDS.TABLE_INFO]:
'Complete detailed information about the {table_name} table including column definitions with data types, constraints, indexes, foreign key relationships, and table statistics.',
[PATTERN_IDS.TABLE_INDEXES]:
'Detailed index information for the {table_name} table including index names, types (primary, unique, regular), column compositions, and performance statistics.',
};
/**
* Convert description to match test expectations
*/
function convertDescriptionForDisplay(
patternId: string,
originalDescription: string
): string {
return DESCRIPTION_OVERRIDES[patternId] ?? originalDescription;
}
/**
* Parameter name conversion mapping for consistency
*/
const PARAMETER_CONVERSIONS: Record<string, string> = {
'{schemaName}': '{schema_name}',
'{tableName}': '{table_name}',
};
/**
* Convert URI pattern from camelCase to snake_case for parameter names
*/
function convertUriPatternToSnakeCase(uriPattern: string): string {
let result = uriPattern;
Object.entries(PARAMETER_CONVERSIONS).forEach(([from, to]) => {
result = result.replace(new RegExp(from.replace(/[{}]/g, '\\$&'), 'g'), to);
});
return result;
}
/**
* Parameter description mapping for consistent parameter documentation
*/
const PARAMETER_DESCRIPTIONS: Record<string, string> = {
schema_name: 'Name of the database schema',
table_name: 'Name of the database table',
};
/**
* Generate parameter description based on parameter name
*/
function generateParameterDescription(paramName: string): string {
if (PARAMETER_DESCRIPTIONS[paramName]) {
return PARAMETER_DESCRIPTIONS[paramName];
}
// Fallback: convert underscore to space and add "parameter"
return `${paramName.replace(/_/g, ' ')} parameter`;
}
/**
* Extract parameter definitions from a URI pattern string
*/
function extractParametersFromUriPattern(uriPattern: string): Array<{
name: string;
description: string;
required: boolean;
}> {
const parameterMatches = uriPattern.match(/\{([^}]+)\}/g);
if (!parameterMatches) {
return [];
}
return parameterMatches.map((match) => {
const paramName = match.slice(1, -1); // Remove { and }
return {
name: paramName,
description: generateParameterDescription(paramName),
required: true,
};
});
}
/**
* Example mappings for each pattern type
*/
const PATTERN_EXAMPLES: Record<string, string[]> = {
[PATTERN_IDS.SCHEMA_LIST]: ['db://schemas'],
[PATTERN_IDS.SCHEMA_TABLES]: [
'db://schemas/public/tables',
'db://schemas/default/tables',
],
[PATTERN_IDS.SCHEMA_INFO]: ['db://schemas/public', 'db://schemas/default'],
[PATTERN_IDS.TABLE_INFO]: [
'db://schemas/public/tables/users',
'db://schemas/default/tables/products',
],
[PATTERN_IDS.TABLE_INDEXES]: [
'db://schemas/public/tables/users/indexes',
'db://schemas/default/tables/products/indexes',
],
};
/**
* Example values for parameter replacement in fallback generation
*/
const EXAMPLE_PARAMETER_VALUES: Record<string, string> = {
schemaName: 'public',
tableName: 'users',
};
/**
* Generate fallback examples by replacing parameters with example values
*/
function generateFallbackExamples(uriPattern: string): string[] {
let example = uriPattern;
Object.entries(EXAMPLE_PARAMETER_VALUES).forEach(([param, value]) => {
example = example.replace(new RegExp(`\\{${param}\\}`, 'g'), value);
});
return [example];
}
/**
* Generate example URIs for a given pattern
*/
function generateExamplesForPattern(pattern: ResourcePattern): string[] {
const predefinedExamples = PATTERN_EXAMPLES[pattern.id];
if (predefinedExamples) {
return predefinedExamples;
}
// Fallback: replace parameters with example values
return generateFallbackExamples(pattern.uriPattern);
}