// @lifecycle canonical - Methodology resource handler for MCP resources protocol.
/**
* Methodology Resources Handler
*
* Registers MCP resources for token-efficient methodology/framework discovery and content retrieval.
*
* URI Patterns:
* - resource://methodology/ → List all methodologies (minimal metadata)
* - resource://methodology/{id} → Methodology definition + guidelines
* - resource://methodology/{id}/system-prompt → Raw system prompt template only
*/
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
import { ResourceNotFoundError, RESOURCE_URI_PATTERNS } from '../types.js';
import type { MethodologyResourceMetadata, ResourceDependencies } from '../types.js';
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import type { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js';
/**
* Build resource URI from pattern and optional ID
* Patterns already include the full URI scheme (resource://...)
*/
function buildUri(pattern: string, id?: string): string {
if (id !== undefined && id !== '') {
return pattern.replace('{id}', id);
}
return pattern;
}
/**
* Register methodology-related MCP resources.
*
* Resources read from the frameworkManager at request time to ensure
* hot-reload compatibility - changes are visible immediately.
*/
export function registerMethodologyResources(
server: McpServer,
dependencies: ResourceDependencies
): void {
const { logger, frameworkManager } = dependencies;
if (frameworkManager === undefined) {
logger.warn('[MethodologyResources] FrameworkManager not available, skipping registration');
return;
}
// Resource: List all methodologies (minimal metadata for token efficiency)
server.registerResource(
'methodologies',
new ResourceTemplate(RESOURCE_URI_PATTERNS.METHODOLOGY_LIST, {
list: async () => {
const frameworks = frameworkManager.listFrameworks(false); // Include disabled
logger.debug(`[MethodologyResources] Listing ${frameworks.length} methodologies`);
const resources: MethodologyResourceMetadata[] = frameworks.map((f) => ({
uri: buildUri(RESOURCE_URI_PATTERNS.METHODOLOGY_ITEM, f.id),
name: f.id,
title: f.name,
description: f.description.slice(0, 80),
mimeType: 'text/markdown',
type: f.type,
enabled: f.enabled,
priority: f.priority,
}));
return { resources };
},
}),
{
description: 'List of all available methodologies/frameworks with minimal metadata',
mimeType: 'text/plain',
},
async (): Promise<ReadResourceResult> => {
// When reading the list resource itself, return compact text format
// Format: id: name [disabled] - 4x more token-efficient than JSON
const frameworks = frameworkManager.listFrameworks(false);
const lines = frameworks.map((f) => {
const status = f.enabled ? '' : ' [disabled]';
return `${f.id}: ${f.name}${status}`;
});
return {
contents: [
{
uri: buildUri(RESOURCE_URI_PATTERNS.METHODOLOGY_LIST),
mimeType: 'text/plain',
text: `Methodologies (${frameworks.length}):\n${lines.join('\n')}`,
},
],
};
}
);
// Resource: Individual methodology with full definition and guidelines
server.registerResource(
'methodology',
new ResourceTemplate(RESOURCE_URI_PATTERNS.METHODOLOGY_ITEM, {
list: undefined, // Individual items discovered via list resource
}),
{
description: 'Individual methodology with definition and execution guidelines',
mimeType: 'text/markdown',
},
async (uri, variables): Promise<ReadResourceResult> => {
const id = variables['id'] as string;
const framework = frameworkManager.getFramework(id);
if (framework === undefined) {
throw new ResourceNotFoundError('Methodology', id);
}
logger.debug(`[MethodologyResources] Reading methodology: ${id}`);
// Build full methodology content with metadata header
const content = buildMethodologyContent(framework);
return {
contents: [
{
uri: uri.toString(),
mimeType: 'text/markdown',
text: content,
},
],
};
}
);
// Resource: Raw system prompt template only (for minimal token usage)
server.registerResource(
'methodology-system-prompt',
new ResourceTemplate(RESOURCE_URI_PATTERNS.METHODOLOGY_SYSTEM_PROMPT, {
list: undefined, // Discovered via methodology/{id} resource
}),
{
description: 'Raw methodology system prompt template only',
mimeType: 'text/markdown',
},
async (uri, variables): Promise<ReadResourceResult> => {
const id = variables['id'] as string;
const framework = frameworkManager.getFramework(id);
if (framework === undefined) {
throw new ResourceNotFoundError('Methodology', id);
}
logger.debug(`[MethodologyResources] Reading system prompt for: ${id}`);
return {
contents: [
{
uri: uri.toString(),
mimeType: 'text/markdown',
text:
framework.systemPromptTemplate.length > 0
? framework.systemPromptTemplate
: '(No system prompt template)',
},
],
};
}
);
logger.info('[MethodologyResources] Registered methodology resources');
}
/**
* Build formatted methodology content with metadata header and guidelines
*/
function buildMethodologyContent(framework: {
id: string;
name: string;
description: string;
type: string;
systemPromptTemplate: string;
executionGuidelines: string[];
priority: number;
enabled: boolean;
}): string {
const lines: string[] = [];
// Metadata header
lines.push(`# ${framework.name}`);
lines.push('');
lines.push(`**ID:** \`${framework.id}\``);
lines.push(`**Type:** ${framework.type}`);
lines.push(`**Priority:** ${framework.priority}`);
lines.push(`**Enabled:** ${framework.enabled ? 'Yes' : 'No'}`);
if (framework.description.length > 0) {
lines.push('');
lines.push(`> ${framework.description}`);
}
// Execution guidelines
if (framework.executionGuidelines.length > 0) {
lines.push('');
lines.push('## Execution Guidelines');
lines.push('');
for (const guideline of framework.executionGuidelines) {
lines.push(`- ${guideline}`);
}
}
// System prompt template (truncated preview)
if (framework.systemPromptTemplate !== '') {
lines.push('');
lines.push('## System Prompt Template');
lines.push('');
const preview =
framework.systemPromptTemplate.length > 500
? framework.systemPromptTemplate.slice(0, 500) + '...'
: framework.systemPromptTemplate;
lines.push('```markdown');
lines.push(preview);
lines.push('```');
if (framework.systemPromptTemplate.length > 500) {
lines.push('');
lines.push(
`*Full template available at \`resource://methodology/${framework.id}/system-prompt\`*`
);
}
}
return lines.join('\n');
}