I've written some rules over the last few days while working on some SvelteKit projects with the Svelte MCP enabled. One of these projects was written entirely from scratch. Two of them were written with some existing older code so that could have caused the LLM to use older APIs or syntax instead. I was using GitHub Copilot with Sonnet 4.5 alongside the default tools, e18e, and the Playwright MCP.
Svelte rules
- do not use
$effect to synchronise state
It likes to use $effect just to assign some state instead of just initialising it with $state(initial_state) or setting it in an event listener callback
- state whose value is an array should take advantage of mutative array methods instead of re-assigning the entire array for fine-grained updates
It's probably biased towards React and Svelte 4 syntax of reassigning the entire array instead of using .push()
window always exists in onMount because it only runs on the client. There's no need to check if it is defined
It doesn't know onMount only runs in the browser.
It prefers to manipulate the style="..." string instead of just using style directives. According the the svelte eslint rule, the style directives perform better
The LLM has a preference to use string templates and conditionals to manipulate the string. Even in React projects there needs to be a rule to tell it to use the shadcn cn helper. It's better if it just uses our built-ins here
SvelteKit rules
- use
$lib as an alias to src/lib
The LLM tends to create relative paths such as ../../lib instead of using $lib.
- use
$app/state instead of $app/stores
Probably not trained to use $app/state since it's new-ish. Also might be following older code
- use the page object from
$app/state to check the current URL instead of window.location.href
- use
goto from $app/navigation instead of assigning a value to window.location.href
These are pretty annoying. Not sure why it prefers native browser APIs for most tasks.
- href link must be wrapped with
resolve from $app/paths
I should have probably asked the LLM to lint or check after each edit. But even when adding the eslint resolve error message as context, it never fixed this issue correctly. We should probably at some point fix the eslint plugin itself to auto fix these more easily.
Extra
These are some issues that probably would have worked if I just used the svelte-task prompt https://svelte.dev/docs/mcp/prompts#svelte-task:
Svelte rules
It has no idea what I meant by boundaries and await. It preferred using the old {#await} syntax. Maybe we need to check if experimental settings are on and give the LLM additional context or give an option to enable those experimental settings.
SvelteKit rules
When I asked it to create a remote function, it would have no idea what that was and would create a +server endpoint instead.
I've written some rules over the last few days while working on some SvelteKit projects with the Svelte MCP enabled. One of these projects was written entirely from scratch. Two of them were written with some existing older code so that could have caused the LLM to use older APIs or syntax instead. I was using GitHub Copilot with Sonnet 4.5 alongside the default tools, e18e, and the Playwright MCP.
Svelte rules
$effectto synchronise statewindowalways exists inonMountbecause it only runs on the client. There's no need to check if it is definedSvelteKit rules
$libas an alias tosrc/lib$app/stateinstead of$app/stores$app/stateto check the current URL instead ofwindow.location.hrefgotofrom$app/navigationinstead of assigning a value towindow.location.hrefresolvefrom$app/pathsExtra
These are some issues that probably would have worked if I just used the
svelte-taskprompt https://svelte.dev/docs/mcp/prompts#svelte-task:Svelte rules
SvelteKit rules