Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
# Values below are PUBLIC (they're shipped to the browser by Astro's
# `envField`, and live behind rate limiting / allow-lists on the vendor side).

# Kapa Website Widget integration ID for the "Ask AI" button in the sidebar.
# If unset, docs falls back to the shared widget integration ID used in preview/prod.
# Kapa Website Widget integration ID for the custom "Ask AI" chat and WebMCP.
# If unset, both the custom chat launcher and the WebMCP widget script are omitted.
# Get yours at: https://app.kapa.ai/admin (Integrations → Widget → Copy integration ID).
# The same value is passed to the widget as `data-website-id`.
# The same value is passed to the widget as `data-website-id` (with
# data-webmcp-enabled=true so in-browser agents can call
# search_warp_knowledge_sources). The default floating Ask button stays hidden;
# the custom sidebar Ask control remains the human entry point.
# PUBLIC_KAPA_INTEGRATION_ID=f31c5644-dd65-4f49-9762-bf580062afa8
PUBLIC_KAPA_INTEGRATION_ID=

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"seo:audit": "node scripts/seo-audit.mjs",
"test:heading-tocs": "node scripts/validate-heading-tocs.mjs",
"test:heading-vars": "node --test src/plugins/heading-vars-core.test.mjs",
"test:kapa-webmcp": "node --test src/lib/kapa-webmcp.test.mjs",
"test:homepage-json-ld": "node scripts/validate-homepage-json-ld.mjs",
"postinstall": "patch-package"
},
Expand Down
3 changes: 3 additions & 0 deletions src/components/CustomHead.astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Analytics from '@vercel/analytics/astro';
import SpeedInsights from '@vercel/speed-insights/astro';
import RudderStackAnalytics from './RudderStackAnalytics.astro';
import ArticleImageLightbox from './ArticleImageLightbox.astro';
import KapaWebMcp from './KapaWebMcp.astro';
import { DOCS_ORIGIN } from '../lib/site.js';

// Note: `<ClientRouter />` (Astro View Transitions) is intentionally NOT
Expand Down Expand Up @@ -189,6 +190,8 @@ const websiteJsonLd = JSON.stringify({
/>
<RudderStackAnalytics />
<ArticleImageLightbox />
{/* WebMCP page tool for in-browser agents; custom Ask chat stays separate. */}
<KapaWebMcp />

<Analytics />
<SpeedInsights />
Expand Down
31 changes: 31 additions & 0 deletions src/components/KapaWebMcp.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
/**
* Loads the Kapa Website Widget script solely so WebMCP can register the
* read-only `search_warp_knowledge_sources` page tool for in-browser agents.
*
* Human chat stays on the custom React SDK surface (`KapaChatLauncher`).
* This script hides Kapa's default floating button and does not replace or
* duplicate the custom Ask launcher.
*
* @see https://docs.kapa.ai/integrations/website-widget/features/webmcp
* @see https://www.kapa.ai/blog/kapa-webmcp
*/
import { PUBLIC_KAPA_INTEGRATION_ID } from 'astro:env/client';
import { getKapaWebMcpScriptAttributes } from '../lib/kapa-webmcp.js';

const attrs = getKapaWebMcpScriptAttributes(PUBLIC_KAPA_INTEGRATION_ID);
---

{
attrs ? (
<script
is:inline
async
src={attrs.src}
data-website-id={attrs['data-website-id']}
data-project-name={attrs['data-project-name']}
data-webmcp-enabled={attrs['data-webmcp-enabled']}
data-button-hide={attrs['data-button-hide']}
/>
) : null
}
44 changes: 44 additions & 0 deletions src/lib/kapa-webmcp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Kapa Website Widget attributes for WebMCP.
*
* Docs use a custom React SDK chat surface (`KapaChatLauncher`), not Kapa's
* default floating Ask AI button. WebMCP still requires the website widget
* script with `data-webmcp-enabled="true"`:
* https://docs.kapa.ai/integrations/website-widget/features/webmcp
*
* `data-button-hide` keeps the default widget launcher off the page so the
* existing custom Ask control remains the only human chat entry point.
* Browsers without WebMCP ignore the enable flag; chat behavior is unchanged.
*/

export const KAPA_WIDGET_SCRIPT_SRC = 'https://widget.kapa.ai/kapa-widget.bundle.js';

/** Becomes `search_warp_knowledge_sources` via Kapa's snake_case tool naming. */
export const KAPA_WEBMCP_PROJECT_NAME = 'Warp';

/**
* @param {string | undefined | null} integrationId
* @returns {null | {
* src: string,
* async: true,
* 'data-website-id': string,
* 'data-project-name': string,
* 'data-webmcp-enabled': 'true',
* 'data-button-hide': 'true',
* }}
*/
export function getKapaWebMcpScriptAttributes(integrationId) {
const websiteId = typeof integrationId === 'string' ? integrationId.trim() : '';
if (!websiteId) return null;

return {
src: KAPA_WIDGET_SCRIPT_SRC,
async: true,
'data-website-id': websiteId,
'data-project-name': KAPA_WEBMCP_PROJECT_NAME,
'data-webmcp-enabled': 'true',
// Hide Kapa's default floating button only. The custom sidebar Ask
// launcher (and search-footer Ask AI CTA) stay visible.
'data-button-hide': 'true',
};
}
32 changes: 32 additions & 0 deletions src/lib/kapa-webmcp.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import {
KAPA_WEBMCP_PROJECT_NAME,
KAPA_WIDGET_SCRIPT_SRC,
getKapaWebMcpScriptAttributes,
} from './kapa-webmcp.js';

test('returns null when the integration id is missing', () => {
assert.equal(getKapaWebMcpScriptAttributes(undefined), null);
assert.equal(getKapaWebMcpScriptAttributes(null), null);
assert.equal(getKapaWebMcpScriptAttributes(''), null);
assert.equal(getKapaWebMcpScriptAttributes(' '), null);
});

test('enables WebMCP on the official widget script without duplicating chat UI', () => {
const integrationId = 'f31c5644-dd65-4f49-9762-bf580062afa8';
const attrs = getKapaWebMcpScriptAttributes(` ${integrationId} `);

assert.deepEqual(attrs, {
src: KAPA_WIDGET_SCRIPT_SRC,
async: true,
'data-website-id': integrationId,
'data-project-name': KAPA_WEBMCP_PROJECT_NAME,
'data-webmcp-enabled': 'true',
// Default floating Ask button stays hidden; custom sidebar Ask remains.
'data-button-hide': 'true',
});
assert.equal(attrs.src, 'https://widget.kapa.ai/kapa-widget.bundle.js');
assert.equal(attrs['data-project-name'], 'Warp');
assert.equal(attrs['data-consent-required'], undefined);
});
Loading