{
  "name": "text.offset",
  "group": "text",
  "summary": "Convert between text positions: byte offset, UTF-16 index, code point, grapheme, and line/column.",
  "description": "One position in a string has at least five different numbers depending on who is counting. A UTF-8 byte offset, a UTF-16 code unit index (what JavaScript and LSP use), a code point index (what Python uses), a grapheme index (what a human means by 'the third character'), and a line/column pair. An emoji is one grapheme, two code units and four bytes; a flag is one grapheme and eight bytes. Models are measurably poor at character-level positions — benchmark accuracy around 43% — and worse when the queried character sits inside a long token. This converts between all of them exactly, so an edit lands where it was meant to.",
  "keywords": [
    "byte offset",
    "line and column",
    "utf-8 vs utf-16",
    "code point index",
    "grapheme",
    "lsp position",
    "character position",
    "string index",
    "cursor position",
    "off by one"
  ],
  "endpoint": "https://fluentedi.com/v1/text/offset",
  "mcp_name": "text_offset",
  "makes_network_request": false,
  "parameters": {
    "type": "object",
    "properties": {
      "text": {
        "description": "The text to measure positions in.",
        "type": "string",
        "maxLength": 500000
      },
      "position": {
        "description": "The position to convert. Interpreted according to `unit`.",
        "type": "integer",
        "default": 0,
        "minimum": 0
      },
      "unit": {
        "description": "Which counting system `position` is expressed in.",
        "type": "string",
        "enum": [
          "utf16",
          "byte",
          "codepoint",
          "grapheme"
        ],
        "default": "utf16"
      },
      "line": {
        "description": "Convert FROM a line/column pair instead of `position`. 1-based.",
        "type": "integer",
        "default": 0,
        "minimum": 0
      },
      "column": {
        "description": "Column for `line`. 1-based, counted in `unit`.",
        "type": "integer",
        "default": 1,
        "minimum": 1
      }
    },
    "required": [
      "text"
    ],
    "additionalProperties": false
  },
  "examples": [
    {
      "description": "Where is UTF-16 index 5 in every counting system?",
      "url": "https://fluentedi.com/v1/text/offset?text=caf%C3%A9%20%E2%98%95%20done&position=5&unit=utf16",
      "args": {
        "text": "café ☕ done",
        "position": 5,
        "unit": "utf16"
      }
    },
    {
      "description": "Convert a line/column pair to an offset",
      "url": "https://fluentedi.com/v1/text/offset?text=one%0Atwo%0Athree&line=2&column=2",
      "args": {
        "text": "one\ntwo\nthree",
        "line": 2,
        "column": 2
      }
    },
    {
      "description": "Measure a string containing an emoji",
      "url": "https://fluentedi.com/v1/text/offset?text=a%F0%9F%91%A8%E2%80%8D%F0%9F%91%A9%E2%80%8D%F0%9F%91%A7b&position=1&unit=grapheme",
      "args": {
        "text": "a👨‍👩‍👧b",
        "position": 1,
        "unit": "grapheme"
      }
    }
  ],
  "suggestion": null
}