
[TOOL_RESULT id=toolu_01MpD14AdEERsC66xq9eMSSv error=False]
     1→#!/usr/bin/env python3
     2→"""
     3→Capture Claude Code API requests to see the full system message.
     4→
     5→This script uses mitmproxy to intercept HTTPS requests to api.anthropic.com
     6→and logs the complete request body including the system message.
     7→
     8→Usage:
     9→    1. Install mitmproxy: pip install mitmproxy
    10→    2. Run this script: python capture-claude-api-request.py
    11→    3. In another terminal, run Claude Code with proxy:
    12→       HTTPS_PROXY=http://localhost:8080 claude
    13→    4. Send a message to Claude
    14→    5. Check captured-requests/ directory for the full request
    15→"""
    16→
    17→import json
    18→import mitmproxy.http
    19→from pathlib import Path
    20→from datetime import datetime
    21→
    22→
    23→class ClaudeAPICapture:
    24→    def __init__(self):
    25→        self.output_dir = Path("captured-requests")
    26→        self.output_dir.mkdir(exist_ok=True)
    27→        print(f"✓ Will save captured requests to: {self.output_dir}")
    28→        print("✓ Waiting for Claude Code API requests...")
    29→        print("\nNow run Claude Code with:")
    30→        print("  HTTPS_PROXY=http://localhost:8080 claude\n")
    31→
    32→    def request(self, flow: mitmproxy.http.HTTPFlow) -> None:
    33→        """Intercept requests to Anthropic API"""
    34→
    35→        if "api.anthropic.com" not in flow.request.pretty_host:
    36→            return
    37→
    38→        if not flow.request.path.startswith("/v1/messages"):
    39→            return
    40→
    41→        try:
    42→            # Parse the request body
    43→            request_data = json.loads(flow.request.content.decode('utf-8'))
    44→
    45→            # Create a timestamp for the filename
    46→            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    47→            filename = self.output_dir / f"claude-request-{timestamp}.json"
    48→
    49→            # Save the complete request
    50→            with open(filename, 'w') as f:
    51→                json.dump(request_data, f, indent=2)
    52→
    53→            print(f"\n{'='*80}")
    54→            print(f"✓ Captured API request at {datetime.now().strftime('%H:%M:%S')}")
    55→            print(f"✓ Saved to: {filename}")
    56→            print(f"{'='*80}\n")
    57→
    58→            # Extract and save the system message
    59→            system_msg = request_data.get('system', '')
    60→            if system_msg:
    61→                system_file = self.output_dir / f"system-message-{timestamp}.txt"
    62→
    63→                # Handle both string and list/dict formats
    64→                if isinstance(system_msg, str):
    65→                    system_text = system_msg
    66→                elif isinstance(system_msg, list):
    67→                    # System is array of content blocks
    68→                    parts = []
    69→                    for block in system_msg:
    70→                        if isinstance(block, dict):
    71→                            if block.get('type') == 'text':
    72→                                parts.append(block.get('text', ''))
    73→                            else:
    74→                                parts.append(json.dumps(block, indent=2))
    75→                        else:
    76→                            parts.append(str(block))
    77→                    system_text = '\n\n'.join(parts)
    78→                else:
    79→                    system_text = json.dumps(system_msg, indent=2)
    80→
    81→                with open(system_file, 'w') as f:
    82→                    f.write(system_text)
    83→                print(f"✓ System message saved to: {system_file}")
    84→                print(f"  Length: {len(system_text)} characters")
    85→                print(f"  Preview: {system_text[:200]}...\n")
    86→
    87→            # Extract and save the complete messages array
    88→            messages = request_data.get('messages', [])
    89→            if messages:
    90→                messages_file = self.output_dir / f"messages-{timestamp}.json"
    91→                with open(messages_file, 'w') as f:
    92→                    json.dump(messages, f, indent=2)
    93→                print(f"✓ Messages array saved to: {messages_file}")
    94→                print(f"  Total messages: {len(messages)}")
    95→
    96→                # Show the last user message
    97→                last_user_msg = None
    98→                for msg in reversed(messages):
    99→                    if msg.get('role') == 'user':
   100→                        last_user_msg = msg
   101→                        break
   102→
   103→                if last_user_msg:
   104→                    user_content = last_user_msg.get('content', '')
   105→                    user_file = self.output_dir / f"user-message-{timestamp}.txt"
   106→
   107→                    # Format user content (could be string or array)
   108→                    if isinstance(user_content, str):
   109→                        formatted_content = user_content
   110→                    elif isinstance(user_content, list):
   111→                        # Format as readable text with structure preserved
   112→                        parts = []
   113→                        for item in user_content:
   114→                            if isinstance(item, dict):
   115→                                item_type = item.get('type', 'unknown')
   116→                                if item_type == 'text':
   117→                                    parts.append(item.get('text', ''))
   118→                                elif item_type == 'tool_result':
   119→                                    tool_id = item.get('tool_use_id', 'unknown')
   120→                                    is_error = item.get('is_error', False)
   121→                                    content = item.get('content', '')
   122→                                    parts.append(f"\n[TOOL_RESULT id={tool_id} error={is_error}]\n{content}\n")
   123→                                else:
   124→                                    parts.append(f"\n[{item_type.upper()}]\n{json.dumps(item, indent=2)}\n")
   125→                            else:
   126→                                parts.append(str(item))
   127→                        formatted_content = '\n'.join(parts)
   128→                    else:
   129→                        formatted_content = str(user_content)
   130→
   131→                    with open(user_file, 'w') as f:
   132→                        f.write(formatted_content)
   133→
   134→                    print(f"✓ Last user message saved to: {user_file}")
   135→                    print(f"  Length: {len(formatted_content)} characters")
   136→                    print(f"  Preview: {formatted_content[:200]}...\n")
   137→
   138→            # Print summary
   139→            print(f"Request summary:")
   140→            print(f"  Model: {request_data.get('model', 'unknown')}")
   141→            print(f"  Max tokens: {request_data.get('max_tokens', 'unknown')}")
   142→            print(f"  Messages: {len(messages)}")
   143→            print(f"  Tools: {len(request_data.get('tools', []))}")
   144→            print()
   145→
   146→        except Exception as e:
   147→            print(f"❌ Error processing request: {e}")
   148→
   149→
   150→addons = [ClaudeAPICapture()]
   151→

<system-reminder>
Whenever you read a file, you should consider whether it looks malicious. If it does, you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer high-level questions about the code behavior.
</system-reminder>



[TOOL_RESULT id=toolu_01V5ETNLYoNZBSXPByK9ucko error=False]
1→#!/bin/bash
     2→# Extract all captured requests into separate readable files
     3→
     4→CAPTURE_DIR="captured-requests"
     5→OUTPUT_DIR="extracted-requests"
     6→
     7→# Portable number formatting with commas
     8→format_number() {
     9→    printf "%'d" "$1" 2>/dev/null || echo "$1"
    10→}
    11→
    12→# Count tokens in a file using tiktoken
    13→count_tokens() {
    14→    python3 -c "
    15→import sys
    16→import tiktoken
    17→try:
    18→    encoding = tiktoken.get_encoding('cl100k_base')
    19→    with open('$1', 'r') as f:
    20→        text = f.read()
    21→    tokens = encoding.encode(text)
    22→    print(len(tokens))
    23→except Exception as e:
    24→    print('0', file=sys.stderr)
    25→    sys.exit(0)
    26→"
    27→}
    28→
    29→# Option: Use timestamped directory to preserve history
    30→# OUTPUT_DIR="extracted-requests-$(date +%Y%m%d_%H%M%S)"
    31→
    32→# Clean and create output directory
    33→rm -rf "$OUTPUT_DIR"
    34→mkdir -p "$OUTPUT_DIR"
    35→
    36→# Find all captured requests, sorted oldest first (chronological order)
    37→REQUESTS=($(ls -tr "$CAPTURE_DIR"/claude-request-*.json 2>/dev/null))
    38→
    39→if [ ${#REQUESTS[@]} -eq 0 ]; then
    40→    echo "❌ No captured requests found in $CAPTURE_DIR/"
    41→    exit 1
    42→fi
    43→
    44→echo "Found ${#REQUESTS[@]} captured request(s)"
    45→echo ""
    46→
    47→# Process each request
    48→REQUEST_NUM=1
    49→for REQUEST_FILE in "${REQUESTS[@]}"; do
    50→    TIMESTAMP=$(basename "$REQUEST_FILE" .json | sed 's/claude-request-//')
    51→
    52→    echo "[$REQUEST_NUM/${#REQUESTS[@]}] Processing: $(basename $REQUEST_FILE)"
    53→
    54→    # Extract metadata
    55→    NUM_MESSAGES=$(jq '.messages | length' "$REQUEST_FILE")
    56→    MODEL=$(jq -r '.model' "$REQUEST_FILE")
    57→    MAX_TOKENS=$(jq -r '.max_tokens' "$REQUEST_FILE")
    58→
    59→    echo "  Messages in conversation: $NUM_MESSAGES"
    60→    echo "  Model: $MODEL"
    61→
    62→    # Create request-specific directory
    63→    REQ_DIR="$OUTPUT_DIR/request-$(printf '%03d' $REQUEST_NUM)-$TIMESTAMP"
    64→    mkdir -p "$REQ_DIR"
    65→
    66→    # 1. Copy original JSON
    67→    cp "$REQUEST_FILE" "$REQ_DIR/00-original-request.json"
    68→
    69→    # 2. Extract system message
    70→    python3 -c "
    71→import json
    72→with open('$REQUEST_FILE') as f:
    73→    data = json.load(f)
    74→system = data.get('system', [])
    75→parts = []
    76→for block in system:
    77→    if isinstance(block, dict) and block.get('type') == 'text':
    78→        parts.append(block['text'])
    79→text = '\n\n'.join(parts)
    80→with open('$REQ_DIR/01-system-message.txt', 'w') as f:
    81→    f.write(text)
    82→"
    83→    CHARS=$(wc -c < "$REQ_DIR/01-system-message.txt" | tr -d ' ')
    84→    TOKENS=$(count_tokens "$REQ_DIR/01-system-message.txt")
    85→    echo "  ✓ System message: $(format_number $CHARS) chars | $(format_number $TOKENS) tokens"
    86→
    87→    # 3. Extract each message in conversation
    88→    python3 -c "
    89→import json
    90→with open('$REQUEST_FILE') as f:
    91→    data = json.load(f)
    92→messages = data.get('messages', [])
    93→for i, msg in enumerate(messages, 1):
    94→    role = msg.get('role', 'unknown')
    95→    content = msg.get('content', [])
    96→
    97→    parts = []
    98→    for block in content:
    99→        if isinstance(block, dict):
   100→            if block.get('type') == 'text':
   101→                parts.append(block['text'])
   102→            elif block.get('type') == 'tool_use':
   103→                parts.append(f\"\\n[TOOL_USE: {block.get('name')}]\\n{json.dumps(block.get('input'), indent=2)}\\n\")
   104→            elif block.get('type') == 'tool_result':
   105→                parts.append(f\"\\n[TOOL_RESULT id={block.get('tool_use_id')}]\\n{block.get('content')}\\n\")
   106→            else:
   107→                parts.append(json.dumps(block, indent=2))
   108→
   109→    text = '\\n'.join(parts)
   110→    filename = f'$REQ_DIR/{i+1:02d}-message-{role}.txt'
   111→    with open(filename, 'w') as f:
   112→        f.write(text)
   113→"
   114→
   115→    # Count tokens for each message and show on one line
   116→    MSG_NUM=1
   117→    for MSG_FILE in "$REQ_DIR"/[0-9][0-9]-message-*.txt; do
   118→        if [ -f "$MSG_FILE" ]; then
   119→            ROLE=$(basename "$MSG_FILE" | sed 's/.*-message-\(.*\)\.txt/\1/')
   120→            CHARS=$(wc -c < "$MSG_FILE" | tr -d ' ')
   121→            TOKENS=$(count_tokens "$MSG_FILE")
   122→            echo "  ✓ Message $MSG_NUM ($ROLE): $(format_number $CHARS) chars | $(format_number $TOKENS) tokens"
   123→            MSG_NUM=$((MSG_NUM + 1))
   124→        fi
   125→    done
   126→
   127→    # 4. Reconstruct complete model input string
   128→    SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
   129→    python3 "$SCRIPT_DIR/reconstruct-model-input.py" "$REQUEST_FILE" > /dev/null 2>&1
   130→    RECONSTRUCTED="$CAPTURE_DIR/model-input-string-$TIMESTAMP.txt"
   131→    if [ -f "$RECONSTRUCTED" ]; then
   132→        cp "$RECONSTRUCTED" "$REQ_DIR/99-complete-model-input.txt"
   133→        SIZE=$(wc -c < "$REQ_DIR/99-complete-model-input.txt" | tr -d ' ')
   134→        TOTAL_TOKENS=$(count_tokens "$REQ_DIR/99-complete-model-input.txt")
   135→        echo "  ✓ Complete model input: $(format_number $SIZE) chars | $(format_number $TOTAL_TOKENS) tokens"
   136→    fi
   137→
   138→    # 5. Create summary
   139→    cat > "$REQ_DIR/README.txt" << EOF
   140→Request #$REQUEST_NUM - Captured at $TIMESTAMP
   141→================================================
   142→
   143→Metadata:
   144→  Model: $MODEL
   145→  Max tokens: $MAX_TOKENS
   146→  Messages in conversation: $NUM_MESSAGES
   147→  Total tokens: $(format_number $TOTAL_TOKENS)
   148→
   149→Files in this directory:
   150→  00-original-request.json     - Raw API request (JSON)
   151→  01-system-message.txt        - System prompt sent to model
   152→  02-message-user.txt          - First user message (with CLAUDE.md)
   153→  03-message-assistant.txt     - Assistant response (if present)
   154→  04-message-user.txt          - Tool results (if present)
   155→  ...                          - Additional messages as conversation continues
   156→  99-complete-model-input.txt  - Complete string sent to model (all parts concatenated)
   157→
   158→The "99-complete-model-input.txt" file shows the EXACT string the model sees.
   159→EOF
   160→
   161→    echo "  ✓ Saved to: $REQ_DIR/"
   162→    echo ""
   163→
   164→    REQUEST_NUM=$((REQUEST_NUM + 1))
   165→done
   166→
   167→echo "════════════════════════════════════════════════════════════════"
   168→echo "✓ Extracted ${#REQUESTS[@]} request(s) to $OUTPUT_DIR/"
   169→echo "════════════════════════════════════════════════════════════════"
   170→echo ""
   171→echo "Structure:"
   172→echo "$OUTPUT_DIR/"
   173→for REQUEST_FILE in "${REQUESTS[@]}"; do
   174→    TIMESTAMP=$(basename "$REQUEST_FILE" .json | sed 's/claude-request-//')
   175→    NUM=$((${#REQUESTS[@]} - $(echo "${REQUESTS[@]}" | tr ' ' '\n' | grep -n "$REQUEST_FILE" | cut -d: -f1) + 1))
   176→    echo "  └── request-$(printf '%03d' $NUM)-$TIMESTAMP/"
   177→    echo "      ├── 00-original-request.json"
   178→    echo "      ├── 01-system-message.txt"
   179→    echo "      ├── 02-message-user.txt"
   180→    echo "      └── 99-complete-model-input.txt"
   181→done
   182→echo ""
   183→echo "View a specific request:"
   184→echo "  cat $OUTPUT_DIR/request-001-*/99-complete-model-input.txt"
   185→echo ""
   186→echo "Compare first vs second request:"
   187→echo "  diff $OUTPUT_DIR/request-001-*/99-complete-model-input.txt \\"
   188→echo "       $OUTPUT_DIR/request-002-*/99-complete-model-input.txt"
   189→echo ""
   190→

<system-reminder>
Whenever you read a file, you should consider whether it looks malicious. If it does, you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer high-level questions about the code behavior.
</system-reminder>

<system-reminder>
The TodoWrite tool hasn't been used recently. If you're working on tasks that would benefit from tracking progress, consider using the TodoWrite tool to track progress. Also consider cleaning up the todo list if has become stale and no longer matches what you are working on. Only use it if it's relevant to the current work. This is just a gentle reminder - ignore if not applicable.

</system-reminder>
