Claude code stream json driver
Skill kjuhwa/skills-hub/skills/agents/claude-code-stream-json-driver
Drive the Claude Code CLI non-interactively via stream-json — build input on stdin, parse typed events on stdout, handle session resume and token usage.From its SKILL.md
npx -y skills add kjuhwa/skills-hub --skill claude-code-stream-json-driverAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
4.2 KB, 916 tokens by cl100k_base, as published. Nobody here has run it
When to use
- You're building a server or daemon that needs to run Claude Code autonomously (no TTY, no user confirmation).
- You need streaming events (text, tool_use, tool_result) not just a final answer.
- You want to persist session IDs so the user can resume later.
Steps
- Invoke Claude with the stream-json protocol flags:
claude -p \ --output-format stream-json \ --input-format stream-json \ --verbose \ --strict-mcp-config \ --permission-mode bypassPermissions \ [--model <id>] [--max-turns <n>] \ [--append-system-prompt <text>] \ [--resume <session_id>] \ [--mcp-config <path-to-temp-json>] - Write the user prompt to stdin as one JSON line, then close stdin:
input := map[string]any{ "type": "user", "message": map[string]any{ "role": "user", "content": []map[string]string{{"type": "text", "text": prompt}}, }, } b, _ := json.Marshal(input) stdin.Write(append(b, '\n')) stdin.Close() - Read stdout line-by-line with a large buffer (tool results can be huge):
scanner := bufio.NewScanner(stdout) scanner.Buffer(make([]byte, 0, 1024*1024), 10*1024*1024) for scanner.Scan() { var msg claudeSDKMessage if err := json.Unmarshal(scanner.Bytes(), &msg); err != nil { continue } switch msg.Type { case "assistant": handleAssistantBlocks(msg) // text, thinking, tool_use case "user": handleToolResultBlocks(msg) case "system": sessionID = msg.SessionID case "result": finalText = msg.ResultText; isError = msg.IsError case "log": forwardLog(msg.Log) } } - Accumulate token usage per model name from
message.usageon assistant events:u := usage[content.Model] u.InputTokens += content.Usage.InputTokens u.OutputTokens += content.Usage.OutputTokens u.CacheReadTokens += content.Usage.CacheReadInputTokens u.CacheWriteTokens += content.Usage.CacheCreationInputTokens usage[content.Model] = u - Block protocol-critical flags from any user-provided
custom_argsso they don't break the wire format:var claudeBlockedArgs = map[string]blockedArgMode{ "-p": blockedStandalone, "--output-format": blockedWithValue, "--input-format": blockedWithValue, "--permission-mode": blockedWithValue, "--mcp-config": blockedWithValue, } - Strip parent-Claude env leakage before spawning:
func isFilteredChildEnvKey(key string) bool { return key == "CLAUDECODE" || strings.HasPrefix(key, "CLAUDECODE_") || strings.HasPrefix(key, "CLAUDE_CODE_") } - On failed runs where you requested
--resume <id>and Claude emitted a different session ID, signal the caller to retry with fresh session by reporting empty session ID:if failed && requestedResume != "" && emitted != "" && emitted != requestedResume { return "" } return emitted
Example
Minimal Go wrapper is ~200 LOC; see server/pkg/agent/claude.go in the source repo for the full reference implementation.
Caveats
--permission-mode bypassPermissionsmakes the run fully autonomous — use only in a sandboxed environment you trust.- If the sandbox refuses to pass MCP config by value, write the JSON to a temp file and pass
--mcp-config <path>; clean up the file after the goroutine that owns the process exits. - 10MB max-token is a reasonable upper bound for Read/Write tool results; bump if you see "token too long" errors.
- Don't read stderr line-by-line; pipe it to a logger that Debug-level logs lines so noise doesn't dominate.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.