Skip to content

Experimental · Unofficial

Built by Tim Benniks. This is not an official Contentstack product and is not supported by Contentstack.

Getting started

This guide gets you from zero to registered WebMCP tools using direct mode — the Delivery SDK runs in the browser tab, so there is no server side to build. If your environment holds content you would not publish, use proxy mode instead; step 5 shows the switch.

Prerequisites

  • Node.js 18+
  • A Contentstack stack with published content
  • Delivery API credentials (API key, delivery token, environment)
  • A Chrome build exposing document.modelContext — origin trial 149+, or #enable-webmcp-testing on older builds (see Browser support)

1. Install

bash
npm install @timbenniks/contentstack-webmcp @contentstack/delivery-sdk

Optional adapters:

bash
npm install react   # @timbenniks/contentstack-webmcp/react
npm install next    # @timbenniks/contentstack-webmcp/next

2. Define your content types

typescript
export const contentTypes = [
  {
    uid: "blogpost",
    label: "Blog Post",
    urlField: "url",
    titleField: "title",
    bodyField: "body",
    searchableFields: ["title", "body"],
  },
];

searchableFields drives which fields contentstack_search_entries matches against, CDA-side.

3. Register tools in the browser

typescript
import contentstack from "@contentstack/delivery-sdk";
import { createContentstackWebMcp, isWebMcpSupported } from "@timbenniks/contentstack-webmcp";
import { contentTypes } from "./content-types";

const stack = contentstack.stack({
  apiKey: import.meta.env.VITE_CONTENTSTACK_API_KEY,
  deliveryToken: import.meta.env.VITE_CONTENTSTACK_DELIVERY_TOKEN,
  environment: import.meta.env.VITE_CONTENTSTACK_ENVIRONMENT,
});

const webmcp = createContentstackWebMcp({ mode: "direct", stack, contentTypes });

async function initWebMcp() {
  if (!isWebMcpSupported()) return;

  const controller = new AbortController();
  await webmcp.register({
    signal: controller.signal,
    navigate: (path) => window.location.assign(path),
  });

  window.addEventListener("pagehide", () => controller.abort());
}

initWebMcp();

That is the whole integration. Use a read-only delivery token scoped to an environment containing only content you are happy to have public — it grants read access to every content type in that environment, not just the ones configured above.

4. Permissions-Policy header

Add to your site so browser agents can access tools:

Permissions-Policy: tools=(self)

See Security for nginx, Vercel, and Next.js examples.

5. Verify in Chrome

  1. Confirm document.modelContext exists — origin trial (149+) or #enable-webmcp-testing
  2. Open DevTools → ApplicationWebMCP
  3. Confirm tools are registered
  4. Try contentstack_search_entries manually, then contentstack_get_entry_by_url on a result, then navigate_to (requires the navigate callback)

6. Optional — switch to proxy mode

To keep the delivery token off the client, add a server route and change one config line. Tools and response shapes are identical either way.

ts
// app/api/contentstack/[...path]/route.ts
import contentstack from "@contentstack/delivery-sdk";
import { createContentstackWebMcpRouteHandlers } from "@timbenniks/contentstack-webmcp/next";
import { contentTypes } from "@/content-types";

const stack = contentstack.stack({
  apiKey: process.env.CONTENTSTACK_API_KEY!,
  deliveryToken: process.env.CONTENTSTACK_DELIVERY_TOKEN!,
  environment: process.env.CONTENTSTACK_ENVIRONMENT!,
});

export const { GET } = createContentstackWebMcpRouteHandlers({ stack, contentTypes });
ts
import contentstack from "@contentstack/delivery-sdk";
import { createContentstackWebMcpProxyHandler } from "@timbenniks/contentstack-webmcp/server";
import { contentTypes } from "./content-types";

const stack = contentstack.stack({
  apiKey: process.env.CONTENTSTACK_API_KEY!,
  deliveryToken: process.env.CONTENTSTACK_DELIVERY_TOKEN!,
  environment: process.env.CONTENTSTACK_ENVIRONMENT!,
});

export default createContentstackWebMcpProxyHandler({ stack, contentTypes });

Then in the browser, drop the stack entirely:

typescript
createContentstackWebMcp({ mode: "proxy", proxyBasePath: "/api/contentstack", contentTypes });

Framework-specific guides

Troubleshooting

SymptomFix
No tools registeredCheck document.modelContext exists; call isWebMcpSupported()
CDA requests blocked in direct modeAllow the Contentstack CDA host in your CSP
404 on proxy routesVerify server route path matches proxyBasePath
Empty search resultsConfirm content type UIDs and searchableFields
Tools disabled entirelySet CONTENTSTACK_WEBMCP_ENABLED=true

See FAQ for more.

Released under the MIT License.