Tools for agents: Strict URL query pair parser

Decode UTF-8 query pairs while preserving duplicate keys, blank values and order, then return a canonical encoded query.

Connect with MCP

{
  "mcpServers": {
    "super-agent-tools": {
      "url": "https://app.getsupers.com/sites/agent-query-pairs/mcp"
    }
  }
}
Streamable HTTP · No API key · Stateless

Use this URL in any client supporting remote Streamable HTTP MCP. Calls return structured JSON; inputs are processed without persistence.

Call parse_query_pairs

Pass only the raw query component, without a leading question mark or fragment. No URL is fetched or validated; question marks, hash characters and semicolons in the supplied text are ordinary data. Ampersand separates pairs and the first equals sign separates key from value. Plus decodes to space; %2B decodes to literal plus. Percent escapes must have exactly two hexadecimal digits and the resulting bytes must be valid UTF-8. Literal Unicode is accepted but is not normalized. Keys are case-sensitive. Empty input has no pairs; empty segments in nonempty input are preserved as empty-key, empty-value pairs. Bare keys have empty values with had_equals=false. Canonical output preserves pair order and duplicates, adds equals to every pair, uses uppercase percent escapes, and encodes spaces as %20. It normalizes syntax, not meaning for arbitrary servers, and is not intended for signing schemes requiring original bytes. values_by_key groups values in encounter order; duplicate_keys lists repeated keys in first-encounter order. Encoded delimiters are decoded only after splitting and decoding happens once. Control characters and embedded null are preserved as data. Standard-library behavior: https://docs.python.org/3/library/urllib.parse.html#urllib.parse.unquote_to_bytes

Input and output schemas
{
  "name": "parse_query_pairs",
  "title": "Strict URL query pair parser",
  "description": "Decode a raw form-style query into ordered pairs without collapsing duplicates; reject malformed percent escapes and invalid UTF-8.",
  "inputSchema": {
    "properties": {
      "query": {
        "title": "Query",
        "type": "string"
      }
    },
    "required": [
      "query"
    ],
    "title": "parse_query_pairsArguments",
    "type": "object"
  },
  "outputSchema": {
    "additionalProperties": true,
    "title": "parse_query_pairsDictOutput",
    "type": "object"
  },
  "icons": null,
  "annotations": {
    "title": null,
    "readOnlyHint": true,
    "destructiveHint": false,
    "idempotentHint": true,
    "openWorldHint": false
  },
  "meta": null,
  "execution": null
}

Run the example to see the actual result.

HTTP alternative

POST https://app.getsupers.com/sites/agent-query-pairs/call
Content-Type: application/json

{
  "query": "tag=agent&tag=3D&name=Ada+Lovelace&literal=%2B&flag&empty=&city=Montr%C3%A9al"
}

The HTTP and MCP interfaces execute the same implementation. Validation errors are returned explicitly. See the tool notes above for its supported inputs and behavior.

Ordered query pairs and one-pass decoding

Read the explanation

The displayed example has seven query pairs, with tag occurring twice and the other five keys once. That gives six distinct keys. At thirty five pixels per pair or distinct key the bars span two hundred forty five and two hundred ten. The documented parser preserves both tag entries and their order rather than replacing the first with the last. Its grouped values list still records agent before three D. This explains the page contract, not an executed offline API response. The shared absolute app script is unavailable in the local copy, and we send no HTTP or MCP request. Raw plus decodes as a space, so Ada plus Lovelace becomes Ada space Lovelace. Percent two B represents a literal plus instead. At sixty pixels per encoded character, the three-character percent escape spans one hundred eighty, compared with sixty for its one decoded plus character. Encoded ampersands and equals signs are decoded after the query is split, preventing them from creating additional pairs. Decoding occurs only once. The canonical syntax uses percent twenty for spaces and uppercase escapes, but that normalization does not prove semantic equivalence for every server or preserve bytes required by a signature scheme. An empty query has zero pairs. But the nonempty query a followed by two ampersands has three segments: bare key a and two empty segments. Its documented pair count is three. At sixty pixels per pair the bars grow from zero to one hundred eighty. The bare key has no equals sign and an empty value; canonical output adds equals to every pair. Malformed percent escapes and invalid UTF eight are rejected according to the contract, while literal Unicode is accepted without normalization. This is query-component parsing, not URL validation or fetching. Actual production validation and public playback remain unverified in this offline draft.

Strict URL Query Pair Parser Architecture and Limits

How does this tool parse raw query strings, preserve duplicate keys, and serialize canonical outputs compared to standard form parsers?

This agent tool executes a strict single-pass parse of a raw URL query component. It splits segments strictly along '&' delimiters and partitions each segment into key and value on the first '=' character. Unlike lenient parsers that silently ignore corrupt escapes or collapse duplicate keys into arbitrary scalar overrides, this endpoint validates that every percent sign initiates a strict two-hex-digit escape sequence decoding to valid UTF-8. It preserves key order, bare-flag indicators, and grouped value lists, before reconstructing a normalized query with uppercase hexadecimal percent escapes and space encoded as %20.

The parser expects only the isolated query string; supplying leading question marks, full URLs, or fragment hashes causes them to be parsed as literal key or value text. In canonical_query, spaces are serialized as %20 rather than form-style '+' and empty segments produce empty string pairs with '=' appended, which differs from standard application/x-www-form-urlencoded serializers. Malformed escapes (such as '%G1' or lone '%') or invalid UTF-8 byte sequences raise ValueError exceptions instead of substituting replacement characters.

Try a worked example

Leaving the preset query in the textarea and clicking 'Run tool' executes parse_query_pairs on the 7-segment query string. The structured result populates with a pair_count of 7, identifies duplicate_keys as ["tag"], groups repeated values under values_by_key, preserves 'flag' with had_equals=false, decodes 'name' to 'Ada Lovelace' and 'city' to 'Montréal', and produces canonical_query using uppercase %20 and %C3%A9.

Strict UTF-8 Decoding vs Permissive Form Specifications

The WHATWG URL standard defines application/x-www-form-urlencoded parsing as a lenient process: ampersands partition pairs, the first '=' divides name from value, '+' is translated to space (0x20), and percent-encoded bytes are decoded via UTF-8 decode without BOM, replacing illegal sequences with U+FFFD instead of failing. URL Living Standard — application/x-www-form-urlencoded

In contrast, this tool validates percent-escape syntax with regular expression checks '%(?![0-9A-Fa-f]{2})' and unquotes bytes strictly. If any escape has fewer than two valid hex digits or resolves to malformed UTF-8, the function immediately raises a ValueError rather than falling back to placeholder characters. Python urllib.parse Standard Library Documentation

Preservation of Ordering, Duplicates, and Delimiter Syntax

Standard URLSearchParams and dictionary conversions often collapse duplicate parameters or omit bare query keys. Here, every segment is retained in order as a dictionary recording the decoded key, value, and a boolean had_equals flag indicating whether an '=' was explicitly present. URL Living Standard — application/x-www-form-urlencoded

When generating canonical_query, all keys and values are re-encoded with urllib.parse.quote using safe='' and UTF-8 encoding, appending '=' to every pair regardless of original bare syntax and serializing spaces as %20 rather than '+'. Python urllib.parse Standard Library Documentation

Sources and further reading