# Punycode / IDN

> Convert internationalised domain names to xn-- form and back.

Category: Encoding · Page: https://util.caruana.me/tools/punycode/

## API, MCP and A2A

### Punycode: To ASCII (`punycode_encode`)

`GET|POST https://util.caruana.me/api/punycode/encode` · MCP tool `punycode_encode` · A2A skill `punycode_encode` · [reference](https://util.caruana.me/reference/punycode_encode/)

`domain` converts each non-ASCII label to `xn--` form (dots and email local parts preserved). `raw` applies bare RFC 3492 Punycode with no prefix.

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `input` | string | yes | Text to convert. As a `text/plain` POST body it is used verbatim. |
| `mode` | domain \| raw | no | `domain`: Converts each label of a domain or email: münchen.de ↔ xn--mnchen-3ya.de. `raw`: Bare RFC 3492 Punycode with no xn-- prefix: münchen ↔ mnchen-3ya |

Privacy: The server receives the `input` text and `mode`, converts it in memory inside the request, and returns the result. Your input is not stored or logged; only the per-call metadata row (operation, time, status, sizes) is recorded. Send secrets in a POST body, not the query string: URLs are recorded in Cloudflare request logs, proxies, and shell history.

### Punycode: To Unicode (`punycode_decode`)

`GET|POST https://util.caruana.me/api/punycode/decode` · MCP tool `punycode_decode` · A2A skill `punycode_decode` · [reference](https://util.caruana.me/reference/punycode_decode/)

`domain` decodes each `xn--` label. `raw` decodes a bare Punycode string.

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `input` | string | yes | Text to convert. As a `text/plain` POST body it is used verbatim. |
| `mode` | domain \| raw | no | `domain`: Converts each label of a domain or email: münchen.de ↔ xn--mnchen-3ya.de. `raw`: Bare RFC 3492 Punycode with no xn-- prefix: münchen ↔ mnchen-3ya |

Privacy: The server receives the `input` text and `mode`, converts it in memory inside the request, and returns the result. Your input is not stored or logged; only the per-call metadata row (operation, time, status, sizes) is recorded. Send secrets in a POST body, not the query string: URLs are recorded in Cloudflare request logs, proxies, and shell history.


## User guide

The tool sits in a window that works like a desktop app: menus on the top row and the **full-screen** icon at its right (**Esc** leaves full screen). The set-up options described below are in **Settings → Options…**, an overlay, and the **Settings** menu also lists the common choices directly. To keep the options in view instead, choose **View → Inline config**; the page remembers that choice. **Help → Keyboard shortcuts…** lists the keys.

**To ASCII** converts an internationalised domain name to the form DNS actually stores: `münchen.de` → `xn--mnchen-3ya.de`. **To Unicode** reverses it.

- **Domain (IDN)** mode works label by label and preserves dots. It also handles email addresses: only the domain part is converted.
- **Raw** mode applies bare Punycode with no `xn--` prefix. You rarely need it except to debug encoders.

**Spotting look-alike domains.** If a link shows a familiar name but its ASCII form contains `xn--`, it includes non-ASCII characters. Paste the address here, convert **To ASCII**, and compare. For example `аpple.com` written with a Cyrillic `а` becomes `xn--pple-43d.com`.

## Specification

- **Algorithm.** RFC 3492 Punycode (Bootstring with the IDNA parameters). Labels containing non-ASCII characters get the `xn--` ACE prefix (RFC 5890).
- **Domain mode.**
  - **Splitting.** Labels are split on `.` and on the IDNA2003 alternative dots `。 ． ｡`.
  - **ASCII labels** pass through unchanged.
  - **Email addresses.** The local part, before `@`, is never converted.
- **What this tool does not do.**
  - No Unicode normalisation (NFC).
  - No case folding.
  - No IDNA2008 / UTS #46 validity checks (disallowed characters, bidi rules, label length limits).
  - So it converts strings that a registry would refuse. Use it to _inspect_ names, not to validate registrations.
- **Errors.** **To Unicode** fails on malformed Punycode (invalid digits, overflow). **To ASCII** fails on input that can't be represented, such as a lone surrogate.
