From e7431e902458012199ee37dd60b921c3e9a817a9 Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Fri, 26 Sep 2025 20:29:55 +0200 Subject: [PATCH 01/30] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e167ec5c..ede829fe 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Repo for the official Svelte MCP server. ``` pnpm i -cp .env.example .env +cp apps/mcp-remote/.env.example apps/mcp-remote/.env pnpm dev ``` From 972cadc410cdcbcfcf1ce3c222e565b0a8c7de16 Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Fri, 26 Sep 2025 20:30:16 +0200 Subject: [PATCH 02/30] Update package.json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 5e5b67e0..e2557b33 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "type": "module", "scripts": { "build": "pnpm -r run build", + "dev": "pnpm --filter @sveltejs/mcp-remote run dev", "check": "pnpm -r run check", "format": "prettier --write .", "lint": "prettier --check . && eslint .", From 917fdf63b1ab2f612401732eb3352539d37b653e Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Fri, 26 Sep 2025 20:34:57 +0200 Subject: [PATCH 03/30] enable --- packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts | 2 +- packages/mcp-server/src/mcp/handlers/tools/list-sections.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts index 6b3ddf06..51951c07 100644 --- a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts +++ b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts @@ -5,7 +5,7 @@ export function get_documentation(server: SvelteMcp) { server.tool( { name: 'get-documentation', - enabled: () => false, + enabled: () => true, description: 'Retrieves full documentation content for Svelte 5 or SvelteKit sections. Supports flexible search by title (e.g., "$state", "routing") or file path (e.g., "docs/svelte/state.md"). Can accept a single section name or an array of sections. Before running this, make sure to analyze the users query, as well as the output from list_sections (which should be called first). Then ask for ALL relevant sections the user might require. For example, if the user asks to build anything interactive, you will need to fetch all relevant runes, and so on.', schema: v.object({ diff --git a/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts b/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts index 6d35a632..5fa661bd 100644 --- a/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts +++ b/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts @@ -4,7 +4,7 @@ export function list_sections(server: SvelteMcp) { server.tool( { name: 'list-sections', - enabled: () => false, + enabled: () => true, description: 'Lists all available Svelte 5 and SvelteKit documentation sections in a structured format. Returns sections as a list of "* title: [section_title], path: [file_path]" - you can use either the title or path when querying a specific section via the get_documentation tool. Always run list_sections first for any query related to Svelte development to discover available content.', }, From 68cf69a117ffa6fe350689456d2d4fd477132814 Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Fri, 26 Sep 2025 20:38:17 +0200 Subject: [PATCH 04/30] wip --- .../src/mcp/handlers/tools/list-sections.ts | 8 +++++++- packages/mcp-server/src/mcp/utils.ts | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 packages/mcp-server/src/mcp/utils.ts diff --git a/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts b/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts index 5fa661bd..7d9f19bb 100644 --- a/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts +++ b/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts @@ -1,4 +1,5 @@ import type { SvelteMcp } from '../../index.js'; +import { getSections } from '../../utils.js'; export function list_sections(server: SvelteMcp) { server.tool( @@ -9,11 +10,16 @@ export function list_sections(server: SvelteMcp) { 'Lists all available Svelte 5 and SvelteKit documentation sections in a structured format. Returns sections as a list of "* title: [section_title], path: [file_path]" - you can use either the title or path when querying a specific section via the get_documentation tool. Always run list_sections first for any query related to Svelte development to discover available content.', }, () => { + const sections = getSections(); + const formattedSections = sections + .map(section => `* title: ${section.title}, path: ${section.url}`) + .join('\n'); + return { content: [ { type: 'text', - text: 'tool list_sections called', + text: formattedSections, }, ], }; diff --git a/packages/mcp-server/src/mcp/utils.ts b/packages/mcp-server/src/mcp/utils.ts new file mode 100644 index 00000000..9358df3d --- /dev/null +++ b/packages/mcp-server/src/mcp/utils.ts @@ -0,0 +1,19 @@ +export function getSections() { + return [ + { + title: 'Overview', + url: 'https://svelte.dev/docs/svelte/overview/llms.txt', + description: 'Useful when a beginner learns about Svelte', + }, + { + title: 'Getting Started', + url: 'https://svelte.dev/docs/svelte/getting-started/llms.txt', + description: 'Useful when a beginner is starting a new Svelte project', + }, + { + title: 'Svelte Files', + url: 'https://svelte.dev/docs/svelte/svelte-files/llms.txt', + description: 'Useful when a beginner is learning about Svelte files', + }, + ]; +} From 19cacf7ed936e2468393786d2eecfff38aa1c801 Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Fri, 26 Sep 2025 20:39:22 +0200 Subject: [PATCH 05/30] refactor --- .../mcp-server/src/mcp/handlers/tools/list-sections.ts | 4 ++-- packages/mcp-server/src/mcp/utils.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts b/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts index 7d9f19bb..514cf0f0 100644 --- a/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts +++ b/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts @@ -9,8 +9,8 @@ export function list_sections(server: SvelteMcp) { description: 'Lists all available Svelte 5 and SvelteKit documentation sections in a structured format. Returns sections as a list of "* title: [section_title], path: [file_path]" - you can use either the title or path when querying a specific section via the get_documentation tool. Always run list_sections first for any query related to Svelte development to discover available content.', }, - () => { - const sections = getSections(); + async () => { + const sections = await getSections(); const formattedSections = sections .map(section => `* title: ${section.title}, path: ${section.url}`) .join('\n'); diff --git a/packages/mcp-server/src/mcp/utils.ts b/packages/mcp-server/src/mcp/utils.ts index 9358df3d..3038fb3e 100644 --- a/packages/mcp-server/src/mcp/utils.ts +++ b/packages/mcp-server/src/mcp/utils.ts @@ -1,19 +1,19 @@ -export function getSections() { +export async function getSections() { return [ { title: 'Overview', url: 'https://svelte.dev/docs/svelte/overview/llms.txt', - description: 'Useful when a beginner learns about Svelte', + use_cases: 'Useful when a beginner learns about Svelte', }, { title: 'Getting Started', url: 'https://svelte.dev/docs/svelte/getting-started/llms.txt', - description: 'Useful when a beginner is starting a new Svelte project', + use_cases: 'Useful when a beginner is starting a new Svelte project', }, { title: 'Svelte Files', url: 'https://svelte.dev/docs/svelte/svelte-files/llms.txt', - description: 'Useful when a beginner is learning about Svelte files', + use_cases: 'Useful when a beginner is learning about Svelte files', }, ]; } From e314ab57b2d3368e210bd563d11552d9b8e2fb63 Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Fri, 26 Sep 2025 20:43:04 +0200 Subject: [PATCH 06/30] Update list-sections.ts --- .../src/mcp/handlers/tools/list-sections.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts b/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts index 514cf0f0..7ab08ffa 100644 --- a/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts +++ b/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts @@ -12,14 +12,22 @@ export function list_sections(server: SvelteMcp) { async () => { const sections = await getSections(); const formattedSections = sections - .map(section => `* title: ${section.title}, path: ${section.url}`) + .map( + (section) => + `* title: ${section.title}, use cases: ${section.use_cases}, path: ${section.url}`, + ) .join('\n'); + const introText = 'List of available Svelte documentation sections and its inteneded uses:'; + + const outroText = + 'Use the title or path with the get-documentation tool to get more details about a specific section.'; + return { content: [ { type: 'text', - text: formattedSections, + text: `${introText}\n\n${formattedSections}\n\n${outroText}`, }, ], }; From 1bb171cea79090a312f3ebe08075e1672e6b2e2a Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Fri, 26 Sep 2025 20:44:41 +0200 Subject: [PATCH 07/30] cleanup --- CLAUDE.md | 6 +++--- .../mcp-server/src/mcp/handlers/tools/get-documentation.ts | 2 +- packages/mcp-server/src/mcp/handlers/tools/list-sections.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index c2408711..855c15cb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -90,12 +90,12 @@ When connected to the svelte-llm MCP server, you have access to comprehensive Sv ## Available MCP Tools: -### 1. list_sections +### 1. list-sections Use this FIRST to discover all available documentation sections. Returns a structured list with titles and paths. When asked about Svelte or SvelteKit topics, ALWAYS use this tool at the start of the chat to find relevant sections. -### 2. get_documentation +### 2. get-documentation Retrieves full documentation content for specific sections. Accepts single or multiple sections. -After calling the list_sections tool, you MUST analyze the returned documentation sections and then use the get_documentation tool to fetch ALL documentation sections that are relevant for the users task. +After calling the list-sections tool, you MUST analyze the returned documentation sections and then use the get_documentation tool to fetch ALL documentation sections that are relevant for the users task. diff --git a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts index 51951c07..ec4b0449 100644 --- a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts +++ b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts @@ -7,7 +7,7 @@ export function get_documentation(server: SvelteMcp) { name: 'get-documentation', enabled: () => true, description: - 'Retrieves full documentation content for Svelte 5 or SvelteKit sections. Supports flexible search by title (e.g., "$state", "routing") or file path (e.g., "docs/svelte/state.md"). Can accept a single section name or an array of sections. Before running this, make sure to analyze the users query, as well as the output from list_sections (which should be called first). Then ask for ALL relevant sections the user might require. For example, if the user asks to build anything interactive, you will need to fetch all relevant runes, and so on.', + 'Retrieves full documentation content for Svelte 5 or SvelteKit sections. Supports flexible search by title (e.g., "$state", "routing") or file path (e.g., "docs/svelte/state.md"). Can accept a single section name or an array of sections. Before running this, make sure to analyze the users query, as well as the output from list-sections (which should be called first). Then ask for ALL relevant sections the user might require. For example, if the user asks to build anything interactive, you will need to fetch all relevant runes, and so on.', schema: v.object({ section: v.pipe( v.union([v.string(), v.array(v.string())]), diff --git a/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts b/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts index 7ab08ffa..fe4815aa 100644 --- a/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts +++ b/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts @@ -7,14 +7,14 @@ export function list_sections(server: SvelteMcp) { name: 'list-sections', enabled: () => true, description: - 'Lists all available Svelte 5 and SvelteKit documentation sections in a structured format. Returns sections as a list of "* title: [section_title], path: [file_path]" - you can use either the title or path when querying a specific section via the get_documentation tool. Always run list_sections first for any query related to Svelte development to discover available content.', + 'Lists all available Svelte 5 and SvelteKit documentation sections in a structured format. Returns sections as a list of "* title: [section_title], use_cases: [use_cases], path: [file_path]" - you can use either the title or path when querying a specific section via the get_documentation tool. Always run list-sections first for any query related to Svelte development to discover available content.', }, async () => { const sections = await getSections(); const formattedSections = sections .map( (section) => - `* title: ${section.title}, use cases: ${section.use_cases}, path: ${section.url}`, + `* title: ${section.title}, use_cases: ${section.use_cases}, path: ${section.url}`, ) .join('\n'); From d33a374417d36fda8644827127b08a6d5e1762af Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Fri, 26 Sep 2025 21:56:38 +0200 Subject: [PATCH 08/30] Update get-documentation.ts --- .../mcp/handlers/tools/get-documentation.ts | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts index ec4b0449..49e38522 100644 --- a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts +++ b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts @@ -1,5 +1,6 @@ import type { SvelteMcp } from '../../index.js'; import * as v from 'valibot'; +import { getSections } from '../../utils.js'; export function get_documentation(server: SvelteMcp) { server.tool( @@ -17,7 +18,7 @@ export function get_documentation(server: SvelteMcp) { ), }), }, - ({ section }) => { + async ({ section }) => { let sections: string[]; if (Array.isArray(section)) { @@ -43,13 +44,37 @@ export function get_documentation(server: SvelteMcp) { sections = []; } - const sections_list = sections.length > 0 ? sections.join(', ') : 'no sections'; + const availableSections = await getSections(); + const results: string[] = []; + + for (const requestedSection of sections) { + const matchedSection = availableSections.find( + s => s.title.toLowerCase() === requestedSection.toLowerCase() || + s.url === requestedSection + ); + + if (matchedSection) { + try { + const response = await fetch(matchedSection.url); + if (response.ok) { + const content = await response.text(); + results.push(`## ${matchedSection.title}\n\n${content}`); + } else { + results.push(`## ${matchedSection.title}\n\nError: Could not fetch documentation (HTTP ${response.status})`); + } + } catch (error) { + results.push(`## ${matchedSection.title}\n\nError: Failed to fetch documentation - ${error}`); + } + } else { + results.push(`## ${requestedSection}\n\nError: Section not found. Available sections: ${availableSections.map(s => s.title).join(', ')}`); + } + } return { content: [ { type: 'text', - text: `called for sections: ${sections_list}`, + text: results.join('\n\n---\n\n'), }, ], }; From 6cb97ac11d5bce9754c90ac4f37b05275921cf99 Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Fri, 26 Sep 2025 22:36:30 +0200 Subject: [PATCH 09/30] Update get-documentation.ts --- .../mcp/handlers/tools/get-documentation.ts | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts index 49e38522..91166042 100644 --- a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts +++ b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts @@ -45,30 +45,31 @@ export function get_documentation(server: SvelteMcp) { } const availableSections = await getSections(); - const results: string[] = []; - for (const requestedSection of sections) { - const matchedSection = availableSections.find( - s => s.title.toLowerCase() === requestedSection.toLowerCase() || - s.url === requestedSection - ); + const results = await Promise.all( + sections.map(async (requestedSection) => { + const matchedSection = availableSections.find( + s => s.title.toLowerCase() === requestedSection.toLowerCase() || + s.url === requestedSection + ); - if (matchedSection) { - try { - const response = await fetch(matchedSection.url); - if (response.ok) { - const content = await response.text(); - results.push(`## ${matchedSection.title}\n\n${content}`); - } else { - results.push(`## ${matchedSection.title}\n\nError: Could not fetch documentation (HTTP ${response.status})`); + if (matchedSection) { + try { + const response = await fetch(matchedSection.url); + if (response.ok) { + const content = await response.text(); + return `## ${matchedSection.title}\n\n${content}`; + } else { + return `## ${matchedSection.title}\n\nError: Could not fetch documentation (HTTP ${response.status})`; + } + } catch (error) { + return `## ${matchedSection.title}\n\nError: Failed to fetch documentation - ${error}`; } - } catch (error) { - results.push(`## ${matchedSection.title}\n\nError: Failed to fetch documentation - ${error}`); + } else { + return `## ${requestedSection}\n\nError: Section not found. Available sections: ${availableSections.map(s => s.title).join(', ')}`; } - } else { - results.push(`## ${requestedSection}\n\nError: Section not found. Available sections: ${availableSections.map(s => s.title).join(', ')}`); - } - } + }) + ); return { content: [ From c49b24d36a3c83b7a451ac51e352c671d2322434 Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Fri, 26 Sep 2025 22:39:52 +0200 Subject: [PATCH 10/30] wip --- .../src/mcp/handlers/tools/get-documentation.ts | 4 ++-- packages/mcp-server/src/mcp/utils.ts | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts index 91166042..2a90e98e 100644 --- a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts +++ b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts @@ -1,6 +1,6 @@ import type { SvelteMcp } from '../../index.js'; import * as v from 'valibot'; -import { getSections } from '../../utils.js'; +import { getSections, fetchWithTimeout } from '../../utils.js'; export function get_documentation(server: SvelteMcp) { server.tool( @@ -55,7 +55,7 @@ export function get_documentation(server: SvelteMcp) { if (matchedSection) { try { - const response = await fetch(matchedSection.url); + const response = await fetchWithTimeout(matchedSection.url); if (response.ok) { const content = await response.text(); return `## ${matchedSection.title}\n\n${content}`; diff --git a/packages/mcp-server/src/mcp/utils.ts b/packages/mcp-server/src/mcp/utils.ts index 3038fb3e..a89135cf 100644 --- a/packages/mcp-server/src/mcp/utils.ts +++ b/packages/mcp-server/src/mcp/utils.ts @@ -1,3 +1,20 @@ +export async function fetchWithTimeout(url: string, timeoutMs: number = 10000): Promise { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeoutMs); + + try { + const response = await fetch(url, { signal: controller.signal }); + clearTimeout(timeoutId); + return response; + } catch (error) { + clearTimeout(timeoutId); + if (error instanceof Error && error.name === 'AbortError') { + throw new Error(`Request timed out after ${timeoutMs}ms`); + } + throw error; + } +} + export async function getSections() { return [ { From b774b463fea02fb17c9289c46a839567158c4fd2 Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Fri, 26 Sep 2025 22:42:42 +0200 Subject: [PATCH 11/30] Update get-documentation.ts --- .../src/mcp/handlers/tools/get-documentation.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts index 2a90e98e..4cc93e07 100644 --- a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts +++ b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts @@ -66,7 +66,19 @@ export function get_documentation(server: SvelteMcp) { return `## ${matchedSection.title}\n\nError: Failed to fetch documentation - ${error}`; } } else { - return `## ${requestedSection}\n\nError: Section not found. Available sections: ${availableSections.map(s => s.title).join(', ')}`; + const formattedSections = availableSections + .map( + (section) => + `* title: ${section.title}, use_cases: ${section.use_cases}, path: ${section.url}`, + ) + .join('\n'); + + const introText = 'List of available Svelte documentation sections and its inteneded uses:'; + + const outroText = + 'Use the title or path with the get-documentation tool to get more details about a specific section.'; + + return `## ${requestedSection}\n\nError: Section not found.\n\n${introText}\n\n${formattedSections}\n\n${outroText}`; } }) ); From 0f5482477a2ad244a145cc6df2fe9d1818efb642 Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Fri, 26 Sep 2025 22:44:28 +0200 Subject: [PATCH 12/30] Update get-documentation.ts --- .../mcp/handlers/tools/get-documentation.ts | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts index 4cc93e07..64e598f9 100644 --- a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts +++ b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts @@ -58,36 +58,43 @@ export function get_documentation(server: SvelteMcp) { const response = await fetchWithTimeout(matchedSection.url); if (response.ok) { const content = await response.text(); - return `## ${matchedSection.title}\n\n${content}`; + return { success: true, content: `## ${matchedSection.title}\n\n${content}` }; } else { - return `## ${matchedSection.title}\n\nError: Could not fetch documentation (HTTP ${response.status})`; + return { success: false, content: `## ${matchedSection.title}\n\nError: Could not fetch documentation (HTTP ${response.status})` }; } } catch (error) { - return `## ${matchedSection.title}\n\nError: Failed to fetch documentation - ${error}`; + return { success: false, content: `## ${matchedSection.title}\n\nError: Failed to fetch documentation - ${error}` }; } } else { - const formattedSections = availableSections - .map( - (section) => - `* title: ${section.title}, use_cases: ${section.use_cases}, path: ${section.url}`, - ) - .join('\n'); - - const introText = 'List of available Svelte documentation sections and its inteneded uses:'; - - const outroText = - 'Use the title or path with the get-documentation tool to get more details about a specific section.'; - - return `## ${requestedSection}\n\nError: Section not found.\n\n${introText}\n\n${formattedSections}\n\n${outroText}`; + return { success: false, content: `## ${requestedSection}\n\nError: Section not found.` }; } }) ); + const hasAnySuccess = results.some(result => result.success); + let finalText = results.map(r => r.content).join('\n\n---\n\n'); + + if (!hasAnySuccess) { + const formattedSections = availableSections + .map( + (section) => + `* title: ${section.title}, use_cases: ${section.use_cases}, path: ${section.url}`, + ) + .join('\n'); + + const introText = 'List of available Svelte documentation sections and its inteneded uses:'; + + const outroText = + 'Use the title or path with the get-documentation tool to get more details about a specific section.'; + + finalText += `\n\n---\n\n${introText}\n\n${formattedSections}\n\n${outroText}`; + } + return { content: [ { type: 'text', - text: results.join('\n\n---\n\n'), + text: finalText, }, ], }; From bf477a6ccfb433e5bc6c62ad1c7697d53c48d3b0 Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Fri, 26 Sep 2025 23:46:07 +0200 Subject: [PATCH 13/30] Update get-documentation.ts --- .../mcp/handlers/tools/get-documentation.ts | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts index 64e598f9..e4a8e77d 100644 --- a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts +++ b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts @@ -49,8 +49,9 @@ export function get_documentation(server: SvelteMcp) { const results = await Promise.all( sections.map(async (requestedSection) => { const matchedSection = availableSections.find( - s => s.title.toLowerCase() === requestedSection.toLowerCase() || - s.url === requestedSection + (s) => + s.title.toLowerCase() === requestedSection.toLowerCase() || + s.url === requestedSection, ); if (matchedSection) { @@ -60,19 +61,28 @@ export function get_documentation(server: SvelteMcp) { const content = await response.text(); return { success: true, content: `## ${matchedSection.title}\n\n${content}` }; } else { - return { success: false, content: `## ${matchedSection.title}\n\nError: Could not fetch documentation (HTTP ${response.status})` }; + return { + success: false, + content: `## ${matchedSection.title}\n\nError: Could not fetch documentation (HTTP ${response.status})`, + }; } } catch (error) { - return { success: false, content: `## ${matchedSection.title}\n\nError: Failed to fetch documentation - ${error}` }; + return { + success: false, + content: `## ${matchedSection.title}\n\nError: Failed to fetch documentation - ${error}`, + }; } } else { - return { success: false, content: `## ${requestedSection}\n\nError: Section not found.` }; + return { + success: false, + content: `## ${requestedSection}\n\nError: Section not found.`, + }; } - }) + }), ); - const hasAnySuccess = results.some(result => result.success); - let finalText = results.map(r => r.content).join('\n\n---\n\n'); + const hasAnySuccess = results.some((result) => result.success); + let finalText = results.map((r) => r.content).join('\n\n---\n\n'); if (!hasAnySuccess) { const formattedSections = availableSections From 7f9ea742d8fceb3bad4223511877c506fcea5323 Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Fri, 26 Sep 2025 23:48:15 +0200 Subject: [PATCH 14/30] don't lint .claude dir --- .prettierignore | 5 ++++- eslint.config.js | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.prettierignore b/.prettierignore index 611d858a..b6c88732 100644 --- a/.prettierignore +++ b/.prettierignore @@ -8,4 +8,7 @@ bun.lockb # Miscellaneous /static/ /drizzle/ -/**/.svelte-kit/* \ No newline at end of file +/**/.svelte-kit/* + +# Claude Code +.claude/ \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js index 2552bfd5..325387fd 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -12,6 +12,9 @@ const gitignore_path = fileURLToPath(new URL('./.gitignore', import.meta.url)); export default /** @type {import("eslint").Linter.Config} */ ([ includeIgnoreFile(gitignore_path), + { + ignores: ['.claude/**/*'], + }, js.configs.recommended, ...ts.configs.recommended, ...svelte.configs.recommended, From c05b6c257ad1b8e8c34c7075069227d06b252660 Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Fri, 26 Sep 2025 23:51:48 +0200 Subject: [PATCH 15/30] eslint --- .../src/mcp/handlers/tools/list-sections.ts | 12 ++++++------ packages/mcp-server/src/mcp/utils.ts | 15 +++++++++------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts b/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts index fe4815aa..a9ff5755 100644 --- a/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts +++ b/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts @@ -1,5 +1,5 @@ import type { SvelteMcp } from '../../index.js'; -import { getSections } from '../../utils.js'; +import { get_sections } from '../../utils.js'; export function list_sections(server: SvelteMcp) { server.tool( @@ -10,24 +10,24 @@ export function list_sections(server: SvelteMcp) { 'Lists all available Svelte 5 and SvelteKit documentation sections in a structured format. Returns sections as a list of "* title: [section_title], use_cases: [use_cases], path: [file_path]" - you can use either the title or path when querying a specific section via the get_documentation tool. Always run list-sections first for any query related to Svelte development to discover available content.', }, async () => { - const sections = await getSections(); - const formattedSections = sections + const sections = await get_sections(); + const formatted_sections = sections .map( (section) => `* title: ${section.title}, use_cases: ${section.use_cases}, path: ${section.url}`, ) .join('\n'); - const introText = 'List of available Svelte documentation sections and its inteneded uses:'; + const intro_text = 'List of available Svelte documentation sections and its inteneded uses:'; - const outroText = + const outro_text = 'Use the title or path with the get-documentation tool to get more details about a specific section.'; return { content: [ { type: 'text', - text: `${introText}\n\n${formattedSections}\n\n${outroText}`, + text: `${intro_text}\n\n${formatted_sections}\n\n${outro_text}`, }, ], }; diff --git a/packages/mcp-server/src/mcp/utils.ts b/packages/mcp-server/src/mcp/utils.ts index a89135cf..76543e04 100644 --- a/packages/mcp-server/src/mcp/utils.ts +++ b/packages/mcp-server/src/mcp/utils.ts @@ -1,21 +1,24 @@ -export async function fetchWithTimeout(url: string, timeoutMs: number = 10000): Promise { +export async function fetch_with_timeout( + url: string, + timeout_ms: number = 10000, +): Promise { const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), timeoutMs); + const timeout_id = setTimeout(() => controller.abort(), timeout_ms); try { const response = await fetch(url, { signal: controller.signal }); - clearTimeout(timeoutId); + clearTimeout(timeout_id); return response; } catch (error) { - clearTimeout(timeoutId); + clearTimeout(timeout_id); if (error instanceof Error && error.name === 'AbortError') { - throw new Error(`Request timed out after ${timeoutMs}ms`); + throw new Error(`Request timed out after ${timeout_ms}ms`); } throw error; } } -export async function getSections() { +export async function get_sections() { return [ { title: 'Overview', From 8328a3572b45bb9ad871054186c8d2fe86f373d4 Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Fri, 26 Sep 2025 23:51:58 +0200 Subject: [PATCH 16/30] eslint --- .../mcp/handlers/tools/get-documentation.ts | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts index e4a8e77d..334b63a7 100644 --- a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts +++ b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts @@ -1,6 +1,6 @@ import type { SvelteMcp } from '../../index.js'; import * as v from 'valibot'; -import { getSections, fetchWithTimeout } from '../../utils.js'; +import { get_sections, fetch_with_timeout } from '../../utils.js'; export function get_documentation(server: SvelteMcp) { server.tool( @@ -44,67 +44,68 @@ export function get_documentation(server: SvelteMcp) { sections = []; } - const availableSections = await getSections(); + const available_sections = await get_sections(); const results = await Promise.all( - sections.map(async (requestedSection) => { - const matchedSection = availableSections.find( + sections.map(async (requested_section) => { + const matched_section = available_sections.find( (s) => - s.title.toLowerCase() === requestedSection.toLowerCase() || - s.url === requestedSection, + s.title.toLowerCase() === requested_section.toLowerCase() || + s.url === requested_section, ); - if (matchedSection) { + if (matched_section) { try { - const response = await fetchWithTimeout(matchedSection.url); + const response = await fetch_with_timeout(matched_section.url); if (response.ok) { const content = await response.text(); - return { success: true, content: `## ${matchedSection.title}\n\n${content}` }; + return { success: true, content: `## ${matched_section.title}\n\n${content}` }; } else { return { success: false, - content: `## ${matchedSection.title}\n\nError: Could not fetch documentation (HTTP ${response.status})`, + content: `## ${matched_section.title}\n\nError: Could not fetch documentation (HTTP ${response.status})`, }; } } catch (error) { return { success: false, - content: `## ${matchedSection.title}\n\nError: Failed to fetch documentation - ${error}`, + content: `## ${matched_section.title}\n\nError: Failed to fetch documentation - ${error}`, }; } } else { return { success: false, - content: `## ${requestedSection}\n\nError: Section not found.`, + content: `## ${requested_section}\n\nError: Section not found.`, }; } }), ); - const hasAnySuccess = results.some((result) => result.success); - let finalText = results.map((r) => r.content).join('\n\n---\n\n'); + const has_any_success = results.some((result) => result.success); + let final_text = results.map((r) => r.content).join('\n\n---\n\n'); - if (!hasAnySuccess) { - const formattedSections = availableSections + if (!has_any_success) { + const formatted_sections = available_sections .map( (section) => `* title: ${section.title}, use_cases: ${section.use_cases}, path: ${section.url}`, ) .join('\n'); - const introText = 'List of available Svelte documentation sections and its inteneded uses:'; + const intro_text = + 'List of available Svelte documentation sections and its inteneded uses:'; - const outroText = + const outro_text = 'Use the title or path with the get-documentation tool to get more details about a specific section.'; - finalText += `\n\n---\n\n${introText}\n\n${formattedSections}\n\n${outroText}`; + final_text += `\n\n---\n\n${intro_text}\n\n${formatted_sections}\n\n${outro_text}`; } return { content: [ { type: 'text', - text: finalText, + text: final_text, }, ], }; From fb2d19fd077bffd07c3119d7d69348c992122d21 Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Fri, 26 Sep 2025 23:59:18 +0200 Subject: [PATCH 17/30] Update list-sections.ts --- .../mcp/handlers/resources/list-sections.ts | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/packages/mcp-server/src/mcp/handlers/resources/list-sections.ts b/packages/mcp-server/src/mcp/handlers/resources/list-sections.ts index c36e8e35..6c9ca003 100644 --- a/packages/mcp-server/src/mcp/handlers/resources/list-sections.ts +++ b/packages/mcp-server/src/mcp/handlers/resources/list-sections.ts @@ -1,25 +1,34 @@ import type { SvelteMcp } from '../../index.js'; +import { get_sections, fetch_with_timeout } from '../../utils.js'; -export function list_sections(server: SvelteMcp) { - server.resource( - { - name: 'list-sections', - enabled: () => false, - description: - 'The list of all the available Svelte 5 and SvelteKit documentation sections in a structured format.', - uri: 'svelte://list-sections', - title: 'Svelte Documentation Section', - }, - async (uri) => { - return { - contents: [ - { - uri, - type: 'text', - text: 'resource list-sections called', - }, - ], - }; - }, - ); +export async function list_sections(server: SvelteMcp) { + const sections = await get_sections(); + + sections.forEach((section) => { + const resource_name = section.title.toLowerCase().replace(/\s+/g, '-'); + const resource_uri = `svelte://docs/${resource_name}`; + + server.resource( + { + name: resource_name, + enabled: () => true, + description: section.use_cases, + uri: resource_uri, + title: section.title, + }, + async (uri) => { + const response = await fetch_with_timeout(section.url); + const content = await response.text(); + return { + contents: [ + { + uri, + type: 'text', + text: content, + }, + ], + }; + }, + ); + }); } From b1a196497daff8c84a03ef4a6761f9fb3af48f17 Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Sat, 27 Sep 2025 00:00:15 +0200 Subject: [PATCH 18/30] Update list-sections.ts --- .../mcp-server/src/mcp/handlers/resources/list-sections.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/mcp-server/src/mcp/handlers/resources/list-sections.ts b/packages/mcp-server/src/mcp/handlers/resources/list-sections.ts index 6c9ca003..fe189af3 100644 --- a/packages/mcp-server/src/mcp/handlers/resources/list-sections.ts +++ b/packages/mcp-server/src/mcp/handlers/resources/list-sections.ts @@ -5,8 +5,9 @@ export async function list_sections(server: SvelteMcp) { const sections = await get_sections(); sections.forEach((section) => { - const resource_name = section.title.toLowerCase().replace(/\s+/g, '-'); - const resource_uri = `svelte://docs/${resource_name}`; + const section_name = section.title.toLowerCase().replace(/\s+/g, '-'); + const resource_name = `docs/svelte/${section_name}`; + const resource_uri = `svelte://docs/${section_name}`; server.resource( { From 77af7ebcc6c3c9209365759e4fdfb9d6aadb1ccf Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Sat, 27 Sep 2025 00:01:50 +0200 Subject: [PATCH 19/30] Update packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts Co-authored-by: Paolo Ricciuti --- packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts index 334b63a7..0bbd823f 100644 --- a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts +++ b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts @@ -6,7 +6,6 @@ export function get_documentation(server: SvelteMcp) { server.tool( { name: 'get-documentation', - enabled: () => true, description: 'Retrieves full documentation content for Svelte 5 or SvelteKit sections. Supports flexible search by title (e.g., "$state", "routing") or file path (e.g., "docs/svelte/state.md"). Can accept a single section name or an array of sections. Before running this, make sure to analyze the users query, as well as the output from list-sections (which should be called first). Then ask for ALL relevant sections the user might require. For example, if the user asks to build anything interactive, you will need to fetch all relevant runes, and so on.', schema: v.object({ From 6a6417d3a558445212f748280be8888a419f39f7 Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Sat, 27 Sep 2025 00:03:40 +0200 Subject: [PATCH 20/30] Use Promise.allSettled() --- .../src/mcp/handlers/tools/get-documentation.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts index 334b63a7..0958de84 100644 --- a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts +++ b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts @@ -46,7 +46,7 @@ export function get_documentation(server: SvelteMcp) { const available_sections = await get_sections(); - const results = await Promise.all( + const settled_results = await Promise.allSettled( sections.map(async (requested_section) => { const matched_section = available_sections.find( (s) => @@ -81,6 +81,17 @@ export function get_documentation(server: SvelteMcp) { }), ); + const results = settled_results.map((result) => { + if (result.status === 'fulfilled') { + return result.value; + } else { + return { + success: false, + content: `Error: Couldn't fetch - ${result.reason}`, + }; + } + }); + const has_any_success = results.some((result) => result.success); let final_text = results.map((r) => r.content).join('\n\n---\n\n'); From 01d5803b5d219c1d418f539dfb864fd063d313c3 Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Sat, 27 Sep 2025 00:06:37 +0200 Subject: [PATCH 21/30] reduce duplication --- .../src/mcp/handlers/tools/get-documentation.ts | 9 ++------- .../mcp-server/src/mcp/handlers/tools/list-sections.ts | 8 ++------ packages/mcp-server/src/mcp/handlers/tools/prompts.ts | 4 ++++ 3 files changed, 8 insertions(+), 13 deletions(-) create mode 100644 packages/mcp-server/src/mcp/handlers/tools/prompts.ts diff --git a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts index 86da8360..d68bd124 100644 --- a/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts +++ b/packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts @@ -1,6 +1,7 @@ import type { SvelteMcp } from '../../index.js'; import * as v from 'valibot'; import { get_sections, fetch_with_timeout } from '../../utils.js'; +import { SECTIONS_LIST_INTRO, SECTIONS_LIST_OUTRO } from './prompts.js'; export function get_documentation(server: SvelteMcp) { server.tool( @@ -102,13 +103,7 @@ export function get_documentation(server: SvelteMcp) { ) .join('\n'); - const intro_text = - 'List of available Svelte documentation sections and its inteneded uses:'; - - const outro_text = - 'Use the title or path with the get-documentation tool to get more details about a specific section.'; - - final_text += `\n\n---\n\n${intro_text}\n\n${formatted_sections}\n\n${outro_text}`; + final_text += `\n\n---\n\n${SECTIONS_LIST_INTRO}\n\n${formatted_sections}\n\n${SECTIONS_LIST_OUTRO}`; } return { diff --git a/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts b/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts index a9ff5755..f6786ca3 100644 --- a/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts +++ b/packages/mcp-server/src/mcp/handlers/tools/list-sections.ts @@ -1,5 +1,6 @@ import type { SvelteMcp } from '../../index.js'; import { get_sections } from '../../utils.js'; +import { SECTIONS_LIST_INTRO, SECTIONS_LIST_OUTRO } from './prompts.js'; export function list_sections(server: SvelteMcp) { server.tool( @@ -18,16 +19,11 @@ export function list_sections(server: SvelteMcp) { ) .join('\n'); - const intro_text = 'List of available Svelte documentation sections and its inteneded uses:'; - - const outro_text = - 'Use the title or path with the get-documentation tool to get more details about a specific section.'; - return { content: [ { type: 'text', - text: `${intro_text}\n\n${formatted_sections}\n\n${outro_text}`, + text: `${SECTIONS_LIST_INTRO}\n\n${formatted_sections}\n\n${SECTIONS_LIST_OUTRO}`, }, ], }; diff --git a/packages/mcp-server/src/mcp/handlers/tools/prompts.ts b/packages/mcp-server/src/mcp/handlers/tools/prompts.ts new file mode 100644 index 00000000..e096479e --- /dev/null +++ b/packages/mcp-server/src/mcp/handlers/tools/prompts.ts @@ -0,0 +1,4 @@ +export const SECTIONS_LIST_INTRO = 'List of available Svelte documentation sections and its inteneded uses:'; + +export const SECTIONS_LIST_OUTRO = + 'Use the title or path with the get-documentation tool to get more details about a specific section.'; \ No newline at end of file From 54763e0f55293b81fbfaa9561ca422d896997438 Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Sat, 27 Sep 2025 00:07:18 +0200 Subject: [PATCH 22/30] Update packages/mcp-server/src/mcp/utils.ts Co-authored-by: Paolo Ricciuti --- packages/mcp-server/src/mcp/utils.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/mcp-server/src/mcp/utils.ts b/packages/mcp-server/src/mcp/utils.ts index 76543e04..218382b7 100644 --- a/packages/mcp-server/src/mcp/utils.ts +++ b/packages/mcp-server/src/mcp/utils.ts @@ -2,17 +2,12 @@ export async function fetch_with_timeout( url: string, timeout_ms: number = 10000, ): Promise { - const controller = new AbortController(); - const timeout_id = setTimeout(() => controller.abort(), timeout_ms); - try { - const response = await fetch(url, { signal: controller.signal }); - clearTimeout(timeout_id); + const response = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) }); return response; } catch (error) { - clearTimeout(timeout_id); if (error instanceof Error && error.name === 'AbortError') { - throw new Error(`Request timed out after ${timeout_ms}ms`); + throw new Error(`Request timed out after ${timeoutMs}ms`); } throw error; } From 76a35f5dc8b5f6514e1a7955c7bc663bc852003c Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Sat, 27 Sep 2025 00:13:37 +0200 Subject: [PATCH 23/30] format --- packages/mcp-server/src/mcp/handlers/tools/prompts.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/mcp-server/src/mcp/handlers/tools/prompts.ts b/packages/mcp-server/src/mcp/handlers/tools/prompts.ts index e096479e..e5740583 100644 --- a/packages/mcp-server/src/mcp/handlers/tools/prompts.ts +++ b/packages/mcp-server/src/mcp/handlers/tools/prompts.ts @@ -1,4 +1,5 @@ -export const SECTIONS_LIST_INTRO = 'List of available Svelte documentation sections and its inteneded uses:'; +export const SECTIONS_LIST_INTRO = + 'List of available Svelte documentation sections and its inteneded uses:'; export const SECTIONS_LIST_OUTRO = - 'Use the title or path with the get-documentation tool to get more details about a specific section.'; \ No newline at end of file + 'Use the title or path with the get-documentation tool to get more details about a specific section.'; From 5dd83d151ef4ddec021c93e2aef0bc472f7a23f3 Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Sat, 27 Sep 2025 00:14:00 +0200 Subject: [PATCH 24/30] Update utils.ts --- packages/mcp-server/src/mcp/utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mcp-server/src/mcp/utils.ts b/packages/mcp-server/src/mcp/utils.ts index 218382b7..2634de71 100644 --- a/packages/mcp-server/src/mcp/utils.ts +++ b/packages/mcp-server/src/mcp/utils.ts @@ -3,11 +3,11 @@ export async function fetch_with_timeout( timeout_ms: number = 10000, ): Promise { try { - const response = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) }); + const response = await fetch(url, { signal: AbortSignal.timeout(timeout_ms) }); return response; } catch (error) { if (error instanceof Error && error.name === 'AbortError') { - throw new Error(`Request timed out after ${timeoutMs}ms`); + throw new Error(`Request timed out after ${timeout_ms}ms`); } throw error; } From ce0861c1cab04f5cfa7f2f4399e6ef043de38df5 Mon Sep 17 00:00:00 2001 From: paoloricciuti Date: Sun, 28 Sep 2025 12:18:52 +0200 Subject: [PATCH 25/30] chore: use mocked current version of sections --- packages/mcp-server/src/mcp/utils.ts | 862 ++++++++++++++++++++++++++- 1 file changed, 845 insertions(+), 17 deletions(-) diff --git a/packages/mcp-server/src/mcp/utils.ts b/packages/mcp-server/src/mcp/utils.ts index 2634de71..4e077119 100644 --- a/packages/mcp-server/src/mcp/utils.ts +++ b/packages/mcp-server/src/mcp/utils.ts @@ -13,22 +13,850 @@ export async function fetch_with_timeout( } } +// TODO hardcoded for now but we'll fetch this from svelte.dev +const sections = { + 'docs/cli/overview': { + metadata: { title: 'Overview' }, + slug: 'docs/cli/overview', + file: 'docs/cli/10-introduction/10-overview.md', + }, + 'docs/cli/faq': { + metadata: { title: 'Frequently asked questions' }, + slug: 'docs/cli/faq', + file: 'docs/cli/10-introduction/20-faq.md', + }, + 'docs/cli/sv-create': { + metadata: { title: 'sv create' }, + slug: 'docs/cli/sv-create', + file: 'docs/cli/20-commands/10-sv-create.md', + }, + 'docs/cli/sv-add': { + metadata: { title: 'sv add' }, + slug: 'docs/cli/sv-add', + file: 'docs/cli/20-commands/20-sv-add.md', + }, + 'docs/cli/sv-check': { + metadata: { title: 'sv check' }, + slug: 'docs/cli/sv-check', + file: 'docs/cli/20-commands/30-sv-check.md', + }, + 'docs/cli/sv-migrate': { + metadata: { title: 'sv migrate' }, + slug: 'docs/cli/sv-migrate', + file: 'docs/cli/20-commands/40-sv-migrate.md', + }, + 'docs/cli/devtools-json': { + metadata: { title: 'devtools-json' }, + slug: 'docs/cli/devtools-json', + file: 'docs/cli/30-add-ons/03-devtools-json.md', + }, + 'docs/cli/drizzle': { + metadata: { title: 'drizzle' }, + slug: 'docs/cli/drizzle', + file: 'docs/cli/30-add-ons/05-drizzle.md', + }, + 'docs/cli/eslint': { + metadata: { title: 'eslint' }, + slug: 'docs/cli/eslint', + file: 'docs/cli/30-add-ons/10-eslint.md', + }, + 'docs/cli/lucia': { + metadata: { title: 'lucia' }, + slug: 'docs/cli/lucia', + file: 'docs/cli/30-add-ons/15-lucia.md', + }, + 'docs/cli/mdsvex': { + metadata: { title: 'mdsvex' }, + slug: 'docs/cli/mdsvex', + file: 'docs/cli/30-add-ons/20-mdsvex.md', + }, + 'docs/cli/paraglide': { + metadata: { title: 'paraglide' }, + slug: 'docs/cli/paraglide', + file: 'docs/cli/30-add-ons/25-paraglide.md', + }, + 'docs/cli/playwright': { + metadata: { title: 'playwright' }, + slug: 'docs/cli/playwright', + file: 'docs/cli/30-add-ons/30-playwright.md', + }, + 'docs/cli/prettier': { + metadata: { title: 'prettier' }, + slug: 'docs/cli/prettier', + file: 'docs/cli/30-add-ons/35-prettier.md', + }, + 'docs/cli/storybook': { + metadata: { title: 'storybook' }, + slug: 'docs/cli/storybook', + file: 'docs/cli/30-add-ons/40-storybook.md', + }, + 'docs/cli/sveltekit-adapter': { + metadata: { title: 'sveltekit-adapter' }, + slug: 'docs/cli/sveltekit-adapter', + file: 'docs/cli/30-add-ons/45-sveltekit-adapter.md', + }, + 'docs/cli/tailwind': { + metadata: { title: 'tailwindcss' }, + slug: 'docs/cli/tailwind', + file: 'docs/cli/30-add-ons/50-tailwind.md', + }, + 'docs/cli/vitest': { + metadata: { title: 'vitest' }, + slug: 'docs/cli/vitest', + file: 'docs/cli/30-add-ons/55-vitest.md', + }, + 'docs/kit/introduction': { + metadata: { title: 'Introduction' }, + slug: 'docs/kit/introduction', + file: 'docs/kit/10-getting-started/10-introduction.md', + }, + 'docs/kit/creating-a-project': { + metadata: { title: 'Creating a project' }, + slug: 'docs/kit/creating-a-project', + file: 'docs/kit/10-getting-started/20-creating-a-project.md', + }, + 'docs/kit/project-types': { + metadata: { title: 'Project types' }, + slug: 'docs/kit/project-types', + file: 'docs/kit/10-getting-started/25-project-types.md', + }, + 'docs/kit/project-structure': { + metadata: { title: 'Project structure' }, + slug: 'docs/kit/project-structure', + file: 'docs/kit/10-getting-started/30-project-structure.md', + }, + 'docs/kit/web-standards': { + metadata: { title: 'Web standards' }, + slug: 'docs/kit/web-standards', + file: 'docs/kit/10-getting-started/40-web-standards.md', + }, + 'docs/kit/routing': { + metadata: { title: 'Routing' }, + slug: 'docs/kit/routing', + file: 'docs/kit/20-core-concepts/10-routing.md', + }, + 'docs/kit/load': { + metadata: { title: 'Loading data' }, + slug: 'docs/kit/load', + file: 'docs/kit/20-core-concepts/20-load.md', + }, + 'docs/kit/form-actions': { + metadata: { title: 'Form actions' }, + slug: 'docs/kit/form-actions', + file: 'docs/kit/20-core-concepts/30-form-actions.md', + }, + 'docs/kit/page-options': { + metadata: { title: 'Page options' }, + slug: 'docs/kit/page-options', + file: 'docs/kit/20-core-concepts/40-page-options.md', + }, + 'docs/kit/state-management': { + metadata: { title: 'State management' }, + slug: 'docs/kit/state-management', + file: 'docs/kit/20-core-concepts/50-state-management.md', + }, + 'docs/kit/remote-functions': { + metadata: { title: 'Remote functions' }, + slug: 'docs/kit/remote-functions', + file: 'docs/kit/20-core-concepts/60-remote-functions.md', + }, + 'docs/kit/building-your-app': { + metadata: { title: 'Building your app' }, + slug: 'docs/kit/building-your-app', + file: 'docs/kit/25-build-and-deploy/10-building-your-app.md', + }, + 'docs/kit/adapters': { + metadata: { title: 'Adapters' }, + slug: 'docs/kit/adapters', + file: 'docs/kit/25-build-and-deploy/20-adapters.md', + }, + 'docs/kit/adapter-auto': { + metadata: { title: 'Zero-config deployments' }, + slug: 'docs/kit/adapter-auto', + file: 'docs/kit/25-build-and-deploy/30-adapter-auto.md', + }, + 'docs/kit/adapter-node': { + metadata: { title: 'Node servers' }, + slug: 'docs/kit/adapter-node', + file: 'docs/kit/25-build-and-deploy/40-adapter-node.md', + }, + 'docs/kit/adapter-static': { + metadata: { title: 'Static site generation' }, + slug: 'docs/kit/adapter-static', + file: 'docs/kit/25-build-and-deploy/50-adapter-static.md', + }, + 'docs/kit/single-page-apps': { + metadata: { title: 'Single-page apps' }, + slug: 'docs/kit/single-page-apps', + file: 'docs/kit/25-build-and-deploy/55-single-page-apps.md', + }, + 'docs/kit/adapter-cloudflare': { + metadata: { title: 'Cloudflare' }, + slug: 'docs/kit/adapter-cloudflare', + file: 'docs/kit/25-build-and-deploy/60-adapter-cloudflare.md', + }, + 'docs/kit/adapter-cloudflare-workers': { + metadata: { title: 'Cloudflare Workers' }, + slug: 'docs/kit/adapter-cloudflare-workers', + file: 'docs/kit/25-build-and-deploy/70-adapter-cloudflare-workers.md', + }, + 'docs/kit/adapter-netlify': { + metadata: { title: 'Netlify' }, + slug: 'docs/kit/adapter-netlify', + file: 'docs/kit/25-build-and-deploy/80-adapter-netlify.md', + }, + 'docs/kit/adapter-vercel': { + metadata: { title: 'Vercel' }, + slug: 'docs/kit/adapter-vercel', + file: 'docs/kit/25-build-and-deploy/90-adapter-vercel.md', + }, + 'docs/kit/writing-adapters': { + metadata: { title: 'Writing adapters' }, + slug: 'docs/kit/writing-adapters', + file: 'docs/kit/25-build-and-deploy/99-writing-adapters.md', + }, + 'docs/kit/advanced-routing': { + metadata: { title: 'Advanced routing' }, + slug: 'docs/kit/advanced-routing', + file: 'docs/kit/30-advanced/10-advanced-routing.md', + }, + 'docs/kit/hooks': { + metadata: { title: 'Hooks' }, + slug: 'docs/kit/hooks', + file: 'docs/kit/30-advanced/20-hooks.md', + }, + 'docs/kit/errors': { + metadata: { title: 'Errors' }, + slug: 'docs/kit/errors', + file: 'docs/kit/30-advanced/25-errors.md', + }, + 'docs/kit/link-options': { + metadata: { title: 'Link options' }, + slug: 'docs/kit/link-options', + file: 'docs/kit/30-advanced/30-link-options.md', + }, + 'docs/kit/service-workers': { + metadata: { title: 'Service workers' }, + slug: 'docs/kit/service-workers', + file: 'docs/kit/30-advanced/40-service-workers.md', + }, + 'docs/kit/server-only-modules': { + metadata: { title: 'Server-only modules' }, + slug: 'docs/kit/server-only-modules', + file: 'docs/kit/30-advanced/50-server-only-modules.md', + }, + 'docs/kit/snapshots': { + metadata: { title: 'Snapshots' }, + slug: 'docs/kit/snapshots', + file: 'docs/kit/30-advanced/65-snapshots.md', + }, + 'docs/kit/shallow-routing': { + metadata: { title: 'Shallow routing' }, + slug: 'docs/kit/shallow-routing', + file: 'docs/kit/30-advanced/67-shallow-routing.md', + }, + 'docs/kit/observability': { + metadata: { title: 'Observability' }, + slug: 'docs/kit/observability', + file: 'docs/kit/30-advanced/68-observability.md', + }, + 'docs/kit/packaging': { + metadata: { title: 'Packaging' }, + slug: 'docs/kit/packaging', + file: 'docs/kit/30-advanced/70-packaging.md', + }, + 'docs/kit/auth': { + metadata: { title: 'Auth' }, + slug: 'docs/kit/auth', + file: 'docs/kit/40-best-practices/03-auth.md', + }, + 'docs/kit/performance': { + metadata: { title: 'Performance' }, + slug: 'docs/kit/performance', + file: 'docs/kit/40-best-practices/05-performance.md', + }, + 'docs/kit/icons': { + metadata: { title: 'Icons' }, + slug: 'docs/kit/icons', + file: 'docs/kit/40-best-practices/06-icons.md', + }, + 'docs/kit/images': { + metadata: { title: 'Images' }, + slug: 'docs/kit/images', + file: 'docs/kit/40-best-practices/07-images.md', + }, + 'docs/kit/accessibility': { + metadata: { title: 'Accessibility' }, + slug: 'docs/kit/accessibility', + file: 'docs/kit/40-best-practices/10-accessibility.md', + }, + 'docs/kit/seo': { + metadata: { title: 'SEO' }, + slug: 'docs/kit/seo', + file: 'docs/kit/40-best-practices/20-seo.md', + }, + 'docs/kit/faq': { + metadata: { title: 'Frequently asked questions' }, + slug: 'docs/kit/faq', + file: 'docs/kit/60-appendix/10-faq.md', + }, + 'docs/kit/integrations': { + metadata: { title: 'Integrations' }, + slug: 'docs/kit/integrations', + file: 'docs/kit/60-appendix/20-integrations.md', + }, + 'docs/kit/debugging': { + metadata: { title: 'Breakpoint Debugging' }, + slug: 'docs/kit/debugging', + file: 'docs/kit/60-appendix/25-debugging.md', + }, + 'docs/kit/migrating-to-sveltekit-2': { + metadata: { title: 'Migrating to SvelteKit v2' }, + slug: 'docs/kit/migrating-to-sveltekit-2', + file: 'docs/kit/60-appendix/30-migrating-to-sveltekit-2.md', + }, + 'docs/kit/migrating': { + metadata: { title: 'Migrating from Sapper' }, + slug: 'docs/kit/migrating', + file: 'docs/kit/60-appendix/40-migrating.md', + }, + 'docs/kit/additional-resources': { + metadata: { title: 'Additional resources' }, + slug: 'docs/kit/additional-resources', + file: 'docs/kit/60-appendix/50-additional-resources.md', + }, + 'docs/kit/glossary': { + metadata: { title: 'Glossary' }, + slug: 'docs/kit/glossary', + file: 'docs/kit/60-appendix/60-glossary.md', + }, + 'docs/kit/@sveltejs-kit': { + metadata: { title: '@sveltejs/kit' }, + slug: 'docs/kit/@sveltejs-kit', + file: 'docs/kit/98-reference/10-@sveltejs-kit.md', + }, + 'docs/kit/@sveltejs-kit-hooks': { + metadata: { title: '@sveltejs/kit/hooks' }, + slug: 'docs/kit/@sveltejs-kit-hooks', + file: 'docs/kit/98-reference/15-@sveltejs-kit-hooks.md', + }, + 'docs/kit/@sveltejs-kit-node-polyfills': { + metadata: { title: '@sveltejs/kit/node/polyfills' }, + slug: 'docs/kit/@sveltejs-kit-node-polyfills', + file: 'docs/kit/98-reference/15-@sveltejs-kit-node-polyfills.md', + }, + 'docs/kit/@sveltejs-kit-node': { + metadata: { title: '@sveltejs/kit/node' }, + slug: 'docs/kit/@sveltejs-kit-node', + file: 'docs/kit/98-reference/15-@sveltejs-kit-node.md', + }, + 'docs/kit/@sveltejs-kit-vite': { + metadata: { title: '@sveltejs/kit/vite' }, + slug: 'docs/kit/@sveltejs-kit-vite', + file: 'docs/kit/98-reference/15-@sveltejs-kit-vite.md', + }, + 'docs/kit/$app-environment': { + metadata: { title: '$app/environment' }, + slug: 'docs/kit/$app-environment', + file: 'docs/kit/98-reference/20-$app-environment.md', + }, + 'docs/kit/$app-forms': { + metadata: { title: '$app/forms' }, + slug: 'docs/kit/$app-forms', + file: 'docs/kit/98-reference/20-$app-forms.md', + }, + 'docs/kit/$app-navigation': { + metadata: { title: '$app/navigation' }, + slug: 'docs/kit/$app-navigation', + file: 'docs/kit/98-reference/20-$app-navigation.md', + }, + 'docs/kit/$app-paths': { + metadata: { title: '$app/paths' }, + slug: 'docs/kit/$app-paths', + file: 'docs/kit/98-reference/20-$app-paths.md', + }, + 'docs/kit/$app-server': { + metadata: { title: '$app/server' }, + slug: 'docs/kit/$app-server', + file: 'docs/kit/98-reference/20-$app-server.md', + }, + 'docs/kit/$app-state': { + metadata: { title: '$app/state' }, + slug: 'docs/kit/$app-state', + file: 'docs/kit/98-reference/20-$app-state.md', + }, + 'docs/kit/$app-stores': { + metadata: { title: '$app/stores' }, + slug: 'docs/kit/$app-stores', + file: 'docs/kit/98-reference/20-$app-stores.md', + }, + 'docs/kit/$app-types': { + metadata: { title: '$app/types' }, + slug: 'docs/kit/$app-types', + file: 'docs/kit/98-reference/20-$app-types.md', + }, + 'docs/kit/$env-dynamic-private': { + metadata: { title: '$env/dynamic/private' }, + slug: 'docs/kit/$env-dynamic-private', + file: 'docs/kit/98-reference/25-$env-dynamic-private.md', + }, + 'docs/kit/$env-dynamic-public': { + metadata: { title: '$env/dynamic/public' }, + slug: 'docs/kit/$env-dynamic-public', + file: 'docs/kit/98-reference/25-$env-dynamic-public.md', + }, + 'docs/kit/$env-static-private': { + metadata: { title: '$env/static/private' }, + slug: 'docs/kit/$env-static-private', + file: 'docs/kit/98-reference/25-$env-static-private.md', + }, + 'docs/kit/$env-static-public': { + metadata: { title: '$env/static/public' }, + slug: 'docs/kit/$env-static-public', + file: 'docs/kit/98-reference/25-$env-static-public.md', + }, + 'docs/kit/$lib': { + metadata: { title: '$lib' }, + slug: 'docs/kit/$lib', + file: 'docs/kit/98-reference/26-$lib.md', + }, + 'docs/kit/$service-worker': { + metadata: { title: '$service-worker' }, + slug: 'docs/kit/$service-worker', + file: 'docs/kit/98-reference/27-$service-worker.md', + }, + 'docs/kit/configuration': { + metadata: { title: 'Configuration' }, + slug: 'docs/kit/configuration', + file: 'docs/kit/98-reference/50-configuration.md', + }, + 'docs/kit/cli': { + metadata: { title: 'Command Line Interface' }, + slug: 'docs/kit/cli', + file: 'docs/kit/98-reference/52-cli.md', + }, + 'docs/kit/types': { + metadata: { title: 'Types' }, + slug: 'docs/kit/types', + file: 'docs/kit/98-reference/54-types.md', + }, + 'docs/svelte/overview': { + metadata: { title: 'Overview' }, + slug: 'docs/svelte/overview', + file: 'docs/svelte/01-introduction/01-overview.md', + }, + 'docs/svelte/getting-started': { + metadata: { title: 'Getting started' }, + slug: 'docs/svelte/getting-started', + file: 'docs/svelte/01-introduction/02-getting-started.md', + }, + 'docs/svelte/svelte-files': { + metadata: { title: '.svelte files' }, + slug: 'docs/svelte/svelte-files', + file: 'docs/svelte/01-introduction/03-svelte-files.md', + }, + 'docs/svelte/svelte-js-files': { + metadata: { title: '.svelte.js and .svelte.ts files' }, + slug: 'docs/svelte/svelte-js-files', + file: 'docs/svelte/01-introduction/04-svelte-js-files.md', + }, + 'docs/svelte/what-are-runes': { + metadata: { title: 'What are runes?' }, + slug: 'docs/svelte/what-are-runes', + file: 'docs/svelte/02-runes/01-what-are-runes.md', + }, + 'docs/svelte/$state': { + metadata: { title: '$state' }, + slug: 'docs/svelte/$state', + file: 'docs/svelte/02-runes/02-$state.md', + }, + 'docs/svelte/$derived': { + metadata: { title: '$derived' }, + slug: 'docs/svelte/$derived', + file: 'docs/svelte/02-runes/03-$derived.md', + }, + 'docs/svelte/$effect': { + metadata: { title: '$effect' }, + slug: 'docs/svelte/$effect', + file: 'docs/svelte/02-runes/04-$effect.md', + }, + 'docs/svelte/$props': { + metadata: { title: '$props' }, + slug: 'docs/svelte/$props', + file: 'docs/svelte/02-runes/05-$props.md', + }, + 'docs/svelte/$bindable': { + metadata: { title: '$bindable' }, + slug: 'docs/svelte/$bindable', + file: 'docs/svelte/02-runes/06-$bindable.md', + }, + 'docs/svelte/$inspect': { + metadata: { title: '$inspect' }, + slug: 'docs/svelte/$inspect', + file: 'docs/svelte/02-runes/07-$inspect.md', + }, + 'docs/svelte/$host': { + metadata: { title: '$host' }, + slug: 'docs/svelte/$host', + file: 'docs/svelte/02-runes/08-$host.md', + }, + 'docs/svelte/basic-markup': { + metadata: { title: 'Basic markup' }, + slug: 'docs/svelte/basic-markup', + file: 'docs/svelte/03-template-syntax/01-basic-markup.md', + }, + 'docs/svelte/if': { + metadata: { title: '{#if ...}' }, + slug: 'docs/svelte/if', + file: 'docs/svelte/03-template-syntax/02-if.md', + }, + 'docs/svelte/each': { + metadata: { title: '{#each ...}' }, + slug: 'docs/svelte/each', + file: 'docs/svelte/03-template-syntax/03-each.md', + }, + 'docs/svelte/key': { + metadata: { title: '{#key ...}' }, + slug: 'docs/svelte/key', + file: 'docs/svelte/03-template-syntax/04-key.md', + }, + 'docs/svelte/await': { + metadata: { title: '{#await ...}' }, + slug: 'docs/svelte/await', + file: 'docs/svelte/03-template-syntax/05-await.md', + }, + 'docs/svelte/snippet': { + metadata: { title: '{#snippet ...}' }, + slug: 'docs/svelte/snippet', + file: 'docs/svelte/03-template-syntax/06-snippet.md', + }, + 'docs/svelte/@render': { + metadata: { title: '{@render ...}' }, + slug: 'docs/svelte/@render', + file: 'docs/svelte/03-template-syntax/07-@render.md', + }, + 'docs/svelte/@html': { + metadata: { title: '{@html ...}' }, + slug: 'docs/svelte/@html', + file: 'docs/svelte/03-template-syntax/08-@html.md', + }, + 'docs/svelte/@attach': { + metadata: { title: '{@attach ...}' }, + slug: 'docs/svelte/@attach', + file: 'docs/svelte/03-template-syntax/09-@attach.md', + }, + 'docs/svelte/@const': { + metadata: { title: '{@const ...}' }, + slug: 'docs/svelte/@const', + file: 'docs/svelte/03-template-syntax/10-@const.md', + }, + 'docs/svelte/@debug': { + metadata: { title: '{@debug ...}' }, + slug: 'docs/svelte/@debug', + file: 'docs/svelte/03-template-syntax/11-@debug.md', + }, + 'docs/svelte/bind': { + metadata: { title: 'bind:' }, + slug: 'docs/svelte/bind', + file: 'docs/svelte/03-template-syntax/12-bind.md', + }, + 'docs/svelte/use': { + metadata: { title: 'use:' }, + slug: 'docs/svelte/use', + file: 'docs/svelte/03-template-syntax/13-use.md', + }, + 'docs/svelte/transition': { + metadata: { title: 'transition:' }, + slug: 'docs/svelte/transition', + file: 'docs/svelte/03-template-syntax/14-transition.md', + }, + 'docs/svelte/in-and-out': { + metadata: { title: 'in: and out:' }, + slug: 'docs/svelte/in-and-out', + file: 'docs/svelte/03-template-syntax/15-in-and-out.md', + }, + 'docs/svelte/animate': { + metadata: { title: 'animate:' }, + slug: 'docs/svelte/animate', + file: 'docs/svelte/03-template-syntax/16-animate.md', + }, + 'docs/svelte/style': { + metadata: { title: 'style:' }, + slug: 'docs/svelte/style', + file: 'docs/svelte/03-template-syntax/17-style.md', + }, + 'docs/svelte/class': { + metadata: { title: 'class' }, + slug: 'docs/svelte/class', + file: 'docs/svelte/03-template-syntax/18-class.md', + }, + 'docs/svelte/await-expressions': { + metadata: { title: 'await' }, + slug: 'docs/svelte/await-expressions', + file: 'docs/svelte/03-template-syntax/19-await-expressions.md', + }, + 'docs/svelte/scoped-styles': { + metadata: { title: 'Scoped styles' }, + slug: 'docs/svelte/scoped-styles', + file: 'docs/svelte/04-styling/01-scoped-styles.md', + }, + 'docs/svelte/global-styles': { + metadata: { title: 'Global styles' }, + slug: 'docs/svelte/global-styles', + file: 'docs/svelte/04-styling/02-global-styles.md', + }, + 'docs/svelte/custom-properties': { + metadata: { title: 'Custom properties' }, + slug: 'docs/svelte/custom-properties', + file: 'docs/svelte/04-styling/03-custom-properties.md', + }, + 'docs/svelte/nested-style-elements': { + metadata: { title: 'Nested