Compare commits

...

59 Commits

Author SHA1 Message Date
Paolo Ricciuti
1f296e5277 Merge pull request #35 from sveltejs/changeset-release/main 2025-10-03 22:42:11 +02:00
github-actions[bot]
121395e98e Version Packages 2025-10-03 20:38:35 +00:00
paoloricciuti
8e7c881838 fix: just build we can figure out later if we want to avoid it 2025-10-03 22:38:08 +02:00
Paolo Ricciuti
91c396e675 Merge pull request #25 from sveltejs/setup-changesets 2025-10-03 22:35:50 +02:00
paoloricciuti
baad760634 chore: add changeset to trigger release 2025-10-03 22:34:55 +02:00
paoloricciuti
b2275587ee fix: remove package manager from inner packages 2025-10-03 22:33:56 +02:00
Paolo Ricciuti
4964303100 chore: apply suggestions from code review
Co-authored-by: Dominik G. <dominik.goepel@gmx.de>
2025-10-03 22:31:46 +02:00
paoloricciuti
81901b2564 fix: small configs 2025-10-03 22:03:59 +02:00
paoloricciuti
5aa1aa401a Merge branch 'main' into setup-changesets 2025-10-03 21:52:45 +02:00
Rich Harris
4201627f53 Merge pull request #31 from sveltejs/docs
docs: add documentation for svelte.dev
2025-10-03 15:22:59 -04:00
paoloricciuti
9a70fbe3aa feat: redirect to docs in case someone visits via browser 2025-10-03 21:12:42 +02:00
Paolo Ricciuti
dc16a42c65 chore: apply suggestions from code review
Co-authored-by: Rich Harris <hello@rich-harris.dev>
2025-10-03 19:11:14 +02:00
Paolo Ricciuti
3b50014b09 chore: apply suggestions from code review
Co-authored-by: Rich Harris <hello@rich-harris.dev>
2025-10-03 18:43:56 +02:00
Paolo Ricciuti
e9214bc470 chore: apply suggestions from code review
Co-authored-by: Rich Harris <hello@rich-harris.dev>
2025-10-03 18:33:31 +02:00
Paolo Ricciuti
3106305902 chore: apply suggestions from code review
Co-authored-by: Rich Harris <hello@rich-harris.dev>
2025-10-03 18:24:35 +02:00
paoloricciuti
3c14872068 fix: LLM -> model 2025-10-03 17:20:38 +02:00
paoloricciuti
216a470bd2 docs: add documentation for svelte.dev 2025-10-03 16:49:43 +02:00
paoloricciuti
b5a88c454d fix: small typos 2025-10-03 16:49:24 +02:00
Paolo Ricciuti
7b5bea6549 Merge pull request #28 from sveltejs/llms-txt
feat: simplified documentation listing
2025-10-02 11:46:33 +02:00
paoloricciuti
a36d0d17a8 fix: validate response and default use_cases 2025-10-02 11:29:35 +02:00
paoloricciuti
70f14bddca feat: use endpoint to get sections 2025-10-02 08:03:03 +02:00
paoloricciuti
a281ef4b66 fix: use server.template with list and complete for docs resources 2025-09-28 12:19:43 +02:00
paoloricciuti
5bc812e4db fix: enable list-sections tool 2025-09-28 12:19:21 +02:00
paoloricciuti
82319661dd feat: add sections to prompt 2025-09-28 12:19:03 +02:00
paoloricciuti
ce0861c1ca chore: use mocked current version of sections 2025-09-28 12:18:52 +02:00
Stanislav Khromov
5dd83d151e Update utils.ts 2025-09-27 00:14:00 +02:00
Stanislav Khromov
76a35f5dc8 format 2025-09-27 00:13:37 +02:00
Stanislav Khromov
54763e0f55 Update packages/mcp-server/src/mcp/utils.ts
Co-authored-by: Paolo Ricciuti <ricciutipaolo@gmail.com>
2025-09-27 00:07:18 +02:00
Stanislav Khromov
01d5803b5d reduce duplication 2025-09-27 00:06:37 +02:00
Stanislav Khromov
0366bc785b Merge branch 'llms-txt' of https://github.com/sveltejs/mcp into llms-txt 2025-09-27 00:03:49 +02:00
Stanislav Khromov
6a6417d3a5 Use Promise.allSettled() 2025-09-27 00:03:40 +02:00
Stanislav Khromov
77af7ebcc6 Update packages/mcp-server/src/mcp/handlers/tools/get-documentation.ts
Co-authored-by: Paolo Ricciuti <ricciutipaolo@gmail.com>
2025-09-27 00:01:50 +02:00
Stanislav Khromov
b1a196497d Update list-sections.ts 2025-09-27 00:00:15 +02:00
Stanislav Khromov
fb2d19fd07 Update list-sections.ts 2025-09-26 23:59:18 +02:00
Stanislav Khromov
8328a3572b eslint 2025-09-26 23:51:58 +02:00
Stanislav Khromov
c05b6c257a eslint 2025-09-26 23:51:48 +02:00
Stanislav Khromov
7f9ea742d8 don't lint .claude dir 2025-09-26 23:48:15 +02:00
Stanislav Khromov
bf477a6ccf Update get-documentation.ts 2025-09-26 23:46:07 +02:00
Stanislav Khromov
0f5482477a Update get-documentation.ts 2025-09-26 22:44:28 +02:00
Stanislav Khromov
b774b463fe Update get-documentation.ts 2025-09-26 22:42:42 +02:00
Stanislav Khromov
c49b24d36a wip 2025-09-26 22:39:52 +02:00
Stanislav Khromov
6cb97ac11d Update get-documentation.ts 2025-09-26 22:36:30 +02:00
Stanislav Khromov
d33a374417 Update get-documentation.ts 2025-09-26 21:56:38 +02:00
Stanislav Khromov
1bb171cea7 cleanup 2025-09-26 20:44:41 +02:00
Stanislav Khromov
e314ab57b2 Update list-sections.ts 2025-09-26 20:43:04 +02:00
Stanislav Khromov
19cacf7ed9 refactor 2025-09-26 20:39:22 +02:00
Stanislav Khromov
68cf69a117 wip 2025-09-26 20:38:17 +02:00
Stanislav Khromov
917fdf63b1 enable 2025-09-26 20:34:57 +02:00
Stanislav Khromov
972cadc410 Update package.json 2025-09-26 20:30:16 +02:00
Stanislav Khromov
dc6c87ce37 Merge branch 'main' into llms-txt 2025-09-26 20:30:11 +02:00
Stanislav Khromov
e7431e9024 Update README.md 2025-09-26 20:29:55 +02:00
Stanislav Khromov
07737a8edd Add Claude Code .mcp.json config 2025-09-26 20:22:50 +02:00
Stanislav Khromov
fdb7689992 fix stdio path 2025-09-26 20:21:43 +02:00
paoloricciuti
1a283f60bc chore: disable unimplemented tools/resources 2025-09-24 22:11:21 +02:00
paoloricciuti
bb16ccca3a fix: pnpm versions and package.json 2025-09-24 16:06:32 +02:00
paoloricciuti
23ddaf9495 fix: update pnpm version 2025-09-24 15:59:34 +02:00
paoloricciuti
4228302ed0 fix: package.json's 2025-09-24 15:57:17 +02:00
Paolo Ricciuti
0bc4d75e13 chore: use the right repo name 🤦
Co-authored-by: Dominik G. <dominik.goepel@gmx.de>
2025-09-24 15:53:12 +02:00
paoloricciuti
4679549401 chore: setup changesets 2025-09-24 15:43:42 +02:00
38 changed files with 1211 additions and 63 deletions

8
.changeset/README.md Normal file
View File

@@ -0,0 +1,8 @@
# Changesets
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)

11
.changeset/config.json Normal file
View File

@@ -0,0 +1,11 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
"changelog": ["@svitejs/changesets-changelog-github-compact", { "repo": "sveltejs/mcp" }],
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}

View File

@@ -18,7 +18,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
version: 10.17.1
- name: Setup Node.js
uses: actions/setup-node@v4

View File

@@ -18,7 +18,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
version: 10.17.1
- name: Setup Node.js
uses: actions/setup-node@v4

58
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,58 @@
name: Release
on:
push:
branches:
- main
permissions: {}
jobs:
release:
permissions:
contents: write # to create release (changesets/action)
id-token: write # OpenID Connect token needed for provenance
pull-requests: write # to create pull request (changesets/action)
# prevents this action from running on forks
if: github.repository == 'sveltejs/mcp'
name: Release
runs-on: ${{ matrix.os }}
strategy:
matrix:
# pseudo-matrix for convenience, NEVER use more than a single combination
node: [24]
os: [ubuntu-latest]
steps:
- name: checkout
uses: actions/checkout@v5
with:
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
fetch-depth: 0
- uses: actions/setup-node@v5
with:
node-version: ${{ matrix.node }}
package-manager-cache: false # pnpm is not installed yet
- name: install pnpm
shell: bash
run: |
PNPM_VER=$(jq -r '.packageManager | if .[0:5] == "pnpm@" then .[5:] else "packageManager in package.json does not start with pnpm@\n" | halt_error(1) end' package.json)
echo installing pnpm version $PNPM_VER
npm i -g pnpm@$PNPM_VER
- uses: actions/setup-node@v5
with:
node-version: ${{ matrix.node }}
package-manager-cache: true # caches pnpm via packageManager field in package.json
- name: install
run: pnpm install --frozen-lockfile --prefer-offline --ignore-scripts
- name: build
run: pnpm run --filter ./packages/mcp-stdio/ build
- name: Create Release Pull Request or Publish to npm
id: changesets
# pinned for security, always review third party action code before updating
uses: changesets/action@e0145edc7d9d8679003495b11f87bd8ef63c0cba # v1.5.3
with:
# This expects you to have a script called release which does a build for your packages and calls changeset publish
publish: pnpm release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_CONFIG_PROVENANCE: true

View File

@@ -18,7 +18,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
version: 10.17.1
- name: Setup Node.js
uses: actions/setup-node@v4

View File

@@ -1,8 +1,9 @@
{
"mcpServers": {
"svelte-llm": {
"type": "http",
"url": "https://svelte-llm.stanislav.garden/mcp/mcp"
"svelte": {
"type": "stdio",
"command": "node",
"args": ["packages/mcp-stdio/dist/index.js"]
}
}
}

View File

@@ -8,4 +8,7 @@ bun.lockb
# Miscellaneous
/static/
/drizzle/
/**/.svelte-kit/*
/**/.svelte-kit/*
# Claude Code
.claude/

2
.vscode/mcp.json vendored
View File

@@ -3,7 +3,7 @@
"Svelte MCP": {
"type": "stdio",
"command": "node",
"args": ["dist/lib/stdio.js"]
"args": ["packages/mcp-stdio/dist/index.js"]
}
},
"inputs": []

View File

@@ -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.

View File

@@ -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
```

View File

@@ -1,7 +1,19 @@
import { http_transport } from '$lib/mcp/index.js';
import { db } from '$lib/server/db/index.js';
import { redirect } from '@sveltejs/kit';
export async function handle({ event, resolve }) {
if (event.request.method === 'GET') {
const accept = event.request.headers.get('accept');
if (accept) {
const accepts = accept.split(',');
if (!accepts.includes('text/event-stream')) {
// the request it's a browser request, not an MCP client request
// it means someone probably opened it from the docs...we should redirect to the docs
redirect(302, 'https://svelte.dev/docs/mcp/overview');
}
}
}
const mcp_response = await http_transport.respond(event.request, {
db,
});

View File

@@ -0,0 +1,44 @@
---
title: Overview
---
The Svelte MCP ([Model Context Protocol](https://modelcontextprotocol.io/docs/getting-started/intro)) server can help your LLM or agent of choice write better Svelte code. It works by providing documentation relevant to the task at hand, and statically analysing generated code so that it can suggest fixes and best practices.
## Setup
The setup varies based on the version of the MCP you prefer — remote or local — and your chosen MCP client (e.g. Claude Code, Codex CLI or GitHub Copilot):
- [local setup](local-setup) using `@sveltejs/mcp`
- [remote setup](remote-setup) using [mcp.svelte.dev/mcp](https://mcp.svelte.dev/mcp)
## Usage
To get the most out of the MCP server we recommend including the following prompt in your `AGENTS.md` (or `CLAUDE.md`, if using Claude Code). This will tell the LLM which tools are available and when it's appropriate to use them.
```md
You are able to use the Svelte MCP server, where you have access to comprehensive Svelte 5 and SvelteKit documentation. Here's how to use the available tools effectively:
## Available MCP Tools:
### 1. list-sections
Use this FIRST to discover all available documentation sections. Returns a structured list with titles, use_cases, 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
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 (especially the use_cases field) and then use the get-documentation tool to fetch ALL documentation sections that are relevant for the user's task.
### 3. svelte-autofixer
Analyzes Svelte code and returns issues and suggestions.
You MUST use this tool whenever writing Svelte code before sending it to the user. Keep calling it until no issues or suggestions are returned.
### 4. playground-link
Generates a Svelte Playground link with the provided code.
After completing the code, ask the user if they want a playground link. Only call this tool after user confirmation and NEVER if code was written to files in their project.
```
If your MCP client supports it, we also recommend using the [svelte-task](prompts#svelte-task) prompt to instruct the LLM on the best way to use the MCP server.

View File

@@ -0,0 +1,3 @@
---
title: Introduction
---

View File

@@ -0,0 +1,113 @@
---
title: Local setup
---
The local (or stdio) version of the MCP server is available via the [`@sveltejs/mcp`](https://www.npmjs.com/package/@sveltejs/mcp) npm package. You can either install it globally and then reference it in your configuration or run it with `npx`:
```bash
npx -y @sveltejs/mcp
```
Here's how to set it up in some common MCP clients:
## Claude Code
To include the local MCP version in Claude Code, simply run the following command:
```bash
claude mcp add -t stdio -s [scope] svelte npx -y @sveltejs/mcp
```
The `[scope]` must be `user`, `project` or `local`.
## Claude Desktop
In the Settings > Developer section, click on Edit Config. It will open the folder with a `claude_desktop_config.json` file in it. Edit the file to include the following configuration:
```json
{
"mcpServers": {
"svelte": {
"command": "npx",
"args": ["-y", "@sveltejs/mcp"]
}
}
}
```
## Codex CLI
Add the following to your `config.toml` (which defaults to `~/.codex/config.toml`, but refer to [the configuration documentation](https://github.com/openai/codex/blob/main/docs/config.md) for more advanced setups):
```toml
[mcp_servers.svelte]
command = "npx"
args = ["-y", "@sveltejs/mcp"]
```
## Gemini CLI
To include the local MCP version in Gemini CLI, simply run the following command:
```bash
gemini mcp add -t stdio -s [scope] svelte npx -y @sveltejs/mcp
```
The `[scope]` must be `user`, `project` or `local`.
## OpenCode
Run the command:
```bash
opencode mcp add
```
and follow the instructions, selecting 'Local' under the 'Select MCP server type' prompt:
```bash
opencode mcp add
┌ Add MCP server
◇ Enter MCP server name
│ svelte
◇ Select MCP server type
│ Local
◆ Enter command to run
│ npx -y @sveltejs/mcp
```
## VS Code
- Open the command palette
- Select "MCP: Add Server..."
- Select "Command (stdio)"
- Insert `npx -y @sveltejs/mcp` in the input and press `Enter`
- When prompted for a name, insert `svelte`
- Select if you want to add it as a `Global` or `Workspace` MCP server
## Cursor
- Open the command palette
- Select "View: Open MCP Settings"
- Click on "Add custom MCP"
It will open a file with your MCP servers where you can add the following configuration:
```json
{
"mcpServers": {
"svelte": {
"command": "npx",
"args": ["-y", "@sveltejs/mcp"]
}
}
}
```
## Other clients
If we didn't include the MCP client you are using, refer to their documentation for `stdio` servers and use `npx` as the command and `-y @sveltejs/mcp` as the arguments.

View File

@@ -0,0 +1,101 @@
---
title: Remote setup
---
The remote version of the MCP server is available on `https://mcp.svelte.dev/mcp`.
Here's how to set it up in some common MCP clients:
## Claude Code
To include the remote MCP version in Claude Code, simply run the following command:
```bash
claude mcp add -t http -s [scope] svelte https://mcp.svelte.dev/mcp
```
You can choose your preferred `scope` (it must be `user`, `project` or `local`) and `name`.
## Claude Desktop
- Open Settings > Connectors
- Click on Add Custom Connector
- When prompted for a name, enter `svelte`
- Under the Remote MCP server URL input, use `https://mcp.svelte.dev/mcp`
- Click Add
## Codex CLI
Add the following to your `config.toml` (which defaults to `~/.codex/config.toml`, but refer to [the configuration documentation](https://github.com/openai/codex/blob/main/docs/config.md) for more advanced setups):
```toml
experimental_use_rmcp_client = true
[mcp_servers.svelte]
url = "https://mcp.svelte.dev/mcp"
```
## Gemini CLI
To use the remote MCP server with Gemini CLI, simply run the following command:
```bash
gemini mcp add -t http -s [scope] svelte https://mcp.svelte.dev/mcp
```
The `[scope]` must be `user`, `project` or `local`.
## OpenCode
Run the command:
```bash
opencode mcp add
```
and follow the instructions, selecting 'Remote' under the 'Select MCP server type' prompt:
```bash
opencode mcp add
┌ Add MCP server
◇ Enter MCP server name
│ svelte
◇ Select MCP server type
│ Remote
◇ Enter MCP server URL
│ https://mcp.svelte.dev/mcp
```
## VS Code
- Open the command palette
- Select "MCP: Add Server..."
- Select "HTTP (HTTP or Server-Sent-Events)"
- Insert `https://mcp.svelte.dev/mcp` in the input and press `Enter`
- Insert your preferred name
- Select if you want to add it as a `Global` or `Workspace` MCP server
## Cursor
- Open the command palette
- Select "View: Open MCP Settings"
- Click on "Add custom MCP"
It will open a file with your MCP servers where you can add the following configuration:
```json
{
"mcpServers": {
"svelte": {
"url": "https://mcp.svelte.dev/mcp"
}
}
}
```
## Other clients
If we didn't include the MCP client you are using, refer to their documentation for `remote` servers and use `https://mcp.svelte.dev/mcp` as the URL.

View File

@@ -0,0 +1,3 @@
---
title: Setup
---

View File

@@ -0,0 +1,21 @@
---
title: Tools
---
The following tools are provided by the MCP server to the model, which can decide to call one or more of them during a session:
## list-sections
Provides a list of all the available documentation sections.
## get-documentation
Allows the model to get the full (and up-to-date) documentation for the requested sections directly from [svelte.dev/docs](/docs).
## svelte-autofixer
Uses static analysis to provide suggestions for the generated code. It should be invoked in a loop by the model until all issues and suggestions are resolved.
## playground-link
Generates an ephemeral playground link with the generated code. It's useful when the generated code is not written to a file in your project and you want to quickly test the generated solution. The code is not stored anywhere except the URL itself (which will often, as a consequence, be quite large).

View File

@@ -0,0 +1,9 @@
---
title: Resources
---
This is the list of available resources provided by the MCP server. Resources are included by the user (not by the LLM) and are useful if you want to include specific knowledge in your session. For example, if you know that the component will need to use transitions you can include the transition documentation directly without asking the LLM to do it for you.
## doc-section
This dynamic resource allows you to add every section of the Svelte documentation as a resource. The URI looks like this `svelte://slug-of-the-docs.md` and the returned resource will contain the `llms.txt` version of the specific page you selected.

View File

@@ -0,0 +1,26 @@
---
title: Prompts
---
This is the list of available prompts provided by the MCP server. Prompts are selected by the user and are sent as a user message. They can be useful to write repetitive instructions for the LLM on how to properly use the MCP server.
## svelte-task
This prompt should be used whenever you are asking the model to work on some Svelte-related task. It will instruct the LLM on which documentation sections are available, which tool to invoke, when to invoke it, and how to interpret the result. It will ask you for the description of the task and the returned value will look like this:
```
You are a Svelte expert tasked to build components and utilities for Svelte developers. If you need documentation for anything related to Svelte you can invoke the tool \`get-documentation\` with one of the following paths:
<available-docs-paths>
[all available docs]
</available-docs-paths>
Every time you write a Svelte component or a Svelte module you MUST invoke the \`svelte-autofixer\` tool providing the code. The tool will return a list of issues or suggestions. If there are any issues or suggestions you MUST fix them and call the tool again with the updated code. You MUST keep doing this until the tool returns no issues or suggestions. Only then you can return the code to the user.
This is the task you will work on:
<task>
[your task here]
</task>
If you are not writing the code into a file, once you have the final version of the code ask the user if they want to generate a playground link to quickly check the code in it and if they answer yes call the \`playground-link\` tool and return the url to the user nicely formatted. The playground link MUST be generated only once you have the final version of the code and you are ready to share it, it MUST include an entry point file called \`App.svelte\` where the main component should live. If you have multiple files to include in the playground link you can include them all at the root.
```

View File

@@ -0,0 +1,3 @@
---
title: Capabilities
---

View File

@@ -0,0 +1,3 @@
---
title: MCP
---

View File

@@ -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,

View File

@@ -3,16 +3,20 @@
"version": "0.0.1",
"description": "The official Svelte MCP server implementation",
"type": "module",
"packageManager": "pnpm@10.17.1",
"scripts": {
"build": "pnpm -r run build",
"dev": "pnpm --filter @sveltejs/mcp-remote run dev",
"check": "pnpm -r run check",
"check:publint": "pnpm -r run check:publint",
"format": "prettier --write .",
"lint": "prettier --check . && eslint .",
"lint:fix": "prettier --write . && eslint . --fix",
"test:unit": "vitest",
"test": "npm run test:unit -- --run",
"test:watch": "npm run test:unit -- --watch",
"inspect": "pnpm mcp-inspector"
"inspect": "pnpm mcp-inspector",
"release": "pnpm --filter @sveltejs/mcp run build && changeset publish"
},
"keywords": [
"svelte",
@@ -22,9 +26,11 @@
],
"private": true,
"devDependencies": {
"@changesets/cli": "^2.29.7",
"@eslint/compat": "^1.3.2",
"@eslint/js": "^9.36.0",
"@modelcontextprotocol/inspector": "^0.16.7",
"@svitejs/changesets-changelog-github-compact": "^1.2.0",
"eslint": "^9.36.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-import": "^2.32.0",
@@ -32,6 +38,7 @@
"globals": "^16.0.0",
"prettier": "^3.4.2",
"prettier-plugin-svelte": "^3.3.3",
"publint": "^0.3.13",
"typescript": "^5.0.0",
"typescript-eslint": "^8.44.1",
"vitest": "^3.2.3"

View File

@@ -12,7 +12,6 @@
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.15.1",
"type": "module",
"dependencies": {
"drizzle-orm": "^0.40.1"

View File

@@ -7,7 +7,6 @@
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.15.1",
"type": "module",
"scripts": {
"test": "vitest"

View File

@@ -1,5 +1,6 @@
import type { SvelteMcp } from '../../index.js';
import * as v from 'valibot';
import { get_sections } from '../../utils.js';
export function setup_svelte_task(server: SvelteMcp) {
server.prompt(
@@ -13,8 +14,7 @@ export function setup_svelte_task(server: SvelteMcp) {
}),
},
async ({ task }) => {
// TODO: implement logic to fetch the available docs paths to return in the prompt
const available_docs: string[] = [];
const available_docs: string[] = (await get_sections()).map((s) => s.title);
return {
messages: [
@@ -22,7 +22,7 @@ export function setup_svelte_task(server: SvelteMcp) {
role: 'user',
content: {
type: 'text',
text: `You are a Svelte expert tasked to build components and utilities for Svelte developers. If you need documentation for anything related to Svelte you can invoke the tool \`get_documentation\` with one of the following paths:
text: `You are a Svelte expert tasked to build components and utilities for Svelte developers. If you need documentation for anything related to Svelte you can invoke the tool \`get-documentation\` with one of the following paths:
<available-docs-paths>
${JSON.stringify(available_docs, null, 2)}
</available-docs-paths>

View File

@@ -0,0 +1,64 @@
import type { SvelteMcp } from '../../index.js';
import { get_sections, fetch_with_timeout } from '../../utils.js';
export async function list_sections(server: SvelteMcp) {
const sections = await get_sections();
server.template(
{
name: 'Svelte Doc Section',
description: 'A single documentation section',
list() {
return sections.map((section) => {
const section_name = section.slug;
const resource_name = section_name;
const resource_uri = `svelte://${section_name}.md`;
return {
name: resource_name,
description: section.use_cases,
uri: resource_uri,
title: section.title,
};
});
},
complete: {
slug: (query) => {
const values = sections
.reduce<string[]>((acc, section) => {
const section_name = section.slug;
const resource_name = section_name;
if (section_name.includes(query.toLowerCase())) {
acc.push(resource_name);
}
return acc;
}, [])
// there's a hard limit of 100 for completions
.slice(0, 100);
return {
completion: {
values,
},
};
},
},
uri: 'svelte://{/slug*}.md',
},
async (uri, { slug }) => {
const section = sections.find((section) => {
return slug === section.slug;
});
if (!section) throw new Error(`Section not found: ${slug}`);
const response = await fetch_with_timeout(section.url);
const content = await response.text();
return {
contents: [
{
uri,
type: 'text',
text: content,
},
],
};
},
);
}

View File

@@ -1 +1 @@
export * from './list-sections.js';
export * from './doc-section.js';

View File

@@ -1,25 +0,0 @@
import type { SvelteMcp } from '../../index.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',
},
],
};
},
);
}

View File

@@ -1,5 +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(
@@ -7,7 +9,7 @@ export function get_documentation(server: SvelteMcp) {
name: 'get-documentation',
enabled: () => false,
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())]),
@@ -17,7 +19,7 @@ export function get_documentation(server: SvelteMcp) {
),
}),
},
({ section }) => {
async ({ section }) => {
let sections: string[];
if (Array.isArray(section)) {
@@ -43,13 +45,73 @@ export function get_documentation(server: SvelteMcp) {
sections = [];
}
const sections_list = sections.length > 0 ? sections.join(', ') : 'no sections';
const available_sections = await get_sections();
const settled_results = await Promise.allSettled(
sections.map(async (requested_section) => {
const matched_section = available_sections.find(
(s) =>
s.title.toLowerCase() === requested_section.toLowerCase() ||
s.url === requested_section,
);
if (matched_section) {
try {
const response = await fetch_with_timeout(matched_section.url);
if (response.ok) {
const content = await response.text();
return { success: true, content: `## ${matched_section.title}\n\n${content}` };
} else {
return {
success: false,
content: `## ${matched_section.title}\n\nError: Could not fetch documentation (HTTP ${response.status})`,
};
}
} catch (error) {
return {
success: false,
content: `## ${matched_section.title}\n\nError: Failed to fetch documentation - ${error}`,
};
}
} else {
return {
success: false,
content: `## ${requested_section}\n\nError: Section not found.`,
};
}
}),
);
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');
if (!has_any_success) {
const formatted_sections = available_sections
.map(
(section) =>
`* title: ${section.title}, use_cases: ${section.use_cases}, path: ${section.url}`,
)
.join('\n');
final_text += `\n\n---\n\n${SECTIONS_LIST_INTRO}\n\n${formatted_sections}\n\n${SECTIONS_LIST_OUTRO}`;
}
return {
content: [
{
type: 'text',
text: `called for sections: ${sections_list}`,
text: final_text,
},
],
};

View File

@@ -1,4 +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(
@@ -6,14 +8,22 @@ export function list_sections(server: SvelteMcp) {
name: 'list-sections',
enabled: () => false,
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 get_sections();
const formatted_sections = sections
.map(
(section) =>
`* title: ${section.title}, use_cases: ${section.use_cases}, path: ${section.url}`,
)
.join('\n');
return {
content: [
{
type: 'text',
text: 'tool list_sections called',
text: `${SECTIONS_LIST_INTRO}\n\n${formatted_sections}\n\n${SECTIONS_LIST_OUTRO}`,
},
],
};

View File

@@ -0,0 +1,5 @@
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.';

View File

@@ -0,0 +1,12 @@
import * as v from 'valibot';
export const documentation_sections_schema = v.record(
v.string(),
v.object({
metadata: v.object({
title: v.string(),
use_cases: v.optional(v.string()),
}),
slug: v.string(),
}),
);

View File

@@ -0,0 +1,31 @@
import * as v from 'valibot';
import { documentation_sections_schema } from '../mcp/schemas/index.js';
export async function fetch_with_timeout(
url: string,
timeout_ms: number = 10000,
): Promise<Response> {
try {
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 ${timeout_ms}ms`);
}
throw error;
}
}
export async function get_sections() {
const sections = await fetch_with_timeout(
'https://svelte.dev/docs/experimental/sections.json',
).then((res) => res.json());
const validated_sections = v.safeParse(documentation_sections_schema, sections);
if (!validated_sections.success) return [];
return Object.entries(validated_sections.output).map(([, section]) => ({
title: section.metadata.title,
use_cases: section.metadata.use_cases ?? 'read document for use cases',
slug: section.slug,
url: `https://svelte.dev/${section.slug}/llms.txt`,
}));
}

View File

@@ -0,0 +1,7 @@
# @sveltejs/mcp
## 0.0.2
### Patch Changes
- feat: latest version ([#25](https://github.com/sveltejs/mcp/pull/25))

View File

@@ -1,6 +1,6 @@
{
"name": "@sveltejs/mcp",
"version": "0.0.1",
"version": "0.0.2",
"type": "module",
"license": "MIT",
"homepage": "https://github.com/sveltejs/mcp#readme",
@@ -22,10 +22,11 @@
"access": "public"
},
"scripts": {
"build": "tsdown",
"build": "tsdown && publint",
"dev": "tsdown --watch",
"test": "vitest",
"check": "tsc --noEmit"
"check": "tsc --noEmit",
"check:publint": "publint --strict"
},
"devDependencies": {
"@sveltejs/mcp-server": "workspace:^",

572
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff