Skip to content

Commit a50bde8

Browse files
cevianclaude
andauthored
feat: add markdown-based prompts support with auto-discovery tool (#5)
* feat: add markdown-based prompts support - Add markdown prompt loader with frontmatter parsing using gray-matter - Add 3 TimescaleDB-focused prompts: hypertable analysis, migration, and setup - Update build process to copy .md files to dist directory - Integrate with updated boilerplate supporting prompts capability - Remove unused handlebars dependency Prompts included: - identifyHypertableCandidates: Analyze tables for hypertable conversion - postgresHypertableMigration: Guide for migrating to hypertables - timescaledbSetup: Complete TimescaleDB setup with compression and policies * feat: add getPromptContent tool for auto-discovery - Add MCP tool that dynamically discovers all available prompts - Tool description includes comprehensive list of all prompts with descriptions - Auto-generates enum of available prompts from markdown frontmatter - Enables LLM auto-discovery of TimescaleDB guidance prompts - Updated timescaledb setup prompt description to be more comprehensive Benefits: - LLM can automatically choose appropriate prompts based on user needs - No need for users to explicitly know prompt names - Stays in sync as new .md files are added * refactor: rename tool from getPromptContent to getGuide - Rename tool name to be more user-friendly and intuitive - Update file name from getPromptContent.ts to getGuide.ts - Clean up tool description by removing redundant text - Update section header from 'Available Prompts' to 'Available Guides' * refactor: simplify prompts architecture by removing factory pattern - Replace factory-based approach with simple Map-based prompts loading - Convert promptFactories to direct prompts Map with {name, title, description, content} structure - Update getGuide tool to use prompts Map directly for O(1) lookup - Simplify mcpServer and stdio to accept prompts Map instead of factories - Remove PromptFactory, PromptDefinition, PromptConfig types from boilerplate - Delete markdownLoader.ts and unused factory complexity - Rename markdown files to camelCase naming convention - Remove name field from frontmatter to establish single source of truth - Both MCP prompts and getGuide tool now use same simple data source - Fix YAML parsing by properly quoting description field 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: update boilerplate submodule to latest main - Update boilerplate submodule to point to latest main with prompts support - Maintain adapter pattern in stdio.ts to convert prompts Map to promptFactories - Everything working correctly with both MCP prompts and getGuide tool 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 1e54c34 commit a50bde8

File tree

10 files changed

+2005
-2
lines changed

10 files changed

+2005
-2
lines changed

package-lock.json

Lines changed: 109 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"dist"
1414
],
1515
"scripts": {
16-
"build": "tsc && shx chmod +x dist/*.js",
16+
"build": "tsc && shx cp -r src/prompts/md dist/prompts/ && shx chmod +x dist/*.js",
1717
"prepare": "npm run build",
1818
"watch": "tsc --watch",
1919
"watch:http": "tsx watch src/index.ts http",
@@ -39,6 +39,7 @@
3939
"ai": "^5.0.17",
4040
"dotenv": "^17.2.0",
4141
"express": "^5.1.0",
42+
"gray-matter": "^4.0.3",
4243
"pg": "^8.16.3",
4344
"zod": "^3.23.8"
4445
},

src/apis/getGuide.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { z } from 'zod';
2+
import { ApiFactory } from '../shared/boilerplate/src/types.js';
3+
import { ServerContext } from '../types.js';
4+
import { prompts } from '../prompts/index.js';
5+
6+
// Create enum schema dynamically
7+
const inputSchema = {
8+
prompt_name: z
9+
.enum(Array.from(prompts.keys()) as [string, ...string[]])
10+
.describe('The name of the prompt to retrieve'),
11+
} as const;
12+
13+
const outputSchema = {
14+
prompt_name: z.string().describe('The name of the requested prompt'),
15+
title: z.string().describe('The display title of the prompt'),
16+
description: z.string().describe('Description of what this prompt does'),
17+
content: z.string().describe('The full prompt content'),
18+
} as const;
19+
20+
export const getPromptContentFactory: ApiFactory<
21+
ServerContext,
22+
typeof inputSchema,
23+
typeof outputSchema
24+
> = () => ({
25+
name: 'getGuide',
26+
config: {
27+
title: 'Get TimescaleDB Guide',
28+
description: `Retrieve detailed TimescaleDB guides and best practices.
29+
30+
Available Guides:
31+
32+
${Array.from(prompts.values()).map(p => `**${p.name}** - ${p.description}`).join('\n\n')}
33+
`,
34+
inputSchema,
35+
outputSchema,
36+
},
37+
fn: async ({ prompt_name }) => {
38+
const prompt = prompts.get(prompt_name);
39+
40+
if (!prompt) {
41+
throw new Error(`Prompt '${prompt_name}' not found`);
42+
}
43+
44+
return {
45+
prompt_name: prompt.name,
46+
title: prompt.title || prompt.name,
47+
description: prompt.description || '',
48+
content: prompt.content,
49+
};
50+
},
51+
});

src/apis/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { semanticSearchPostgresDocsFactory } from './semanticSearchPostgresDocs.js';
22
import { semanticSearchTimescaleDocsFactory } from './semanticSearchTimescaleDocs.js';
3+
import { getPromptContentFactory } from './getGuide.js';
34

45
export const apiFactories = [
56
semanticSearchPostgresDocsFactory,
67
semanticSearchTimescaleDocsFactory,
8+
getPromptContentFactory,
79
] as const;

src/prompts/index.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { dirname, join } from 'path';
2+
import { fileURLToPath } from 'url';
3+
import { readdirSync, readFileSync } from 'fs';
4+
import matter from 'gray-matter';
5+
6+
const __dirname = dirname(fileURLToPath(import.meta.url));
7+
const markdownPromptsDir = join(__dirname, 'md');
8+
9+
// Load all markdown prompts with their metadata
10+
function loadPrompts() {
11+
const files = readdirSync(markdownPromptsDir).filter(file => file.endsWith('.md'));
12+
const promptsMap = new Map();
13+
14+
for (const file of files) {
15+
const promptName = file.replace('.md', '');
16+
const filePath = join(markdownPromptsDir, file);
17+
const fileContent = readFileSync(filePath, 'utf-8');
18+
const { data, content } = matter(fileContent);
19+
20+
promptsMap.set(promptName, {
21+
name: promptName,
22+
title: data.title,
23+
description: data.description,
24+
content: content.trim(),
25+
});
26+
}
27+
28+
return promptsMap;
29+
}
30+
31+
export const prompts = loadPrompts();

0 commit comments

Comments
 (0)