Poppy website discovery
View .mdInstall 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.
Install the compatible SDK
Section titled “Install the compatible SDK”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:
npx kismet init --updateThe 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.
Resolve and serve together
Section titled “Resolve and serve together”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.
Website-only draft selection
Section titled “Website-only draft selection”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.
Validate with the official Company kit
Section titled “Validate with the official Company kit”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:
- Preview and confirm
prepare_collection_domainfor the registered exact hostname and authorized collection. The confirmed response supplieshostname,proofandexpires_at. - Put that response into trusted server/deployment configuration, mapping
expires_attoexpiresAt. Do not accept proofs from query strings, cookies, browser forms or an anonymous lookup. - 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. - 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.tsexport const dynamic = 'force-dynamic'; // check expiry on every requestexport 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.
Versioned issuer and token contracts
Section titled “Versioned issuer and token contracts”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.
Discovery is not complete acceptance
Section titled “Discovery is not complete acceptance”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.