Skip to content

Commit 7c76210

Browse files
Merge pull request #39 from sveltejs/llms-txt
2 parents 7ada706 + e3dbb5c commit 7c76210

File tree

18 files changed

+895
-96
lines changed

18 files changed

+895
-96
lines changed

.changeset/hip-ducks-perform.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/mcp': patch
3+
---
4+
5+
feat: `use_cases` documentation metadata

.cocoignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
.claude
2-
.github
2+
.github
3+
.vscode

.cocominify

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
packages/mcp-server/src/use_cases.json

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
"test": "npm run test:unit -- --run",
1717
"test:watch": "npm run test:unit -- --watch",
1818
"inspect": "pnpm mcp-inspector",
19+
"generate-summaries": "pnpm --filter @sveltejs/mcp-server run generate-summaries",
20+
"debug:generate-summaries": "pnpm --filter @sveltejs/mcp-server run debug:generate-summaries",
1921
"release": "pnpm --filter @sveltejs/mcp run build && changeset publish"
2022
},
2123
"keywords": [

packages/mcp-server/.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Anthropic API Key from: https://console.anthropic.com/
2+
ANTHROPIC_API_KEY=your_api_key_here

packages/mcp-server/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
"license": "ISC",
1010
"type": "module",
1111
"scripts": {
12-
"test": "vitest"
12+
"test": "vitest",
13+
"generate-summaries": "node scripts/generate-summaries.ts --experimental-strip-types",
14+
"debug:generate-summaries": "DEBUG_MODE=1 node scripts/generate-summaries.ts --experimental-strip-types"
1315
},
1416
"exports": {
1517
".": "./src/index.ts"
@@ -32,6 +34,8 @@
3234
"zimmerframe": "^1.1.4"
3335
},
3436
"devDependencies": {
37+
"@anthropic-ai/sdk": "^0.65.0",
38+
"dotenv": "^17.2.3",
3539
"@sveltejs/kit": "^2.42.2",
3640
"@types/eslint-scope": "^8.3.2",
3741
"@types/estree": "^1.0.8",
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
#!/usr/bin/env node
2+
import 'dotenv/config';
3+
import { writeFile, mkdir } from 'fs/promises';
4+
import path from 'path';
5+
import { fileURLToPath } from 'url';
6+
import { get_sections } from '../src/mcp/utils.ts';
7+
import { AnthropicProvider } from '../src/lib/anthropic.ts';
8+
import { type AnthropicBatchRequest, type SummaryData } from '../src/lib/schemas.ts';
9+
10+
const current_filename = fileURLToPath(import.meta.url);
11+
const current_dirname = path.dirname(current_filename);
12+
13+
const USE_CASES_PROMPT = `
14+
You are tasked with analyzing Svelte 5 and SvelteKit documentation pages to identify when they would be useful.
15+
16+
Your task:
17+
1. Read the documentation page content provided
18+
2. Identify the main use cases, scenarios, or queries where this documentation would be relevant
19+
3. Create a VERY SHORT, comma-separated list of use cases (maximum 200 characters total)
20+
4. Think about what a developer might be trying to build or accomplish when they need this documentation
21+
22+
Guidelines:
23+
- Focus on WHEN this documentation would be needed, not WHAT it contains
24+
- Consider specific project types (e.g., "e-commerce site", "blog", "dashboard", "social media app")
25+
- Consider specific features (e.g., "authentication", "forms", "data fetching", "animations")
26+
- Consider specific components (e.g., "slider", "modal", "dropdown", "card")
27+
- Consider development stages (e.g., "project setup", "deployment", "testing", "migration")
28+
- Use "always" for fundamental concepts that apply to virtually all Svelte projects
29+
- Be concise but specific
30+
- Use lowercase
31+
- Separate multiple use cases with commas
32+
33+
Examples of good use_cases:
34+
- "always, any svelte project, core reactivity"
35+
- "authentication, login systems, user management"
36+
- "e-commerce, product listings, shopping carts"
37+
- "forms, user input, data submission"
38+
- "deployment, production builds, hosting setup"
39+
- "animation, transitions, interactive ui"
40+
- "routing, navigation, multi-page apps"
41+
- "blog, content sites, markdown rendering"
42+
43+
Requirements:
44+
- Maximum 200 characters (including spaces and commas)
45+
- Lowercase only
46+
- Comma-separated list of use cases
47+
- Focus on WHEN/WHY someone would need this, not what it is
48+
- Be specific about project types, features, or components when applicable
49+
- Use "always" sparingly, only for truly universal concepts
50+
- Do not include quotes or special formatting in your response
51+
- Respond with ONLY the use cases text, no additional text
52+
53+
Here is the documentation page content to analyze:
54+
55+
`;
56+
57+
async function fetch_section_content(url: string) {
58+
const response = await fetch(url, { signal: AbortSignal.timeout(30000) });
59+
if (!response.ok) {
60+
throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
61+
}
62+
return await response.text();
63+
}
64+
65+
async function main() {
66+
console.log('🚀 Starting use cases generation...');
67+
68+
// Check for API key
69+
const api_key = process.env.ANTHROPIC_API_KEY;
70+
if (!api_key) {
71+
console.error('❌ Error: ANTHROPIC_API_KEY environment variable is required');
72+
console.error('Please set it in packages/mcp-server/.env file or export it:');
73+
console.error('export ANTHROPIC_API_KEY=your_api_key_here');
74+
process.exit(1);
75+
}
76+
77+
// Get all sections
78+
console.log('📚 Fetching documentation sections...');
79+
let sections = await get_sections();
80+
console.log(`Found ${sections.length} sections`);
81+
82+
// Debug mode: limit to 2 sections
83+
const debug_mode = process.env.DEBUG_MODE === '1';
84+
if (debug_mode) {
85+
console.log('🐛 DEBUG_MODE enabled - processing only 2 sections');
86+
sections = sections.slice(0, 2);
87+
}
88+
89+
// Fetch content for each section
90+
console.log('📥 Downloading section content...');
91+
const sections_with_content: Array<{
92+
section: (typeof sections)[number];
93+
content: string;
94+
index: number;
95+
}> = [];
96+
const download_errors: Array<{ section: string; error: string }> = [];
97+
98+
for (let i = 0; i < sections.length; i++) {
99+
const section = sections[i]!;
100+
try {
101+
console.log(`Fetching ${i + 1}/${sections.length}: ${section.title}`);
102+
const content = await fetch_section_content(section.url);
103+
sections_with_content.push({
104+
section,
105+
content,
106+
index: i,
107+
});
108+
} catch (error) {
109+
const error_msg = error instanceof Error ? error.message : String(error);
110+
console.error(`⚠️ Failed to fetch ${section.title}:`, error_msg);
111+
download_errors.push({ section: section.title, error: error_msg });
112+
}
113+
}
114+
115+
console.log(`✅ Successfully downloaded ${sections_with_content.length} sections`);
116+
117+
if (sections_with_content.length === 0) {
118+
console.error('❌ No sections were successfully downloaded');
119+
process.exit(1);
120+
}
121+
122+
// Initialize Anthropic client
123+
console.log('🤖 Initializing Anthropic API...');
124+
const anthropic = new AnthropicProvider('claude-sonnet-4-5-20250929', api_key);
125+
126+
// Prepare batch requests
127+
console.log('📦 Preparing batch requests...');
128+
const batch_requests: AnthropicBatchRequest[] = sections_with_content.map(
129+
({ content, index }) => ({
130+
custom_id: `section-${index}`,
131+
params: {
132+
model: anthropic.get_model_identifier(),
133+
max_tokens: 250,
134+
messages: [
135+
{
136+
role: 'user',
137+
content: USE_CASES_PROMPT + content,
138+
},
139+
],
140+
temperature: 0,
141+
},
142+
}),
143+
);
144+
145+
// Create and process batch
146+
console.log('🚀 Creating batch job...');
147+
const batch_response = await anthropic.create_batch(batch_requests);
148+
console.log(`✅ Batch created with ID: ${batch_response.id}`);
149+
150+
// Poll for completion
151+
console.log('⏳ Waiting for batch to complete...');
152+
let batch_status = await anthropic.get_batch_status(batch_response.id);
153+
154+
while (batch_status.processing_status === 'in_progress') {
155+
const { succeeded, processing, errored } = batch_status.request_counts;
156+
console.log(` Progress: ${succeeded} succeeded, ${processing} processing, ${errored} errored`);
157+
await new Promise((resolve) => setTimeout(resolve, 5000));
158+
batch_status = await anthropic.get_batch_status(batch_response.id);
159+
}
160+
161+
console.log('✅ Batch processing completed!');
162+
163+
// Get results
164+
if (!batch_status.results_url) {
165+
throw new Error('Batch completed but no results URL available');
166+
}
167+
168+
console.log('📥 Downloading results...');
169+
const results = await anthropic.get_batch_results(batch_status.results_url);
170+
171+
// Process results
172+
console.log('📊 Processing results...');
173+
const summaries: Record<string, string> = {};
174+
const errors: Array<{ section: string; error: string }> = [];
175+
176+
for (const result of results) {
177+
const index = parseInt(result.custom_id.split('-')[1] ?? '0');
178+
const section_data = sections_with_content.find((s) => s.index === index);
179+
180+
if (!section_data) {
181+
console.warn(`⚠️ Could not find section for index ${index}`);
182+
continue;
183+
}
184+
185+
const { section } = section_data;
186+
187+
if (result.result.type !== 'succeeded' || !result.result.message) {
188+
const error_msg = result.result.error?.message || 'Failed or no message';
189+
console.error(` ❌ ${section.title}: ${error_msg}`);
190+
errors.push({ section: section.title, error: error_msg });
191+
continue;
192+
}
193+
194+
const output_content = result.result.message.content[0]?.text;
195+
if (output_content) {
196+
summaries[section.slug] = output_content.trim();
197+
console.log(` ✅ ${section.title}`);
198+
}
199+
}
200+
201+
// Write output to JSON file
202+
console.log('💾 Writing results to file...');
203+
const output_path = path.join(current_dirname, '../src/use_cases.json');
204+
const output_dir = path.dirname(output_path);
205+
206+
await mkdir(output_dir, { recursive: true });
207+
208+
const summary_data: SummaryData = {
209+
generated_at: new Date().toISOString(),
210+
model: anthropic.get_model_identifier(),
211+
total_sections: sections.length,
212+
successful_summaries: Object.keys(summaries).length,
213+
failed_summaries: errors.length,
214+
summaries,
215+
errors: errors.length > 0 ? errors : undefined,
216+
download_errors: download_errors.length > 0 ? download_errors : undefined,
217+
};
218+
219+
await writeFile(output_path, JSON.stringify(summary_data, null, 2), 'utf-8');
220+
221+
// Print summary
222+
console.log('\n📊 Summary:');
223+
console.log(` Total sections: ${sections.length}`);
224+
console.log(` Successfully downloaded: ${sections_with_content.length}`);
225+
console.log(` Download failures: ${download_errors.length}`);
226+
console.log(` Successfully analyzed: ${Object.keys(summaries).length}`);
227+
console.log(` Analysis failures: ${errors.length}`);
228+
console.log(`\n✅ Results written to: ${output_path}`);
229+
230+
if (download_errors.length > 0) {
231+
console.log('\n⚠️ Some sections failed to download:');
232+
download_errors.forEach((e) => console.log(` - ${e.section}: ${e.error}`));
233+
}
234+
235+
if (errors.length > 0) {
236+
console.log('\n⚠️ Some sections failed to analyze:');
237+
errors.forEach((e) => console.log(` - ${e.section}: ${e.error}`));
238+
}
239+
}
240+
241+
main().catch((error) => {
242+
console.error('❌ Fatal error:', error);
243+
process.exit(1);
244+
});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"extends": "../../../tsconfig.json",
3+
"compilerOptions": {
4+
"allowImportingTsExtensions": true
5+
}
6+
}

0 commit comments

Comments
 (0)