Current-request context (portable default)
Put the bounded context needed for the current operation in the business tool’s own input. This keeps the reliable path to one business-tool call and one host-owned approval. Do not require the model to call a setup, context-sharing, or consent tool before it can complete the customer’s task. For example, a catalog tool can accept a closedtaskContext object containing only fields it can really use:
tools/call, so it must produce no context event. Splitting a search into begin_task and search_catalog can
therefore produce two host approvals; prefer one bounded business operation when the task permits it.
Previously approved profile context should be loaded by the company server using verified product identity,
merged with current-request context under documented precedence rules, and passed to the business handler
before it runs. Do not depend on the agent selecting a separate context tool. Derive customer and journey
references from verified auth or product-owned state, never model-authored arguments. If no verified journey
exists, use request scope rather than claiming that data lasts for the chat or MCP transport session.
Optional retained personalization
Use Epode’s customer-enrichment tools when the customer explicitly chooses to build a profile that survives the current request. This is separate from the portable current-request path:share_customer_context on the company’s own MCP server and decorates selected business-tool
results with the exact allowed action. On MCP 2026 clients that both advertise form elicitation and have been
version-tested to surface and resume it, share_customer_context can return MRTR input_required with
Always allow, This session only, and Don’t allow. This is a best-effort enhancement, not a portable
client guarantee.
Do not make successful product use depend on MRTR or on the agent selecting this second tool. If a client cannot
surface or resume input, continue without profile enrichment and never infer approval. Noninteractive Codex and
Claude CLI runs may exhaust their configured inputRequired.maxRounds because there is no interactive user
surface; treat that result as unsupported, not as consent or refusal. record_customer_context_consent is an
explicit compatibility fallback, not an instruction to add an unsolicited second question after the host has
already approved the business call.
Test the exact host, version, transport, and interactive mode you support. Claude Desktop, Claude CLI, Codex CLI,
and ChatGPT integrations have different approval and elicitation behavior. Capability metadata alone does not
prove that the client will render a form, resume the original call, or choose share_customer_context.
The customer needs no Epode account, plugin, or direct connection. Context retrieval, personalization decisions,
and outcomes remain server-to-server. Failed (isError), canceled, incomplete, and input_required results are
never decorated or counted as a completed enrichment opportunity. A session-only choice forces every submitted
item to remain interaction-scoped even if the model marked it durable. Here, session scope means a verified,
product-issued journey reference; it does not mean an MCP transport session or whatever chat the host currently
shows.
Importing broader assistant memory is not part of first-use personalization. MCP has no portable operation that
lets a company server autonomously read a host’s full saved memory. Offer Import more from your assistant…
only as an optional profile-settings flow: show a preview of the exact bounded items, their claimed provenance,
purpose, and retention; let the customer remove items; then save only the approved selection. The normal product
task must remain complete if the customer skips or cancels that flow.
The enrichment request itself carries surface: "mcp", so the initial successful product-tool interaction is
confirmed immediately. Do not add separate Epode telemetry instrumentation around the same MCP server.
The session/runtime extractors deliberately receive only MCP server context. Resolve them from verified auth or
product-owned state; tool arguments and model-authored result fields cannot establish a journey or runtime.
Run the travel MCP example
Legacy structured outcome feedback
MCP is Epode’s strongest integration: every completed product-tool call is a confirmed interaction. Epode registersrecord_product_feedback_consent and report_product_feedback; the product result tells the
customer agent which action is currently allowed.
For a tool without
outputSchema, the feedback envelope is
structuredContent._agentFeedback. If the business tool declares outputSchema, Epode leaves its
structuredContent exactly schema-valid and appends a standalone JSON text block shaped as
{"_agentFeedback": {...}} instead. A plain-language instruction follows either form. Agents should
use the machine-readable envelope in whichever one of those two locations is present.Install
.epode/artifacts and include it in source control or the deployment build
context alongside package.json and package-lock.json. Both manifests refer to that project-local file,
so npm ci still works after the npm cache is cleared or the original shell exits.
Configure once
Create one process-level Epode instrumentation object so telemetry can batch across requests. Create a fresh MCP server for each stateless HTTP request and instrument it before registering business tools.productTokenVerifier is the adapter for your product’s real access-token verification, not a decoder
or example token table. It must verify the signature or introspection result, expiration, audience,
the mcp scope, and the caller’s membership in the selected account before returning. The MCP Express
middleware then attaches the verified AuthInfo to the request; the Node adapter passes it through as
context.http.authInfo. accountRef and userRef are verified company assertions; anonymousRef is a
product-owned first-party pre-login ID. When both are present, customerRef must exactly equal accountRef
and exists only for durable Ask once consent compatibility. Never populate these values from a request header,
query parameter, name, email, prompt, tool argument, or unverified JWT payload.
For a journey-creation tool, create a canonical ID in the authenticated account’s registry before returning it.
On follow-up calls, resolve the argument candidate against that same account-owned registry. Repeated follow-ups,
cache hits, and deduplicated calls reuse the canonical product journey; a separate create gets a different ID.
Every completion nevertheless gets a fresh telemetry interaction UUID. A failed product result may remain linked
when the lookup proves ownership. Missing, malformed, unknown, and cross-account candidates return undefined
and remain unlinked. Typed argument and result schemas constrain candidates but do not prove ownership alone.
Use durable shared product storage in production; an in-memory map is suitable only for an instructional example.
Multi-tool journeys
Epode should observe the whole journey without asking for micro-feedback after every low-level action:includeToolsselects the product calls shown in Journeys.excludeToolsremoves health, admin, or internal tools.feedbackToolsselects outcome boundaries that receive feedback instructions. An empty array records interactions without asking for feedback.shouldRequestFeedbackcan inspect a completed result when the boundary is dynamic.sessionRefmust come from server/product state that proves continuity. Typed tool arguments and result fields may carry candidates, but caller headers and model-proposed IDs are not ownership evidence by themselves.accountRef/userRefcome from verified product authentication;anonymousRefcomes from a product-owned first-party ID.customerRefis only the durable Ask once compatibility subject and, when paired with richer identity, must exactly equalaccountRef. Without it, Ask once safely falls back to per-interaction permission.- Never ask is the most reliable mode on current Codex and Claude MCP clients. Ask once and Ask every time depend on client support for surfacing and resuming consent; treat them as experimental until you verify the exact client versions you support. Never infer approval when a client stops early.
instructions, near the beginning if you also
have product-specific instructions. Result-local metadata alone is not reliable across all MCP clients.
Epode adds a monotonic client sequence to preserve rapid tool order. The backend also reconciles a report
that arrives before its background interaction metadata.
Verify
From an MCP 2026-07-28 client:- Call
server/discover. - Call
tools/list. When feedback isoff, confirm only your product tools are present and stop. Otherwise, confirmrecord_product_feedback_consentandreport_product_feedbackare also present. - When feedback is enabled, call one normal product tool and confirm its result contains
_agentFeedback. - If the result has
state: "consent_required", ask its exact question and callrecord_product_feedback_consentwith onlyapprovedordeclined. Do not call the report tool unless an approved decision returnsstate: "feedback_ready"and a report action. - When the product result or approved decision returns
state: "feedback_ready", callreport_product_feedbackwith that action’sfeedbackHandle. - Confirm the interaction and report appear in Epode.
retryable: true, retry exactly once with the
same feedbackHandle and identical report. Capabilities are idempotent, so a lost success response cannot create
a second report.
The report tool allows 10 seconds by default; set reportTimeoutMs only for unusually slow private
deployments. Background telemetry uses bounded exponential retry and a longer timeout, but never blocks the
product tool result.
The legacy: "stateless" option retains a 2025-11-25 compatibility handshake without introducing
transport-session state.
View the runnable Node MCP example
Browser journey example ·
Documentation MCP example ·
Authenticated operations example