Skip to content
KismetKismetDevelopers
Beta accessllms.txt

Poppy website discovery

View .md

Install one server-side AI service for your site. Use the same resolved entry for llms.txt, public page Markdown, the guest MCP card and Poppy discovery. Visible agent marks are presentation, not the installation or an access grant.

Use the compatible poppy02 release for both draft 0.1 and draft 0.2 discovery. Verify the exact version is available in npm before installing:

Terminal window
npm install --save-exact @kismet-tech/[email protected]
npx kismet init --update

The existing beta tag is a separate Forms evaluation release; do not assume it contains these helpers. Package installation does not activate Poppy, verify ownership, grant API permissions, or establish authenticated-agent acceptance.

import { createKismetNextPageAdapter, defineSite } from '@kismet-tech/sdk';
import { resolveAgentEntry } from '@kismet-tech/sdk/server';
const site = defineSite({
name: 'Example Stays',
origin: 'https://www.example-stays.com',
collectionSlug: 'example-stays',
pages, // your existing public/private typed page definitions
});
// Run in server request/data loading, not once in a permanent static build.
const agentEntry = await resolveAgentEntry({
host: new URL(site.origin).hostname,
collectionSlug: site.collectionSlug,
});
const adapter = createKismetNextPageAdapter(site, { agentEntry });

Mount these handlers in your framework:

Route Handler Purpose
/llms.txt adapter.llmsTxt() Public page index, existing connector briefs and authorized Poppy link
Public page Markdown adapter.markdown(request) or adapter.html(request, render) for Accept: text/markdown Existing public content plus the same authorized Poppy link
/.well-known/mcp.json adapter.mcpServerCard() Existing signed-out collection MCP card
/.well-known/poppy.json adapter.poppyDiscovery() Website discovery or the centrally authorized collection manifest
/robots.txt adapter.robots({ environment: 'production' }) Existing crawler signposts; not an access grant

Private pages remain private. Poppy does not replace the existing guest MCP card or turn the brand MCP relay into a personal-agent token relay. Its manifest names the canonical issuer and broader MCP resource separately.

resolveAgentEntry opts into the shared anonymous authority at GET /v1/public/agent-surface/connector?host=<exact-host>&surface=poppy (with an agent parameter for each existing brief). The response adds poppy, poppyMarkdown and poppyReadiness. The SDK validates the manifest against the requested hostname; it does not infer permission from a collection slug, installed script or local configuration. It uses its own validated manifest to render the link.

The platform decides eligibility from stored verified production ownership and a current public collection. Apex and www are distinct for collection authorization; do not substitute one for the other. Staging registration alone cannot authorize collection Poppy access. Cloudflare and WordPress consume the same authority.

Refresh the resolved entry at least every five minutes, the SDK cache lifetime. Use cache: false for a fresh diagnostic lookup. Recreate the adapter with the refreshed entry; retaining an adapter forever retains its old answer. Collection manifests are served with Cache-Control: no-store; the bounded SDK lookup cache is separate. Final token/tool authorization remains server-side, including after ownership withdrawal.

When the authority is unavailable or returns no eligible manifest, llms.txt, Markdown and the guest MCP card keep working, without a collection Poppy link. The manifest falls back to website-only discovery without auth or apis. A mismatched host or collection manifest is refused, not relabelled as the current site’s authority.

For a Node server, use poppyDiscoveryResponse({ name, origin }, agentEntry?.poppy) from @kismet-tech/sdk/server.

When the installed SDK exposes poppyWebsiteProtocolVersion, choose draft 0.2 for a website-only staging installation explicitly:

const adapter = createKismetNextPageAdapter(site, {
agentEntry,
poppyWebsiteProtocolVersion: '0.2',
});

For a Node handler, pass protocolVersion: '0.2' to poppyDiscoveryResponse({ name, origin, protocolVersion: '0.2' }). Existing callers retain the 0.1 website-only default. This choice changes only the website fallback: a resolved Company manifest passes through unchanged, and a foreign authority is still refused. It does not enable Sessions or promote a staging domain into production authority.

Use the official Poppy Company skill and its downloadable kit. Download each new audit’s kit outside your application repository. Record its retrieval time and SHA-256 before reviewing and running it; retain that exact copy with the report. Fetching a newer kit starts a new evidence run rather than rewriting a previous pass. Do not modify the validator to pass a check.

Start with node <kit-dir>/validate/index.ts survey https://<exact-host>. The kit’s live Session checks create real Sessions, so follow its test-identity and production-approval instructions. Discovery-only or website-only success does not demonstrate authenticated MCP, sign-in or checkout. Record skips and manual checklist results alongside passes.

Ownership proof during authorized installation

Section titled “Ownership proof during authorized installation”

The SDK can deliver an existing domain challenge; it cannot issue a challenge, register ownership or mark a domain verified. An authorized installation operator uses the existing collection-domain tools:

  1. Preview and confirm prepare_collection_domain for the registered exact hostname and authorized collection. The confirmed response supplies hostname, proof and expires_at.
  2. Put that response into trusted server/deployment configuration, mapping expires_at to expiresAt. Do not accept proofs from query strings, cookies, browser forms or an anonymous lookup.
  3. Publish the fixed HTTPS proof route below, then preview and confirm verify_collection_domain. The platform independently fetches the exact bytes, consumes the challenge and updates the stored domain.
  4. Remove the proof after verification; expired proofs are refused automatically. If deployment takes longer than the challenge lifetime, prepare and publish a new challenge. Configuration alone never grants access.
const adapter = createKismetNextPageAdapter(site, {
agentEntry,
domainVerification: {
hostname: prepared.hostname,
proof: prepared.proof,
expiresAt: prepared.expires_at,
}, // prepared is the authorized operator response, never request input
});
// app/.well-known/kismet-domain-verification/route.ts
export const dynamic = 'force-dynamic'; // check expiry on every request
export const GET = (request: Request) => adapter.domainVerification(request);
export const HEAD = GET;

For a non-Next server, domainVerificationResponse(request, { origin, proof }) is exported from @kismet-tech/sdk/server. GET returns the exact proof with no trailing newline; HEAD has no body. Responses use text/plain, no-store and nosniff. Wrong host, path, HTTP origin, missing or expired configuration return 404; writes return 405 for an otherwise valid binding. The helper never fetches a challenge or verifies ownership itself. Do not redirect this route from apex to www.

The Next.js SDK starter includes both well-known routes. Store the authorized prepare result as server-only KISMET_DOMAIN_VERIFICATION_PROOF JSON with hostname, proof and expiresAt (mapped from expires_at), and configure NEXT_PUBLIC_SITE_URL as that exact HTTPS origin. The proof handler does not load the catalog or call the anonymous connector. Remove the binding after successful verification or expiry; never populate it from browser input.

Run npx kismet check <url> --env production. The opt-in site/poppy-discovery warning checks the exact-host manifest against llms.txt, public Markdown and the guest MCP card’s collection. Sites that do not advertise Poppy are unchanged. Website-only discovery is valid without collection auth. These are consistency checks, not ownership verification or session acceptance; use checkPoppyReadiness and the acceptance sequence below for those boundaries.

Automatic prepare, publish and verify during managed installation is a separate onboarding integration. Do not assume an installed worker/plugin or this SDK helper has completed it: check the stored ownership result, then discovery and registered-agent acceptance. There is no additional public onboarding API implied by this guide.

Capture agent fetches before the response cache

Section titled “Capture agent fetches before the response cache”

Publishing or updating the SDK does not instrument an existing application. Adopt one server-side capture owner for each route: middleware/edge before the cache for static or CDN-served documents, or the dynamic adapter when every request reaches its handler. Do not enable both for the same fetch. The helper’s per-Request deduplication is not a distributed deduplication guarantee.

Canonical public /markdown and …/markdown routes are document fetches for both GET and HEAD, including requests without an Accept header. Capture keeps the original URL, query string and User-Agent; do not rewrite request headers to make an otherwise untracked request appear to negotiate Markdown. Keep private routes and ownership proofs outside both machine and legacy page-view capture.

Use the collection tracking key supplied through telemetry onboarding, stored as server-only KISMET_TRACKING_KEY. It is not your Developer API server key. If a managed worker, plugin or existing middleware already owns these fetch events, verify that capture instead of adding a second SDK emitter.

import { recordAgentFetch } from '@kismet-tech/sdk/edge';
// In your host's middleware/edge request hook, before returning a cached response:
recordAgentFetch(request, {
origin: 'https://www.example-stays.com',
collectionSlug: 'example-stays',
trackingKey: serverTrackingKey, // trusted installation setting; never browser code
waitUntil: (pending) => context.waitUntil(pending),
environment: 'production',
});
// Continue normal serving. Omit responseStatus unless this plane knows it.

The host supplies the incoming Request and its request-lifetime scheduler. Do not replace the incoming User-Agent with the SDK’s identity or truncate it. recordAgentFetch retains the exact URL, installed collection, serving domain, original User-Agent (including unknown or missing values), and marks the event as actionType: 'fetch' without a human session. It records GET and HEAD; it does not issue agent sessions or confer access. Static files still need this serving-plane hook even when the app’s dynamic routes are instrumented.

For dynamic-only routes, use the alternative adapter-owned capture:

const adapter = createKismetNextPageAdapter(site, {
agentEntry,
fetchTracking: {
trackingKey: serverTrackingKey,
waitUntil: (pending) => context.waitUntil(pending),
environment: 'production',
},
});
export const GET = (request: Request) => adapter.poppyDiscovery(request);
export const HEAD = GET;

With fetchTracking enabled, pass the real request to llmsTxt, mcpServerCard, poppyDiscovery, and the other mounted discovery handlers. HEAD responses have no body. A PHP-only WordPress hook cannot see a file or page served directly by a static server/CDN: add edge/host capture or explicit cache exclusions, then prove the deployed behavior. Cloudflare workers likewise must capture early discovery returns, not only the normal page-render path.

Before claiming tracking coverage, request every installed surface (llms.txt, llms-full.txt when present, public Markdown including negotiated Markdown, Poppy and MCP discovery). Use uniquely marked GET and HEAD requests with a known agent UA, an unknown UA, and no UA. Repeat the same URL to exercise warm-cache hits. Verify matching persisted content_events: full original UA, exact URL, installed collection/domain, action_type = fetch, and no human session. Check the request count for duplicate capture. A successful manifest response, a tracking-key health response, or a passing source test is not this evidence.

Compatible adapters accept both draft 0.1 and draft 0.2 central collection manifests. They preserve the authority’s version; they do not relabel an older document or infer a company from a hostname or configured collection slug.

For draft 0.1 compatibility, the issuer is https://kismet.travel and the legacy endpoint is https://kismet.travel/api/oauth/token. Send the manifest’s kismet.token_parameters on issuance and renewal; its required kismet_domain extension selects the exact verified website scope. Omitting that parameter is not a collection-scoped request. Its existing MCP resource remains https://mcp.kismet.travel/mcp; ordinary anonymous/guest OAuth clients keep that existing entry point.

For draft 0.2, each company has a stable issuer: https://kismet.travel/poppy/collections/{collection_id}. The collection_id is the stored lowercase UUID in the central manifest, not a slug or a value to construct locally. Kismet’s separate network issuer is https://kismet.travel/poppy, with collection_id: null; it does not grant a partner collection’s authority.

Fetch RFC 8414 metadata by inserting /.well-known/oauth-authorization-server before the issuer path, for example https://kismet.travel/.well-known/oauth-authorization-server/poppy/collections/{collection_id}. Require exact issuer agreement and the website in poppy_domains before using the metadata’s issuer-specific token_endpoint and revocation_endpoint. Both client and session assertion aud values are that exact issuer, not the token endpoint, website hostname or MCP URL. A company must not renew through another company’s issuer or the network issuer.

Draft 0.2 kismet.token_parameters may be {} or contain a matching kismet_domain legacy adapter hint. Standard 0.2 clients do not need that hint; it never selects or changes the UUID-bound company scope. Do not carry the 0.1 parameter-selection behavior into the new endpoints.

The draft 0.2 MCP URL and resource are https://kismet.travel/poppy/mcp, shared by the eligible company issuers. This entry requires a verified 0.2 Poppy token for every POST; it does not replace the older public/guest MCP entry point. Its protected-resource metadata is https://kismet.travel/.well-known/oauth-protected-resource/poppy/mcp and must include the company’s exact issuer.

General 0.2 session creation requires an ES256 DPoP proof. This MCP resource’s protected-resource metadata does not advertise DPoP: request its Bearer token without a proof and explicitly set resource to that exact URL for issuance and renewal. This exception does not authorize Bearer tokens on other APIs or downgrading a DPoP-bound token. See the pinned draft 0.2 specification.

These are signed-out discovery and published-inventory contracts. No guest sign-in, account, booking or payment permissions are advertised. Use the registered personal-agent protocol client, not guest OAuth helpers, for these assertions and tokens.

Run checkPoppyReadiness({ origin }) from @kismet-tech/sdk/server after installation. It checks the served manifest and version-specific issuer-domain agreement. For 0.2 it also checks issuer-specific token/revocation metadata, JWT-bearer/private-key/ES256 capabilities and the declared MCP resource’s issuer/Bearer metadata. Its result deliberately keeps session: 'unverified' and ready: false: public discovery alone cannot prove session issuance, cross-collection denial or usage attribution. A registered-agent acceptance run must verify those separately before advertising full support.

The website-only helper retains its draft 0.1 output for compatibility and grants no authentication authority. A centrally authorized 0.2 manifest passes through the compatible adapter as 0.2. Do not enable 0.2 discovery for an old installed parser: publish and adopt compatible adapters first, or continue serving a negotiated compatible version. A source change does not upgrade pinned packages or static files, and passing these checks is not a deployment/conformance claim.

Before calling a deployment fully supported, verify the exact production host’s GET/HEAD manifest, llms.txt and a real public Markdown page. Then use a registered agent to prove current published inventory access, denial of another collection and its properties, same-host renewal, refusal of omitted/cross-host renewal, and durable collection/domain usage attribution. Repeat with a second collection. Agent authentication is not guest sign-in and does not confer payment authority.

For 0.2, also prove that unauthenticated initialize on the strict MCP URL returns HTTP 401 and that a correctly bound registered-agent token succeeds. Anonymous access on the older public MCP entry is not evidence for this check. checkPoppyReadiness reads public metadata; it does not issue tokens or perform this authenticated MCP acceptance run.

Keep source tests, published SDK/plugin version, deployed server/worker revision and live acceptance evidence separate. A merged change, HTTP 200, website-only manifest or discovery: 'verified' is not complete acceptance. Confirm your installed release exports these helpers before using them; a source change does not update already pinned packages. Install the compatible server routes once, then refresh central configuration for supported changes. A script embed can render marks but cannot create well-known HTTP routes or establish ownership.