[Agent Tools](https://tools.openkrill.app/) / [Blog](https://tools.openkrill.app/blog)

# Why a 134-character text takes 3 SMS segments: counting words, UTF-8 bytes and GSM-7 vs UCS-2 segments

A 134-character message looks well inside the classic 160-character SMS limit. Add one party-popper emoji, though, and carriers switch the whole message from 7-bit GSM-7 to 16-bit UCS-2 encoding. That splits the 134-character confirmation text across 3 billed SMS segments. We ran a real restaurant booking message through the [Document Tools](https://tools.openkrill.app/tools/docs) MCP server, Twilio's segment calculator and Python's `textstat` library to compare what each one counts.

By OpenKrill. Published 2026-10-20. All counts below come from live runs on 2026-10-19 and 2026-10-20 against `https://docs.openkrill.app/mcp`, `@twilio-labs/sms-segments-calculator` and Python `textstat`.

## The short answer

Send any string to `count_text` on the free [Document Tools](https://tools.openkrill.app/tools/docs) MCP server (`https://docs.openkrill.app/mcp`) and one response gives you the word count, character count (with and without spaces), Unicode grapheme clusters, UTF-8 byte length, sentence, paragraph and line counts, silent reading time (`reading_minutes`), Flesch Reading Ease, Flesch-Kincaid Grade Level, and the SMS segment breakdown.

On our 134-character booking confirmation, which contains Café Lumière and one emoji (`🎉`), `count_text` returned 29 words, 3 sentences, 139 UTF-8 bytes, `reading_minutes: 0.1`, Flesch Reading Ease 97.2 (grade 1.9), and 3 UCS-2 SMS segments (135 UTF-16 code units at 67 units per concatenated segment). Replacing that one emoji with a period dropped the message to 1 GSM-7 segment of 133 septets, with 27 characters to spare.

## The test string and how 3 tools measured it

We tested this exact restaurant confirmation message on 2026-10-19:

```
Hi Ana! Your table for 4 at Café Lumière is booked for Friday 7:30 pm 🎉 Reply YES to confirm or call us if plans change. See you soon!
```

Here is what each tool returned on that input:

| Tool | Time | Words and sentences | Characters and bytes | Readability | SMS encoding and segments |
| --- | --- | --- | --- | --- | --- |
| [Document Tools](https://tools.openkrill.app/tools/docs) (`count_text`) | 1,039 ms | 29 words, 3 sentences, 1 paragraph | 134 characters (106 without spaces), 134 graphemes, 139 UTF-8 bytes | Flesch Reading Ease 97.2 (very easy), Flesch-Kincaid grade 1.9 | UCS-2, 135 units, 3 segments (67 per segment) |
| Twilio `@twilio-labs/sms-segments-calculator` | 1,997 ms | Not supported | 135 UCS-2 code units (1,080 bits) | Not supported | UCS-2, 135 units, 3 segments |
| Python `textstat` | 2,429 ms | 28 words; sentence count failed without external `nltk_data` | 134 characters (106 without spaces) | Failed without `punkt_tab` download | Not supported |

We also fetched WordCounter.net on the same day. Its static HTML page serves `0 words` and `0 characters` unless you run a browser with client-side JavaScript.

## Why 134 characters became 135 UTF-16 units and 3 SMS segments

Three encoding rules stack up on this one message:

1. **GSM-7 vs UCS-2 switching.** Standard SMS uses the 7-bit GSM 03.38 alphabet, which fits 160 septets into one 140-byte SMS frame. As soon as a message contains even one character outside GSM-7 (here `🎉`, U+1F389), the carrier re-encodes the entire message as 16-bit UCS-2, where a single-part SMS holds at most 70 UTF-16 code units.
2. **Surrogate pairs above U+FFFF.** Our string has 134 user-perceived characters (grapheme clusters), but `🎉` sits in the astral plane (U+1F389) and takes a UTF-16 surrogate pair of 2 code units. That brings the UCS-2 unit count from 134 to 135. It also brings the UTF-8 byte count to 139: 131 ASCII bytes, 2 bytes each for é and è, and 4 bytes for `🎉`.
3. **User Data Header (UDH) overhead on split messages.** Once a UCS-2 message goes past 70 units, each segment reserves 6 bytes (3 UTF-16 units) for the concatenation header that tells the handset how to reassemble the text. So each segment carries 67 units instead of 70. Two segments hold 134 units (`67 × 2 = 134`), which means unit 135 spills into a 3rd segment.

## Dropping the one emoji cuts the cost from 3 segments to 1

A common misconception is that the French accents in Café Lumière also force UCS-2 encoding. They don't. Both lowercase é (U+00E9) and lowercase è (U+00E8) are in the basic GSM 03.38 7-bit character table, at 1 septet each. On 2026-10-20 we replaced `🎉` with a period and ran `count_text` again:

| Version | Characters | Words and sentences | UTF-8 bytes | SMS encoding | SMS units | Segments |
| --- | --- | --- | --- | --- | --- | --- |
| With `🎉` emoji | 134 (106 no spaces) | 29 words, 3 sentences | 139 bytes | UCS-2 | 135 UTF-16 units (67 per segment) | 3 segments |
| Emoji replaced by `.` | 133 (106 no spaces) | 28 words, 4 sentences | 135 bytes | GSM-7 | 133 septets (160 per segment) | 1 segment |

To run the same check from any MCP client or script, send this JSON-RPC call to [Document Tools](https://tools.openkrill.app/tools/docs) (see also the [ChatGPT integration guide](https://tools.openkrill.app/integrations/docs/chatgpt) and the [Claude integration guide](https://tools.openkrill.app/integrations/docs/claude)):

```
{"method":"tools/call","params":{"name":"count_text",
 "arguments":{"text":"Hi Ana! Your table for 4 at Café Lumière is booked for Friday 7:30 pm. Reply YES to confirm or call us if plans change. See you soon!"}}}
```

## Frequently asked questions

### Why does a 134-character message take 3 SMS segments instead of 1?

Any character outside the GSM-7 alphabet, such as an emoji, switches the entire SMS to UCS-2 (16-bit) encoding. A single UCS-2 message holds at most 70 UTF-16 code units. A multi-part UCS-2 message holds 67 units per segment, because 6 bytes go to the concatenation header. Our message comes to 135 UTF-16 units (133 characters plus 2 units for the emoji surrogate pair), and 135 divided by 67 rounds up to 3 segments.

### Do French accents like é and è force UCS-2 encoding?

Not when they are part of the standard GSM 03.38 7-bit alphabet. Lowercase é and è each take 1 septet in the basic GSM-7 table, so our 133-character booking text for Café Lumière stays in 1 GSM-7 segment once the emoji is removed.

### Why did Python textstat count 28 words while Document Tools counted 29?

Document Tools counts runs of non-whitespace separated by whitespace, so it counts the standalone emoji token between 7:30 pm and Reply as 1 word. Python textstat returned 28 words on the same string because it excludes the standalone emoji token from its word count.

### What else does the Document Tools MCP server do besides count_text?

The server at https://docs.openkrill.app/mcp provides 4 tools with no API key (up to 60 calls a minute per network): count_text, make_calendar_file (RFC 5545 .ics calendar invites), make_csv_file (RFC 4180 CSV export from JSON rows) and translate_page (bilingual or translated HTML and Markdown documents).

## Sources

- Text, readability and SMS segment runs: [Document Tools MCP server](https://tools.openkrill.app/tools/docs) (`https://docs.openkrill.app/mcp`), read on 2026-10-19 and 2026-10-20.
- GSM 03.38 character set and SMS concatenation specification: [3GPP TS 23.038 / ETSI TS 123 038](https://www.etsi.org/deliver/etsi_ts/123000_123099/123038/) (Alphabets and language-specific information) and [3GPP TS 23.040](https://www.etsi.org/deliver/etsi_ts/123000_123099/123040/) (Technical realization of the Short Message Service).
- Twilio segment benchmark: [`@twilio-labs/sms-segments-calculator`](https://github.com/twilio-labs/sms-segments-calculator), run via Node.js on 2026-10-19.
- Python readability benchmark: [`textstat` on PyPI](https://pypi.org/project/textstat/), run on 2026-10-19.
