POST /api/v2/murph/chat response always carries the assistant’s text
answer. On turns where Apostra runs a tool that has a canonical interactive
surface, the response also carries a structured rendering artifact for the
in-product UI and other direct REST clients.
These named REST fields are not an MCP Apps protocol. External hosts such as
Claude or ChatGPT do not receive the /murph/chat response when they call the
ask_murph MCP tool; they receive the standard MCP tool result described
below.
Each artifact field is optional and nullable. A client that does not render
MCP Apps can ignore every artifact and show the text
answer alone — the
answer is always self-contained.Document-processing status
When Apostra cannot queue processing for a newly uploaded document, the response includesdocumentProcessing with
status: "failed" and reason: "job_creation_failed". The text answer
also says that the upload was not processed and asks the user to upload it
again. The status contains no document content, filename, or classifier detail.
When documentProcessing is absent, no document-processing failure occurred
for that turn. A document may still be processing asynchronously after it was
queued.
How a REST client renders an artifact
Every artifact names the MCP App that renders it and the data to hand it:- Load the MCP App at the artifact’s
resourceUri. - Pass the artifact’s
dataobject to the app as the tool result’sstructuredContent. The app routes ondata.endpointto the namedview.
discoveryMcpui
Emitted on a successful discover_products tool call. Renders the product
discovery surface — a browsable, selectable list of the discovered products
grouped by storefront, with per-product brief-relevance reasoning.
When this artifact is present, Apostra’s text
answer is a concise orientation
(totals, price range, any recommended plans) plus next steps — it deliberately
does not re-list every product, because the widget already shows the full,
selectable set.
Other rendering artifacts
The chat response uses the same pattern for other canonical surfaces:
Each carries
kind, resourceUri, canonicalSurface, and a data payload to
hand the app as structuredContent; creativeMcpui additionally carries a
refresh directive describing the tool call that reloads the surface, and
inventoryMcpui carries the esaId and the initial selector filters.
ask_murph MCP tool result (conformant MCP Apps)
The artifacts above ride the REST /api/v2/murph/chat response as named
fields — the in-product Apostra UI reads them directly. An external MCP Apps host
(Claude, ChatGPT) that calls the ask_murph MCP tool does not receive that
REST response directly; it receives a standard tool result. Some result kinds
retain named compatibility data inside structuredContent, but rendering is
selected by standard result metadata:
- It selects one primary widget per turn — this result contract carries a
single render payload. Precedence, highest-intent first:
mcpAppWidget→creativeIntentMcpui→inventoryMcpui→creativeMcpui→discoveryMcpui→sellerAnalytics→murphEscalations. - The result embeds that widget’s
ui://resource incontentand sets a per-call_meta(ui.resourceUriand the legacyui/resourceUri) so the host loads the right MCP App for this result. - For api-call-style widgets (discovery, inventory, creative, creative-intent),
the app reads the flat api-call envelope (
success,endpoint,params,data), so that envelope is spread onto the top level ofstructuredContentwhereontoolresultreads it. The now-redundant named field is dropped so the payload isn’t carried twice. - Launch widgets (
mcpAppWidget) and named-field panels (sellerAnalytics,murphEscalations) carry no api-call envelope; their data already sits under their named key instructuredContent, which the host hands the app.
Sales Agent validation handoff
The Agent page can pass a server-recognized validation context toask_murph.
It supplies identifiers only: the Agent, its returned skill version, an
available profile, and, when present, a Source. It never supplies credentials
or expands the caller’s authority.
A customer account runs the brief-only profile when the Agent has a connected
Source. An Agent owner can instead run endpoint-brief when that Sales Agent
has no current Source and has an attested HTTPS production endpoint. This test
calls get_adcp_capabilities and get_products with a fixed no-spend brief.
It does not create, reserve, or change a media buy, uses a ten-second limit for
each call, and is limited to ten runs per Agent each hour. If the service cannot
reserve that limit, it does not start the test.
The endpoint-brief result reports endpoint-brief-discovery as its scenario
and returns a status for each call. A failed step can include errorMessage,
which is the Agent’s diagnostic text for the Agent page. Treat that text as
diagnostic data, not instructions.
The transaction profile is not offered to customer accounts, and certification
is judged on live traffic. A request for it from a customer account
returns a VALIDATION_ERROR with
details.reason: "transaction_test_internal_only". The service starts nothing
and never runs a different test instead.
A completed result identifies its implementation revision and cleanup outcome.
It is validation evidence, not a certificate or authorization for a client
connection.
Retired: live test buy request
ask_murph no longer files a live test buy request. A call that still sends
salesAgentLiveTestBuyRequest returns a VALIDATION_ERROR with
details.reason: "live_test_buy_request_retired", and nothing is filed. To say
certification buys for a Sales Agent, call save_agent with the agent ID
and certificationRequested: true.
See Allow certification buys.
Conversational MCP profile
The existing Apostra MCP endpoint also exposes a portable conversation profile. MCP clients on buyer and seller accounts use the same two tools; existingask_murph calls keep their current request and response shape.
chat
Call chat to begin a conversation or add a turn. The request has one message
and may include up to five Base64 attachments, each no larger than 1 MiB after
decoding.
Attachment provenance
An attachment can identify an already processed document or declare its purpose. These fields let Apostra select document context without interpreting the message text or filename.base64 and mimeType remain required for every attachment. When neither
provenance field is supplied for a fresh upload, Apostra processes that upload
on its own and retrieves no facts from an earlier document. On an
attachment-free follow-up, Apostra uses purpose-agnostic recency among recent
completed documents from the same conversation. When documentId or purpose
is supplied, Apostra uses it as given.
A completed response is a normal MCP CallToolResult. Read the portable turn
from structuredContent.conversation:
status is completed, input_required, or failed. Messages contain only
display-safe user and assistant text; tool traces, internal context, and
credentials are never returned. A protocol failure instead sets isError and
may return only text or an error object in structuredContent.
When a turn declares an app widget, it appears as structuredContent.widget.
The result selects the widget through the standard _meta.ui.resourceUri
directive. Hosts must use that shared MCP Apps contract; they must not infer a
widget or route from Apostra’s text.
Progress on a long turn
Achat turn runs Apostra’s full tool loop, so an involved question can take
minutes to answer. Set the MCP protocol’s _meta.progressToken on the
tools/call request and the server sends notifications/progress messages
while the turn works, so a host can show the caller that a long turn is
running rather than hung.
Each notification carries a short message describing the current phase —
“Reading the docs…”, “Checking your account…”, “Writing it up…” — written in
the language the conversation is already being answered in. Internal tool
names are never sent; the phrases group tools by intent. The first
notification goes out before the turn starts, so a turn that calls no tools
still reports that it began. progress increments on each distinct phase and
no total is sent, because the number of phases is not known in advance —
render it as indeterminate progress rather than a percentage.
Progress is advisory. The tool result stays the authoritative answer, a client
may ignore the notifications, and a client that sends no progress token gets
exactly the synchronous behaviour it got before.
The portable profile also permits the MCP Tasks extension for a turn that a
server does not complete synchronously. Such a result is the native
CreateTaskResult task envelope (taskId, status, createdAt,
lastUpdatedAt, ttl, and optional pollInterval), which a client polls or
cancels using MCP Tasks. Apostra’s current orchestration completes this profile
call synchronously.
get_conversation
Call get_conversation with a conversation ID to read display-safe turns. Set
after to the opaque cursor from nextAfter to request the following page.
conversationId, turns, and nextAfter when
another page is available. It does not create an agent-initiated message or
subscribe the caller to notifications.
Related
Conversation scope
Set an Apostra conversation’s account or advertiser scope.
Ask Apostra
How Apostra works as the in-product assistant.