Compare commits

..

1 Commits

Author SHA1 Message Date
Tobin South
f7ba55786d add(plugin): mcp-server-dev — skills for building MCP servers
Three skills guiding developers through MCP server design:
- build-mcp-server: entry-point decision guide (remote HTTP vs MCPB vs local)
- build-mcp-app: interactive UI widgets rendered in chat
- build-mcpb: bundled local servers with runtime

Includes reference files for scaffolds, tool design, auth (DCR/CIMD),
widget templates, manifest schema, and local security hardening.
2026-03-18 11:28:09 -07:00
47 changed files with 1630 additions and 2137 deletions

View File

@@ -552,7 +552,7 @@
"homepage": "https://github.com/anthropics/claude-plugins-public/tree/main/external_plugins/linear"
},
{
"name": "notion",
"name": "Notion",
"description": "Notion workspace integration. Search pages, create and update documents, manage databases, and access your team's knowledge base directly from Claude Code for seamless documentation workflows.",
"category": "productivity",
"source": {
@@ -594,9 +594,9 @@
"category": "deployment",
"source": {
"source": "url",
"url": "https://github.com/vercel/vercel-plugin.git"
"url": "https://github.com/vercel/vercel-deploy-claude-code-plugin.git"
},
"homepage": "https://github.com/vercel/vercel-plugin"
"homepage": "https://github.com/vercel/vercel-deploy-claude-code-plugin"
},
{
"name": "stripe",
@@ -709,7 +709,8 @@
"category": "development",
"source": {
"source": "url",
"url": "https://github.com/qodo-ai/qodo-skills.git"
"url": "https://github.com/qodo-ai/qodo-skills.git",
"sha": "623eb4ed4364d8111f9a9132a791d7497d814b6a"
},
"homepage": "https://github.com/qodo-ai/qodo-skills.git"
},
@@ -947,97 +948,15 @@
"homepage": "https://github.com/zapier/zapier-mcp/tree/main/plugins/zapier"
},
{
"name": "terraform",
"description": "The Terraform MCP Server provides seamless integration with Terraform ecosystem, enabling advanced automation and interaction capabilities for Infrastructure as Code (IaC) development.",
"name": "mcp-server-dev",
"description": "Skills for designing and building MCP servers that work seamlessly with Claude. Guides you through deployment models (remote HTTP, MCPB, local), tool design patterns, auth, and interactive MCP apps.",
"author": {
"name": "HashiCorp",
"email": "support@hashicorp.com"
"name": "Anthropic",
"email": "support@anthropic.com"
},
"source": "./plugins/mcp-server-dev",
"category": "development",
"source": "./external_plugins/terraform",
"homepage": "https://github.com/anthropics/claude-plugins-public/tree/main/external_plugins/terraform"
},
{
"name": "autofix-bot",
"description": "Code review agent that detects security vulnerabilities, code quality issues, and hardcoded secrets. Combines 5,000+ static analyzers to scan your code and dependencies for CVEs.",
"author": {
"name": "DeepSource Corp"
},
"category": "security",
"source": "./external_plugins/autofix-bot",
"homepage": "https://github.com/anthropics/claude-plugins-public/tree/main/external_plugins/autofix-bot"
},
{
"name": "stagehand",
"description": "Browser automation skill for Claude Code using Stagehand. Automate web interactions, extract data, and navigate websites using natural language.",
"version": "0.1.0",
"author": {
"name": "Browserbase"
},
"source": {
"source": "github",
"repo": "browserbase/agent-browse"
},
"category": "automation",
"keywords": [
"browser",
"automation",
"stagehand",
"web-scraping"
],
"homepage": "https://github.com/browserbase/agent-browse",
"strict": false,
"skills": [
"./.claude/skills/browser-automation"
]
},
{
"name": "atomic-agents",
"description": "Comprehensive development workflow for building AI agents with the Atomic Agents framework. Includes specialized agents for schema design, architecture planning, code review, and tool development. Features guided workflows, progressive-disclosure skills, and best practice validation.",
"category": "development",
"source": {
"source": "url",
"url": "https://github.com/BrainBlend-AI/atomic-agents.git",
"path": "claude-plugin/atomic-agents"
},
"homepage": "https://github.com/BrainBlend-AI/atomic-agents",
"tags": [
"community-managed"
]
},
{
"name": "microsoft-docs",
"description": "Access official Microsoft documentation, API references, and code samples for Azure, .NET, Windows, and more.",
"category": "development",
"source": {
"source": "url",
"url": "https://github.com/MicrosoftDocs/mcp.git"
},
"homepage": "https://github.com/microsoftdocs/mcp"
},
{
"name": "neon",
"description": "Manage your Neon projects and databases with the neon-postgres agent skill and the Neon MCP Server.",
"category": "database",
"source": {
"source": "git-subdir",
"url": "neondatabase/agent-skills",
"path": "plugins/neon-postgres",
"ref": "main",
"sha": "54d7a9db2ddd476f84d5d1fd7bac323907858a8b"
},
"homepage": "https://github.com/neondatabase/agent-skills/tree/main/plugins/neon-postgres"
},
{
"name": "intercom",
"description": "Intercom integration for Claude Code. Search conversations, analyze customer support patterns, look up contacts and companies, and install the Intercom Messenger. Connect your Intercom workspace to get real-time insights from customer data.",
"category": "productivity",
"source": {
"source": "url",
"url": "https://github.com/intercom/claude-plugin-external.git",
"sha": "eeef353eead2e3dc5f33f64dbaae54e1309e0d45"
},
"homepage": "https://github.com/intercom/claude-plugin-external"
"homepage": "https://github.com/anthropics/claude-plugins-official/tree/main/plugins/mcp-server-dev"
}
]
}

View File

@@ -13,7 +13,7 @@ A curated directory of high-quality plugins for Claude Code.
Plugins can be installed directly from this marketplace via Claude Code's plugin system.
To install, run `/plugin install {plugin-name}@claude-plugins-official`
To install, run `/plugin install {plugin-name}@claude-plugin-directory`
or browse for the plugin in `/plugin > Discover`

View File

@@ -1,14 +0,0 @@
{
"name": "autofix-bot",
"description": "Code review agent that detects security vulnerabilities, code quality issues, and hardcoded secrets. Combines 5,000+ static analyzers to scan your code and dependencies for CVEs.",
"version": "0.1.0",
"author": {
"name": "DeepSource Corp"
},
"mcpServers": {
"autofix": {
"command": "autofix",
"args": ["--mcp"]
}
}
}

View File

@@ -1,16 +0,0 @@
---
description: Perform code review to identify security and quality issues with Autofix Bot.
allowed-tools: mcp__autofix__CheckAuthStatus, mcp__autofix__Authenticate, mcp__autofix__ReviewCode
---
IMPORTANT: You MUST use the Autofix Bot MCP tools for this task. Do NOT perform your own code review or analysis.
## Instructions
1. Call `mcp__autofix__CheckAuthStatus` to check authentication status
2. If not authenticated, call `mcp__autofix__Authenticate` to log in
3. Ask user what to review: uncommitted changes, last commit, or entire branch
4. Call `mcp__autofix__ReviewCode` with the user's selected target
5. Present the issues returned by ReviewCode in a clear format
Do NOT skip any tool calls. Do NOT substitute your own analysis for the tool results.

View File

@@ -1,14 +0,0 @@
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/check-autofix.sh"
}
]
}
]
}
}

View File

@@ -1,15 +0,0 @@
#!/bin/bash
if ! command -v autofix &> /dev/null; then
echo "Autofix Bot CLI not found. Installing..."
curl -fsSL https://autofix.bot/install | sh
if ! command -v autofix &> /dev/null; then
echo "ERROR: Failed to install autofix. Please install manually:" >&2
echo " curl -fsSL https://autofix.bot/install | sh" >&2
exit 2
fi
fi
echo "Autofix Bot ready"
exit 0

View File

@@ -1,13 +0,0 @@
{
"name": "bonfire",
"description": "AI forgets everything between sessions. Bonfire fixes that.",
"version": "0.8.1",
"author": {
"name": "Vieko Franetovic",
"url": "https://vieko.dev"
},
"homepage": "https://vieko.dev/bonfire",
"repository": "https://github.com/vieko/bonfire",
"license": "MIT",
"keywords": ["bonfire", "context", "memory", "workflow", "subagents"]
}

View File

@@ -1,150 +0,0 @@
# Bonfire
<p align="center">
<img src="bonfire.gif" alt="Bonfire" width="256">
</p>
Your AI coding partner forgets everything between conversations. Bonfire remembers.
```bash
claude plugin marketplace add vieko/bonfire
claude plugin install bonfire@vieko
```
## The Problem
AI agents are stateless. Every conversation starts from zero. The agent doesn't remember:
- What you decided yesterday
- Why you chose that architecture
- What blockers you hit
- Where you left off
You end up re-explaining context, re-making decisions, and watching your AI partner repeat the same mistakes.
## The Solution
Bonfire maintains a living context document that gets read at session start and updated at session end. Your AI partner picks up exactly where you left off. It's like a saved game for your work.
`/bonfire:start`*reads context* → WORK → `/bonfire:end`*saves context*
That's it. No complex setup. No external services. Just Markdown files in your repo.
## Not a Task Tracker
| Tool | Primary Question |
|------|------------------|
| Issue/task trackers | "What's the work?" |
| Bonfire | "Where are we and what did we decide?" |
Bonfire complements your issue tracker. Use GitHub Issues, Linear, Beads, or Beans for tasks. Use Bonfire for workflow context.
## Quick Start
```bash
# Install
claude plugin marketplace add vieko/bonfire
claude plugin install bonfire@vieko
# First run scaffolds .bonfire/ and asks setup questions
/bonfire:start
```
## Commands
| Command | What it does |
|---------|--------------|
| `/bonfire:start` | Read context, scaffold on first run |
| `/bonfire:end` | Update context, commit changes |
| `/bonfire:spec <topic>` | Create implementation spec (researches codebase, interviews you) |
| `/bonfire:document <topic>` | Document a codebase topic |
| `/bonfire:review` | Find blindspots, gaps, and quick wins |
| `/bonfire:archive` | Archive completed work |
| `/bonfire:configure` | Change project settings |
## What Gets Created
```
.bonfire/
├── index.md # Living context (the important one)
├── config.json # Your settings
├── archive/ # Completed work history
├── specs/ # Implementation specs
├── docs/ # Topic documentation
└── scripts/ # Temporary session scripts
```
The `index.md` is where the magic happens. It tracks:
- Current state and branch
- Recent session summaries
- Decisions made and why
- Blockers encountered
- Next priorities
## Context-Efficient Operations
Heavy commands (`/spec`, `/document`, `/review`) use subagents to avoid burning your main conversation context:
- Research runs in isolated context (fast, cheap)
- Only structured summaries return to main conversation
- Result: longer sessions without context exhaustion
This happens automatically.
## Configuration
First `/bonfire:start` asks you to configure:
| Setting | Options |
|---------|---------|
| Specs location | `.bonfire/specs/` or `specs/` |
| Docs location | `.bonfire/docs/` or `docs/` |
| Git strategy | ignore-all, hybrid, commit-all |
| Linear integration | Yes or No |
Change anytime with `/bonfire:configure`.
### Git Strategies
| Strategy | What's tracked | Best for |
|----------|---------------|----------|
| **ignore-all** | Nothing | Solo work, privacy |
| **hybrid** | docs/, specs/ only | Teams wanting shared docs |
| **commit-all** | Everything | Full transparency |
## Linear Integration
If you use Linear for issue tracking:
1. Install [Linear MCP](https://github.com/anthropics/anthropic-quickstarts/tree/main/mcp-linear)
2. Enable via `/bonfire:configure`
3. Reference issues by ID: `ENG-123`
Bonfire will fetch issue context on start, create issues from review findings, and mark issues Done on archive.
## Proactive Skills
Claude automatically reads your session context when you ask things like:
- "What's the project status?"
- "What were we working on?"
- "What decisions have we made?"
And suggests archiving when you merge PRs or mention shipping.
## Requirements
- [Claude Code CLI](https://claude.ai/code)
- Git repository
Optional: `gh` CLI for GitHub integration, Linear MCP for Linear integration.
## Learn More
**Blog post**: [Save Your Progress](https://vieko.dev/bonfire)
**Changelog**: [CHANGELOG.md](CHANGELOG.md)
## License
MIT © [Vieko Franetovic](https://vieko.dev)

View File

@@ -1,90 +0,0 @@
---
name: codebase-explorer
description: Fast codebase exploration for patterns, architecture, and constraints. Use for research phases in spec and document commands.
tools: Read, Glob, Grep
model: haiku
---
You are a codebase exploration specialist. Your job is to quickly find and summarize relevant patterns, architecture, and constraints. Return structured findings, not raw file contents.
## Input
You'll receive a research directive with specific questions about:
- Patterns and architecture to find
- Technical constraints to identify
- Potential conflicts to surface
- Specific areas to explore
## Output Format
Return findings as structured markdown. Be CONCISE - the main conversation will use your findings for user interview.
```markdown
## Patterns Found
- **[Pattern name]**: Found in `path/to/file.ts` - [1-2 sentence description]
## Key Files
| File | Role |
|------|------|
| `path/to/file.ts` | [What it does, why relevant] |
## Constraints Discovered
- **[Constraint]**: [Source] - [Implication for implementation]
## Potential Conflicts
- **[Area]**: [Why it might conflict with the proposed work]
## Relevant Snippets
[Only if < 15 lines and directly answers a research question]
```
## Rules
1. **DO NOT** return entire file contents
2. **DO NOT** include files that aren't directly relevant
3. **BE CONCISE** - aim for < 100 lines total output
4. **ANSWER** the research questions, don't just explore randomly
5. **PRIORITIZE** - most important findings first
6. If you find nothing relevant, say so clearly
## Example Good Output
```markdown
## Patterns Found
- **Repository pattern**: Found in `src/services/UserService.ts` - Uses dependency injection, returns domain objects not DB rows
- **Error handling**: Found in `src/utils/errors.ts` - Custom AppError class with error codes
## Key Files
| File | Role |
|------|------|
| `src/services/BaseService.ts` | Abstract base class all services extend |
| `src/types/index.ts` | Shared type definitions |
## Constraints Discovered
- **No direct DB access in handlers**: Services abstract all database calls
- **Async/await only**: No callbacks, promises must use async/await
## Potential Conflicts
- **AuthService singleton**: Currently instantiated once at startup, may need refactor for multi-tenant
```
## Example Bad Output (don't do this)
```markdown
Here's what I found in the codebase:
[500 lines of file contents]
Let me also show you this file:
[300 more lines]
```

View File

@@ -1,101 +0,0 @@
---
name: spec-writer
description: Synthesizes research findings and interview answers into implementation specs. Use after codebase exploration and user interview.
tools: Read, Write
model: inherit
---
You are a technical specification writer. Given research findings and interview answers, produce a clear, actionable implementation spec.
## Input
You'll receive:
1. **Research findings** - Structured output from codebase-explorer
2. **Interview Q&A** - User's answers to clarifying questions
3. **Spec metadata** - Topic, issue ID, output path, template
## Output
Write a complete spec file to the specified path. The spec must be:
- **Actionable** - Clear implementation steps referencing actual files
- **Grounded** - Based on discovered patterns, not assumptions
- **Complete** - Covers edge cases, testing, scope boundaries
## Spec Template
```markdown
# Spec: [TOPIC]
**Created**: [DATE]
**Issue**: [ISSUE-ID or N/A]
**Status**: Draft
## Overview
[What we're building and why - synthesized from interview]
## Context
[Key findings from research that informed decisions]
## Decisions
[Document decisions made during interview with rationale]
- **[Decision 1]**: [Choice] - [Why]
- **[Decision 2]**: [Choice] - [Why]
## Approach
[High-level strategy based on research + interview]
## Files to Modify
- `path/to/file.ts` - [what changes]
## Files to Create
- `path/to/new.ts` - [purpose]
## Implementation Steps
1. [ ] Step one (reference actual files)
2. [ ] Step two
3. [ ] Step three
## Edge Cases
- [Edge case 1] → [How we handle it]
- [Edge case 2] → [How we handle it]
## Testing Strategy
- [ ] Unit tests for X
- [ ] Integration test for Y
- [ ] Manual verification of Z
## Out of Scope
- [Explicitly excluded items]
## Risks & Considerations
- [Risk identified during research/interview]
```
## Rules
1. **Ground in research** - Reference actual files and patterns discovered
2. **Honor interview answers** - Don't override user decisions
3. **Be specific** - "Update UserService.ts" not "Update the service"
4. **Don't invent** - If something wasn't discussed, don't add it
5. **Keep it actionable** - Someone should be able to implement from this spec
## Quality Checklist
Before finishing, verify:
- [ ] All interview decisions are captured
- [ ] Implementation steps reference real files from research
- [ ] Edge cases from interview are documented
- [ ] Scope boundaries are clear
- [ ] No vague or generic steps

View File

@@ -1,121 +0,0 @@
---
name: work-reviewer
description: Strategic code review for blindspots, gaps, and improvements. Returns categorized findings with severity and effort estimates.
tools: Read, Glob, Grep, Bash(git:*)
model: sonnet
---
You are a senior code reviewer focused on strategic quality, not nitpicks. Your job is to find what the developer might have missed.
## Input
You'll receive:
1. **Review scope** - Branch diff, specific files, or session context
2. **Intent** - What was the developer trying to accomplish
3. **Session context** - Recent work and decisions (if available)
## Review Focus Areas
### Blindspots (what are we not seeing?)
- Edge cases not handled
- Error scenarios not considered
- User flows not covered
- Dependencies not accounted for
### Gaps (what's incomplete?)
- Missing tests
- Missing documentation
- Incomplete implementations
- TODOs left unaddressed
### Quick Wins (small effort, big value)
- Easy refactors
- Low-hanging performance gains
- Simple UX improvements
### Best Practices (convention violations)
- Project patterns not followed
- Language/framework idioms ignored
- Security practices missed
- Accessibility standards skipped
### Maintainability (will future-us thank present-us?)
- Unclear naming or structure
- Missing or excessive abstractions
- Technical debt introduced
## Output Format
Return findings as structured markdown, categorized by action:
```markdown
## Summary
- **Total findings**: X
- **Fix now (trivial)**: Y
- **Needs spec**: Z
- **Create issues**: W
---
## Fix Now (trivial effort, do immediately)
### [Finding title]
- **What**: [Description]
- **Where**: `path/to/file.ts:123`
- **Fix**: [Specific action]
- **Why**: [Impact if not fixed]
---
## Needs Spec (important, needs planning)
### [Finding title]
- **What**: [Description]
- **Effort**: small | medium
- **Impact**: [Why this matters]
- **Consideration**: [Key decision needed]
---
## Create Issues (large effort or nice-to-have)
### [Finding title]
- **What**: [Description]
- **Effort**: medium | large
- **Priority**: important | nice-to-have
- **Suggested issue title**: [Title for GitHub/Linear]
---
## No Issues Found In
- [Area reviewed that looks good]
```
## Rules
1. **Strategic, not pedantic** - Skip style nitpicks, focus on substance
2. **Consider intent** - Review against what they were trying to do
3. **Categorize by action** - Fix now vs spec vs issue
4. **Estimate effort** - trivial/small/medium/large
5. **Be specific** - Include file paths and line numbers
6. **Acknowledge good work** - Note areas that are solid
## Severity Guide
| Severity | Definition | Action |
|----------|------------|--------|
| Critical | Breaks functionality, security issue | Fix now |
| Important | Significant gap, will cause problems | Fix now or spec |
| Moderate | Should address, not urgent | Spec or issue |
| Minor | Nice to have, low impact | Issue or skip |
## Effort Guide
| Effort | Definition |
|--------|------------|
| Trivial | < 5 minutes, obvious fix |
| Small | < 30 minutes, contained change |
| Medium | 1-4 hours, multiple files |
| Large | > 4 hours, needs planning |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

View File

@@ -1,126 +0,0 @@
---
description: Archive completed session work
allowed-tools: Bash(git:*), Read, Write, Glob, mcp__linear__*
model: haiku
---
# Archive Session
## Step 1: Find Git Root
Run `git rev-parse --show-toplevel` to locate the repository root.
## Step 2: Review Completed Work
Read `<git-root>/.bonfire/index.md` and identify completed work:
- Sessions with merged PRs
- Completed features/tasks
- Work that's no longer active
## Step 3: Create Archive Entry
Move completed session content to `<git-root>/.bonfire/archive/`.
**Naming convention**: `YYYY-MM-DD-<issue-id>-<topic>.md`
Examples:
- `2025-12-22-GTMENG-387-inbound-improvements.md` (with issue ID)
- `2025-12-22-fix-login-redirect.md` (without issue ID)
Use this template:
```markdown
# [TOPIC]
**Date**: [DATE]
**Issue**: [ISSUE-ID or N/A]
**PR**: [PR link if available]
**Status**: Completed
---
## Summary
[Brief description of what was accomplished]
## Accomplished
- [List of completed items]
## Decisions Made
- [Key decisions and rationale]
## Impact
- [Before/after metrics if applicable]
- Files changed: [count]
## Related
- [Links to related docs, specs, or code]
```
## Step 4: Clean Up Index
Update `<git-root>/.bonfire/index.md`:
- Remove archived session entries from Recent Sessions
- Keep Current State focused on active work
- Update Next Session Priorities
- Add link to archive file in Archived Sessions section:
```markdown
## Archived Sessions
- [YYYY-MM-DD - Topic](archive/YYYY-MM-DD-issue-topic.md)
```
## Step 5: Clean Up Specs (if applicable)
Read `specsLocation` from `<git-root>/.bonfire/config.json` (default `.bonfire/specs/`).
Check if any specs in the configured location are now complete:
- If the spec was fully implemented, delete the spec file (archive has the record)
- If the spec has reusable reference material, move that content to `docs/` first
## Step 6: Update Linear Issue (if applicable)
Read `<git-root>/.bonfire/config.json` and check `linearEnabled`.
**If `linearEnabled` is true**:
1. Check if archived work references a Linear issue (look in session context for `[A-Z]+-[0-9]+` pattern)
2. If Linear issue found, ask user: "Mark Linear issue [ISSUE-ID] as Done?"
3. If user confirms:
- Use Linear MCP `linear_update_issue` tool with:
- `id`: The issue ID (e.g., `ENG-123`)
- `status`: Set to "Done" or completed state
- Optionally use `linear_add_comment` to add link to archive/PR
4. On failure: Warn user - "Couldn't update Linear issue. You may need to update it manually."
Note: Tool names may vary by Linear MCP implementation.
**If `linearEnabled` is false or not set**: Skip this step.
## Step 7: Commit Archive (if tracked)
Read `gitStrategy` from `<git-root>/.bonfire/config.json`.
**If gitStrategy is "ignore-all"**: Skip committing - archive is local only.
**If gitStrategy is "hybrid" or "commit-all"**:
1. **NEVER use `git add -f`** - respect gitignore
2. Stage unignored files:
```bash
git add .bonfire/
```
3. Check if anything was staged before committing:
```bash
git diff --cached --quiet .bonfire/ || git commit -m "docs: archive completed session work"
```
## Step 8: Confirm
Report:
- What was archived
- Any specs cleaned up
- Current state of index.md
- Ready for next session

View File

@@ -1,99 +0,0 @@
---
description: Change project settings (locations, git strategy, Linear)
allowed-tools: Bash(git:*), Read, Write, AskUserQuestion
model: haiku
---
# Configure Bonfire
Always runs interactively - asks all configuration questions regardless of arguments.
## Step 1: Find Git Root
Run `git rev-parse --show-toplevel` to locate the repository root.
## Step 2: Check for Bonfire Directory
If `<git-root>/.bonfire/` does not exist, tell the user to run `/bonfire:start` first.
## Step 3: Read Current Config
Read `<git-root>/.bonfire/config.json` if it exists to see current settings.
## Step 4: Ask All Configuration Questions
Use AskUserQuestion to ask configuration questions (4 questions, one round):
1. "Where should specs be saved?" (Header: "Specs")
- .bonfire/specs/ (Recommended) - Keep with session context
- specs/ - Project root level
2. "Where should docs be saved?" (Header: "Docs")
- .bonfire/docs/ (Recommended) - Keep with session context
- docs/ - Project root level
3. "How should `.bonfire/` be handled in git?" (Header: "Git")
- ignore-all (Recommended) - Keep sessions private/local
- hybrid - Commit docs/specs, keep notes private
- commit-all - Share everything with team
4. "Enable Linear MCP integration?" (Header: "Linear")
- No (Recommended) - Skip Linear integration
- Yes - Fetch/create Linear issues (requires Linear MCP)
## Step 5: Update Config
**Completely overwrite** `<git-root>/.bonfire/config.json` with only these fields (do not preserve old fields like `models`):
```json
{
"specsLocation": "<user-answer>",
"docsLocation": "<user-answer>",
"gitStrategy": "<user-answer>",
"linearEnabled": <true-or-false>
}
```
## Step 6: Update Git Strategy
If git strategy or locations changed, update `<git-root>/.bonfire/.gitignore`:
**Ignore all**:
```
*
!.gitignore
```
**Hybrid** (only include dirs that are inside .bonfire/):
```
*
!.gitignore
```
If docsLocation is `.bonfire/docs/`, add:
```
!docs/
!docs/**
```
If specsLocation is `.bonfire/specs/`, add:
```
!specs/
!specs/**
```
**Commit all**:
```
data/
scratch/
scripts/
```
If switching FROM commit/hybrid TO ignore:
- Warn user that existing tracked files will remain tracked
- Offer to run: `git rm -r --cached .bonfire/`
## Step 7: Confirm
Report:
- Settings updated
- Any manual steps needed (git cleanup)
- New configuration summary

View File

@@ -1,114 +0,0 @@
---
description: Create documentation about a topic in the codebase
allowed-tools: Read, Write, Bash(git:*), Task
---
# Document Topic
Create reference documentation using subagent for research, preserving main context.
## Step 1: Find Git Root
Run `git rev-parse --show-toplevel` to locate the repository root.
## Step 2: Check Config
Read `<git-root>/.bonfire/config.json` if it exists.
**Docs location**: Read `docsLocation` from config. Default to `.bonfire/docs/` if not set.
## Step 3: Understand the Topic
The topic to document is: $ARGUMENTS
If no topic provided, ask the user what they want documented.
## Step 4: Explore the Codebase (Subagent)
Use the Task tool to invoke the **codebase-explorer** subagent for research.
Provide a research directive:
```
Research the codebase to document: [TOPIC]
Find:
1. **Architecture**: How this system/feature is structured, key components
2. **Key Files**: Important files and their roles
3. **Flow**: How data/control flows through the system
4. **Patterns**: Design patterns and conventions used
5. **Gotchas**: Important details, edge cases, things to watch out for
Return structured findings with file paths and brief descriptions.
```
**Wait for the subagent to return findings** before proceeding.
The subagent runs in isolated context (haiku model, fast), preserving main context for writing.
## Step 5: Create Documentation
**Naming convention**: `<topic>.md` (kebab-case)
Examples:
- `inbound-agent-architecture.md`
- `sampling-strategies.md`
- `authentication-flow.md`
Write the documentation to `<git-root>/<docsLocation>/<topic>.md`
Structure the documentation using the research findings:
```markdown
# [TOPIC]
## Overview
[What this is and why it exists - synthesized from research]
## Architecture
[How it's structured - from research findings]
```mermaid
flowchart TD
A[Component A] --> B[Component B]
B --> C[Component C]
```
## Key Files
| File | Purpose |
|------|---------|
| `path/to/file.ts` | [From research findings] |
| `path/to/other.ts` | [From research findings] |
## How It Works
[Step-by-step flow and behavior - from research]
## Usage Examples
[Code examples, CLI commands, etc.]
## Gotchas
- [From research findings]
- [Common mistakes or edge cases]
## Related
- [Link to related doc](other-doc.md)
- [Code reference]: `path/to/file.ts`
```
## Step 6: Link to Session Context
Add a reference to the doc in `<git-root>/.bonfire/index.md` under Key Resources or Notes.
## Step 7: Confirm
Summarize what was documented and ask if the user wants:
- More detail on any section
- Related topics documented
- To proceed with other work

View File

@@ -1,84 +0,0 @@
---
description: End session - update context and commit changes
allowed-tools: Bash(git:*), Bash(rm:*), Bash(mv:*), Bash(mkdir:*), Read, Write, Glob, AskUserQuestion
model: haiku
---
# End Session
## Step 1: Find Git Root
Run `git rev-parse --show-toplevel` to locate the repository root.
## Step 2: Review Session Work
Review what was accomplished this session by examining:
- Recent git commits
- Files changed
- Conversation context
## Step 3: Update Session Context
Update `<git-root>/.bonfire/index.md`:
1. Update the session entry with:
- **Accomplished**: List what was completed
- **Decisions**: Key decisions made and rationale
- **Files Modified**: Important files changed (if relevant)
- **Blockers**: Any issues encountered
2. Update "Next Session Priorities" based on remaining work
3. Update "Current State" to reflect new status
## Step 4: Manage Session Scripts
Check if `<git-root>/.bonfire/scripts/` exists and contains any files.
**If scripts exist**, use AskUserQuestion to ask what to do with each script:
"What should happen to these session scripts?" (Header: "Scripts", multiSelect: false)
For each script found, present options:
- **Keep** - Leave in `.bonfire/scripts/` for next session
- **Move to project** - Move to `<git-root>/scripts/` (create if needed)
- **Delete** - Remove the script
Execute the user's choices:
- **Keep**: No action needed
- **Move to project**: `mkdir -p <git-root>/scripts/ && mv <script> <git-root>/scripts/`
- **Delete**: `rm <script>`
**If no scripts exist**, skip this step.
## Step 5: Commit Changes (if tracked)
Read `<git-root>/.bonfire/config.json` to check `gitStrategy`.
**If gitStrategy is "ignore-all"**: Skip committing - nothing is tracked. Tell the user session context was updated locally.
**If gitStrategy is "hybrid" or "commit-all"**:
1. **Check what can be staged**: Run `git status .bonfire/` to see what files are not ignored
2. **NEVER use `git add -f`** - if a file is gitignored, respect that
3. **Stage only unignored files**:
```bash
git add .bonfire/
```
4. **Check if anything was staged**: Run `git diff --cached --quiet .bonfire/`
- If nothing staged (exit code 0), tell user "Session context updated locally (files are gitignored)"
- If changes staged, commit:
```bash
git commit -m "docs: update session context"
```
If the commit fails due to hooks, help resolve the issue (but never bypass hooks with `--no-verify`).
## Step 6: Confirm
Summarize:
- What was documented
- Next priorities
- Any follow-up needed
Let the user know they can run `/bonfire:archive` when this work is merged and complete.

View File

@@ -1,94 +0,0 @@
---
description: Change how .bonfire/ is handled in git
allowed-tools: Bash(git:*), Read, Write, AskUserQuestion
model: haiku
---
# Change Git Strategy
## Step 1: Find Git Root
Run `git rev-parse --show-toplevel` to locate the repository root.
## Step 2: Read Current Config
Read `<git-root>/.bonfire/config.json` to check current `specsLocation` and `docsLocation` settings.
## Step 3: Explain Options
Present the git strategy options:
1. **Ignore all** - Keep sessions completely local
- Everything in .bonfire/ is gitignored
- Most private, nothing shared
- Good for: solo work, sensitive projects
2. **Hybrid** - Commit docs/specs, keep notes private
- docs/ and specs/ are committed
- index.md and archive/ stay local
- Good for: teams that want shared docs but private notes
3. **Commit all** - Share everything with team
- All session content is committed
- Only data/ and scratch/ ignored
- Good for: full transparency, team continuity
## Step 4: Get User Choice
Use AskUserQuestion to ask which strategy:
"Which git strategy for `.bonfire/`?" (Header: "Git")
- ignore-all (Recommended) - Keep sessions private/local
- hybrid - Commit docs/specs, keep notes private
- commit-all - Share everything with team
## Step 5: Update .gitignore
Write the appropriate `.gitignore` to `<git-root>/.bonfire/.gitignore`:
**Ignore all**:
```
*
!.gitignore
```
**Hybrid** (only include dirs that are inside .bonfire/):
```
*
!.gitignore
```
If docsLocation is `.bonfire/docs/`, add:
```
!docs/
!docs/**
```
If specsLocation is `.bonfire/specs/`, add:
```
!specs/
!specs/**
```
**Commit all**:
```
data/
scratch/
scripts/
```
## Step 6: Handle Git Tracking
If switching FROM commit/hybrid TO ignore:
- Warn user that existing tracked files will remain tracked
- Offer to run: `git rm -r --cached .bonfire/` (removes from git but keeps files)
- They'll need to commit this change
If switching TO commit/hybrid:
- Files will be picked up on next commit
- No special action needed
## Step 7: Confirm
Report:
- New strategy applied
- Any manual steps needed
- How to verify the change

View File

@@ -1,119 +0,0 @@
---
description: Review work for blindspots, gaps, and improvements
allowed-tools: Bash(git:*), Bash(gh:*), Read, Write, Task, mcp__linear__*
---
# Review Work
Strategic review using subagent for analysis, preserving main context for action decisions.
## Step 1: Determine Scope
Based on $ARGUMENTS:
- No args: Review current branch vs base
- `--session`: Review work captured in current session context
- Topic/area: Focus review on specific aspect
## Step 2: Gather Context
- Read session context from `<git-root>/.bonfire/index.md`
- Get branch diff: `git diff main...HEAD` (or appropriate base)
- Read relevant specs/docs from `.bonfire/`
- Understand intent: what were we trying to accomplish?
## Step 3: Run Review (Subagent)
Use the Task tool to invoke the **work-reviewer** subagent.
Provide the review context:
```
Review this work for blindspots, gaps, and improvements.
**Scope**: [Branch diff / session work / specific area]
**Intent**: [What we were trying to accomplish - from session context]
**Files changed**:
[List of modified files from git diff]
**Session context**:
[Relevant notes from index.md]
Return categorized findings with severity and effort estimates.
```
**Wait for the subagent to return findings** before proceeding.
The subagent runs in isolated context (sonnet model), preserving main context for action decisions.
## Step 4: Session Scripts Review
Check if `<git-root>/.bonfire/scripts/` contains any files.
If scripts exist, include in findings:
- List scripts that may need attention
- Note if any appear to be temporary/one-off vs reusable
- Suggest moving useful scripts to project `scripts/` directory
This is informational - actual script management happens during `/bonfire:end`.
## Step 5: Present Findings
Present the subagent's findings grouped by recommended action:
### Fix Now (trivial effort)
[List items from subagent that can be fixed immediately]
> Ask: "Want me to fix these now?"
### Needs Spec (important, needs planning)
[List items that need implementation planning]
> Ask: "Want me to create an implementation spec?"
### Create Issues (large effort or nice-to-have)
[List items for future sessions]
> Ask: "Want me to create GitHub/Linear issues?"
## Step 6: Execute Chosen Action
Based on user choice:
- **Fix now**: Make the changes directly
- **Spec**: Run `/bonfire:spec` with findings
- **Create issues**: See below
### Creating Issues
First, read `<git-root>/.bonfire/config.json` and check `linearEnabled`.
**Offer choices based on config:**
- Always offer: "Create GitHub issue"
- If `linearEnabled` is true: Also offer "Create Linear issue"
**GitHub Issue Creation:**
```bash
gh issue create --title "Finding title" --body "Finding details"
```
**Linear Issue Creation (if enabled):**
1. Use Linear MCP `linear_create_issue` tool with:
- `title`: Finding summary
- `description`: Finding details with context
- `teamId`: Infer from session context if available, otherwise use default
2. On success: Return issue URL/ID
3. On failure: Warn user, offer to create GitHub issue instead
Note: Tool names may vary by Linear MCP implementation.
**For each created issue:**
- Record the issue ID and URL
- Note which tracker (GitHub/Linear) was used
## Step 7: Update Session Context
Add review outcomes to `<git-root>/.bonfire/index.md`:
- Key findings noted
- Issues created (with links)
- Work deferred to future sessions

View File

@@ -1,149 +0,0 @@
---
description: Create an implementation spec for a feature or task
allowed-tools: Read, Write, Bash(git:*), AskUserQuestion, Task
---
# Create Implementation Spec
A hybrid approach using subagents: research in isolated context, interview in main context, write in isolated context.
## Step 1: Find Git Root
Run `git rev-parse --show-toplevel` to locate the repository root.
## Step 2: Check Config
Read `<git-root>/.bonfire/config.json` if it exists.
**Specs location**: Read `specsLocation` from config. Default to `.bonfire/specs/` if not set.
## Step 3: Gather Initial Context
Get the topic from $ARGUMENTS or ask if unclear.
Check for existing context:
- Read `<git-root>/.bonfire/index.md` for project state
- Check for `SPEC.md` or `spec.md` in git root (user's spec template)
- If issue ID provided, note for filename
## Step 4: Research Phase (Subagent)
Use the Task tool to invoke the **codebase-explorer** subagent for research.
Provide a research directive with these questions:
```
Research the codebase for implementing: [TOPIC]
Find:
1. **Patterns & Architecture**: How similar features are implemented, existing abstractions to reuse, naming conventions
2. **Technical Constraints**: Dependencies, API boundaries, performance considerations
3. **Potential Conflicts**: Files that need changes, intersections with existing code, migration concerns
Return structured findings only - no raw file contents.
```
**Wait for the subagent to return findings** before proceeding.
The subagent runs in isolated context (haiku model, fast), preserving main context for interview.
## Step 5: Interview Phase (Main Context)
Using the research findings, interview the user with **informed questions** via AskUserQuestion.
### Round 1: Core Decisions
Ask about fundamental approach based on patterns found:
Example questions (adapt based on actual findings):
- "I found [Pattern A] in `services/` and [Pattern B] in `handlers/`. Which pattern should this feature follow?"
- "The existing [Component] handles [X]. Should we extend it or create a new [Y]?"
- "I see [Library] is used for [purpose]. Should we use it here or try [Alternative]?"
### Round 2: Edge Cases & Tradeoffs
Based on Round 1 answers and research, ask about:
- Error handling approach
- Edge cases identified in research
- Performance vs simplicity tradeoffs
- User experience considerations
Example questions:
- "What should happen when [edge case from research]?"
- "I found [potential conflict]. How should we handle it?"
- "[Approach A] is simpler but [tradeoff]. [Approach B] is more complex but [benefit]. Preference?"
### Round 3: Scope & Boundaries (if needed)
If scope is still unclear:
- What's explicitly out of scope?
- MVP vs full implementation?
- Dependencies on other work?
### Continue Until Complete
Keep asking rounds of questions until you have clarity on:
- [ ] Core approach and architecture
- [ ] Key technical decisions
- [ ] Error handling strategy
- [ ] Edge cases covered
- [ ] Testing approach
- [ ] Scope boundaries
Tell the user "I have enough to write the spec" when ready.
## Step 6: Write the Spec (Subagent)
Use the Task tool to invoke the **spec-writer** subagent.
Provide:
1. **Research findings** from Step 4
2. **Interview Q&A** from Step 5
3. **Metadata**: topic, issue ID, output path (`<git-root>/<specsLocation>/<filename>.md`)
The subagent will write the spec file directly.
**Naming convention**: `<issue-id>-<topic>.md` or `<topic>.md`
## Step 7: Link to Session Context
Add a reference to the spec in `<git-root>/.bonfire/index.md` under Current State.
## Step 8: Confirm
Read the generated spec and present a summary. Ask if user wants to:
- Proceed with implementation
- Refine specific sections
- Add more detail to any area
- Save for later
## Interview Tips
**Good questions are:**
- Informed by research (not generic)
- About tradeoffs (not yes/no)
- Specific to the codebase
- Non-obvious (user wouldn't think to mention)
**Bad questions:**
- "What features do you want?" (too broad)
- "Should we add error handling?" (obvious)
- Generic without codebase context
**Examples of good informed questions:**
- "I found `UserService` uses repository pattern but `OrderService` uses direct DB access. Which approach?"
- "The `auth` middleware validates JWT but doesn't check permissions. Should this feature add permission checks or assume auth is enough?"
- "There's a `BaseController` with shared logic. Extend it or keep this feature standalone?"
## Spec Lifecycle
Specs are **temporary artifacts** - they exist to guide implementation:
1. **Draft** → Created, ready for review
2. **In Progress** → Being implemented
3. **Completed** → Implementation done
**When a spec is fully implemented**:
- If it contains reusable reference material, move to `docs/`
- Delete the spec file - archive has the record
- Don't let specs accumulate

View File

@@ -1,246 +0,0 @@
---
description: Start a new session - reads context and scaffolds .bonfire/ if needed
allowed-tools: Bash(git:*), Bash(gh:*), Bash(mkdir:*), Read, Write, Glob, AskUserQuestion, mcp__linear__*
model: haiku
---
# Start Session
## Step 1: Find Git Root
Run `git rev-parse --show-toplevel` to locate the repository root. All session files live at `<git-root>/.bonfire/`.
## Step 2: Check for Bonfire Directory
Check if `<git-root>/.bonfire/index.md` exists.
**If .bonfire/ does NOT exist**, scaffold it:
1. Tell the user: "No bonfire directory found. Let me set that up for you."
2. Use AskUserQuestion to ask setup questions (4 questions, one round):
1. "Where should specs be saved?" (Header: "Specs")
- .bonfire/specs/ (Recommended) - Keep with session context
- specs/ - Project root level
2. "Where should docs be saved?" (Header: "Docs")
- .bonfire/docs/ (Recommended) - Keep with session context
- docs/ - Project root level
3. "How should `.bonfire/` be handled in git?" (Header: "Git")
- ignore-all (Recommended) - Keep sessions private/local
- hybrid - Commit docs/specs, keep notes private
- commit-all - Share everything with team
4. "Enable Linear MCP integration?" (Header: "Linear")
- No (Recommended) - Skip Linear integration
- Yes - Fetch/create Linear issues (requires Linear MCP)
3. Create the directory structure based on user choices:
**Always create in .bonfire/**:
```
.bonfire/
├── index.md
├── config.json
├── archive/
├── scripts/
└── .gitignore
```
**If specsLocation is `.bonfire/specs/`**: create `.bonfire/specs/`
**If specsLocation is `specs/`**: create `<git-root>/specs/`
**If docsLocation is `.bonfire/docs/`**: create `.bonfire/docs/`
**If docsLocation is `docs/`**: create `<git-root>/docs/`
4. Detect project name from: package.json name → git remote → directory name
5. Create `config.json` with user's answers:
```json
{
"specsLocation": "<user-answer>",
"docsLocation": "<user-answer>",
"gitStrategy": "<user-answer>",
"linearEnabled": <true-or-false>
}
```
6. Create `index.md` with template:
```markdown
# Session Context: [PROJECT_NAME]
**Date**: [CURRENT_DATE]
**Status**: Active
**Branch**: main
---
## Current State
[Describe what you're working on]
---
## Recent Sessions
### Session 1 - [CURRENT_DATE]
**Goal**: [What you want to accomplish]
**Accomplished**:
- [List completed items]
**Decisions**:
- [Key decisions made]
**Blockers**: None
---
## Next Session Priorities
1. [Priority items]
---
## Key Resources
**Code References**:
- [Component/feature]: `path/to/file.ts`
**External Links**:
- [Issue Tracker](url)
---
## Archived Sessions
[Links to archived sessions will appear here]
---
## Notes
[Any additional context]
```
7. Create `.gitignore` based on chosen strategy and locations:
**Ignore all**:
```
*
!.gitignore
```
**Hybrid** (only include dirs that are inside .bonfire/):
```
*
!.gitignore
```
If docsLocation is `.bonfire/docs/`, add:
```
!docs/
!docs/**
```
If specsLocation is `.bonfire/specs/`, add:
```
!specs/
!specs/**
```
**Commit all**:
```
data/
scratch/
scripts/
```
**If .bonfire/ EXISTS**, proceed to Step 3.
## Step 3: Check/Update CLAUDE.md
Check if `<git-root>/CLAUDE.md` exists.
**If CLAUDE.md does NOT exist**, create it:
```markdown
# [PROJECT_NAME]
## Quick Context
Read `.bonfire/index.md` for current project state, recent work, and priorities.
## Bonfire Commands
- `/bonfire:start` - Start a session (reads context)
- `/bonfire:end` - End session (updates context)
- `/bonfire:spec` - Create implementation spec
- `/bonfire:document <topic>` - Document a topic
- `/bonfire:review` - Review work for blindspots and improvements
- `/bonfire:archive` - Archive completed work
- `/bonfire:configure` - Change project settings
```
**If CLAUDE.md EXISTS**, check if it references `.bonfire/index.md`. If not, append:
```markdown
## Session Context
Read `.bonfire/index.md` for current project state, recent work, and priorities.
```
## Step 4: Read Session Context
Read `<git-root>/.bonfire/index.md` and report when ready.
Summarize:
- Current state
- Recent work
- Next priorities
Then ask: "What do you want to work on this session?"
## Step 5: Fetch External Context (Optional)
**Only fetch if user provides a new URL or issue ID:**
### Detecting Issue Type
- **GitHub**: Starts with `#`, contains `github.com`, or doesn't match Linear pattern
- **Linear**: Matches `[A-Z]+-[0-9]+` pattern (e.g., `ENG-123`, `ABC-456`) or contains `linear.app`
### GitHub Issues/PRs
Use `gh` CLI:
- `gh pr view [URL] --json title,body,state,labels`
- `gh issue view [URL] --json title,body,state,labels`
### Linear Issues
First, read `<git-root>/.bonfire/config.json` and check `linearEnabled`.
**If `linearEnabled` is false or not set**: Skip Linear, treat as ad-hoc task.
**If `linearEnabled` is true**:
1. Use Linear MCP `linear_search_issues` tool to find the issue by ID (e.g., `ENG-123`)
2. Extract: title, description, state, priority, labels, assignee
3. On success: Summarize the issue context
4. On failure: Warn user - "Couldn't fetch Linear issue. Linear MCP may not be configured. Continue without issue context?"
Note: Tool names may vary by Linear MCP implementation. Common tools: `linear_search_issues`, `linear_create_issue`, `linear_update_issue`.
### Update Session Context
If issue was fetched successfully:
- Add reference to `index.md` under Current State
- Include issue ID, title, and link
- Note the issue tracker type (GitHub/Linear)
### Fallback
If no URL/issue ID provided (continuing work, ad-hoc task):
- Proceed with existing session context
- Session notes are the source of truth for ongoing work
Confirm understanding and ask how to proceed.

View File

@@ -1,53 +0,0 @@
---
description: Suggest archiving completed work when PRs are merged or work is completed. Triggers when user asks to merge a PR ("merge it", "merge the PR"), after successful gh pr merge, or mentions completion ("shipped", "done with X", "merged to main"). Does NOT archive automatically - suggests running /bonfire:archive.
allowed-tools: Read, Glob, Bash(gh:*)
---
# Archive Bonfire Awareness
This skill detects when session work may be ready for archiving and suggests the appropriate action.
## When to Use This Skill
Trigger when:
- User asks to merge: "merge it", "merge the PR", "go ahead and merge", "ship it"
- After you successfully run `gh pr merge`
- User mentions completion: "PR merged", "shipped", "done with X", "finished"
- User references merged state: "merged to main", "closed the issue"
## Instructions
1. If user asks to merge a PR:
- Perform the merge as requested
- On success, continue to step 2
- On failure, help resolve the issue (don't suggest archiving)
2. Find git root and check if `.bonfire/index.md` exists
3. If it exists, read it to assess work state
4. If user provided a PR URL/number (or you just merged one), verify status:
```
gh pr view [URL/number] --json state,mergedAt,title
```
5. Assess if work appears complete:
- PR merged?
- Related tasks marked done in session notes?
- No obvious follow-up work mentioned?
6. If work appears complete, suggest archiving:
> "PR merged successfully. This session's work looks complete - want me to archive it?
> Run `/bonfire:archive` to move completed work to the archive."
7. If there's more work in the session:
> "PR merged. I see there's still [X, Y] in the session notes - want to continue
> with those or archive what's done so far?"
## Important
- This skill **suggests** archiving, it does NOT archive automatically
- User must explicitly run `/bonfire:archive` to perform the archive
- Trigger AFTER merge succeeds, not before
- Multiple PRs may be part of one logical session - check context
- If `.bonfire/` doesn't exist, don't suggest archiving

View File

@@ -1,51 +0,0 @@
---
description: Read project session context from .bonfire/index.md to understand ongoing work, previous decisions, blockers, and history. Use when the user asks about project context, previous sessions, what was worked on before, architectural decisions, blockers, or when they reference "last time", "previously", "the session", or "what we decided".
allowed-tools: Read, Glob
---
# Bonfire Context
This project may use the Bonfire pattern to maintain continuity across AI coding sessions. Context is stored in `.bonfire/index.md` rather than relying on conversation memory.
## When to Use This Skill
Read session context when the user:
- Asks about previous work or decisions
- References "last time", "previously", "before"
- Wants to know about blockers or pending issues
- Asks what the project status is
- Starts a significant task that might have prior context
## Instructions
1. Find the git root: `git rev-parse --show-toplevel`
2. Check if `.bonfire/index.md` exists at the git root
3. If it exists, read it to understand:
- Current project status and recent work
- Active decisions and their rationale
- Known blockers or pending issues
- Links to relevant specs or documentation
4. Check `.bonfire/specs/` if the user asks about implementation specs
5. Check `.bonfire/docs/` if the user asks about documented topics
## File Structure
```
.bonfire/
├── index.md # Main session context (read this first)
├── config.json # Project settings
├── archive/ # Completed work history
├── docs/ # Topic documentation
└── specs/ # Implementation specs
```
## Important
- This skill is for **reading** context, not updating it
- Session updates happen via `/bonfire:end` command
- Don't modify `.bonfire/index.md` unless explicitly asked
- If `.bonfire/` doesn't exist, the project may not use this pattern

View File

@@ -1,6 +1,6 @@
{
"context7": {
"type": "http",
"url": "https://mcp.context7.com/mcp"
"command": "npx",
"args": ["-y", "@upstash/context7-mcp"]
}
}

View File

@@ -1,72 +0,0 @@
# Context7 Plugin for Claude Code
Context7 solves a common problem with AI coding assistants: outdated training data and hallucinated APIs. Instead of relying on stale knowledge, Context7 fetches current documentation directly from source repositories.
## What's Included
This plugin provides:
- **MCP Server** - Connects Claude Code to Context7's documentation service
- **Skills** - Auto-triggers documentation lookups when you ask about libraries
- **Agents** - A dedicated `docs-researcher` agent for focused lookups
- **Commands** - `/context7:docs` for manual documentation queries
## Installation
Install the plugin from the official marketplace:
```bash
claude plugin install context7@claude-plugins-official
```
## Available Tools
### resolve-library-id
Searches for libraries and returns Context7-compatible identifiers.
```
Input: "next.js"
Output: { id: "/vercel/next.js", name: "Next.js", versions: ["v15.1.8", "v14.2.0", ...] }
```
### query-docs
Fetches documentation for a specific library, ranked by relevance to your question.
```
Input: { libraryId: "/vercel/next.js", query: "app router middleware" }
Output: Relevant documentation snippets with code examples
```
## Usage Examples
The plugin works automatically when you ask about libraries:
- "How do I set up authentication in Next.js 15?"
- "Show me React Server Components examples"
- "What's the Prisma syntax for relations?"
For manual lookups, use the command:
```
/context7:docs next.js app router
/context7:docs /vercel/next.js/v15.1.8 middleware
```
Or spawn the docs-researcher agent when you want to keep your main context clean:
```
spawn docs-researcher to look up Supabase auth methods
```
## Version Pinning
To get documentation for a specific version, include the version in the library ID:
```
/vercel/next.js/v15.1.8
/supabase/supabase/v2.45.0
```
The `resolve-library-id` tool returns available versions, so you can pick the one that matches your project.

View File

@@ -1,40 +0,0 @@
---
name: docs-researcher
description: Lightweight agent for fetching library documentation without cluttering your main conversation context.
model: sonnet
---
You are a documentation researcher specializing in fetching up-to-date library and framework documentation from Context7.
## Your Task
When given a question about a library or framework, fetch the relevant documentation and return a concise, actionable answer with code examples.
## Process
1. **Identify the library**: Extract the library/framework name from the user's question.
2. **Resolve the library ID**: Call `resolve-library-id` with:
- `libraryName`: The library name (e.g., "react", "next.js", "prisma")
- `query`: The user's full question for relevance ranking
3. **Select the best match**: From the results, pick the library with:
- Exact or closest name match
- Highest benchmark score
- Appropriate version if the user specified one (e.g., "React 19" → look for v19.x)
4. **Fetch documentation**: Call `query-docs` with:
- `libraryId`: The selected Context7 library ID (e.g., `/vercel/next.js`)
- `query`: The user's specific question for targeted results
5. **Return a focused answer**: Summarize the relevant documentation with:
- Direct answer to the question
- Code examples from the docs
- Links or references if available
## Guidelines
- Pass the user's full question as the query parameter for better relevance
- When the user mentions a version (e.g., "Next.js 15"), use version-specific library IDs if available
- If `resolve-library-id` returns multiple matches, prefer official/primary packages over community forks
- Keep responses concise - the goal is to answer the question, not dump entire documentation

View File

@@ -1,45 +0,0 @@
---
description: Look up documentation for any library
argument-hint: <library> [query]
---
# /context7:docs
Fetches up-to-date documentation and code examples for a library.
## Usage
```
/context7:docs <library> [query]
```
- **library**: The library name, or a Context7 ID starting with `/`
- **query**: What you're looking for (optional but recommended)
## Examples
```
/context7:docs react hooks
/context7:docs next.js authentication
/context7:docs prisma relations
/context7:docs /vercel/next.js/v15.1.8 app router
/context7:docs /supabase/supabase row level security
```
## How It Works
1. If the library starts with `/`, it's used directly as the Context7 ID
2. Otherwise, `resolve-library-id` finds the best matching library
3. `query-docs` fetches documentation relevant to your query
4. Results include code examples and explanations
## Version-Specific Lookups
Include the version in the library ID for pinned documentation:
```
/context7:docs /vercel/next.js/v15.1.8 middleware
/context7:docs /facebook/react/v19.0.0 use hook
```
This is useful when you're working with a specific version and want docs that match exactly.

View File

@@ -1,53 +0,0 @@
---
name: documentation-lookup
description: This skill should be used when the user asks about libraries, frameworks, API references, or needs code examples. Activates for setup questions, code generation involving libraries, or mentions of specific frameworks like React, Vue, Next.js, Prisma, Supabase, etc.
---
When the user asks about libraries, frameworks, or needs code examples, use Context7 to fetch current documentation instead of relying on training data.
## When to Use This Skill
Activate this skill when the user:
- Asks setup or configuration questions ("How do I configure Next.js middleware?")
- Requests code involving libraries ("Write a Prisma query for...")
- Needs API references ("What are the Supabase auth methods?")
- Mentions specific frameworks (React, Vue, Svelte, Express, Tailwind, etc.)
## How to Fetch Documentation
### Step 1: Resolve the Library ID
Call `resolve-library-id` with:
- `libraryName`: The library name extracted from the user's question
- `query`: The user's full question (improves relevance ranking)
### Step 2: Select the Best Match
From the resolution results, choose based on:
- Exact or closest name match to what the user asked for
- Higher benchmark scores indicate better documentation quality
- If the user mentioned a version (e.g., "React 19"), prefer version-specific IDs
### Step 3: Fetch the Documentation
Call `query-docs` with:
- `libraryId`: The selected Context7 library ID (e.g., `/vercel/next.js`)
- `query`: The user's specific question
### Step 4: Use the Documentation
Incorporate the fetched documentation into your response:
- Answer the user's question using current, accurate information
- Include relevant code examples from the docs
- Cite the library version when relevant
## Guidelines
- **Be specific**: Pass the user's full question as the query for better results
- **Version awareness**: When users mention versions ("Next.js 15", "React 19"), use version-specific library IDs if available from the resolution step
- **Prefer official sources**: When multiple matches exist, prefer official/primary packages over community forks

View File

@@ -1,13 +0,0 @@
{
"name": "stagehand",
"description": "Browser automation skill for Claude Code using Stagehand. Automate web interactions, extract data, and navigate websites using natural language.",
"version": "0.1.0",
"author": {
"name": "Browserbase"
},
"homepage": "https://github.com/browserbase/agent-browse",
"repository": "https://github.com/browserbase/agent-browse",
"keywords": ["browser", "automation", "stagehand", "web-scraping"],
"strict": false
}

View File

@@ -1,104 +0,0 @@
# Stagehand Browser Automation Plugin
Browser automation skill for Claude Code using [Stagehand](https://github.com/browserbase/stagehand). This plugin enables Claude to automate web browser interactions, extract data, and navigate websites using natural language.
## Installation
Install the plugin from the Claude Code marketplace:
```bash
/plugin install stagehand@claude-plugin-directory
```
## Prerequisites
This plugin requires the browser automation CLI tools to be installed separately. The CLI tools are available from the GitHub marketplace.
### Step 1: Add the GitHub Marketplace
```bash
/plugin marketplace add browserbase/agent-browse
```
### Step 2: Install the Browser Automation CLI Plugin
```bash
/plugin install browser-automation@browser-tools
```
### Step 3: Set Up the CLI Tools
After installing the browser-automation plugin, you need to set up the CLI tools:
1. Navigate to the plugin directory (typically `~/.claude/plugins/browser-automation/`)
2. Install dependencies and build:
```bash
npm install
```
3. Link the browser command globally:
```bash
npm link
```
4. Configure your Anthropic API key:
```bash
export ANTHROPIC_API_KEY="your-api-key-here"
```
Or use Claude Code's subscription token (recommended if you have Claude Pro/Max):
```bash
claude setup-token
```
### Step 4: Verify Installation
Test that the browser command is available:
```bash
browser navigate https://example.com
```
## Usage
Once installed and configured, you can use natural language to automate browser tasks:
- *"Go to Hacker News, get the top post comments, and summarize them"*
- *"QA test http://localhost:3000 and fix any bugs you encounter"*
- *"Extract product information from example.com/products"*
Claude will automatically use the browser automation skill when you ask for web-related tasks.
## Features
- **Natural Language Control**: Describe browser actions in plain English
- **Data Extraction**: Extract structured data from web pages
- **Screenshot Capture**: Take screenshots for visual verification
- **Persistent Sessions**: Browser state persists between commands
- **Chrome Profile Integration**: Uses your Chrome profile for cookies and sessions
## Troubleshooting
### Chrome not found
Install Chrome for your platform:
- **macOS** or **Windows**: https://www.google.com/chrome/
- **Linux**: `sudo apt install google-chrome-stable`
### Browser command not found
Make sure you've run `npm link` in the browser-automation plugin directory after installing it.
### API Key Issues
- If you have Claude Pro/Max, use `claude setup-token` (recommended)
- Otherwise, export `ANTHROPIC_API_KEY` in your terminal
- Or create a `.env` file in the plugin directory with your API key
## Resources
- [Stagehand Documentation](https://github.com/browserbase/stagehand)
- [GitHub Marketplace](https://github.com/browserbase/agent-browse)
- [Claude Code Skills Documentation](https://code.claude.com/docs/en/plugins)
## Support
For issues or questions, please visit the [GitHub repository](https://github.com/browserbase/agent-browse).

View File

@@ -1,7 +0,0 @@
{
"name": "terraform",
"description": "The Terraform MCP Server provides seamless integration with Terraform ecosystem, enabling advanced automation and interaction capabilities for Infrastructure as Code (IaC) development.",
"author": {
"name": "HashiCorp"
}
}

View File

@@ -1,12 +0,0 @@
{
"terraform": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e", "TFE_TOKEN=${TFE_TOKEN}",
"hashicorp/terraform-mcp-server:0.3.3"
]
}
}

View File

@@ -0,0 +1,8 @@
{
"name": "mcp-server-dev",
"description": "Skills for designing and building MCP servers that work seamlessly with Claude — guides you through deployment models (remote HTTP, MCPB, local), tool design patterns, auth, and interactive MCP apps.",
"author": {
"name": "Anthropic",
"email": "support@anthropic.com"
}
}

View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,32 @@
# mcp-server-dev
Skills for designing and building MCP servers that work seamlessly with Claude.
## What's inside
Three skills that compose into a full build path:
| Skill | Purpose |
|---|---|
| **`build-mcp-server`** | Entry point. Interrogates the use case, picks deployment model (remote HTTP / MCPB / local stdio), picks tool-design pattern, routes to a specialized skill. |
| **`build-mcp-app`** | Adds interactive UI widgets (forms, pickers, confirm dialogs) rendered inline in chat. Works on remote servers and MCPB bundles. |
| **`build-mcpb`** | Packages a local stdio server with its runtime so users can install it without Node/Python. For servers that must touch the local machine. |
## How it works
`build-mcp-server` is the front door. It asks what you're connecting to, who'll use it, how big the action surface is, and whether you need in-chat UI. From those answers it recommends one of four paths:
- **Remote streamable-HTTP** (the default recommendation for anything wrapping a cloud API) — scaffolded inline
- **MCP app** — hands off to `build-mcp-app`
- **MCPB** — hands off to `build-mcpb`
- **Local stdio prototype** — scaffolded inline with an MCPB upgrade note
Each skill ships reference files for the parts that don't fit in the main instructions: auth flows (DCR/CIMD), tool-description writing, widget templates, manifest schemas, security hardening.
## Usage
Ask Claude to "help me build an MCP server" and the entry skill will trigger. Or invoke directly:
```
/mcp-server-dev:build-mcp-server
```

View File

@@ -0,0 +1,199 @@
---
name: build-mcp-app
description: This skill should be used when the user wants to build an "MCP app", add "interactive UI" or "widgets" to an MCP server, "render components in chat", build "MCP UI resources", make a tool that shows a "form", "picker", "dashboard" or "confirmation dialog" inline in the conversation, or mentions "apps SDK" in the context of MCP. Use AFTER the build-mcp-server skill has settled the deployment model, or when the user already knows they want UI widgets.
version: 0.1.0
---
# Build an MCP App (Interactive UI Widgets)
An MCP app is a standard MCP server that **also serves UI resources** — interactive components rendered inline in the chat surface. Build once, runs in Claude *and* ChatGPT and any other host that implements the apps surface.
The UI layer is **additive**. Under the hood it's still tools, resources, and the same wire protocol. If you haven't built a plain MCP server before, the `build-mcp-server` skill covers the base layer. This skill adds widgets on top.
---
## When a widget beats plain text
Don't add UI for its own sake — most tools are fine returning text or JSON. Add a widget when one of these is true:
| Signal | Widget type |
|---|---|
| Tool needs structured input Claude can't reliably infer | Form |
| User must pick from a list Claude can't rank (files, contacts, records) | Picker / table |
| Destructive or billable action needs explicit confirmation | Confirm dialog |
| Output is spatial or visual (charts, maps, diffs, previews) | Display widget |
| Long-running job the user wants to watch | Progress / live status |
If none apply, skip the widget. Text is faster to build and faster for the user.
---
## Architecture: two deployment shapes
### Remote MCP app (most common)
Hosted streamable-HTTP server. Widget templates are served as **resources**; tool results reference them. The host fetches the resource, renders it in an iframe sandbox, and brokers messages between the widget and Claude.
```
┌──────────┐ tools/call ┌────────────┐
│ Claude │─────────────> │ MCP server │
│ host │<── result ────│ (remote) │
│ │ + widget ref │ │
│ │ │ │
│ │ resources/read│ │
│ │─────────────> │ widget │
│ ┌──────┐ │<── template ──│ HTML/JS │
│ │iframe│ │ └────────────┘
│ │widget│ │
│ └──────┘ │
└──────────┘
```
### MCPB-packaged MCP app (local + UI)
Same widget mechanism, but the server runs locally inside an MCPB bundle. Use this when the widget needs to drive a **local** application — e.g., a file picker that browses the actual local disk, a dialog that controls a desktop app.
For MCPB packaging mechanics, defer to the **`build-mcpb`** skill. Everything below applies to both shapes.
---
## How widgets attach to tools
A tool declares a widget by returning an **embedded resource** in its result alongside (or instead of) text content. The resource's `mimeType` tells the host to render it, and the `text` field carries the widget's HTML.
```typescript
server.tool(
"pick_contact",
"Open an interactive contact picker. The user selects one contact; its ID is returned.",
{
filter: z.string().optional().describe("Optional name/email prefix filter"),
},
async ({ filter }) => {
const contacts = await listContacts(filter);
return {
content: [
{
type: "resource",
resource: {
uri: "ui://widgets/contact-picker",
mimeType: "text/html+skybridge",
text: renderContactPicker(contacts),
},
},
],
};
},
);
```
The host renders the resource in a sandboxed iframe. The widget posts a message back when the user picks something; the host injects that result into the conversation so Claude can continue.
---
## Widget runtime contract
Widgets run in a sandboxed iframe. They talk to the host via `window.parent.postMessage` with a small set of message types. The exact envelope is host-defined — the MCP apps SDK wraps it so you don't hand-roll `postMessage`.
**What widgets can do:**
- Render arbitrary HTML/CSS/JS (sandboxed — no same-origin access to the host page)
- Receive an initial `data` payload from the tool result
- Post a **result** back (ends the interaction, value flows to Claude)
- Post **progress** updates (for long-running widgets)
- Request the host **call another tool** on the same server
**What widgets cannot do:**
- Access the host page's DOM, cookies, or storage
- Make network calls to origins other than your MCP server (CSP-restricted)
- Persist state across renders (each tool call is a fresh iframe)
Keep widgets **small and single-purpose**. A picker picks. A form submits. Don't build a whole sub-app inside the iframe — split it into multiple tools with focused widgets.
---
## Scaffold: minimal form widget
**Tool (TypeScript SDK):**
```typescript
import { renderWidget } from "./widgets";
server.tool(
"create_ticket",
"Open a form to create a support ticket. User fills in title, priority, and description.",
{},
async () => ({
content: [
{
type: "resource",
resource: {
uri: "ui://widgets/create-ticket",
mimeType: "text/html+skybridge",
text: renderWidget("create-ticket", {
priorities: ["low", "medium", "high", "urgent"],
}),
},
},
],
}),
);
```
**Widget template (`widgets/create-ticket.html`):**
```html
<!doctype html>
<meta charset="utf-8" />
<style>
body { font: 14px system-ui; margin: 12px; }
label { display: block; margin-top: 8px; font-weight: 500; }
input, select, textarea { width: 100%; padding: 6px; margin-top: 2px; }
button { margin-top: 12px; padding: 8px 16px; }
</style>
<form id="f">
<label>Title <input name="title" required /></label>
<label>Priority
<select name="priority">
{{#each priorities}}<option>{{this}}</option>{{/each}}
</select>
</label>
<label>Description <textarea name="description" rows="4"></textarea></label>
<button type="submit">Create</button>
</form>
<script type="module">
import { submit } from "https://esm.sh/@modelcontextprotocol/apps-sdk";
document.getElementById("f").addEventListener("submit", (e) => {
e.preventDefault();
const data = Object.fromEntries(new FormData(e.target));
submit(data); // → flows back to Claude as the tool's result
});
</script>
```
`renderWidget` is a ~10-line template function — see `references/widget-templates.md`.
---
## Design notes that save you a rewrite
**One widget per tool.** Resist the urge to build one mega-widget that does everything. One tool → one focused widget → one clear result shape. Claude reasons about these far better.
**Tool description must mention the widget.** Claude only sees the tool description when deciding what to call. "Opens an interactive picker" in the description is what makes Claude reach for it instead of guessing an ID.
**Widgets are optional at runtime.** Hosts that don't support the apps surface fall back to showing the resource as a link or raw text. Your tool should still return something sensible in `content[].text` alongside the widget for that case.
**Don't block on widget results for read-only tools.** A widget that just *displays* data (chart, preview) shouldn't require a user action to complete. Return the display widget *and* a text summary in the same result so Claude can continue reasoning without waiting.
---
## Testing
- **Local:** point Claude desktop's MCP config at `http://localhost:3000/mcp`, trigger the tool, check the widget renders and submits.
- **Host fallback:** disable the apps surface (or use a host without it) and confirm the tool degrades gracefully.
- **CSP:** open browser devtools on the iframe — CSP violations are the #1 reason widgets silently fail.
---
## Reference files
- `references/widget-templates.md` — reusable HTML scaffolds for form / picker / confirm / progress
- `references/apps-sdk-messages.md` — the `postMessage` protocol between widget and host

View File

@@ -0,0 +1,99 @@
# Apps SDK — Widget ↔ Host Message Protocol
Widgets communicate with the MCP host through `window.parent.postMessage`. The apps SDK wraps this in helpers so you rarely touch the raw envelope, but knowing the shape helps when debugging.
---
## Widget → host
### `submit(result)`
Ends the interaction. `result` is returned to Claude as the tool's output (serialized to JSON). The iframe is torn down after this fires.
```js
import { submit } from "@modelcontextprotocol/apps-sdk";
submit({ id: "usr_abc123", action: "selected" });
```
Raw envelope:
```json
{ "type": "mcp:result", "result": { "id": "usr_abc123", "action": "selected" } }
```
### `callTool(name, args)`
Ask the host to invoke **another tool on the same server** and return the result to the widget. Use for widgets that need to fetch more data after initial render (pagination, drill-down).
```js
import { callTool } from "@modelcontextprotocol/apps-sdk";
const page2 = await callTool("list_items", { offset: 20, limit: 20 });
```
Round-trips through the host, so it's slower than embedding all data upfront. Only use when the full dataset is too large to ship in the initial payload.
### `resize(height)`
Tell the host the widget's content height so the iframe can be sized. The SDK auto-calls this on load via `ResizeObserver`; call manually only if your content height changes after an async operation.
---
## Host → widget
### Initial data
The widget's initial payload is **not** a message — it's baked into the HTML by the server (the `__DATA__` substitution pattern). This avoids a round-trip and works even if the message channel is slow to establish.
### `onMessage(handler)`
Subscribe to pushes from the server. Used by progress widgets and anything live-updating.
```js
import { onMessage } from "@modelcontextprotocol/apps-sdk";
onMessage((msg) => {
if (msg.type === "progress") updateBar(msg.percent);
});
```
Server side (TypeScript SDK), push via the notification stream keyed to the tool call's request context. The SDK exposes this as a `notify` callback on the tool handler:
```typescript
server.tool("long_job", "...", schema, async (args, { notify }) => {
for (let i = 0; i <= 100; i += 10) {
await step();
notify({ type: "progress", percent: i, label: `Step ${i / 10}/10` });
}
return { content: [...] };
});
```
---
## Lifecycle
```
1. Claude calls tool
2. Server returns content with embedded resource (mimeType: text/html+skybridge)
3. Host renders resource text in sandboxed iframe
4. Widget hydrates from inline __DATA__
5. (optional) Widget ↔ host messages: callTool, progress pushes
6. Widget calls submit(result)
7. Host tears down iframe, injects result into conversation
8. Claude continues with the result
```
If step 6 never happens (user closes the widget, host times out), the tool call resolves with a cancellation result. Your tool description should account for this — "Returns the selected ID, or null if the user cancels."
---
## CSP gotchas
The iframe sandbox enforces a strict Content Security Policy. Common failures:
| Symptom | Cause | Fix |
|---|---|---|
| Widget renders but JS doesn't run | Inline script blocked | Use `<script type="module">` with SDK import; avoid inline event handlers in HTML attributes |
| `fetch()` fails silently | Cross-origin blocked | Route through `callTool()` instead |
| External CSS doesn't load | `style-src` restriction | Inline your styles in a `<style>` tag |
| Fonts don't load | `font-src` restriction | Use system fonts (`font: 14px system-ui`) |
When in doubt, open the iframe's devtools console — CSP violations log there.

View File

@@ -0,0 +1,140 @@
# Widget Templates
Minimal HTML scaffolds for the common widget shapes. Copy, fill in, ship.
All templates assume the apps-SDK helper is available at an ESM CDN. They're intentionally framework-free — widgets render in a fresh iframe each time, so React/Vue hydration cost usually isn't worth it for something this small.
---
## The render helper
Ten lines of string templating. Good enough for almost every case.
```typescript
import { readFileSync } from "node:fs";
import { join } from "node:path";
const TEMPLATE_DIR = join(import.meta.dirname, "../widgets");
export function renderWidget(name: string, data: unknown): string {
const tpl = readFileSync(join(TEMPLATE_DIR, `${name}.html`), "utf8");
return tpl.replace(
"__DATA__",
JSON.stringify(data).replace(/</g, "\\u003c"),
);
}
```
Every template below hydrates from `<script id="data">__DATA__</script>`. The `<` escape prevents `</script>` injection.
---
## Picker (single-select list)
```html
<!doctype html>
<meta charset="utf-8" />
<script id="data" type="application/json">__DATA__</script>
<style>
body { font: 14px system-ui; margin: 0; }
ul { list-style: none; padding: 0; margin: 0; max-height: 280px; overflow-y: auto; }
li { padding: 10px 14px; cursor: pointer; border-bottom: 1px solid #eee; }
li:hover { background: #f5f5f5; }
.sub { color: #666; font-size: 12px; }
</style>
<ul id="list"></ul>
<script type="module">
import { submit } from "https://esm.sh/@modelcontextprotocol/apps-sdk";
const { items } = JSON.parse(document.getElementById("data").textContent);
const ul = document.getElementById("list");
for (const it of items) {
const li = document.createElement("li");
li.innerHTML = `<div>${it.label}</div><div class="sub">${it.sub ?? ""}</div>`;
li.onclick = () => submit({ id: it.id });
ul.append(li);
}
</script>
```
**Data shape:** `{ items: [{ id, label, sub? }] }`
**Result shape:** `{ id }`
---
## Confirm dialog
```html
<!doctype html>
<meta charset="utf-8" />
<script id="data" type="application/json">__DATA__</script>
<style>
body { font: 14px system-ui; margin: 16px; }
.actions { display: flex; gap: 8px; margin-top: 16px; }
button { padding: 8px 16px; cursor: pointer; }
.danger { background: #d33; color: white; border: none; }
</style>
<p id="msg"></p>
<div class="actions">
<button id="cancel">Cancel</button>
<button id="confirm" class="danger">Confirm</button>
</div>
<script type="module">
import { submit } from "https://esm.sh/@modelcontextprotocol/apps-sdk";
const { message, confirmLabel } = JSON.parse(document.getElementById("data").textContent);
document.getElementById("msg").textContent = message;
if (confirmLabel) document.getElementById("confirm").textContent = confirmLabel;
document.getElementById("confirm").onclick = () => submit({ confirmed: true });
document.getElementById("cancel").onclick = () => submit({ confirmed: false });
</script>
```
**Data shape:** `{ message, confirmLabel? }`
**Result shape:** `{ confirmed: boolean }`
---
## Progress (long-running)
```html
<!doctype html>
<meta charset="utf-8" />
<script id="data" type="application/json">__DATA__</script>
<style>
body { font: 14px system-ui; margin: 16px; }
.bar { height: 8px; background: #eee; border-radius: 4px; overflow: hidden; }
.fill { height: 100%; background: #2a7; transition: width 200ms; }
</style>
<p id="label">Starting…</p>
<div class="bar"><div id="fill" class="fill" style="width:0%"></div></div>
<script type="module">
import { submit, onMessage } from "https://esm.sh/@modelcontextprotocol/apps-sdk";
const { jobId } = JSON.parse(document.getElementById("data").textContent);
const label = document.getElementById("label");
const fill = document.getElementById("fill");
onMessage((msg) => {
if (msg.type === "progress") {
label.textContent = msg.label;
fill.style.width = `${msg.percent}%`;
}
if (msg.type === "done") submit(msg.result);
});
</script>
```
The server pushes updates via the transport's notification channel targeting this widget's session. See `apps-sdk-messages.md` for the server-side push.
---
## Display-only (chart / preview)
Display widgets don't need `submit()` — they render and sit there. Return a text summary **alongside** the widget so Claude can keep reasoning:
```typescript
return {
content: [
{ type: "text", text: "Revenue is up 12% MoM. Chart rendered below." },
{ type: "resource", resource: { uri: "ui://widgets/chart", mimeType: "text/html+skybridge", text: renderWidget("chart", data) } },
],
};
```

View File

@@ -0,0 +1,177 @@
---
name: build-mcp-server
description: This skill should be used when the user asks to "build an MCP server", "create an MCP", "make an MCP integration", "wrap an API for Claude", "expose tools to Claude", "make an MCP app", or discusses building something with the Model Context Protocol. It is the entry point for MCP server development — it interrogates the user about their use case, determines the right deployment model (remote HTTP, MCPB, local stdio), picks a tool-design pattern, and hands off to specialized skills.
version: 0.1.0
---
# Build an MCP Server
You are guiding a developer through designing and building an MCP server that works seamlessly with Claude. MCP servers come in many forms — picking the wrong shape early causes painful rewrites later. Your first job is **discovery, not code**.
Do not start scaffolding until you have answers to the questions in Phase 1. If the user's opening message already answers them, acknowledge that and skip straight to the recommendation.
---
## Phase 1 — Interrogate the use case
Ask these questions conversationally (batch them into one message, don't interrogate one-at-a-time). Adapt wording to what the user has already told you.
### 1. What does it connect to?
| If it connects to… | Likely direction |
|---|---|
| A cloud API (SaaS, REST, GraphQL) | Remote HTTP server |
| A local process, filesystem, or desktop app | MCPB or local stdio |
| Hardware, OS-level APIs, or user-specific state | MCPB |
| Nothing external — pure logic / computation | Either — default to remote |
### 2. Who will use it?
- **Just me / my team, on our machines** → Local stdio is acceptable (easiest to prototype)
- **Anyone who installs it** → Remote HTTP (strongly preferred) or MCPB (if it *must* be local)
- **Users of Claude desktop who want UI widgets** → MCP app (remote or MCPB)
### 3. How many distinct actions does it expose?
This determines the tool-design pattern — see Phase 3.
- **Under ~15 actions** → one tool per action
- **Dozens to hundreds of actions** (e.g. wrapping a large API surface) → search + execute pattern
### 4. Does it need interactive UI in the chat?
Forms, pickers, dashboards, confirmation dialogs rendered inline in the conversation → **MCP app** (adds UI resources on top of a standard server).
### 5. What auth does the upstream service use?
- None / API key → straightforward
- OAuth 2.0 → you'll need a remote server with DCR (Dynamic Client Registration) or CIMD support; see `references/auth.md`
---
## Phase 2 — Recommend a deployment model
Based on the answers, recommend **one** path. Be opinionated. The ranked options:
### ⭐ Remote streamable-HTTP MCP server (default recommendation)
A hosted service speaking MCP over streamable HTTP. This is the **recommended path** for anything wrapping a cloud API.
**Why it wins:**
- Zero install friction — users add a URL, done
- One deployment serves all users; you control upgrades
- OAuth flows work properly (the server can handle redirects, DCR, token storage)
- Works across Claude desktop, Claude Code, Claude.ai, and third-party MCP hosts
**Choose this unless** the server *must* touch the user's local machine.
→ Scaffold with `references/remote-http-scaffold.md`
### MCP app (remote HTTP + interactive UI)
Same as above, plus **UI resources** — interactive widgets rendered in chat. Forms, file pickers, rich previews, confirmation dialogs. Built once, renders in Claude *and* ChatGPT.
**Choose this when** one or more tools benefit from structured user input or rich output that plain text can't handle.
Usually remote, but can be shipped as MCPB if the UI needs to drive a local app.
→ Hand off to the **`build-mcp-app`** skill.
### MCPB (bundled local server)
A local MCP server **packaged with its runtime** so users don't need Node/Python installed. The sanctioned way to ship local servers.
**Choose this when** the server *must* run on the user's machine — it reads local files, drives a desktop app, talks to localhost services, or needs OS-level access.
→ Hand off to the **`build-mcpb`** skill.
### Local stdio (npx / uvx) — *not recommended for distribution*
A script launched via `npx` / `uvx` on the user's machine. Fine for **personal tools and prototypes**. Painful to distribute: users need the right runtime, you can't push updates, and the only distribution channel is Claude Code plugins.
Recommend this only as a stepping stone. If the user insists, scaffold it but note the MCPB upgrade path.
---
## Phase 3 — Pick a tool-design pattern
Every MCP server exposes tools. How you carve them matters more than most people expect — tool schemas land directly in Claude's context window.
### Pattern A: One tool per action (small surface)
When the action space is small (< ~15 operations), give each a dedicated tool with a tight description and schema.
```
create_issue — Create a new issue. Params: title, body, labels[]
update_issue — Update an existing issue. Params: id, title?, body?, state?
search_issues — Search issues by query string. Params: query, limit?
add_comment — Add a comment to an issue. Params: issue_id, body
```
**Why it works:** Claude reads the tool list once and knows exactly what's possible. No discovery round-trips. Each tool's schema validates inputs precisely.
**Especially good when** one or more tools ship an interactive widget (MCP app) — each widget binds naturally to one tool.
### Pattern B: Search + execute (large surface)
When wrapping a large API (dozens to hundreds of endpoints), listing every operation as a tool floods the context window and degrades model performance. Instead, expose **two** tools:
```
search_actions — Given a natural-language intent, return matching actions
with their IDs, descriptions, and parameter schemas.
execute_action — Run an action by ID with a params object.
```
The server holds the full catalog internally. Claude searches, picks, executes. Context stays lean.
**Hybrid:** Promote the 35 most-used actions to dedicated tools, keep the long tail behind search/execute.
→ See `references/tool-design.md` for schema examples and description-writing guidance.
---
## Phase 4 — Pick a framework
Recommend one of these two. Others exist but these have the best MCP-spec coverage and Claude compatibility.
| Framework | Language | Use when |
|---|---|---|
| **Official TypeScript SDK** (`@modelcontextprotocol/sdk`) | TS/JS | Default choice. Best spec coverage, first to get new features. |
| **FastMCP 2.0** | Python | User prefers Python, or wrapping a Python library. Decorator-based, very low boilerplate. |
If the user already has a language/stack in mind, go with it — both produce identical wire protocol.
---
## Phase 5 — Scaffold and hand off
Once you've settled the four decisions (deployment model, tool pattern, framework, auth), do **one** of:
1. **Remote HTTP, no UI** → Scaffold inline using `references/remote-http-scaffold.md`. This skill can finish the job.
2. **MCP app (UI widgets)** → Summarize the decisions so far, then load the **`build-mcp-app`** skill.
3. **MCPB (bundled local)** → Summarize the decisions so far, then load the **`build-mcpb`** skill.
4. **Local stdio prototype** → Scaffold inline (simplest case), flag the MCPB upgrade path.
When handing off, restate the design brief in one paragraph so the next skill doesn't re-ask.
---
## Quick reference: decision matrix
| Scenario | Deployment | Tool pattern |
|---|---|---|
| Wrap a small SaaS API | Remote HTTP | One-per-action |
| Wrap a large SaaS API (50+ endpoints) | Remote HTTP | Search + execute |
| SaaS API with rich forms / pickers | MCP app (remote) | One-per-action |
| Drive a local desktop app | MCPB | One-per-action |
| Local desktop app with in-chat UI | MCP app (MCPB) | One-per-action |
| Read/write local filesystem | MCPB | Depends on surface |
| Personal prototype | Local stdio | Whatever's fastest |
---
## Reference files
- `references/remote-http-scaffold.md` — minimal remote server in TS SDK and FastMCP
- `references/tool-design.md` — writing tool descriptions and schemas Claude understands well
- `references/auth.md` — OAuth, DCR, CIMD, token storage patterns

View File

@@ -0,0 +1,73 @@
# Auth for MCP Servers
Auth is the reason most people end up needing a **remote** server even when a local one would be simpler. OAuth redirects, token storage, and refresh all work cleanly when there's a real hosted endpoint to redirect back to.
---
## The three tiers
### Tier 1: No auth / static API key
Server reads a key from env. User provides it once at setup. Done.
```typescript
const apiKey = process.env.UPSTREAM_API_KEY;
if (!apiKey) throw new Error("UPSTREAM_API_KEY not set");
```
Works for local stdio, MCPB, and remote servers alike. If this is all you need, stop here.
### Tier 2: OAuth 2.0 via Dynamic Client Registration (DCR)
The MCP host (Claude desktop, Claude Code, etc.) discovers your server's OAuth metadata, **registers itself as a client dynamically**, runs the auth-code flow, and stores the token. Your server never sees credentials — it just receives bearer tokens on each request.
This is the **recommended path** for any remote server wrapping an OAuth-protected API.
**Server responsibilities:**
1. Serve OAuth Authorization Server Metadata (RFC 8414) at `/.well-known/oauth-authorization-server`
2. Serve an MCP-protected-resource metadata document pointing at (1)
3. Implement (or proxy to) a DCR endpoint that hands out client IDs
4. Validate bearer tokens on incoming `/mcp` requests
Most of this is boilerplate — the SDK has helpers. The real decision is whether you **proxy** to the upstream's OAuth (if they support DCR) or run your own **shim** authorization server that exchanges your tokens for upstream tokens.
```
┌─────────┐ DCR + auth code ┌──────────────┐ upstream OAuth ┌──────────┐
│ MCP host│ ──────────────────> │ Your MCP srv │ ─────────────────> │ Upstream │
└─────────┘ <── bearer token ── └──────────────┘ <── access token ──└──────────┘
```
### Tier 3: CIMD (Client ID Metadata Document)
An alternative to DCR for ecosystems that don't want dynamic registration. The host publishes its client metadata at a well-known URL; your server fetches it, validates it, and issues a client credential. Lower friction than DCR for the host, slightly more work for you.
Use CIMD when targeting hosts that advertise CIMD support in their client metadata. Otherwise default to DCR — it's more broadly implemented.
---
## Hosting providers with built-in DCR/CIMD support
Several MCP-focused hosting providers handle the OAuth plumbing for you — you implement tool logic, they run the authorization server. Check their docs for current capabilities. If the user doesn't have strong hosting preferences, this is usually the fastest path to a working OAuth-protected server.
---
## Local servers and OAuth
Local stdio servers **can** do OAuth (open a browser, catch the redirect on a localhost port, stash the token in the OS keychain). It's fragile:
- Breaks in headless/remote environments
- Every user re-does the dance
- No central token refresh or revocation
If OAuth is required, lean hard toward remote HTTP. If you *must* ship local + OAuth, the `@modelcontextprotocol/sdk` includes a localhost-redirect helper, and MCPB is the right packaging so at least the runtime is predictable.
---
## Token storage
| Deployment | Store tokens in |
|---|---|
| Remote, stateless | Nowhere — host sends bearer each request |
| Remote, stateful | Session store keyed by MCP session ID (Redis, etc.) |
| MCPB / local | OS keychain (`keytar` on Node, `keyring` on Python). **Never plaintext on disk.** |

View File

@@ -0,0 +1,151 @@
# Remote Streamable-HTTP MCP Server — Scaffold
Minimal working servers in both recommended frameworks. Start here, then add tools.
---
## TypeScript SDK (`@modelcontextprotocol/sdk`)
```bash
npm init -y
npm install @modelcontextprotocol/sdk zod express
npm install -D typescript @types/express @types/node tsx
```
**`src/server.ts`**
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import express from "express";
import { z } from "zod";
const server = new McpServer({
name: "my-service",
version: "0.1.0",
});
// Pattern A: one tool per action
server.tool(
"search_items",
"Search items by keyword. Returns up to `limit` matches ranked by relevance.",
{
query: z.string().describe("Search keywords"),
limit: z.number().int().min(1).max(50).default(10),
},
async ({ query, limit }) => {
const results = await upstreamApi.search(query, limit);
return {
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
};
},
);
server.tool(
"get_item",
"Fetch a single item by its ID.",
{ id: z.string() },
async ({ id }) => {
const item = await upstreamApi.get(id);
return { content: [{ type: "text", text: JSON.stringify(item) }] };
},
);
// Streamable HTTP transport (stateless mode — simplest)
const app = express();
app.use(express.json());
app.post("/mcp", async (req, res) => {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined, // stateless
});
res.on("close", () => transport.close());
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
});
app.listen(process.env.PORT ?? 3000);
```
**Stateless vs stateful:** The snippet above creates a fresh transport per request (stateless). Fine for most API-wrapping servers. If tools need to share state across calls in a session (rare), use a session-keyed transport map — see the SDK's `examples/server/simpleStreamableHttp.ts`.
---
## FastMCP 2.0 (Python)
```bash
pip install fastmcp
```
**`server.py`**
```python
from fastmcp import FastMCP
mcp = FastMCP(name="my-service")
@mcp.tool
def search_items(query: str, limit: int = 10) -> list[dict]:
"""Search items by keyword. Returns up to `limit` matches ranked by relevance."""
return upstream_api.search(query, limit)
@mcp.tool
def get_item(id: str) -> dict:
"""Fetch a single item by its ID."""
return upstream_api.get(id)
if __name__ == "__main__":
mcp.run(transport="http", host="0.0.0.0", port=3000)
```
FastMCP derives the JSON schema from type hints and the docstring becomes the tool description. Keep docstrings terse and action-oriented — they land in Claude's context window verbatim.
---
## Search + execute pattern (large API surface)
When wrapping 50+ endpoints, don't register them all. Two tools:
```typescript
const CATALOG = loadActionCatalog(); // { id, description, paramSchema }[]
server.tool(
"search_actions",
"Find available actions matching an intent. Call this first to discover what's possible. Returns action IDs, descriptions, and parameter schemas.",
{ intent: z.string().describe("What you want to do, in plain English") },
async ({ intent }) => {
const matches = rankActions(CATALOG, intent).slice(0, 10);
return { content: [{ type: "text", text: JSON.stringify(matches, null, 2) }] };
},
);
server.tool(
"execute_action",
"Execute an action by ID. Get the ID and params schema from search_actions first.",
{
action_id: z.string(),
params: z.record(z.unknown()),
},
async ({ action_id, params }) => {
const action = CATALOG.find(a => a.id === action_id);
if (!action) throw new Error(`Unknown action: ${action_id}`);
validate(params, action.paramSchema);
const result = await dispatch(action, params);
return { content: [{ type: "text", text: JSON.stringify(result) }] };
},
);
```
`rankActions` can be simple keyword matching to start. Upgrade to embeddings if precision matters.
---
## Deployment checklist
- [ ] `POST /mcp` responds to `initialize` with server capabilities
- [ ] `tools/list` returns your tools with complete schemas
- [ ] Errors return structured MCP errors, not HTTP 500s with HTML bodies
- [ ] CORS headers set if browser clients will connect
- [ ] Health check endpoint separate from `/mcp` (hosts poll it)
- [ ] Secrets from env vars, never hardcoded
- [ ] If OAuth: DCR endpoint implemented — see `auth.md`

View File

@@ -0,0 +1,112 @@
# Tool Design — Writing Tools Claude Uses Correctly
Tool schemas and descriptions are prompt engineering. They land directly in Claude's context and determine whether Claude picks the right tool with the right arguments. Most MCP integration bugs trace back to vague descriptions or loose schemas.
---
## Descriptions
**The description is the contract.** It's the only thing Claude reads before deciding whether to call the tool. Write it like a one-line manpage entry plus disambiguating hints.
### Good
```
search_issues — Search issues by keyword across title and body. Returns up
to `limit` results ranked by recency. Does NOT search comments or PRs —
use search_comments / search_prs for those.
```
- Says what it does
- Says what it returns
- Says what it *doesn't* do (prevents wrong-tool calls)
### Bad
```
search_issues — Searches for issues.
```
Claude will call this for anything vaguely search-shaped, including things it can't do.
### Disambiguate siblings
When two tools are similar, each description should say when to use the *other* one:
```
get_user — Fetch a user by ID. If you only have an email, use find_user_by_email.
find_user_by_email — Look up a user by email address. Returns null if not found.
```
---
## Parameter schemas
**Tight schemas prevent bad calls.** Every constraint you express in the schema is one fewer thing that can go wrong at runtime.
| Instead of | Use |
|---|---|
| `z.string()` for an ID | `z.string().regex(/^usr_[a-z0-9]{12}$/)` |
| `z.number()` for a limit | `z.number().int().min(1).max(100).default(20)` |
| `z.string()` for a choice | `z.enum(["open", "closed", "all"])` |
| optional with no hint | `.optional().describe("Defaults to the caller's workspace")` |
**Describe every parameter.** The `.describe()` text shows up in the schema Claude sees. Omitting it is leaving money on the table.
```typescript
{
query: z.string().describe("Keywords to search for. Supports quoted phrases."),
status: z.enum(["open", "closed", "all"]).default("open")
.describe("Filter by status. Use 'all' to include closed items."),
limit: z.number().int().min(1).max(50).default(10)
.describe("Max results. Hard cap at 50."),
}
```
---
## Return shapes
Claude reads whatever you put in `content[].text`. Make it parseable.
**Do:**
- Return JSON for structured data (`JSON.stringify(result, null, 2)`)
- Return short confirmations for mutations (`"Created issue #123"`)
- Include IDs Claude will need for follow-up calls
- Truncate huge payloads and say so (`"Showing 10 of 847 results. Refine the query to narrow down."`)
**Don't:**
- Return raw HTML
- Return megabytes of unfiltered API response
- Return bare success with no identifier (`"ok"` after a create — Claude can't reference what it made)
---
## How many tools?
| Tool count | Guidance |
|---|---|
| 115 | One tool per action. Sweet spot. |
| 1530 | Still workable. Audit for near-duplicates that could merge. |
| 30+ | Switch to search + execute. Optionally promote the top 35 to dedicated tools. |
The ceiling isn't a hard protocol limit — it's context-window economics. Every tool schema is tokens Claude spends *every turn*. Thirty tools with rich schemas can eat 35k tokens before the conversation even starts.
---
## Errors
Return MCP tool errors, not exceptions that crash the transport. Include enough detail for Claude to recover or retry differently.
```typescript
if (!item) {
return {
isError: true,
content: [{
type: "text",
text: `Item ${id} not found. Use search_items to find valid IDs.`,
}],
};
}
```
The hint ("use search_items…") turns a dead end into a next step.

View File

@@ -0,0 +1,179 @@
---
name: build-mcpb
description: This skill should be used when the user wants to "package an MCP server", "bundle an MCP", "make an MCPB", "ship a local MCP server", "distribute a local MCP", discusses ".mcpb files", mentions bundling a Node or Python runtime with their MCP server, or needs an MCP server that interacts with the local filesystem, desktop apps, or OS and must be installable without the user having Node/Python set up.
version: 0.1.0
---
# Build an MCPB (Bundled Local MCP Server)
MCPB is a local MCP server **packaged with its runtime**. The user installs one file; it runs without needing Node, Python, or any toolchain on their machine. It's the sanctioned way to distribute local MCP servers.
**Use MCPB when the server must run on the user's machine** — reading local files, driving a desktop app, talking to localhost services, OS-level APIs. If your server only hits cloud APIs, you almost certainly want a remote HTTP server instead (see `build-mcp-server`). Don't pay the MCPB packaging tax for something that could be a URL.
---
## What an MCPB bundle contains
```
my-server.mcpb (zip archive)
├── manifest.json ← identity, entry point, permissions, config schema
├── server/ ← your MCP server code
│ ├── index.js
│ └── node_modules/ ← bundled dependencies (or vendored)
├── runtime/ ← optional: pinned Node/Python if not using host's
└── icon.png
```
The host reads `manifest.json`, launches the entry point as a **stdio** MCP server, and pipes messages. From your code's perspective it's identical to a local stdio server — the only difference is packaging.
---
## Manifest
```json
{
"name": "local-files",
"version": "0.1.0",
"description": "Read, search, and watch files on the local filesystem.",
"entry": {
"type": "node",
"main": "server/index.js"
},
"permissions": {
"filesystem": { "read": true, "write": false },
"network": false
},
"config": {
"rootDir": {
"type": "string",
"description": "Directory to expose. Defaults to ~/Documents.",
"default": "~/Documents"
}
}
}
```
**`entry.type`** — `node`, `python`, or `binary`. Determines which bundled/host runtime launches `main`.
**`permissions`** — declared upfront and shown to the user at install. Request the minimum. Broad permissions (`filesystem.write: true`, `network: true`) trigger scarier consent UI and more scrutiny.
**`config`** — user-settable values surfaced in the host's settings UI. Your server reads them from env vars (`MCPB_CONFIG_<KEY>`).
---
## Server code: same as local stdio
The server itself is a standard stdio MCP server. Nothing MCPB-specific in the tool logic.
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { readFile, readdir } from "node:fs/promises";
import { join } from "node:path";
import { homedir } from "node:os";
const ROOT = (process.env.MCPB_CONFIG_ROOTDIR ?? "~/Documents")
.replace(/^~/, homedir());
const server = new McpServer({ name: "local-files", version: "0.1.0" });
server.tool(
"list_files",
"List files in a directory under the configured root.",
{ path: z.string().default(".") },
async ({ path }) => {
const entries = await readdir(join(ROOT, path), { withFileTypes: true });
const list = entries.map(e => ({ name: e.name, dir: e.isDirectory() }));
return { content: [{ type: "text", text: JSON.stringify(list, null, 2) }] };
},
);
server.tool(
"read_file",
"Read a file's contents. Path is relative to the configured root.",
{ path: z.string() },
async ({ path }) => {
const text = await readFile(join(ROOT, path), "utf8");
return { content: [{ type: "text", text }] };
},
);
const transport = new StdioServerTransport();
await server.connect(transport);
```
**Sandboxing is your job.** The manifest permissions gate what the *host* allows the process to do, but don't rely on that alone — validate paths, refuse to escape `ROOT`, etc. See `references/local-security.md`.
---
## Build pipeline
### Node
```bash
npm install
npx esbuild src/index.ts --bundle --platform=node --outfile=server/index.js
# or: copy node_modules wholesale if native deps resist bundling
npx @modelcontextprotocol/mcpb pack . -o my-server.mcpb
```
`mcpb pack` zips the directory, validates `manifest.json`, and optionally pulls a pinned Node runtime into `runtime/`.
### Python
```bash
pip install -t server/vendor -r requirements.txt
npx @modelcontextprotocol/mcpb pack . -o my-server.mcpb --runtime python3.12
```
Vendor dependencies into a subdirectory and prepend it to `sys.path` in your entry script. Native extensions (numpy, etc.) must be built for each target platform — `mcpb pack --multiarch` cross-builds, but it's slow; avoid native deps if you can.
---
## Permissions: request the minimum
The install prompt shows what you ask for. Every extra permission is friction.
| Need | Request |
|---|---|
| Read files in one directory | `filesystem.read: true` + enforce root in code |
| Write files | `filesystem.write: true` — justify in description |
| Call a local HTTP service | `network: { "allow": ["localhost:*"] }` |
| Call the internet | `network: true` — but ask yourself why this isn't a remote server |
| Spawn processes | `process.spawn: true` — highest scrutiny |
If you find yourself requesting `network: true` to hit a cloud API, stop — that's a remote server wearing an MCPB costume. The user gains nothing from running it locally.
---
## MCPB + UI widgets
MCPB servers can serve UI resources exactly like remote MCP apps — the widget mechanism is transport-agnostic. A local file picker that browses the actual disk, a dialog that controls a native app, etc.
Widget authoring is covered in the **`build-mcp-app`** skill; it works the same here. The only difference is where the server runs.
---
## Testing
```bash
# Run the server directly over stdio, poke it with the inspector
npx @modelcontextprotocol/inspector node server/index.js
# Pack and validate
npx @modelcontextprotocol/mcpb pack . -o test.mcpb
npx @modelcontextprotocol/mcpb validate test.mcpb
# Install into Claude desktop for end-to-end
npx @modelcontextprotocol/mcpb install test.mcpb
```
Test on a machine **without** your dev toolchain before shipping. "Works on my machine" failures in MCPB almost always trace to a dependency that wasn't actually bundled.
---
## Reference files
- `references/manifest-schema.md` — full `manifest.json` field reference
- `references/local-security.md` — path traversal, sandboxing, least privilege

View File

@@ -0,0 +1,111 @@
# Local MCP Security
An MCPB server runs as the user, with whatever permissions the manifest was granted. Claude drives it. That combination means: **tool inputs are untrusted**, even though they come from an AI the user trusts. A prompt-injected web page can make Claude call your `delete_file` tool with a path you didn't intend.
Defense in depth. Manifest permissions are the outer wall; validation in your tool handlers is the inner wall.
---
## Path traversal
The #1 bug in local MCP servers. If you take a path parameter and join it to a root, **resolve and check containment**.
```typescript
import { resolve, relative, isAbsolute } from "node:path";
function safeJoin(root: string, userPath: string): string {
const full = resolve(root, userPath);
const rel = relative(root, full);
if (rel.startsWith("..") || isAbsolute(rel)) {
throw new Error(`Path escapes root: ${userPath}`);
}
return full;
}
```
`resolve` normalizes `..`, symlink segments, etc. `relative` tells you if the result left the root. Don't just `String.includes("..")` — that misses encoded and symlink-based escapes.
**Python equivalent:**
```python
from pathlib import Path
def safe_join(root: Path, user_path: str) -> Path:
full = (root / user_path).resolve()
if not full.is_relative_to(root.resolve()):
raise ValueError(f"Path escapes root: {user_path}")
return full
```
---
## Command injection
If you spawn processes, **never pass user input through a shell**.
```typescript
// ❌ catastrophic
exec(`git log ${branch}`);
// ✅ array-args, no shell
execFile("git", ["log", branch]);
```
If you're wrapping a CLI, build the full argv as an array. Validate each flag against an allowlist if the tool accepts flags at all.
---
## Read-only by default
Split read and write into separate tools. Most workflows only need read. A tool that's read-only can't be weaponized into data loss no matter what Claude is tricked into calling it with.
```
list_files ← safe to call freely
read_file ← safe to call freely
write_file ← separate tool, separate permission, separate scrutiny
delete_file ← consider not shipping this at all
```
If you ship write/delete, consider requiring a confirmation widget (see `build-mcp-app`) so the user explicitly approves each destructive call.
---
## Resource limits
Claude will happily ask to read a 4GB log file. Cap everything:
```typescript
const MAX_BYTES = 1_000_000;
const buf = await readFile(path);
if (buf.length > MAX_BYTES) {
return {
content: [{
type: "text",
text: `File is ${buf.length} bytes — too large. Showing first ${MAX_BYTES}:\n\n`
+ buf.subarray(0, MAX_BYTES).toString("utf8"),
}],
};
}
```
Same for directory listings (cap entry count), search results (cap matches), and anything else unbounded.
---
## Secrets
- **Config secrets** (`secret: true` in manifest): host stores in OS keychain, delivers via env var. Don't log them. Don't include them in tool results.
- **Never store secrets in plaintext files.** If the host's keychain integration isn't enough, use `keytar` (Node) / `keyring` (Python) yourself.
- **Tool results flow into the chat transcript.** Anything you return, the user (and any log export) can see. Redact before returning.
---
## Checklist before shipping
- [ ] Every path parameter goes through containment check
- [ ] No `exec()` / `shell=True``execFile` / array-argv only
- [ ] Write/delete split from read tools
- [ ] Size caps on file reads, listing lengths, search results
- [ ] Manifest permissions match actual code behavior (no over-requesting)
- [ ] Secrets never logged or returned in tool results
- [ ] Tested with adversarial inputs: `../../etc/passwd`, `; rm -rf ~`, 10GB file

View File

@@ -0,0 +1,132 @@
# MCPB Manifest Schema
Every `.mcpb` bundle has a `manifest.json` at its root. The host validates it before install.
---
## Top-level fields
| Field | Type | Required | Notes |
|---|---|---|---|
| `name` | string | ✅ | Unique identifier. Lowercase, hyphens only. Shown in settings. |
| `version` | string | ✅ | Semver. Host compares for update prompts. |
| `description` | string | ✅ | One line. Shown in the install prompt. |
| `entry` | object | ✅ | How to launch the server — see below. |
| `permissions` | object | ✅ | What the server needs — see below. |
| `config` | object | — | User-settable values surfaced in settings UI. |
| `icon` | string | — | Path to PNG inside the bundle. 256×256 recommended. |
| `homepage` | string | — | URL shown in settings. |
| `minHostVersion` | string | — | Refuse install on older hosts. |
---
## `entry`
```json
{ "type": "node", "main": "server/index.js" }
```
| `type` | `main` points at | Runtime resolution |
|---|---|---|
| `node` | `.js` or `.mjs` file | `runtime/node` if present, else host-bundled Node |
| `python` | `.py` file | `runtime/python` if present, else host-bundled Python |
| `binary` | executable | Run directly. Must be built per-platform. |
**`args`** (optional array) — extra argv passed to the entry. Rarely needed.
**`env`** (optional object) — static env vars set at launch. For user-configurable values use `config` instead.
---
## `permissions`
```json
{
"filesystem": { "read": true, "write": false },
"network": { "allow": ["localhost:*", "127.0.0.1:*"] },
"process": { "spawn": false }
}
```
### `filesystem`
| Value | Meaning |
|---|---|
| `false` or omitted | No filesystem access beyond the bundle itself |
| `{ "read": true }` | Read anywhere the OS user can |
| `{ "read": true, "write": true }` | Read and write |
There's no path scoping at the manifest level — scope in your code. The manifest permission is a coarse consent gate, not a sandbox.
### `network`
| Value | Meaning |
|---|---|
| `false` or omitted | No network (most local-first servers) |
| `{ "allow": ["host:port", ...] }` | Allowlisted destinations. `*` wildcards ports. |
| `true` | Unrestricted. Heavy scrutiny — explain why in `description`. |
### `process`
| Value | Meaning |
|---|---|
| `false` or omitted | Can't spawn child processes |
| `{ "spawn": true }` | Can spawn. Needed for wrapping CLIs. |
| `{ "spawn": true, "allow": ["git", "ffmpeg"] }` | Spawn only allowlisted binaries |
---
## `config`
User-editable settings, surfaced in the host UI. Each key becomes an env var: `MCPB_CONFIG_<UPPERCASE_KEY>`.
```json
{
"config": {
"rootDir": {
"type": "string",
"description": "Directory to expose",
"default": "~/Documents"
},
"maxFileSize": {
"type": "number",
"description": "Skip files larger than this (MB)",
"default": 10,
"min": 1,
"max": 500
},
"includeHidden": {
"type": "boolean",
"description": "Include dotfiles in listings",
"default": false
},
"apiKey": {
"type": "string",
"description": "Optional API key for the sync feature",
"secret": true
}
}
}
```
**`type`** — `string`, `number`, `boolean`. Enums: use `string` with `"enum": ["a", "b", "c"]`.
**`secret: true`** — host masks the value in UI and stores it in the OS keychain instead of a plain config file.
**`required: true`** — host blocks server launch until the user sets it. Use sparingly — a server that won't start until configured is a bad first-run experience.
---
## Minimal valid manifest
```json
{
"name": "hello",
"version": "0.1.0",
"description": "Minimal MCPB server.",
"entry": { "type": "node", "main": "server/index.js" },
"permissions": {}
}
```
Empty `permissions` means no filesystem, no network, no spawn — pure computation only. Valid, if unusual.

View File

@@ -1,8 +0,0 @@
{
"name": "plugin-dev",
"description": "Plugin development toolkit with skills for creating agents, commands, hooks, MCP integrations, and comprehensive plugin structure guidance",
"author": {
"name": "Anthropic",
"email": "support@anthropic.com"
}
}

View File

@@ -169,24 +169,6 @@ Keep trying until success. The loop handles retry logic automatically.
- One $50k contract completed for $297 in API costs
- Created entire programming language ("cursed") over 3 months using this approach
## Windows Compatibility
The stop hook uses a bash script that requires Git for Windows to run properly.
**Issue**: On Windows, the `bash` command may resolve to WSL bash (often misconfigured) instead of Git Bash, causing the hook to fail with errors like:
- `wsl: Unknown key 'automount.crossDistro'`
- `execvpe(/bin/bash) failed: No such file or directory`
**Workaround**: Edit the cached plugin's `hooks/hooks.json` to use Git Bash explicitly:
```json
"command": "\"C:/Program Files/Git/bin/bash.exe\" ${CLAUDE_PLUGIN_ROOT}/hooks/stop-hook.sh"
```
**Location**: `~/.claude/plugins/cache/claude-plugins-official/ralph-wiggum/<hash>/hooks/hooks.json`
**Note**: Use `Git/bin/bash.exe` (the wrapper with proper PATH), not `Git/usr/bin/bash.exe` (raw MinGW bash without utilities in PATH).
## Learn More
- Original technique: https://ghuntley.com/ralph/

View File

@@ -110,7 +110,7 @@ HELP_EOF
done
# Join all prompt parts with spaces
PROMPT="${PROMPT_PARTS[*]:-}"
PROMPT="${PROMPT_PARTS[*]}"
# Validate prompt is non-empty
if [[ -z "$PROMPT" ]]; then