scaffold_project
Get instructions for a hands-on reference project to practice certification concepts. Specify a project ID or leave blank to see available projects.
Instructions
Get instructions for a reference project to practice certification concepts hands-on.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| projectId | No | Project ID (e.g. "capstone", "d1-agentic"). Omit to see available projects. |
Implementation Reference
- src/tools/scaffold-project.ts:33-99 (handler)The registerScaffoldProject function registers the 'scaffold_project' tool on the MCP server. The handler logic: if no projectId is provided, lists available projects; otherwise it looks up the project in a predefined list, reads its README.md and lists all files recursively, then returns a formatted response with the project info, README content, file listing, and next steps guidance.
export function registerScaffoldProject(server: McpServer, _db: Database.Database, _userConfig: UserConfig): void { server.tool( 'scaffold_project', 'Get instructions for a reference project to practice certification concepts hands-on.', { projectId: z.string().optional().describe('Project ID (e.g. "capstone", "d1-agentic"). Omit to see available projects.') }, async ({ projectId }) => { if (!projectId) { const lines = [ '═══ REFERENCE PROJECTS ═══', '', ...PROJECTS.map(p => ` ${p.id}: ${p.name} (Domains: ${p.domains.join(', ')})`), ]; return { content: [{ type: 'text' as const, text: lines.join('\n') }] }; } const project = PROJECTS.find(p => p.id === projectId); if (!project) { return { content: [{ type: 'text' as const, text: `Project "${projectId}" not found. Use scaffold_project without arguments to see available projects.` }], isError: true, }; } const projectDir = path.join(PROJECTS_DIR, projectId); if (!fs.existsSync(projectDir)) { return { content: [{ type: 'text' as const, text: `Project directory for "${project.name}" not found. The project files may not be installed yet.` }], isError: true, }; } const readmePath = path.join(projectDir, 'README.md'); const readme = fs.existsSync(readmePath) ? fs.readFileSync(readmePath, 'utf-8') : null; const files = listFilesRecursive(projectDir); const sections = [ `═══ ${project.name} ═══`, '', `Domains: ${project.domains.join(', ')}`, '', ]; if (readme) { sections.push('--- README ---', '', readme, ''); } sections.push( '--- Project Files ---', '', ...files.map(f => ` ${f}`), '', '--- Next Steps ---', '', 'Explore the project files above to understand the architecture.', 'Each file demonstrates certification concepts in practice.', `Project root: projects/${projectId}/`, ); return { content: [{ type: 'text' as const, text: sections.join('\n') }], }; } ); } - src/tools/scaffold-project.ts:37-37 (schema)Input schema: projectId is an optional string described as 'Project ID (e.g. "capstone", "d1-agentic"). Omit to see available projects.' Defined via Zod with z.string().optional().
{ projectId: z.string().optional().describe('Project ID (e.g. "capstone", "d1-agentic"). Omit to see available projects.') }, - src/tools/index.ts:32-32 (registration)The tool is registered by calling registerScaffoldProject(server, db, userConfig) inside the registerTools() function, which is the central registration point for all tools.
registerScaffoldProject(server, db, userConfig); - src/tools/index.ts:12-12 (registration)Import statement for registerScaffoldProject from './scaffold-project.js'.
import { registerScaffoldProject } from './scaffold-project.js'; - src/tools/scaffold-project.ts:21-31 (helper)Helper function listFilesRecursive that recursively walks a directory and returns a flat list of relative file paths, used to list all project files.
function listFilesRecursive(dir: string, prefix = ''): readonly string[] { if (!fs.existsSync(dir)) return []; const entries = fs.readdirSync(dir, { withFileTypes: true }); return entries.flatMap((entry): readonly string[] => { const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name; if (entry.isDirectory()) { return listFilesRecursive(path.join(dir, entry.name), relativePath); } return [relativePath]; }); }