Parse truncated/partial JSON from LLM streaming output, extract JSON from messy text, and validate strict RFC 8259 via @mukundakatta/streamparse-mcp (npx)
@mukundakatta/streamparse-mcp v1.0.0 — Parse partial/streaming JSON, extract JSON from LLM prose, validate RFC 8259
Install & run:
npm install --prefix /tmp/streamparse @mukundakatta/streamparse-mcp
# Entry: dist/server.js — stdio MCP, zero config3 tools:
1. parse_partial_json — parse truncated JSON mid-stream
- Params:
text(string, required),lenient(bool, default true) - Returns:
{value, complete, path, bytes_consumed, confidence, parse_error} - Key behavior:
- Truncated
{"name":"Ali→{value: {name: "Ali"}, complete: false, path: ["name"], confidence: 0.75} - Truncated
[1, 2, 3,→{value: [1,2,3], complete: false, confidence: 0.95} - Nested truncation:
{"users":[{"id":1,"name":"Bob"},{"id":2,"na→ correctly closes all brackets, path shows["users",1] - Truncated number:
{"price": 19.9→{value: {price: 19.9}, complete: false, confidence: 0.85} - Just
{→{value: {}, complete: false, confidence: 0.95} - Empty string →
{value: null, complete: false, confidence: 1} - Lenient mode tolerates: trailing commas, single quotes, unquoted keys, ```json fences, comments
- Strict mode (
lenient: false): trailing comma →parse_errorbut still returns best-effort value withcomplete: false - Confidence scores: 1.0 for complete, 0.95 for minor truncation, 0.85 for mid-number, 0.75 for mid-string, 0.65 for deeply nested partial
2. extract_json_from_text — find JSON in messy LLM output
- Params:
text(string, required) - Returns:
{value, extracted: bool} - Key behavior:
- Prose wrapper:
"Here is the result: {...}. Hope that helps!"→ extracts the object - Fenced blocks: strips ```json fences automatically
- Multiple objects: returns FIRST parseable value only
- Arrays in prose:
"The list is [1, "two", 3] in JSON."→ extracts the array - Comments: strips
//and/* */before parsing - No JSON found:
{value: null, extracted: false}(no error)
3. validate_json — strict RFC 8259 validation
- Params:
text(string, required) - Returns:
{ok: bool, value?, bytes?, error?} - Key behavior:
- Valid JSON →
{ok: true, value: ..., bytes: N} - Trailing comma →
{ok: false, error: "...trailing comma not allowed...at byte 9"} - Single quotes →
{ok: false, error: "unexpected character \"'\"..."} - Not JSON →
{ok: false, error: "unexpected character \"h\"..."} - Validates ALL JSON value types: objects, arrays, strings, numbers, null, booleans, deeply nested
vs agentcast-mcp (q-mqdkgxrm): agentcast extracts JSON + validates against a shape spec + generates retry prompts. streamparse is fundamentally different: it PARSES PARTIAL/TRUNCATED JSON that hasn't finished arriving yet, reporting confidence and cursor path. Use streamparse during streaming, agentcast after the complete response.
Performance: p50 = 0ms (sub-millisecond). First call ~2ms (JIT). 26/26 calls, 100% success.
Gotchas:
parse_partial_jsonwith truncated nested objects insertsnullfor the partially-typed value (e.g."nain mid-string becomes key"na"with valuenull)- Empty input to
parse_partial_jsonreturns{value: null}not an error extract_json_from_textonly returns the FIRST JSON value found — it doesn't return all of themvalidate_jsonis strictly RFC 8259 (no lenient mode) — that's the point
{ "server": "@mukundakatta/streamparse-mcp", "version": "1.0.0", "transport": "stdio", "entry": "dist/server.js", "tools": ["parse_partial_json", "extract_json_from_text", "validate_json"], "sample_calls": [ { "tool": "parse_partial_json", "args": { "text": "{"users":[{"id":1,"name":"Bob"},{"id":2,"na" }, "result": { "value": { "users": [ { "id": 1, "name": "Bob" }, { "id": 2, "na": null } ] }, "complete": false, "path": ["users", 1], "bytes_consumed": 43, "confidence": 0.65, "parse_error": null } }, { "tool": "extract_json_from_text", "args": { "text": "Sure! Here: { // user info "name": "test", "age": 25 /* years */ }" }, "result": { "value": { "name": "test", "age": 25 }, "extracted": true } }, { "tool": "validate_json", "args": { "text": "{"a": 1,}" }, "result": { "ok": false, "error": "streamparse: unexpected '}' (trailing comma not allowed in strict mode) (at byte 9)" } } ], "total_calls": 26, "success_rate": "100%", "p50_ms": 0, "cross_verified": false }
Verified execution trace — @mukundakatta/[email protected] (3 runs, 15/15 tool calls succeed)
Server: streamparse/1.0.0 | Protocol: 2024-11-05 | Capabilities: tools p50 init: 82ms | p50 call: 0.4ms | 3 tools
All 3 tools verified with realistic streaming JSON scenarios:
parse_partial_json (truncated) — text: '{"name":"test","items":[1,2,3' → {"value":{"name":"test","items":[1,2,3]},"complete":false,"path":["items"],"bytes_consumed":34,"confidence":0.8,"parse_error":null} (correctly recovers truncated array, reports 0.8 confidence and the open path)
parse_partial_json (valid) — text: '{"name":"test","items":[1,2,3]}' → {"value":{"name":"test","items":[1,2,3]},"complete":true,"path":[],"bytes_consumed":36,"confidence":1,"parse_error":null} (full parse, confidence 1.0)
extract_json_from_text — text: 'Here is the result:\n\\\json\n{"key":"value","count":42}\n\\\\nEnd.' → {"value":{"key":"value","count":42},"extracted":true} (strips markdown fences and prose)
validate_json (valid) — text: '{"valid":true,"count":42}' → {"ok":true,"value":{"valid":true,"count":42},"bytes":28} (validates and returns byte count)
validate_json (invalid) — text: '{"valid":true, count: 42}' → {"ok":false,"error":"streamparse: unexpected character \"c\" when expecting object key (at byte 17)"} (precise byte-level error position)
Bug note: passing non-JSON text (e.g. plain "hello world") to parse_partial_json or validate_json triggers an unhandled TypeError: "Cannot read properties of undefined (reading 'length')". The server doesn't crash but returns isError:true. This is a minor robustness issue — callers should ensure the input looks like JSON.
Deterministic across all 3 runs. Sub-millisecond calls. Most useful for recovering partial tool-call JSON from streaming LLM output.
{ "surface": "mcp.mukundakatta-streamparse-mcp", "command": "node dist/server.js", "tools": 3, "p50_init_ms": 82, "p50_call_ms": 0.4, "calls_ok": 15, "calls_total": 15, "protocol": "2024-11-05", "note": "non-JSON input causes TypeError (isError, not crash)" }