Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
0cda036
add initial
khromov Oct 1, 2025
207d1f8
Create README.md
khromov Oct 1, 2025
56b47f1
wip
khromov Oct 1, 2025
a49773f
wip
khromov Oct 1, 2025
0f27474
refactor
khromov Oct 1, 2025
5736e85
Update generate-summaries.ts
khromov Oct 1, 2025
fd72afc
Update generate-summaries.ts
khromov Oct 1, 2025
b086e63
wip
khromov Oct 1, 2025
988da97
add example summaries
khromov Oct 1, 2025
1332513
Update utils.ts
khromov Oct 1, 2025
22aaad4
wip
khromov Oct 1, 2025
7366dad
wip
khromov Oct 1, 2025
94f1296
fix path
khromov Oct 1, 2025
de45c1b
Update generate-summaries.ts
khromov Oct 1, 2025
7dedb27
fix paths
khromov Oct 1, 2025
e723198
Update utils.ts
khromov Oct 2, 2025
e1a1cb1
Update anthropic.ts
khromov Oct 2, 2025
d138349
fixes
khromov Oct 2, 2025
6fc0419
wip
khromov Oct 2, 2025
b0888d6
format, delete readme
khromov Oct 2, 2025
b16448b
wip
khromov Oct 2, 2025
334f933
Update list-sections.ts
khromov Oct 2, 2025
c3134bd
Update prompts.ts
khromov Oct 2, 2025
eb5444e
format
khromov Oct 2, 2025
02042da
Update svelte-task.ts
khromov Oct 2, 2025
55f9009
wip
khromov Oct 2, 2025
7acbaad
Merge main into llms-txt-plus-use-cases
khromov Oct 2, 2025
12a8051
Update utils.ts
khromov Oct 2, 2025
005b9d4
Prioritize use_cases from metadata, fallback to JSON, then empty string
khromov Oct 2, 2025
8064a4f
remove docs/ prefix
khromov Oct 2, 2025
6a23b9c
use slug for path
khromov Oct 2, 2025
937216e
use format function
khromov Oct 2, 2025
1f94d33
Update packages/mcp-server/src/mcp/utils.ts
khromov Oct 3, 2025
522fae6
Update packages/mcp-server/src/mcp/utils.ts
khromov Oct 3, 2025
cde6d70
Update packages/mcp-server/src/mcp/utils.ts
khromov Oct 3, 2025
f82ceac
validate sections
khromov Oct 3, 2025
1e7ebea
Merge branch 'llms-txt-plus-use-cases' of https://github.com/sveltejs…
khromov Oct 3, 2025
1916c41
use --experimental-strip-types
khromov Oct 3, 2025
f747c4a
remove tsx
khromov Oct 3, 2025
0385914
rewrite to valibot
khromov Oct 3, 2025
a881cb5
move to devdeps
khromov Oct 3, 2025
3bb71f3
chore: infer when possible, move schemas, add tsconfig to scripts
paoloricciuti Oct 3, 2025
1dbd93a
Update use_cases documentation metadata format
paoloricciuti Oct 3, 2025
fa969fe
fix: update package name
paoloricciuti Oct 3, 2025
891fae8
Merge pull request #29 from sveltejs/llms-txt-plus-use-cases
paoloricciuti Oct 3, 2025
273c780
Merge branch 'main' into llms-txt
paoloricciuti Oct 3, 2025
03ce6c5
fix: package.json and lock
paoloricciuti Oct 3, 2025
e3dbb5c
fix: lint
paoloricciuti Oct 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/hip-ducks-perform.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/mcp': patch
---

feat: `use_cases` documentation metadata
3 changes: 2 additions & 1 deletion .cocoignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.claude
.github
.github
.vscode
1 change: 1 addition & 0 deletions .cocominify
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
packages/mcp-server/src/use_cases.json
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"test": "npm run test:unit -- --run",
"test:watch": "npm run test:unit -- --watch",
"inspect": "pnpm mcp-inspector",
"generate-summaries": "pnpm --filter @sveltejs/mcp-server run generate-summaries",
"debug:generate-summaries": "pnpm --filter @sveltejs/mcp-server run debug:generate-summaries",
"release": "pnpm --filter @sveltejs/mcp run build && changeset publish"
},
"keywords": [
Expand Down
2 changes: 2 additions & 0 deletions packages/mcp-server/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Anthropic API Key from: https://console.anthropic.com/
ANTHROPIC_API_KEY=your_api_key_here
6 changes: 5 additions & 1 deletion packages/mcp-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
"license": "ISC",
"type": "module",
"scripts": {
"test": "vitest"
"test": "vitest",
"generate-summaries": "node scripts/generate-summaries.ts --experimental-strip-types",
"debug:generate-summaries": "DEBUG_MODE=1 node scripts/generate-summaries.ts --experimental-strip-types"
},
"exports": {
".": "./src/index.ts"
Expand All @@ -32,6 +34,8 @@
"zimmerframe": "^1.1.4"
},
"devDependencies": {
"@anthropic-ai/sdk": "^0.65.0",
"dotenv": "^17.2.3",
"@sveltejs/kit": "^2.42.2",
"@types/eslint-scope": "^8.3.2",
"@types/estree": "^1.0.8",
Expand Down
244 changes: 244 additions & 0 deletions packages/mcp-server/scripts/generate-summaries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
#!/usr/bin/env node
import 'dotenv/config';
import { writeFile, mkdir } from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
import { get_sections } from '../src/mcp/utils.ts';
import { AnthropicProvider } from '../src/lib/anthropic.ts';
import { type AnthropicBatchRequest, type SummaryData } from '../src/lib/schemas.ts';

const current_filename = fileURLToPath(import.meta.url);
const current_dirname = path.dirname(current_filename);

const USE_CASES_PROMPT = `
You are tasked with analyzing Svelte 5 and SvelteKit documentation pages to identify when they would be useful.

Your task:
1. Read the documentation page content provided
2. Identify the main use cases, scenarios, or queries where this documentation would be relevant
3. Create a VERY SHORT, comma-separated list of use cases (maximum 200 characters total)
4. Think about what a developer might be trying to build or accomplish when they need this documentation

Guidelines:
- Focus on WHEN this documentation would be needed, not WHAT it contains
- Consider specific project types (e.g., "e-commerce site", "blog", "dashboard", "social media app")
- Consider specific features (e.g., "authentication", "forms", "data fetching", "animations")
- Consider specific components (e.g., "slider", "modal", "dropdown", "card")
- Consider development stages (e.g., "project setup", "deployment", "testing", "migration")
- Use "always" for fundamental concepts that apply to virtually all Svelte projects
- Be concise but specific
- Use lowercase
- Separate multiple use cases with commas

Examples of good use_cases:
- "always, any svelte project, core reactivity"
- "authentication, login systems, user management"
- "e-commerce, product listings, shopping carts"
- "forms, user input, data submission"
- "deployment, production builds, hosting setup"
- "animation, transitions, interactive ui"
- "routing, navigation, multi-page apps"
- "blog, content sites, markdown rendering"

Requirements:
- Maximum 200 characters (including spaces and commas)
- Lowercase only
- Comma-separated list of use cases
- Focus on WHEN/WHY someone would need this, not what it is
- Be specific about project types, features, or components when applicable
- Use "always" sparingly, only for truly universal concepts
- Do not include quotes or special formatting in your response
- Respond with ONLY the use cases text, no additional text

Here is the documentation page content to analyze:

`;

async function fetch_section_content(url: string) {
const response = await fetch(url, { signal: AbortSignal.timeout(30000) });
if (!response.ok) {
throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
}
return await response.text();
}

async function main() {
console.log('🚀 Starting use cases generation...');

// Check for API key
const api_key = process.env.ANTHROPIC_API_KEY;
if (!api_key) {
console.error('❌ Error: ANTHROPIC_API_KEY environment variable is required');
console.error('Please set it in packages/mcp-server/.env file or export it:');
console.error('export ANTHROPIC_API_KEY=your_api_key_here');
process.exit(1);
}

// Get all sections
console.log('📚 Fetching documentation sections...');
let sections = await get_sections();
console.log(`Found ${sections.length} sections`);

// Debug mode: limit to 2 sections
const debug_mode = process.env.DEBUG_MODE === '1';
if (debug_mode) {
console.log('🐛 DEBUG_MODE enabled - processing only 2 sections');
sections = sections.slice(0, 2);
}

// Fetch content for each section
console.log('📥 Downloading section content...');
const sections_with_content: Array<{
section: (typeof sections)[number];
content: string;
index: number;
}> = [];
const download_errors: Array<{ section: string; error: string }> = [];

for (let i = 0; i < sections.length; i++) {
const section = sections[i]!;
try {
console.log(`Fetching ${i + 1}/${sections.length}: ${section.title}`);
const content = await fetch_section_content(section.url);
sections_with_content.push({
section,
content,
index: i,
});
} catch (error) {
const error_msg = error instanceof Error ? error.message : String(error);
console.error(`⚠️ Failed to fetch ${section.title}:`, error_msg);
download_errors.push({ section: section.title, error: error_msg });
}
}

console.log(`✅ Successfully downloaded ${sections_with_content.length} sections`);

if (sections_with_content.length === 0) {
console.error('❌ No sections were successfully downloaded');
process.exit(1);
}

// Initialize Anthropic client
console.log('🤖 Initializing Anthropic API...');
const anthropic = new AnthropicProvider('claude-sonnet-4-5-20250929', api_key);

// Prepare batch requests
console.log('📦 Preparing batch requests...');
const batch_requests: AnthropicBatchRequest[] = sections_with_content.map(
({ content, index }) => ({
custom_id: `section-${index}`,
params: {
model: anthropic.get_model_identifier(),
max_tokens: 250,
messages: [
{
role: 'user',
content: USE_CASES_PROMPT + content,
},
],
temperature: 0,
},
}),
);

// Create and process batch
console.log('🚀 Creating batch job...');
const batch_response = await anthropic.create_batch(batch_requests);
console.log(`✅ Batch created with ID: ${batch_response.id}`);

// Poll for completion
console.log('⏳ Waiting for batch to complete...');
let batch_status = await anthropic.get_batch_status(batch_response.id);

while (batch_status.processing_status === 'in_progress') {
const { succeeded, processing, errored } = batch_status.request_counts;
console.log(` Progress: ${succeeded} succeeded, ${processing} processing, ${errored} errored`);
await new Promise((resolve) => setTimeout(resolve, 5000));
batch_status = await anthropic.get_batch_status(batch_response.id);
}

console.log('✅ Batch processing completed!');

// Get results
if (!batch_status.results_url) {
throw new Error('Batch completed but no results URL available');
}

console.log('📥 Downloading results...');
const results = await anthropic.get_batch_results(batch_status.results_url);

// Process results
console.log('📊 Processing results...');
const summaries: Record<string, string> = {};
const errors: Array<{ section: string; error: string }> = [];

for (const result of results) {
const index = parseInt(result.custom_id.split('-')[1] ?? '0');
const section_data = sections_with_content.find((s) => s.index === index);

if (!section_data) {
console.warn(`⚠️ Could not find section for index ${index}`);
continue;
}

const { section } = section_data;

if (result.result.type !== 'succeeded' || !result.result.message) {
const error_msg = result.result.error?.message || 'Failed or no message';
console.error(` ❌ ${section.title}: ${error_msg}`);
errors.push({ section: section.title, error: error_msg });
continue;
}

const output_content = result.result.message.content[0]?.text;
if (output_content) {
summaries[section.slug] = output_content.trim();
console.log(` ✅ ${section.title}`);
}
}

// Write output to JSON file
console.log('💾 Writing results to file...');
const output_path = path.join(current_dirname, '../src/use_cases.json');
const output_dir = path.dirname(output_path);

await mkdir(output_dir, { recursive: true });

const summary_data: SummaryData = {
generated_at: new Date().toISOString(),
model: anthropic.get_model_identifier(),
total_sections: sections.length,
successful_summaries: Object.keys(summaries).length,
failed_summaries: errors.length,
summaries,
errors: errors.length > 0 ? errors : undefined,
download_errors: download_errors.length > 0 ? download_errors : undefined,
};

await writeFile(output_path, JSON.stringify(summary_data, null, 2), 'utf-8');

// Print summary
console.log('\n📊 Summary:');
console.log(` Total sections: ${sections.length}`);
console.log(` Successfully downloaded: ${sections_with_content.length}`);
console.log(` Download failures: ${download_errors.length}`);
console.log(` Successfully analyzed: ${Object.keys(summaries).length}`);
console.log(` Analysis failures: ${errors.length}`);
console.log(`\n✅ Results written to: ${output_path}`);

if (download_errors.length > 0) {
console.log('\n⚠️ Some sections failed to download:');
download_errors.forEach((e) => console.log(` - ${e.section}: ${e.error}`));
}

if (errors.length > 0) {
console.log('\n⚠️ Some sections failed to analyze:');
errors.forEach((e) => console.log(` - ${e.section}: ${e.error}`));
}
}

main().catch((error) => {
console.error('❌ Fatal error:', error);
process.exit(1);
});
6 changes: 6 additions & 0 deletions packages/mcp-server/scripts/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"allowImportingTsExtensions": true
}
}
Loading