Vue 3
No adapter needed. A composable with onMounted / onUnmounted is the whole integration.
Composable
// composables/useContentstackWebMcp.ts
import { onMounted, onUnmounted, ref } from "vue";
import { createContentstackWebMcp, isWebMcpSupported } from "@timbenniks/contentstack-webmcp";
import type {
ContentstackWebMcpConfig,
WebMcpNavigateCallback,
} from "@timbenniks/contentstack-webmcp";
export function useContentstackWebMcp(
config: ContentstackWebMcpConfig,
navigate?: WebMcpNavigateCallback
) {
const registeredTools = ref<string[]>([]);
let cleanup = () => {};
onMounted(async () => {
if (!isWebMcpSupported()) return;
const webmcp = createContentstackWebMcp(config);
const controller = new AbortController();
cleanup = () => controller.abort();
registeredTools.value = await webmcp.register({
signal: controller.signal,
navigate: navigate ?? ((path) => window.location.assign(path)),
});
});
onUnmounted(() => cleanup());
return { registeredTools };
}cleanup is assigned before the await, so unmounting mid-registration still aborts.
Usage
<!-- App.vue — mount once, above <RouterView /> -->
<script setup lang="ts">
import contentstack from "@contentstack/delivery-sdk";
import { useRouter } from "vue-router";
import { useContentstackWebMcp } from "@/composables/useContentstackWebMcp";
import { contentTypes } from "@/content-types";
const router = useRouter();
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 { registeredTools } = useContentstackWebMcp({ mode: "direct", stack, contentTypes }, (path) =>
router.push(path)
);
</script>
<template>
<RouterView />
</template>Mounting in App.vue rather than a page component means tools register once instead of on every route change.
TIP
router.push keeps navigate_to a client-side transition, so guards and scroll behaviour still apply. window.location.assign would do a full reload and re-register everything.
Proxy mode
Vue has no server of its own, so host the handler wherever you serve the app — see Server runtimes for Express, Hono, and the Vite dev-server middleware, which is handy locally.
useContentstackWebMcp({ mode: "proxy", proxyBasePath: "/api/contentstack", contentTypes }, (path) =>
router.push(path)
);Env vars
Vite exposes only VITE_-prefixed variables to the client, which is exactly the mode boundary: direct mode needs VITE_ vars, proxy mode keeps them unprefixed and server-side.
Nuxt
Nuxt has its own lifecycle and server routes — see Nuxt.