tool-renderer
Implement specialized rendering for Claude Code tools. Use when adding a new tool type (WebSearch, WebFetch, etc.) to the transcript viewer, or when asked to implement tool rendering.
Install
mkdir -p .claude/skills/tool-renderer && curl -L -o skill.zip "https://mcp.directory/api/skills/download/4935" && unzip -o skill.zip -d .claude/skills/tool-renderer && rm skill.zipInstalls to .claude/skills/tool-renderer
About this skill
Implementing a Tool Renderer
This guide walks through adding rendering support for a new Claude Code tool, using WebSearch as an example.
Before You Start
Examine existing test data to understand the tool's actual JSON structure:
# Find test files containing the tool
rg -l "ToolName" test/test_data/
# Look at actual JSONL entries
rg '"name":\s*"ToolName"' test/test_data/ -A 2 -B 2
Key fields to identify:
- Input parameters: What's in
tool_use.input? - toolUseResult structure: What metadata does the structured result contain?
- tool_result.content: What does the raw text output look like?
The toolUseResult field on transcript entries often contains richer structured data than tool_result.content. Always prefer parsing from toolUseResult when available.
Overview
Tool rendering involves several components working together:
- Models (
models.py) - Type definitions for tool inputs and outputs - Factory (
factories/tool_factory.py) - Parsing raw JSON into typed models - HTML Formatters (
html/tool_formatters.py) - HTML rendering functions - Renderers - Integration with HTML and Markdown renderers
Step 1: Define Models
Tool Input Model
Add a Pydantic model for the tool's input parameters in models.py:
class WebSearchInput(BaseModel):
"""Input parameters for the WebSearch tool."""
query: str
Tool Output Model
Add a dataclass for the parsed output. Output models are dataclasses (not Pydantic) since they're created by our parsers, not from JSON:
@dataclass
class WebSearchLink:
"""Single search result link."""
title: str
url: str
@dataclass
class WebSearchOutput:
"""Parsed WebSearch tool output."""
query: str
links: list[WebSearchLink]
preamble: Optional[str] = None # Text before the Links
summary: Optional[str] = None # Markdown analysis after the Links
Note: Some tools have structured output with multiple sections. WebSearch is parsed as preamble/links/summary - text before Links, the Links JSON array, and markdown analysis after. This allows flexible rendering while preserving all content.
Update Type Unions
Add the new types to the ToolInput and ToolOutput unions:
ToolInput = Union[
# ... existing types ...
WebSearchInput,
ToolUseContent, # Generic fallback - keep last
]
ToolOutput = Union[
# ... existing types ...
WebSearchOutput,
ToolResultContent, # Generic fallback - keep last
]
Step 2: Implement Factory Functions
In factories/tool_factory.py:
Register Input Model
Add the input model to TOOL_INPUT_MODELS:
TOOL_INPUT_MODELS: dict[str, type[BaseModel]] = {
# ... existing entries ...
"WebSearch": WebSearchInput,
}
Implement Output Parser
Important: Always check if the tool has structured toolUseResult data available. This is the preferred approach because:
- It's more reliable than regex parsing of text content
- It often contains metadata (timing, byte counts, status codes) not in the text
- The structure is well-defined and type-safe
Example toolUseResult structures in test data:
// WebSearch
{"query": "...", "results": [...], "durationSeconds": 15.7}
// WebFetch
{"url": "...", "result": "...", "code": 200, "codeText": "OK", "bytes": 12345, "durationMs": 1500}
Create a parser function that extracts from toolUseResult:
def _parse_websearch_from_structured(
tool_use_result: ToolUseResult,
) -> Optional[WebSearchOutput]:
"""Parse WebSearch from structured toolUseResult data.
The toolUseResult for WebSearch has the format:
{
"query": "search query",
"results": [
{"tool_use_id": "...", "content": [{"title": "...", "url": "..."}]},
"Analysis text..."
]
}
"""
if not isinstance(tool_use_result, dict):
return None
query = tool_use_result.get("query")
results = tool_use_result.get("results")
# ... extract links from results[0].content, summary from results[1] ...
return WebSearchOutput(query=query, links=links, preamble=None, summary=summary)
def parse_websearch_output(
tool_result: ToolResultContent,
file_path: Optional[str],
tool_use_result: Optional[ToolUseResult] = None, # Extended signature
) -> Optional[WebSearchOutput]:
"""Parse WebSearch tool result from structured toolUseResult."""
del tool_result, file_path # Unused
if tool_use_result is None:
return None
return _parse_websearch_from_structured(tool_use_result)
Register Output Parser
Add to TOOL_OUTPUT_PARSERS and register in PARSERS_WITH_TOOL_USE_RESULT if using the extended signature:
TOOL_OUTPUT_PARSERS: dict[str, ToolOutputParser] = {
# ... existing entries ...
"WebSearch": parse_websearch_output,
}
# REQUIRED for parsers that use toolUseResult - without this, the structured
# data won't be passed to your parser!
PARSERS_WITH_TOOL_USE_RESULT: set[str] = {"WebSearch", "WebFetch"}
Note: If your parser has the 3-argument signature (tool_result, file_path, tool_use_result), you MUST add it to PARSERS_WITH_TOOL_USE_RESULT. Otherwise create_tool_output() won't pass the structured data.
Step 3: Implement HTML Formatters
In html/tool_formatters.py:
Input Formatter
Design consideration: The title already shows key info (tool name + primary parameter). Only show content in the body if it adds value or is too long for the title.
def format_websearch_input(search_input: WebSearchInput) -> str:
"""Format WebSearch tool use content."""
# If query is short enough to fit in title, return empty
if len(search_input.query) <= 100:
return "" # Full query shown in title
escaped_query = escape_html(search_input.query)
return f'<div class="websearch-query">{escaped_query}</div>'
This avoids redundancy when the title already shows everything important.
Output Formatter
For tools with structured content like WebSearch, combine all parts into markdown then render:
def _websearch_as_markdown(output: WebSearchOutput) -> str:
"""Convert WebSearch output to markdown: preamble + links list + summary."""
parts = []
if output.preamble:
parts.extend([output.preamble, ""])
for link in output.links:
parts.append(f"- [{link.title}]({link.url})")
if output.summary:
parts.extend(["", output.summary])
return "\n".join(parts)
def format_websearch_output(output: WebSearchOutput) -> str:
"""Format WebSearch as single collapsible markdown block."""
markdown_content = _websearch_as_markdown(output)
return render_markdown_collapsible(markdown_content, "websearch-results")
Update Exports
Add functions to __all__:
__all__ = [
# ... existing exports ...
"format_websearch_input",
"format_websearch_output",
]
Step 4: Wire Up HTML Renderer
In html/renderer.py:
Import Formatters
from .tool_formatters import (
# ... existing imports ...
format_websearch_input,
format_websearch_output,
)
Add Format Methods
def format_WebSearchInput(self, input: WebSearchInput, _: TemplateMessage) -> str:
return format_websearch_input(input)
def format_WebSearchOutput(self, output: WebSearchOutput, _: TemplateMessage) -> str:
return format_websearch_output(output)
Add Title Method (Optional)
For a custom title in the message header:
def title_WebSearchInput(self, input: WebSearchInput, message: TemplateMessage) -> str:
return self._tool_title(message, "🔎", f'"{input.query}"')
Step 5: Implement Markdown Renderer
In markdown/renderer.py:
Import Models
from ..models import (
# ... existing imports ...
WebSearchInput,
WebSearchOutput,
)
Add Format Methods
def format_WebSearchInput(self, input: WebSearchInput, _: TemplateMessage) -> str:
"""Format -> empty (query shown in title)."""
return ""
def format_WebSearchOutput(self, output: WebSearchOutput, _: TemplateMessage) -> str:
"""Format -> markdown list of links."""
parts = [f"Query: *{output.query}*", ""]
for link in output.links:
parts.append(f"- [{link.title}]({link.url})")
return "\n".join(parts)
def title_WebSearchInput(self, input: WebSearchInput, _: TemplateMessage) -> str:
"""Title -> '🔎 WebSearch `query`'."""
return f'🔎 WebSearch `{input.query}`'
Step 6: Add Tests
Create a dedicated test file test/test_{toolname}_rendering.py. Tests are required - they catch regressions and document expected behavior.
Test Structure
"""Test cases for {ToolName} tool rendering."""
from claude_code_log.factories.tool_factory import parse_{toolname}_output
from claude_code_log.html.tool_formatters import (
format_{toolname}_input,
format_{toolname}_output,
)
from claude_code_log.models import (
ToolResultContent,
{ToolName}Input,
{ToolName}Output,
)
class Test{ToolName}Input:
"""Test input model and formatting."""
def test_input_basic(self):
"""Test input model creation."""
...
def test_format_input_short(self):
"""Test formatting when content fits in title."""
...
def test_format_input_long(self):
"""Test formatting when content is too long for title."""
...
class Test{ToolName}Parser:
"""Test output parsing."""
def test_parse_structured_output(self):
"""Test parsing from structured toolUseResult."""
...
def test_parse_minimal_output(self):
"""Test parsing with only required fields."""
...
def test_parse_missing_field(self):
"""Test graceful failure with missing required field."""
...
def test_parse_no_tool_use_result(self):
"""Test
---
*Content truncated.*
You might also like
flutter-development
aj-geddes
Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.
drawio-diagrams-enhanced
jgtolentino
Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.
godot
bfollington
This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.
ui-ux-pro-max
nextlevelbuilder
"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."
nano-banana-pro
garg-aayush
Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.
fastapi-templates
wshobson
Create production-ready FastAPI projects with async patterns, dependency injection, and comprehensive error handling. Use when building new FastAPI applications or setting up backend API projects.
Related MCP Servers
Browse all serversClaude Skills offers advanced GitHub search to find coding skills using semantic retrieval in bioinformatics and data an
Build persistent semantic networks for enterprise & engineering data management. Enable data persistence and memory acro
Boost productivity with Task Master: an AI-powered tool for project management and agile development workflows, integrat
Claude Historian is a free AI search engine offering advanced search, file context, and solution discovery in Claude Cod
Claude Historian: AI-powered search for Claude Code conversations—find files, errors, context, and sessions via JSONL pa
Sub-Agents delegates tasks to specialized AI assistants, automating workflow orchestration with performance monitoring a
Stay ahead of the MCP ecosystem
Get weekly updates on new skills and servers.