> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sedata-ai.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# PII sanitization

> Keep emails, phone numbers, and card numbers out of your telemetry.

`@sedata-ai/mcp` exposes two layers for keeping sensitive data out of spans:

1. **`enableArgumentCollection: false`** (the default) — tool arguments are
   never written to span attributes.
2. **`dataProcessors`** — full control over every attribute set before export.

## The default is safe

```ts theme={null}
const config: TelemetryConfig = {
  serverName: 'my-server',
  serverVersion: '1.0.0',
  // enableArgumentCollection defaults to false
}
```

With this config, your spans contain tool name, request id, session id,
client address, status, and duration — but **not** the arguments. This is the
recommended default for any tool that handles user input.

## Turning on argument collection

If you really want to see arguments in traces (e.g. for an internal tool
chain), turn it on explicitly:

```ts theme={null}
const config: TelemetryConfig = {
  // ...
  enableArgumentCollection: true,
}
```

Each argument is flattened into a `mcp.request.argument.<key>` attribute.

<Warning>
  Once on, **every** registered tool emits args. There's no per-tool toggle.
  Pair this with `dataProcessors` if you need redaction.
</Warning>

## Custom redaction with `dataProcessors`

Data processors run on every attribute set before export. They're the right
hook for "scrub anything that looks like email/phone/card."

```ts theme={null}
const PII_PATTERNS = [
  { name: 'email', re: /\b[\w.+-]+@\w[\w.-]+\.\w+\b/g, replace: '[REDACTED:email]' },
  { name: 'phone', re: /\b\+?\d[\d\s().-]{7,}\d\b/g, replace: '[REDACTED:phone]' },
  { name: 'card', re: /\b(?:\d[ -]*?){13,16}\b/g, replace: '[REDACTED:card]' },
]

const redact = (data: Record<string, any>) => {
  const out: Record<string, any> = {}
  for (const [k, v] of Object.entries(data)) {
    if (typeof v !== 'string') {
      out[k] = v
      continue
    }
    let s = v
    for (const p of PII_PATTERNS) s = s.replace(p.re, p.replace)
    out[k] = s
  }
  return out
}

const config: TelemetryConfig = {
  // ...
  dataProcessors: [redact],
}
```

Processors compose left to right. You can chain redaction, hashing, allow-list
filtering, etc.

## What about the safety check?

`safetyCheck` writes the **original** content under
`mcp.safety_check.content` so analysts can debug what was flagged. If you'd
rather hash it:

```ts theme={null}
import { createHash } from 'node:crypto'

const hashSafetyContent = (data: Record<string, any>) => {
  if (typeof data['mcp.safety_check.content'] === 'string') {
    data['mcp.safety_check.content'] = createHash('sha256')
      .update(data['mcp.safety_check.content'])
      .digest('hex')
      .slice(0, 16)
  }
  return data
}
```

## Verify your redaction

Easiest way: switch to console exporter and run a few representative calls.

```ts theme={null}
const config: TelemetryConfig = {
  // ...
  exporterType: 'console',
  enableArgumentCollection: true,
  dataProcessors: [redact],
}
```

Look at the printed spans — every PII pattern should already be replaced.

## What `enablePIISanitization` does today

`config.enablePIISanitization` defaults to `true` and is reserved as a hook
for built-in sanitization patterns. The current build leaves the actual
matching to your own `dataProcessors` — keep yours wired even if you set
`enablePIISanitization: true`.

## Next

<CardGroup cols={2}>
  <Card title="Data processors" icon="filter" href="/guides/data-processors">
    The full processor API and ordering rules.
  </Card>

  <Card title="Tracing reference" icon="route" href="/concepts/tracing">
    Every attribute the package writes.
  </Card>
</CardGroup>
