Skip to main content
Outcome: You can encrypt OAuth tokens so clients only see token ciphertexts instead of plaintext Bearer tokens. This example focuses on Notion MCP OAuth, but this approach should extend to any OAuth DCR flow.
Prerequisites: The Formal Endpoint transparent proxy on macOS. Use Secure Enclave encryption on the Endpoint, or forward traffic to a with token encryption.

Overview

Notion MCP (https://mcp.notion.com/mcp) issues OAuth tokens from the same host (POST /token). Clients then send Authorization: Bearer <token> on MCP calls. Formal can encrypt those tokens in two places: In both cases:
  1. An encrypt encrypts access_token and refresh_token on the OAuth token response.
  2. The MCP client stores formalsealed:v1:....
  3. A decrypt policy decrypts Authorization before Notion MCP sees the request.
The MCP client keeps using https://mcp.notion.com. You do not rewrite the MCP URL.

Policies

These policies apply whether encryption runs on the Endpoint or the Connector.

Encrypt OAuth tokens

  1. Navigate to Policies
  2. Click Create Policy
  3. Set name to notion-mcp-encrypt-oauth-tokens and add a description
  4. Paste the Rego below into the editor
  5. Click Create Policy to save

Decrypt Authorization on MCP requests

  1. Navigate to Policies
  2. Click Create Policy
  3. Set name to notion-mcp-decrypt-authorization and add a description
  4. Paste the Rego below into the editor
  5. Click Create Policy to save
Decrypt supports embedded encrypted tokens, so Bearer formalsealed:v1:... becomes Bearer <plaintext> for Notion. Values without an encrypted token pass through unchanged.
Encrypt is fail-closed. If encryption fails, Formal blocks the response. If decryption fails, Formal forwards the request unchanged.

Encrypt on the Formal Endpoint (Secure Enclave)

On macOS, the Formal Endpoint can encrypt and decrypt tokens with the device Secure Enclave. Traffic stays on the Endpoint. You do not set Forward to Connector. Create a network rule that matches Notion MCP. No Connector listener or KMS key is required for this path.
  1. Navigate to Network Rules (or use Network Rules in the Desktop App)
  2. Create a rule named notion-mcp-endpoint
  3. Match hostname mcp.notion.com on the pre-TLS condition
  4. Leave Forward to Connector unset / false
  5. Save the rule
Verify:
  1. Enable the transparent proxy: formal transparent-proxy enable
  2. Confirm the network rule is Active
  3. Complete Notion MCP OAuth. Confirm access_token starts with formalsealed:v1:
Endpoint encrypt is macOS-only. See Encrypt & Decrypt.

Encrypt on the Connector

To encrypt tokens on the Connector, forward Notion MCP through a Connector and attach a token encryption key (formal_connector_token_encryption_key). Without that KEK, Connector encrypt fails closed and blocks the OAuth response. Set up the following:
  1. An MCP resource for mcp.notion.com
  2. A Connector listener and listener rule so the resource is reachable
  3. A token encryption key on the Connector (cloud KMS KEK)
  4. A network rule with forward_to_connector set to true
  1. Create an MCP resource for hostname mcp.notion.com on port 443
  2. On your Connector, add a listener on port 443 and a listener rule that points at that resource
  3. Attach a token encryption key to the Connector
  4. Create a network rule named notion-mcp-connector that matches mcp.notion.com and sets Forward to Connector
See Token Encryption for GCP and Azure KEK setup and IAM requirements.
Forward to Connector must be true. Without it, the Endpoint does not send Notion traffic to the Connector for KMS-backed encrypt and decrypt.
Verify:
  1. Notion MCP appears under the Connector’s Reachable Resources
  2. The Connector has a token encryption key configured
  3. The network rule is Active with Forward to Connector
  4. Complete Notion MCP OAuth. Confirm encrypted formalsealed:v1: tokens and successful MCP tool calls

Add Notion MCP and complete OAuth

After policies and routing are in place, add Notion MCP as usual. Formal does not change Dynamic Client Registration (DCR) or the OAuth browser flow.
  1. Add the Notion MCP server at https://mcp.notion.com/mcp. For Claude Code:
You can also add it from the Formal console registry or another MCP client config.
  1. Complete DCR and OAuth when the client prompts you.
Network rules keep intercepting mcp.notion.com, so Claude Code still uses the real Notion URL. Encrypted tokens are what the client persists after /token.

Verify end to end

  1. Keep the MCP client pointed at https://mcp.notion.com/mcp.
  2. Finish OAuth through Claude Code (or your MCP client).
  3. Call a Notion MCP tool. The call should succeed.
  4. Confirm encrypt on POST /token and decrypt on later MCP requests in Formal logs.

Inspect encrypted tokens in Claude Code (macOS)

Claude Code stores MCP OAuth credentials in the macOS Keychain. After OAuth completes, read that entry and confirm Notion tokens are encrypted:
Look under the Notion server entry (key shape like notion|<id>). Both accessToken and refreshToken should start with formalsealed:v1::
Verify:
If accessToken is a raw Notion token instead of formalsealed:v1:..., encrypt did not run on POST /token (check the network rule, policies, and Connector token encryption key if you use the Connector path).
Keychain entry names can vary by Claude Code version. If the command finds no password, search Keychain Access for Claude credentials or re-run OAuth after confirming the transparent proxy is enabled.

Troubleshooting

Possible causes:
  • Transparent proxy or network rule is inactive
  • Connector path missing forward_to_connector or KEK
  • Policy path does not match POST /token
Fix:
  1. Run formal transparent-proxy status
  2. Set the network rule to Active
  3. For Connector encryption, confirm the token encryption key, listener rule, and forward_to_connector
Possible causes:
  • Encrypt ran, but decrypt did not match mcp.notion.com
  • Encrypted token came from a different Endpoint or Connector KEK
  • Client truncated the formalsealed:v1: value
Fix:
  1. Confirm the decrypt policy matches input.resource.hostname == "mcp.notion.com"
  2. Re-authenticate after rotating keys
  3. Keep the full encrypted string in Authorization: Bearer ...

Next Steps

MCP Resources

Proxy and govern MCP traffic

Token Encryption

Configure the Connector KMS KEK

Encrypt & Decrypt

Full action reference

Desktop App

Enable the transparent proxy