Compare commits

...

33 Commits

Author SHA1 Message Date
github-actions[bot]
a9994310c0 Version Packages (#92)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-10-25 17:07:07 +02:00
Paolo Ricciuti
9fb1a403b7 fix: add async parameter to svelte-autofixer (#94) 2025-10-25 17:04:15 +02:00
github-actions[bot]
3c7b5033a4 docs: update prompt documentation (#93)
Co-authored-by: paoloricciuti <26281609+paoloricciuti@users.noreply.github.com>
2025-10-24 23:12:06 +02:00
Paolo Ricciuti
b911a00bb7 feat: analytics (#88) 2025-10-24 23:06:58 +02:00
paoloricciuti
f6ce89ff34 fix: install latest eslint svelte packages to support $state.eager 2025-10-24 21:02:25 +02:00
github-actions[bot]
846514858e Version Packages (#86)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-10-24 10:42:04 +02:00
Yuichiro Yamashita
b69ea052bd feat: support: $state.eager (#90)
Co-authored-by: Paolo Ricciuti <ricciutipaolo@gmail.com>
2025-10-24 10:34:00 +02:00
Tee Ming
e56159dda6 chore: add FUNDING.yml (#89) 2025-10-24 16:37:32 +09:00
paoloricciuti
1e83c35faa fix: update transport and cancel request only after sending it 2025-10-23 19:10:34 +02:00
Paolo Ricciuti
31edfe1b5f Merge pull request #85 from sveltejs/renovate/major-vitest-monorepo 2025-10-23 10:07:20 +02:00
paoloricciuti
3fabcc0f9b fix: add preferred-frame-size to UI resource 2025-10-23 09:57:51 +02:00
renovate[bot]
deb5f2670c chore(deps): update dependency vitest to v4 2025-10-22 19:36:46 +00:00
paoloricciuti
02c951baa8 fix: add icons to server.json 2025-10-22 21:35:01 +02:00
Paolo Ricciuti
41ceb83838 Merge pull request #79 from sveltejs/changeset-release/main 2025-10-22 17:23:40 +02:00
github-actions[bot]
3c3a26f031 Version Packages 2025-10-22 15:22:27 +00:00
Paolo Ricciuti
6589c7e250 Merge pull request #83 from sveltejs/renovate/all-minor-patch 2025-10-22 17:21:52 +02:00
Paolo Ricciuti
e09b8cd0b9 Merge pull request #84 from sveltejs/mcp-ui-playground 2025-10-22 17:21:33 +02:00
paoloricciuti
7f52a2b1be fix: use embed for mcp-ui embed 2025-10-22 14:56:24 +02:00
paoloricciuti
4eecd75759 chore: changesets 2025-10-22 09:59:39 +02:00
paoloricciuti
9015753f77 feat: return mcp ui resource for playground 2025-10-22 09:47:57 +02:00
renovate[bot]
4d6a9cb333 chore(deps): update pnpm to v10.19.0 2025-10-21 23:14:53 +00:00
Paolo Ricciuti
60aa30397f Merge pull request #80 from UltimateStarCoder/patch-1 2025-10-19 11:11:12 +02:00
paoloricciuti
17ed3a3e23 fix: lint 2025-10-19 11:08:57 +02:00
Paolo Ricciuti
f49bd06fbd chore: revert spacing 2025-10-19 11:05:50 +02:00
UltimateStarCoder
b98c042ae3 Correct JSON structure in remote setup documentation
Fixed JSON syntax for mcpServers configuration.
2025-10-18 17:43:58 -06:00
paoloricciuti
917a93d3fd fix: update deps and add path 2025-10-18 16:20:25 +02:00
Paolo Ricciuti
371e96befc Merge pull request #72 from adamtegen/patch-2 2025-10-17 18:58:29 +02:00
Paolo Ricciuti
bdfd5a109f fix: lint 2025-10-17 17:31:09 +02:00
Paolo Ricciuti
1c6c0a9fa7 Merge pull request #78 from sveltejs/invalid-css-autofixer
feat: suggest against js variables in css
2025-10-17 16:33:44 +02:00
paoloricciuti
a321244543 feat: suggest against js variables in css 2025-10-17 15:31:35 +02:00
paoloricciuti
ed25933466 fix: use right url + add manual triggering 2025-10-17 08:24:31 +02:00
Paolo Ricciuti
e639e3ad5c chore: apply suggestions from code review
Co-authored-by: Willow (GHOST) <ghostdevbusiness@gmail.com>
2025-10-16 16:34:25 +02:00
Adam Tegen
d0bed3e8f0 Add GitHub Coding Agent setup instructions
Added instructions for configuring GitHub Coding Agent.
2025-10-14 18:30:27 -05:00
32 changed files with 626 additions and 329 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
open_collective: svelte

View File

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

View File

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

View File

@@ -5,6 +5,7 @@ on:
secrets:
MCP_KEY:
required: true
workflow_dispatch:
jobs:
publish-mcp:
@@ -19,7 +20,7 @@ jobs:
env:
MCP_KEY: ${{ secrets.MCP_KEY }}
run: |
NAME=mcp-publisher_1.3.3_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz
NAME=mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz
# Download MCP Publisher pinned to v1.3.3 using latest https for security and save it to a file named mcp-publisher.tar.gz
curl --proto '=https' --proto-redir '=https' --tlsv1.2 -fL "https://github.com/modelcontextprotocol/registry/releases/download/v1.3.3/$NAME" -O

View File

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

View File

@@ -11,4 +11,5 @@ bun.lockb
/**/.svelte-kit/*
# Claude Code
.claude/
.claude/
.changeset/

View File

@@ -59,12 +59,13 @@
"typescript": "^5.0.0",
"vite": "^7.0.4",
"vite-plugin-devtools-json": "^1.0.0",
"vitest": "^3.2.3"
"vitest": "^4.0.0"
},
"dependencies": {
"@sveltejs/mcp-schema": "workspace:^",
"@sveltejs/mcp-server": "workspace:^",
"@tmcp/transport-http": "^0.6.3",
"tmcp": "^1.15.0"
"@tmcp/transport-http": "^0.7.1",
"@vercel/analytics": "^1.5.0",
"tmcp": "^1.15.3"
}
}

View File

@@ -1,8 +1,13 @@
import { dev } from '$app/environment';
import { http_transport } from '$lib/mcp/index.js';
import { db } from '$lib/server/db/index.js';
import { redirect } from '@sveltejs/kit';
import { track } from '@vercel/analytics/server';
export async function handle({ event, resolve }) {
if (event.request.method === 'POST') {
console.log(await event.request.clone().text());
}
if (event.request.method === 'GET') {
const accept = event.request.headers.get('accept');
if (accept) {
@@ -16,6 +21,12 @@ export async function handle({ event, resolve }) {
}
const mcp_response = await http_transport.respond(event.request, {
db,
// only add analytics in production
track: dev
? undefined
: async (session_id, event, slug) => {
await track(event, { session_id, ...(slug ? { slug } : {}) });
},
});
// we are deploying on vercel the SSE connection will timeout after 5 minutes...for
// the moment we are not sending back any notifications (logs, or list changed notifications)
@@ -25,11 +36,14 @@ export async function handle({ event, resolve }) {
// 200 or the MCP client will complain)
if (mcp_response && event.request.method === 'GET') {
try {
await mcp_response.body?.cancel();
} catch {
// ignore
return mcp_response;
} finally {
try {
await mcp_response.body?.cancel();
} catch {
// ignore
}
}
return new Response('', { status: 200 });
}
return mcp_response ?? resolve(event);
}

View File

@@ -3,4 +3,5 @@ import { HttpTransport } from '@tmcp/transport-http';
export const http_transport = new HttpTransport(server, {
cors: true,
path: '/mcp',
});

View File

@@ -1,8 +1,6 @@
import { createClient } from '@libsql/client';
import { drizzle } from 'drizzle-orm/libsql';
import * as schema from './schema.js';
// let's disable it for the moment...i can't figure out a way to make it wotk with eslint
// eslint-disable-next-line import/extensions
import { DATABASE_TOKEN, DATABASE_URL } from '$env/static/private';
if (!DATABASE_URL) throw new Error('DATABASE_URL is not set');
if (!DATABASE_TOKEN) throw new Error('DATABASE_TOKEN is not set');

View File

@@ -96,6 +96,27 @@ It will open a file with your MCP servers where you can add the following config
}
```
## GitHub Coding Agent
- Open your repository in GitHub
- Go to Settings
- Open Copilot > Coding agent
- Edit the MCP configuration
```json
{
"mcpServers": {
"svelte": {
"type": "http",
"url": "https://mcp.svelte.dev/mcp",
"tools": ["*"]
}
}
}
```
- Click _Save MCP configuration_
## 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

@@ -25,6 +25,7 @@ You are a Svelte expert tasked to build components and utilities for Svelte deve
- title: drizzle, use_cases: database setup, sql queries, orm integration, data modeling, postgresql, mysql, sqlite, server-side data access, database migrations, type-safe queries, path: cli/drizzle
- title: eslint, use_cases: code quality, linting, error detection, project setup, code standards, team collaboration, typescript projects, path: cli/eslint
- title: lucia, use_cases: authentication, login systems, user management, registration pages, session handling, auth setup, path: cli/lucia
- title: mcp, use_cases: use title and path to estimate use case, path: cli/mcp
- title: mdsvex, use_cases: blog, content sites, markdown rendering, documentation sites, technical writing, cms integration, article pages, path: cli/mdsvex
- title: paraglide, use_cases: internationalization, multi-language sites, i18n, translation, localization, language switching, global apps, multilingual content, path: cli/paraglide
- title: playwright, use_cases: browser testing, e2e testing, integration testing, test automation, quality assurance, ci/cd pipelines, testing user flows, path: cli/playwright

View File

@@ -13,7 +13,7 @@ const gitignore_path = fileURLToPath(new URL('./.gitignore', import.meta.url));
export default /** @type {import("eslint").Linter.Config} */ ([
includeIgnoreFile(gitignore_path),
{
ignores: ['.claude/**/*'],
ignores: ['.claude/**/*', '.changeset/*'],
},
js.configs.recommended,
...ts.configs.recommended,
@@ -48,13 +48,17 @@ export default /** @type {import("eslint").Linter.Config} */ ([
'import/no-unresolved': 'off', // this doesn't work well with typescript path mapping
'import/extensions': [
'error',
'ignorePackages',
{
js: 'always',
mjs: 'always',
cjs: 'always',
ts: 'always',
svelte: 'always',
ignorePackages: true,
pattern: {
js: 'always',
mjs: 'always',
cjs: 'always',
ts: 'always',
svelte: 'always',
svg: 'always',
json: 'always',
},
},
],
},

View File

@@ -3,7 +3,7 @@
"version": "0.0.1",
"description": "The official Svelte MCP server implementation",
"type": "module",
"packageManager": "pnpm@10.18.3",
"packageManager": "pnpm@10.19.0",
"scripts": {
"build": "pnpm -r run build",
"dev": "pnpm --filter @sveltejs/mcp-remote run dev",
@@ -46,7 +46,7 @@
"publint": "^0.3.13",
"typescript": "^5.0.0",
"typescript-eslint": "^8.44.1",
"vitest": "^3.2.3"
"vitest": "^4.0.0"
},
"pnpm": {
"onlyBuiltDependencies": [

View File

@@ -20,18 +20,19 @@
"drizzle-orm": "^0.44.0"
},
"dependencies": {
"@mcp-ui/server": "^5.12.0",
"@sveltejs/mcp-schema": "workspace:^",
"@tmcp/adapter-valibot": "^0.1.4",
"@typescript-eslint/parser": "^8.44.0",
"eslint": "^9.36.0",
"eslint-plugin-svelte": "^3.12.3",
"eslint-plugin-svelte": "^3.12.5",
"svelte": "^5.39.2",
"svelte-eslint-parser": "^1.3.2",
"tmcp": "^1.15.0",
"svelte-eslint-parser": "^1.4.0",
"tmcp": "^1.15.3",
"ts-blank-space": "^0.6.2",
"typescript-eslint": "^8.44.0",
"valibot": "^1.1.0",
"vitest": "^3.2.4",
"vitest": "^4.0.0",
"zimmerframe": "^1.1.4"
},
"devDependencies": {

View File

@@ -11,6 +11,7 @@ export const base_runes = [
export const nested_runes = [
'$state.raw',
'$state.snapshot',
'$state.eager',
'$effect.pre',
'$effect.tracking',
'$effect.pending',

View File

@@ -8,6 +8,7 @@ export function add_autofixers_issues(
code: string,
desired_svelte_version: number,
filename = 'Component.svelte',
async = false,
) {
const parsed = parse(code, filename);
@@ -15,7 +16,7 @@ export function add_autofixers_issues(
for (const autofixer of Object.values(autofixers)) {
walk(
parsed.ast as unknown as Node,
{ output: content, parsed, desired_svelte_version },
{ output: content, parsed, desired_svelte_version, async },
autofixer,
);
}

View File

@@ -7,6 +7,7 @@ export function add_compile_issues(
code: string,
desired_svelte_version: number,
filename = 'Component.svelte',
async = false,
) {
let compile = compile_component;
const extension = extname(filename);
@@ -27,6 +28,7 @@ export function add_compile_issues(
filename: filename || 'Component.svelte',
generate: false,
runes: desired_svelte_version >= 5,
experimental: { async },
});
for (const warning of compilation_result.warnings) {

View File

@@ -51,7 +51,7 @@ function base_config(svelte_config: Config): ESLint.Options['baseConfig'] {
];
}
function get_linter(version: number) {
function get_linter(version: number, async = false) {
if (version < 5) {
return (svelte_4_linter ??= new ESLint({
overrideConfigFile: true,
@@ -67,6 +67,7 @@ function get_linter(version: number) {
baseConfig: base_config({
compilerOptions: {
runes: true,
experimental: { async },
},
}),
}));
@@ -77,8 +78,9 @@ export async function add_eslint_issues(
code: string,
desired_svelte_version: number,
filename = 'Component.svelte',
async = false,
) {
const eslint = get_linter(desired_svelte_version);
const eslint = get_linter(desired_svelte_version, async);
const results = await eslint.lintText(code, { filePath: filename || './Component.svelte' });
for (const message of results[0]?.messages ?? []) {

View File

@@ -7,6 +7,7 @@ export type AutofixerState = {
output: { issues: string[]; suggestions: string[] };
parsed: ParseResult;
desired_svelte_version: number;
async?: boolean;
};
export type Autofixer = Visitors<Node | AST.SvelteNode, AutofixerState>;

View File

@@ -68,6 +68,9 @@ export function setup_svelte_task(server: SvelteMcp) {
icons,
},
async ({ task }) => {
if (server.ctx.sessionId && server.ctx.custom?.track) {
await server.ctx.custom?.track?.(server.ctx.sessionId, 'svelte-task');
}
const available_docs = await format_sections_list();
return {

View File

@@ -46,6 +46,13 @@ export async function list_sections(server: SvelteMcp) {
icons,
},
async (uri, { slug }) => {
if (server.ctx.sessionId && server.ctx.custom?.track) {
await server.ctx.custom?.track?.(
server.ctx.sessionId,
'svelte-doc-section',
Array.isArray(slug) ? slug.join(',') : slug,
);
}
const section = sections.find((section) => {
return slug === section.slug;
});

View File

@@ -21,6 +21,9 @@ export function get_documentation(server: SvelteMcp) {
icons,
},
async ({ section }) => {
if (server.ctx.sessionId && server.ctx.custom?.track) {
await server.ctx.custom?.track?.(server.ctx.sessionId, 'get-documentation');
}
let sections: string[];
if (Array.isArray(section)) {

View File

@@ -12,6 +12,9 @@ export function list_sections(server: SvelteMcp) {
icons,
},
async () => {
if (server.ctx.sessionId && server.ctx.custom?.track) {
await server.ctx.custom?.track?.(server.ctx.sessionId, 'list-sections');
}
const formatted_sections = await format_sections_list();
return {

View File

@@ -1,6 +1,7 @@
import type { SvelteMcp } from '../../index.js';
import * as v from 'valibot';
import { icons } from '../../icons/index.js';
import { createUIResource } from '@mcp-ui/server';
async function compress_and_encode_text(input: string) {
const reader = new Blob([input]).stream().pipeThrough(new CompressionStream('gzip')).getReader();
@@ -58,6 +59,9 @@ export function playground_link(server: SvelteMcp) {
icons,
},
async ({ files, name, tailwind }) => {
if (server.ctx.sessionId && server.ctx.custom?.track) {
await server.ctx.custom?.track?.(server.ctx.sessionId, 'playground-link');
}
const playground_base = new URL('https://svelte.dev/playground');
const playground_files: File[] = [];
@@ -75,6 +79,9 @@ export function playground_link(server: SvelteMcp) {
}
if (!has_app_svelte) {
if (server.ctx.sessionId && server.ctx.custom?.track) {
await server.ctx.custom?.track?.(server.ctx.sessionId, 'playground-link-no-app-svelte');
}
return {
isError: true,
content: [
@@ -100,12 +107,26 @@ export function playground_link(server: SvelteMcp) {
url: playground_base.toString(),
};
// use the embed path to have a cleaner UI for mcp-ui
playground_base.pathname = '/playground/embed';
return {
content: [
{
type: 'text',
text: JSON.stringify(content),
},
createUIResource({
uri: 'ui://svelte/playground-link',
content: {
type: 'externalUrl',
iframeUrl: playground_base.toString(),
},
uiMetadata: {
'preferred-frame-size': ['100%', '1200px'],
},
encoding: 'text',
}),
],
structuredContent: content,
};

View File

@@ -0,0 +1,155 @@
import { beforeEach, describe, expect, it } from 'vitest';
import { server } from '../../index.js';
/**
* Small utility to create a JSON-RPC request without having to always specify as const
*/
function request<const T>(request: T) {
return request;
}
async function autofixer_tool_call(
code: string,
is_error = false,
desired_svelte_version = 5,
async = false,
) {
const result = await server.receive({
jsonrpc: '2.0',
id: 2,
method: 'tools/call',
params: {
name: 'svelte-autofixer',
arguments: {
code,
desired_svelte_version,
filename: 'App.svelte',
async,
},
},
});
expect(result).toBeDefined();
expect(result.result).toBeDefined();
if (is_error) {
return result.result;
}
expect(result.result.structuredContent).toBeDefined();
return result.result.structuredContent;
}
describe('svelte-autofixer tool', () => {
beforeEach(async () => {
const initialize_request = request({
jsonrpc: '2.0',
id: 1,
method: 'initialize',
params: {
protocolVersion: '2025-06-18',
capabilities: {
roots: { listChanged: true },
},
clientInfo: {
name: 'test-client',
version: '1.0.0',
},
},
});
await server.receive(initialize_request, {
sessionId: 'svelte-autofixer-session',
});
});
it('should add suggestions for js parse errors', async () => {
const content = await autofixer_tool_call(`<script>
$state count = 0;
</script>`);
expect(content.issues.length).toBeGreaterThan(0);
expect(content.suggestions).toContain(
"The code can't be compiled because a Javascript parse error. In case you are using runes like this `$state variable_name = 3;` or `$derived variable_name = 3 * count` that's not how runes are used. You need to use them as function calls without importing them: `const variable_name = $state(3)` and `const variable_name = $derived(3 * count)`.",
);
});
it('should error out if async is true with a version less than 5', async () => {
const content = await autofixer_tool_call(
`<script>
$state count = 0;
</script>`,
true,
4,
true,
);
expect(content.isError).toBeTruthy();
expect(content.content[0]).toBeDefined();
expect(content.content[0].text).toBe(
'The async option can only be used with Svelte version 5 or higher.',
);
});
it('should not add suggestion/issues if async is true and await is used in the template/derived', async () => {
const content = await autofixer_tool_call(
`<script>
import { slow_double } from './utils.js';
let count = $state(0);
let double = $derived(await slow_double(count));
</script>
{double}
{await slow_double(count)}`,
false,
5,
true,
);
expect(content.issues).toHaveLength(0);
expect(content.suggestions).toHaveLength(0);
expect(content.require_another_tool_call_after_fixing).toBeFalsy();
});
it('should add suggestion/issues if async is false and await is used in the template/derived', async () => {
const content = await autofixer_tool_call(
`<script>
import { slow_double } from './utils.js';
let count = $state(0);
let double = $derived(await slow_double(count));
</script>
{double}
{await slow_double(count)}`,
false,
5,
);
expect(content.issues.length).toBeGreaterThanOrEqual(1);
expect(content.issues).toEqual(
expect.arrayContaining([expect.stringContaining('experimental_async')]),
);
expect(content.require_another_tool_call_after_fixing).toBeTruthy();
});
it('should add suggestions for css invalid identifier', async () => {
const content = await autofixer_tool_call(`<script>
let my_color = $state('red');
</script>
<style>
.my-class {
color: {my_color};
}
</style>`);
expect(content.issues.length).toBeGreaterThan(0);
expect(content.suggestions).toContain(
"The code can't be compiled because a valid CSS identifier is expected. This sometimes means you are trying to use a variable in CSS like this: `color: {my_color}` but Svelte doesn't support that. You can use inline CSS variables for that `<div style:--color={my_color}></div>` and then use the variable as usual in CSS with `color: var(--color)`.",
);
});
it('should error in case the passed in version is different from 4 or 5', async () => {
const content = await autofixer_tool_call(`whatever`, true, 3);
expect(content.content).toBeDefined();
expect(content.content[0]).toBeDefined();
expect(content.content[0].text).toContain(
'The desired_svelte_version MUST be either 4 or 5 but received "3"',
);
});
});

View File

@@ -21,6 +21,12 @@ export function svelte_autofixer(server: SvelteMcp) {
'The desired svelte version...if possible read this from the package.json of the user project, otherwise use some hint from the wording (if the user asks for runes it wants version 5). Default to 5 in case of doubt.',
),
),
async: v.pipe(
v.optional(v.boolean()),
v.description(
'If true the code is an async component/module and might use await in the markup or top-level awaits in the script tag. If possible check the svelte.config.js/svelte.config.ts to check if the option is enabled otherwise asks the user if they prefer using it or not. You can only use this option if the version is 5.',
),
),
filename: v.pipe(
v.optional(v.string()),
v.description(
@@ -45,13 +51,20 @@ export function svelte_autofixer(server: SvelteMcp) {
code,
filename: filename_or_path,
desired_svelte_version: desired_svelte_version_unchecked,
async,
}) => {
if (server.ctx.sessionId && server.ctx.custom?.track) {
await server.ctx.custom?.track?.(server.ctx.sessionId, 'svelte-autofixer');
}
// we validate manually because some clients don't support union in the input schema (looking at you cursor)
const parsed_version = v.safeParse(
v.union([v.literal(4), v.literal(5), v.literal('4'), v.literal('5')]),
desired_svelte_version_unchecked,
);
if (parsed_version.success === false) {
if (server.ctx.sessionId && server.ctx.custom?.track) {
await server.ctx.custom?.track?.(server.ctx.sessionId, 'svelte-autofixer-wrong-version');
}
return {
isError: true,
content: [
@@ -65,6 +78,18 @@ export function svelte_autofixer(server: SvelteMcp) {
const desired_svelte_version = parsed_version.output;
if (async && +desired_svelte_version < 5) {
return {
isError: true,
content: [
{
type: 'text',
text: `The async option can only be used with Svelte version 5 or higher.`,
},
],
};
}
const content: {
issues: string[];
suggestions: string[];
@@ -76,11 +101,11 @@ export function svelte_autofixer(server: SvelteMcp) {
const filename = filename_or_path ? basename(filename_or_path) : 'Component.svelte';
add_compile_issues(content, code, +desired_svelte_version, filename);
add_compile_issues(content, code, +desired_svelte_version, filename, async);
add_autofixers_issues(content, code, +desired_svelte_version, filename);
add_autofixers_issues(content, code, +desired_svelte_version, filename, async);
await add_eslint_issues(content, code, +desired_svelte_version, filename);
await add_eslint_issues(content, code, +desired_svelte_version, filename, async);
} catch (e: unknown) {
const error = e as Error & { start?: { line: number; column: number } };
content.issues.push(
@@ -90,6 +115,10 @@ export function svelte_autofixer(server: SvelteMcp) {
content.suggestions.push(
"The code can't be compiled because a Javascript parse error. In case you are using runes like this `$state variable_name = 3;` or `$derived variable_name = 3 * count` that's not how runes are used. You need to use them as function calls without importing them: `const variable_name = $state(3)` and `const variable_name = $derived(3 * count)`.",
);
} else if (error.message.includes('css_expected_identifier')) {
content.suggestions.push(
"The code can't be compiled because a valid CSS identifier is expected. This sometimes means you are trying to use a variable in CSS like this: `color: {my_color}` but Svelte doesn't support that. You can use inline CSS variables for that `<div style:--color={my_color}></div>` and then use the variable as usual in CSS with `color: var(--color)`.",
);
}
}

View File

@@ -24,7 +24,10 @@ export const server = new McpServer(
instructions:
'This is the official Svelte MCP server. It MUST be used whenever svelte development is involved. It can provide official documentation, code examples and correct your code. After you correct the component call this tool again to confirm all the issues are fixed.',
},
).withContext<{ db: LibSQLDatabase<Schema> }>();
).withContext<{
db: LibSQLDatabase<Schema>;
track?: (sessionId: string, event: string, slug?: string) => Promise<void>;
}>();
export type SvelteMcp = typeof server;

View File

@@ -1,5 +1,31 @@
# @sveltejs/mcp
## 0.1.11
### Patch Changes
- fix: add `async` parameter to `svelte-autofixer` ([#94](https://github.com/sveltejs/mcp/pull/94))
- fix: install latest eslint svelte packages to support `$state.eager` ([`f6ce89f`](https://github.com/sveltejs/mcp/commit/f6ce89ff34faabc3d746a350ea347298ecfed2ec))
## 0.1.10
### Patch Changes
- fix: add icons to `server.json` ([`02c951b`](https://github.com/sveltejs/mcp/commit/02c951baa86ac8103ffc158a202c06cfe6b15c01))
- fix: add `preferred-frame-size` to UI resource ([`3fabcc0`](https://github.com/sveltejs/mcp/commit/3fabcc0f9bfee916c0deb9c2ffa931ed2168af2d))
- feat: support: `$state.eager` ([#90](https://github.com/sveltejs/mcp/pull/90))
## 0.1.9
### Patch Changes
- feat: return `mcp-ui` resource from `playground-link` ([#84](https://github.com/sveltejs/mcp/pull/84))
- feat: suggest against js variables in css ([#78](https://github.com/sveltejs/mcp/pull/78))
## 0.1.8
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@sveltejs/mcp",
"version": "0.1.8",
"version": "0.1.11",
"type": "module",
"license": "MIT",
"mcpName": "dev.svelte/mcp",
@@ -37,10 +37,10 @@
"publint": "^0.3.13",
"tsdown": "^0.15.0",
"typescript": "^5.8.3",
"vitest": "^3.1.3"
"vitest": "^4.0.0"
},
"dependencies": {
"eslint": "^9.36.0",
"tmcp": "^1.15.0"
"tmcp": "^1.15.3"
}
}

View File

@@ -8,13 +8,23 @@
"subfolder": "packages/mcp-stdio",
"source": "github"
},
"version": "0.1.8",
"version": "0.1.11",
"websiteUrl": "https://svelte.dev/docs/mcp/overview",
"icons": [
{
"src": "https://mcp.svelte.dev/logo.svg",
"mimeType": "image/svg+xml"
},
{
"src": "https://mcp.svelte.dev/logo.png",
"mimeType": "image/png"
}
],
"packages": [
{
"registryType": "npm",
"identifier": "@sveltejs/mcp",
"version": "0.1.8",
"version": "0.1.11",
"runtimeHint": "npx",
"transport": {
"type": "stdio"

566
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff