Skip to content

Privacy & Redaction

The SDK captures MCP activity inside the vendor server process, then applies Within privacy rules before anything leaves that process. The Within API runs server-side Presidio redaction before SDK activity is stored. CRM connectors must create subjects locally and omit raw CRM identities before delivery.

The TypeScript and Python SDKs apply identical redaction rules and identical subject hashing — the key patterns below, the value patterns, and the hash construction are byte-for-byte equivalent and verified by cross-language parity tests.

What is captured

With the default configuration, the SDK captures:

  • initialize, tools/list, and tools/call lifecycle events
  • tool name, timing, success or error state, and MCP client/server metadata
  • tool arguments and tool responses after redaction and truncation
  • get_more_tools feedback, stored outside SDK lead scoring
  • the subject created locally from identify().userId, when available
  • redacted userData traits returned from identify()

Raw userId and userName are never sent. userId is hashed locally with vendorSlug; userName is dropped.

Default redacted keys

Any object key containing these substrings is removed or replaced with [redacted]:

Pattern
auth
authorization
cookie
email
e_mail
full_name
identity
firstname
first_name
lastname
last_name
org_domain
orgdomain
phone
requester_name
author_name
customer_name
user_name
username
user_id
userid
token
secret
password
api_key
apikey
access_token
refresh_token
ssn
card
cvv

Matching is case-insensitive and treats spaces or dashes as underscores. Common personal-data and secret-like string values, including email addresses, phone-like strings, SSNs, card-like strings, bearer tokens, and API-key-like values, are also redacted.

Server-side Presidio redaction

Within runs a separate server-side redaction service before writing SDK activity or CRM outcomes to storage. This service uses Microsoft's Presidio analyzer and anonymizer APIs with a model-free recognizer profile. It does not download or run spaCy, transformer, or LLM models.

The server-side profile is rule/checksum/pattern based. It looks for values such as:

  • email addresses
  • phone numbers
  • SSNs, ITINs, passport-like identifiers, MBI, NPI, and other US identifier patterns
  • credit-card-like values, bank/routing-number-like values, IBANs, crypto addresses
  • URLs and IP addresses
  • bearer tokens, API keys, secrets, and password-like assignments

Detected values are replaced with typed placeholders such as <EMAIL_ADDRESS>, <PHONE_NUMBER>, <US_SSN>, or <API_KEY>. Within stores counts-only redaction metadata, such as entity counts, redaction profile, service version, and latency. It does not store original spans or raw values.

Because the Presidio profile is model-free, arbitrary names, locations, and free-form person references are not a guaranteed detection target unless they match a configured pattern or sensitive field key.

Additional redaction

Add domain-specific key redaction with privacy.redactKeys. You can also provide text redaction with redactSensitiveInformation.

redactSensitiveInformation is called once per string value in the event — it receives individual strings (a tool argument, a response fragment, a trait), never the event object. Return the string, transformed or not. Two behaviors to know:

  • If your redactor throws, the event is dropped rather than sent unredacted (fail closed). Your server is unaffected, but the event is gone — wrap risky logic and return a placeholder instead of letting errors escape.
  • Returning None/undefined or a non-string is treated as a no-op for that value; the built-in pipeline still applies.
ts
track(server, 'acme', {
  apiKey: process.env.WITHIN_SDK_API_KEY!,
  privacy: {
    redactKeys: ['license_key', 'private_note', 'internal_cost'],
  },
  redactSensitiveInformation: async (text) =>
    text.replace(/tenant-secret-[a-z0-9]+/gi, '[redacted]'),
});
python
import re

from within_sdk import PrivacyOptions

track(server, "acme", WithinOptions(
    privacy=PrivacyOptions(
        redact_keys=["license_key", "private_note", "internal_cost"],
    ),
    redact_sensitive_information=lambda text: re.sub(
        r"tenant-secret-[a-z0-9]+", "[redacted]", text, flags=re.IGNORECASE
    ),
))

Within privacy sanitization runs after custom redaction and before the Within API or vendor exporters. Server-side Presidio redaction runs again before Within storage.

CRM outcome privacy

The upcoming Salesforce connector sends subjects and normalized lifecycle fields, not raw Salesforce identities or record IDs. The CRM outcomes API rejects direct identity and record fields, limits metadata to scalar values, and applies server-side redaction before storage. See Salesforce CRM outcomes for the exact request constraints and availability.

Binary and large payload handling

The SDK omits or truncates values that are risky or too large.

Data shapeBehavior
ArrayBuffer or typed arrayReplaced with [binary omitted].
Circular referenceReplaced with [circular].
Function or symbolOmitted.
BigIntSerialized as a string.
ErrorCaptures name and redacted truncated message.
ArraysLimited to first 100 entries.
Long stringTruncated above privacy.maxFieldBytes, default 32768.
Large captured payloadReplaced with an omission marker above privacy.maxEventBytes, default 128000.

Exporters

Datadog, Sentry, PostHog, and OTLP exporters (TypeScript SDK only) receive sanitized events only. Exporters do not receive raw userId, userName, direct identity fields, tokens, or unredacted MCP payloads from the SDK.

Within SDK turns privacy-safe MCP activity into workflow intelligence and SDK leads.