BoilerplateHub

MCP Servers for SvelteKit

SvelteKit moved fast enough that training data contains three incompatible generations of it: Sapper, SvelteKit 1.0, and SvelteKit 2 on Svelte 5. The single most useful thing an agent can know is which Svelte version the project runs and whether runes are enabled, because that decides the syntax of nearly every component it writes. It also needs the universal versus server-only load distinction, since that boundary is enforced by filename rather than by anything visible inside the file.

Wiring MCP into a SvelteKit project

  • Commit .mcp.json at the repo root so contributors share the same browser and database servers for testing form actions.
  • Give the Postgres server a read-only role, since server load functions already give agents plenty of write paths through the app.
  • Scope the filesystem server to src/ so the agent does not wander into node_modules or .svelte-kit while searching for a route.

Servers worth adding

Filesystem MCP server

Scoped read and write access to the project directory.

SvelteKit routing is entirely filename driven, so the agent has to read the real src/routes tree before it can place a +page.server.js correctly.

Git MCP server

Repository history, diffs, and blame.

History tells the agent whether the codebase has already migrated to runes, which is otherwise a guess.

Playwright MCP server

Real browser automation for navigating and asserting on the rendered app.

Form actions and use:enhance only prove correct when a real submit round trip runs, including the no-JavaScript fallback.

A Postgres MCP server

Schema inspection and read queries against your database.

Server load functions query directly, so real schema access stops the agent from inventing table and column names.

Skills, MCP and plugins compared →

Rule these out for SvelteKit

These are the failures that repeat across sessions, so each one belongs in .mcp.json.

Writing pre-2.0 load and session patterns

Agents still reach for a session store from $app/stores, a load function that receives page and fetch as a legacy shape, or Sapper's preload. None of that exists any more, and session in particular was removed in favour of event.locals plus data returned from load. Put the version in your rules file and add: 'Load functions live in +page.js or +page.server.js, take a single event argument, and per request state comes from event.locals set in hooks.server.js.'

Mixing runes and legacy reactivity in the same component

In a Svelte 5 runes project an agent will still write export let for props and a $: block for derived values, which either fails to compile or silently opts the component out of runes mode. The mix is hard to spot because both syntaxes look correct in isolation. State it plainly: 'Svelte 5 runes mode. Props use $props(), state uses $state(), derived values use $derived(), side effects use $effect(). Never use export let or $: labels.'

Putting database or secret access in +page.js

A +page.js load runs on the server for the first render and then again in the browser on client navigation, so an agent that queries the database or imports from $env/static/private there ships a build error at best and a leaked secret at worst. Agents pick the wrong file because both are named load. Write the rule as: 'Anything touching the database, private env, or a server-only SDK goes in +page.server.js or a .server.js module. +page.js is browser code too.'

Building a custom API route where a form action belongs

Asked to add a form, agents default to a POST endpoint under routes/api plus a client fetch handler, which throws away progressive enhancement, the built-in validation return shape, and automatic invalidation of load data. It works, so nobody catches it in review. Add: 'Mutations from a form use a named action in +page.server.js with use:enhance on the client. Only create a route under api/ for external consumers or webhooks.'

Full page reloads instead of client side navigation

For internal navigation and post-mutation refreshes, agents write window.location.href or location.reload(), which drops the router, refetches every load, and loses client state. Both are common in generic JavaScript training data and neither is ever correct inside a SvelteKit app. Specify: 'Use goto from $app/navigation for internal navigation and invalidate or invalidateAll to refresh load data. window.location is only for external URLs.'

Mutating arrays and objects in place and expecting an update

Agents call items.push(x) or obj.key = v and move on. Under Svelte 5 $state that works because of the proxy, but in a legacy component or on a plain non-reactive value it silently renders nothing, and agents cannot tell which mode a file is in without looking. Make it explicit: state your Svelte version and add 'in legacy components reassign rather than mutate, and never assume a plain module level object is reactive.'

Same framework, other agents