Skip to main content
When a Digital Twin calls your MCP server, Pria adds headers that say which signed-in user the call is for, and signs them with the twin’s MCP Signing Secret. The identity comes from the user’s login session, not from anything the AI wrote, so a crafted prompt cannot change it. Check the signature on your server and you can safely answer per-user questions: a learner’s own progress, their quiz results, their tutor’s notes. This page is the complete contract: the headers, the rule that produces the signature, verifiers for Node and Python, and a worked example you can check your own code against.

The headers

Header names can arrive in any letter case, so match them case-insensitively.
When Pria calls your server itself (every conversation model except OpenAI models with native MCP support), each tool call is signed on its own, with a fresh nonce and timestamp. On OpenAI models with native MCP support the headers are fixed when Pria builds the tool list and reused for every call that list serves: once per reply in chat, and for the life of the voice session’s cached context in voice and realtime voice, so a long uninterrupted voice session can reach the 60-minute window. Several calls can therefore carry the same nonce, so do not reject a repeated nonce as a replay.

The rule

  1. Take the seven headers listed above that are present, except x-pria-signature: six fields, or seven when the email is sent. Ignore any other x-pria-* header.
  2. Lowercase each header name and sort the fields by that name.
  3. Write each field as name=value and join them with a single newline (\n). No trailing newline.
  4. Compute HMAC-SHA256 of that string (UTF-8) with the MCP Signing Secret as the key. Use the secret exactly as the text shown in the admin screen: do not hex-decode it.
  5. Accept the call only if |now − x-pria-timestamp| ≤ 60 minutes, and the lowercase hex digest equals x-pria-signature (compared in constant time).
x-pria-user-id, x-pria-institution-id, x-pria-server-label, x-pria-timestamp and x-pria-nonce must all be present. A request that carries only the email, user id and Digital Twin id, with no signature, was sent unsigned, for example because the twin has no signing secret yet. Treat it as unverified.

Verifiers

Both check staleness first and the signature second, the same order Pria’s own verifier uses, so the reason codes match. Each returns { ok: true, keyId } or { ok: false, reason }.

Worked example

Run your verifier on this input before you point it at real traffic. The secret is a dummy: never use it for anything else. Signing secret (64 characters, used as text):
Headers received:
The string that is signed, the seven fields sorted by name and joined by newlines:
The expected signature is 856beaf60e86b85ebff5785af750ba845b5a55559400115c172ab1bfd1d92e5f, and the key id of this secret is a8ae6e6e. The timestamp is September 16, 2025, so checked against today’s clock the example returns stale. That is correct. To reproduce the pass, give the verifier a clock one second after the timestamp: now: 1758000001000 in Node, now_ms=1758000001000 in Python. It then returns ok with key id a8ae6e6e. Change any header value and it returns bad-signature.

Which secret

Two secrets sit side by side in the Digital Twin’s settings, and only one of them verifies these headers.
  • MCP Signing Secret: what Pria signs the identity headers with when it calls your connector (outbound). Use this one.
  • MCP Server Secret (labelled MCP Secret in some versions of the editor): what an MCP client sends to call the Digital Twin’s own MCP server (inbound). It is not used for signing, and verifying with it always fails with bad-signature.
Ask the Digital Twin’s administrator to share the MCP Signing Secret through a secure channel, not email.

Rotation

Regenerating the MCP Signing Secret takes effect immediately. Pria never signs with the old and new secrets at the same time, so plan the change together with the twin’s administrator:
  1. Pick a quiet hour and have the administrator regenerate the secret and send you the new value.
  2. Install the new secret. The new x-pria-key-id tells you which calls it signed.
  3. A realtime voice session that started before the change keeps the headers it began with. For the next 60 minutes, accept either secret, choosing by x-pria-key-id, then remove the old one.
The key id of a secret is the first 8 hex characters of its SHA-256, so you can compute it yourself: in the worked example, the dummy secret’s key id is a8ae6e6e.