一键导入
add-widget
Create a neeter widget — custom for an MCP tool or a replacement for a built-in SDK widget.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Create a neeter widget — custom for an MCP tool or a replacement for a built-in SDK widget.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | add-widget |
| description | Create a neeter widget — custom for an MCP tool or a replacement for a built-in SDK widget. |
Create a purpose-built React component for a tool call. Replaces the default raw-JSON fallback in ToolCallCard with a widget that shows structured, phase-aware output.
Two modes:
@neeter/reactIf the user passed a tool name as an argument, check it against the 11 built-in tool names:
Bash, Edit, Read, Write, Glob, Grep, WebFetch, WebSearch, AskUserQuestion, TodoWrite, NotebookEdit
Match → replacement mode. No match → custom widget mode. If no argument was provided, ask the user which tool the widget is for.
Search the project for existing server code that defines the tool (grep for the tool name, createSdkMcpServer, or tool().
If the tool already exists: read the handler to determine:
mcp__{server}__{tool})stripMcpPrefix (this is what toolName must match)content[0].text)If the tool doesn't exist: create it using the Claude Agent SDK's in-process MCP server pattern:
import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
import { z } from "zod/v4";
export function create{Server}Server() {
return createSdkMcpServer({
name: "{server}",
tools: [
tool(
"{tool_name}",
"{description}",
{ /* zod schema for input */ },
async (input) => {
// tool logic here
return {
content: [{
type: "text" as const,
text: JSON.stringify({ /* result shape */ }),
}],
};
}
),
],
});
}
Then wire it into the session config:
mcpServers: { {server}: create{Server}Server() },
allowedTools: ["mcp__{server}__*"],
Reference: Claude Agent SDK MCP guide (SDK MCP servers section) for the canonical pattern.
Create a TypeScript interface for the parsed result. neeter JSON-parses MCP text content automatically — the widget receives the parsed object.
Create {widgetDir}/{Name}Widget.tsx with debug logging:
console.log("[{Name}Widget debug]", { phase, result, input, resultType: typeof result });
Follow the established pattern:
inputRenderer — render the tool's key input fields for approval previewsrichLabel(result, input) — short string for the collapsed CollapsibleCard headercomponent — loading state for pending/running, full render for completeregisterWidget<ResultType>({...}) at module bottomreact-markdown + markdownComponents from ../markdown-overrides.js if the result contains markdownIn the app's entry point:
import "./widgets/{Name}Widget.js";
Run the project's build command to verify compilation.
Ask the user to trigger the tool and paste the debug log output from the browser console. Fix the parser to match the real data shape.
console.log from step 3console.log — there should be nonefeat(react): add {Name} widget — body explains why (what was wrong with the default rendering), not what.
Open packages/react/src/widgets/{Name}Widget.tsx to understand what it currently renders — its result parsing, inputRenderer, richLabel, and phase handling.
Ask the user what should change: different layout, extra data, different styling, merged info, etc.
Create the widget in the user's project (not in the neeter packages). Use the same toolName as the built-in — later registrations overwrite earlier ones.
Add debug logging as in the custom widget flow.
The user's side-effect import must run after @neeter/react's built-in imports so the override takes precedence:
// Built-ins register when @neeter/react is imported
import "@neeter/react";
// Override registers after
import "./widgets/MyCustomBashWidget.js";
Same as custom widget steps 5–8.
packages/react/src/widgets/WebSearchWidget.tsx — parsing, favicon pills, expand/collapsepackages/react/src/widgets/WebFetchWidget.tsx — plain-string result, markdown, input-based richLabelpackages/react/src/registry.ts — registerWidget(), getWidget(), stripMcpPrefix()packages/react/src/types.ts — WidgetProps<TResult>, WidgetRegistrationpackages/react/src/CollapsibleCard.tsx — wrapper used by ToolCallCardpackages/react/src/markdown-overrides.tsx — shared markdownComponentscreateSdkMcpServer, tool(), zod schemas, session wiringsettingSources, discoveryapps/docs/content/docs/custom-widgets.mdx (online) — end-to-end tutorial, API referenceapps/docs/content/docs/built-in-widgets.mdx (online) — all built-in widgets, override pattern, widget anatomyIf the skill's instructions led you astray — the docs described a wrong pattern, the result shape guidance was incorrect, a step was missing, or the logic was faulty — file an issue on neeter's GitHub after resolving the problem:
gh issue create --repo quantumleeps/neeter \
--title "add-widget skill: {brief description}" \
--label "skill-feedback" \
--body "## What happened
{What went wrong and how it was resolved}
## Suggestion
{Whether the skill steps, the docs, or both need updating}"
This ensures fixes flow back upstream so the next person doesn't hit the same problem.
.claude/CLAUDE.md.