Integrations
The core is vanilla TypeScript, so every framework uses the same three calls: createContentstackWebMcp(), isWebMcpSupported(), and register({ signal, navigate }). What changes per framework is only:
- Where you put the init — a lifecycle hook that runs in the browser
- How you expose the delivery token to the client, for direct mode
- Which router function you hand to
navigate, sonavigate_todoes client-side nav - Where you tear down — abort the
AbortSignalwhen the page or component goes away
Pick your stack
| You're using | Guide | Adapter |
|---|---|---|
| Plain HTML, no build step | Vanilla JS / HTML | — |
| Vite, Webpack, Parcel, esbuild | Vanilla JS / HTML | — |
| React (SPA) | React | /react |
| Next.js | Next.js | /react, /next |
| Remix / React Router v7 | Remix | /react |
| Vue 3 | Vue | — |
| Nuxt | Nuxt | — |
| Svelte / SvelteKit | Svelte | — |
| Angular | Angular | — |
| Solid / SolidStart | Solid | — |
| Astro | Astro | — |
| Eleventy, Hugo, Jekyll, static output | Vanilla JS / HTML | — |
| Cloudflare Workers | Cloudflare Workers | — |
| Node, Express, Hono, Deno, Bun, serverless | Server runtimes | — |
Only React gets an adapter, and even that is optional — /react is a thin useEffect wrapper you could write yourself in fifteen lines. Nothing else needs one.
The shared config
Every guide assumes a content type list like this. Define it once and import it everywhere:
// content-types.ts
export const contentTypes = [
{
uid: "blogpost",
label: "Blog Post",
urlField: "url",
titleField: "title",
bodyField: "body",
searchableFields: ["title", "body"],
includeReferences: ["author"],
},
];See Configuration for every field.
Two modes, one API
Direct mode (the default) runs the Delivery SDK in the browser tab. No server, no routes — but the delivery token ships in your bundle, so the environment it reads must contain only content you are happy to have public.
Proxy mode keeps the token on your server behind same-origin routes. Every guide below shows direct mode first, then the proxy route if that framework has a server side.
Both register identical tools and return identical shapes, so switching is a one-line config change. See Execution modes for how to choose.
Every framework needs this header
Permissions-Policy must be sent as an HTTP response header on the document. It cannot be set with <meta http-equiv> — browsers ignore that.
Permissions-Policy: tools=(self)See Security for Vercel, Netlify, Next.js, nginx, Apache, and Workers examples.
Direct mode needs a CSP allowance
If you send a Content-Security-Policy, direct mode fetches the CDA from the browser, so the host needs to be in connect-src:
connect-src 'self' https://cdn.contentstack.io;Use your region's host — eu-cdn.contentstack.com, azure-na-cdn.contentstack.com, and so on. Proxy mode needs no CSP change, since tool handlers only ever call your own origin.