name: mcp-tools-registry description: Use when calling any external API or service (Linear, Praetorian CLI, GitHub, Playwright) - provides registry of MCP tool wrappers in .claude/tools/ with TRUE progressive loading achieving 0 tokens at session start (was 71.8k tokens). REQUIRED before implementing API calls. Tools load ONLY when imported. allowed-tools: "Read, Bash, Grep"
MCP Tools Registry
USAGE skill - for using existing wrappers
๐ก Need to CREATE a new wrapper? See
mcp-code-createskill instead. This skill is for USING wrappers that already exist in.claude/tools/.
TRUE Progressive Loading: 0 tokens at session start (was 71.8k tokens)
Registry of TypeScript wrappers that connect to MCP servers on-demand, eliminating all session-start token overhead.
Core Principle
MANDATORY: Before calling external APIs, ALWAYS check .claude/tools/ for available wrappers.
Architecture Achieved (November 2024):
- โ MCP servers removed from
.mcp.json(no longer loaded at startup) - โ
TypeScript wrappers in
.claude/tools/spawn MCP connections on-demand - โ 0 tokens at session start (down from 71.8k tokens)
- โ Tools load only when imported (~50-100 tokens each)
When using this skill: Announce "Checking .claude/tools/ registry for {service}..."
Agent Requirements
To use MCP wrappers, agents MUST have:
# Agent frontmatter
skills:
- mcp-tools-registry # This skill (teaches discovery + usage)
tools:
- Bash # REQUIRED: Execute TypeScript (npx tsx)
- Read # REQUIRED: Discover available tools
- Write # Optional: For complex multi-tool scripts
Why Bash is required: Wrappers are TypeScript files. Agents execute them via:
npx tsx -e "import { tool } from '$ROOT/.claude/tools/...'; ..."
Without Bash tool, agents cannot execute wrappers!
Discovery Workflow (REQUIRED)
Step 1: Discover Available Tools
# Quick discovery - list all categories
ls .claude/tools/
# Or use discovery tool for detailed info
npx tsx .claude/tools/discover.ts
# Search for specific tools
npx tsx .claude/tools/discover.ts praetorian-cli asset
Discovery tool output:
Found 18 tools:
๐ฆ praetorian-cli/assets-list
Purpose: List assets with intelligent filtering
Import: .claude/tools/praetorian-cli/assets-list.ts
๐ฆ praetorian-cli/risks-get
Purpose: Get single risk with details
Import: .claude/tools/praetorian-cli/risks-get.ts
Step 2: Execute TypeScript Wrapper (Using Bash Tool)
CRITICAL: Agents must use Bash tool with npx tsx to execute TypeScript wrappers!
Pattern A: Inline Execution (Simple, One Tool)
# Use Bash tool to execute TypeScript inline
# Note: 2>/dev/null suppresses MCP debug logs
ROOT="$(git rev-parse --show-superproject-working-tree --show-toplevel | head -1)" && npx tsx -e "(async () => {
const { assetsList } = await import('$ROOT/.claude/tools/praetorian-cli/assets-list.ts');
const result = await assetsList.execute({
key_prefix: '#asset#example.com',
pages: 1
});
console.log('Found:', result.summary.total_count, 'assets');
console.log('Token usage:', result.estimated_tokens, 'tokens');
console.log('First 5:', result.assets.slice(0, 5).map(a => a.name).join(', '));
})();" 2>/dev/null
Pattern B: Script File (Complex, Multiple Tools)
# Step 1: Write TypeScript file using Write tool
# File: .claude/tools/scripts/analyze-risks.ts
import { assetsList } from '../praetorian-cli/assets-list.js';
import { risksList } from '../praetorian-cli/risks-list.js';
import { createIssue } from '../linear/create-issue.js';
async function analyzeRisks() {
// Get active assets
const assets = await assetsList.execute({ pages: 1 });
const active = assets.assets.filter(a => a.status === 'A');
// Get high-priority risks
const risks = await risksList.execute({ pages: 1 });
const critical = risks.risks.filter(r => r.priority <= 10);
// Create tracking issue
if (critical.length > 0) {
await createIssue.execute({
title: `Security Review: ${critical.length} critical risks`,
team: 'Security',
description: `Found ${critical.length} critical risks across ${active.length} assets`
});
}
return { active: active.length, critical: critical.length };
}
analyzeRisks().then(result => {
console.log(JSON.stringify(result, null, 2));
});
# Step 2: Execute using Bash tool
npx tsx .claude/tools/scripts/analyze-risks.ts
Why .js in imports when files are .ts?
- TypeScript source files:
.ts(what you write) - Import paths:
.js(ESM convention with"type": "module") - tsx transpiles on-the-fly:
.tsโ.jsautomatically
Step 3: Process Results
Wrappers return pre-filtered data:
// Result structure (already filtered!)
{
summary: {
total_count: 156, // Total available
returned_count: 20, // Returned (limited)
statuses: { A: 100, F: 30, D: 26 }
},
assets: [...], // Only first 20
estimated_tokens: 850 // Actual token cost
}
Process in your code:
ROOT="$(git rev-parse --show-superproject-working-tree --show-toplevel | head -1)" && npx tsx -e "(async () => {
const { assetsList } = await import('$ROOT/.claude/tools/praetorian-cli/assets-list.ts');
const result = await assetsList.execute({ pages: 1 });
// Summary already calculated
console.log('Total:', result.summary.total_count);
console.log('Returned:', result.summary.returned_count);
// Iterate over limited dataset (not all!)
for (const asset of result.assets) {
console.log(\`- \${asset.name} (\${asset.status})\`);
}
})();" 2>/dev/null
Available Tool Categories
ALWAYS use discover.ts to get the current list of available tools:
# List ALL available services and tools
npx tsx .claude/tools/discover.ts
# List tools for a specific service
npx tsx .claude/tools/discover.ts linear
# Search for tools by name
npx tsx .claude/tools/discover.ts praetorian-cli asset
Why dynamic discovery?
- Tool lists stay current as wrappers are added/removed
- No stale documentation
- Single source of truth: the actual
.claude/tools/directory
Service-Specific Skills
For detailed tool catalogs with import paths and examples, use the service-specific skills:
| Service | Skill | Location |
|---|---|---|
| Linear | mcp-tools-linear |
.claude/skill-library/claude/mcp-tools/mcp-tools-linear/ |
| Praetorian CLI | mcp-tools-praetorian-cli |
.claude/skill-library/claude/mcp-tools/mcp-tools-praetorian-cli/ |
| Context7 | mcp-tools-context7 |
.claude/skill-library/claude/mcp-tools/mcp-tools-context7/ |
| Currents | mcp-tools-currents |
.claude/skill-library/claude/mcp-tools/mcp-tools-currents/ |
These skills are auto-generated by npm run generate-skill -- <service> and contain:
- Complete tool catalog with descriptions
- Import paths for each wrapper
- Service-specific examples
Quick Category Overview
| Category | Location | Purpose |
|---|---|---|
praetorian-cli/ |
.claude/tools/praetorian-cli/ |
Chariot API (assets, risks, jobs) |
linear/ |
.claude/tools/linear/ |
Linear issues, projects, teams |
context7/ |
.claude/tools/context7/ |
Library documentation lookup |
currents/ |
.claude/tools/currents/ |
CI test performance metrics |
chrome-devtools/ |
.claude/tools/chrome-devtools/ |
Browser automation (Playwright) |
Token savings: 71.8k tokens freed at session start (was loaded immediately, now 0)
When tools used: ~50-100 tokens per imported tool (only what you need)
How Wrappers Work
Architecture
โโโโโโโโโโโโโโโ
โ Your Code โ import { tool } from '.claude/tools/praetorian-cli/tool'
โโโโโโโโฌโโโโโโโ
โ (0 tokens at startup, ~50 tokens when imported)
โผ
โโโโโโโโโโโโโโโโโโโ
โ TypeScript โ tool.execute({ params })
โ Wrapper โ โข Validates with Zod
โ (.claude/tools) โ โข Calls MCP via SDK
โ โ โข Filters/summarizes results
โโโโโโโโฌโโโโโโโโโโโ
โ Spawns on-demand
โผ
โโโโโโโโโโโโโโโโโโโ
โ MCP Server โ Independent process
โ (not in Claude โ โข praetorian CLI
โ Code context) โ โข Linear API
โ โ โข Playwright
โโโโโโโโโโโโโโโโโโโ
Key Benefits
- 0 tokens at startup - No MCP definitions loaded
- On-demand connections - MCP servers spawn only when called
- Pre-filtered results - Summaries + limited data returned
- Type safety - Zod validation on inputs/outputs
- Reusable - No rewriting TypeScript each time
- Format normalization - Handles variable MCP response formats automatically
Response Format Handling
Wrappers normalize variable MCP response formats automatically. Output is always consistent - arrays are iterable, pagination extracted.
Don't call MCP servers directly - use wrappers. See references/troubleshooting.md if you encounter format issues.
Import Patterns (With Bash Execution)
All patterns use Bash tool to execute TypeScript via npx tsx
Important: File Extensions
- Source files:
.ts(TypeScript) - Import paths:
.js(ESM convention with"type": "module") - Why: TypeScript compiles
.tsโ.js, imports reference output - tsx handles: Finds
.tsfiles, transpiles on-the-fly
All wrappers are TypeScript - no mixed .js/.sh files!
Pattern 1: Single Tool (Inline)
# Bash tool command with stderr suppression
ROOT="$(git rev-parse --show-superproject-working-tree --show-toplevel | head -1)" && npx tsx -e "(async () => {
const { assetsList } = await import('$ROOT/.claude/tools/praetorian-cli/assets-list.ts');
const assets = await assetsList.execute({
key_prefix: '#asset#example.com',
pages: 1
});
console.log(JSON.stringify(assets.summary, null, 2));
})();" 2>/dev/null
Pattern 2: Multiple Tools from Same Category (Inline)
# Bash tool command with stderr suppression
ROOT="$(git rev-parse --show-superproject-working-tree --show-toplevel | head -1)" && npx tsx -e "(async () => {
const { assetsList, risksGet } = await import('$ROOT/.claude/tools/praetorian-cli/index.ts');
const assets = await assetsList.execute({ pages: 1 });
console.log('Assets:', assets.summary.total_count);
const risk = await risksGet.execute({
key: '#risk#example.com#sql-injection',
details: true
});
console.log('Risk:', risk.name, '-', risk.status);
})();" 2>/dev/null
Pattern 3: Tools from Multiple Categories (Script File)
# Step 1: Write script using Write tool
# File: .claude/tools/scripts/cross-service.ts
import { assetsList } from "../praetorian-cli/assets-list.js";
import { createIssue } from "../linear/create-issue.js";
async function main() {
// Get critical assets
const assets = await assetsList.execute({ pages: 1 });
const critical = assets.assets.filter((a) => a.status === "A");
// Create tracking issue
for (const asset of critical) {
await createIssue.execute({
title: `Review: ${asset.name}`,
team: "Security",
});
}
console.log(`Created ${critical.length} tracking issues`);
}
main();
# Step 2: Execute using Bash tool
npx tsx .claude/tools/scripts/cross-service.ts
Pattern 4: Dynamic Discovery (Inline)
# Bash tool command with stderr suppression
ROOT="$(git rev-parse --show-superproject-working-tree --show-toplevel | head -1)" && npx tsx -e "(async () => {
const { discoverTools } = await import('$ROOT/.claude/tools/discover.ts');
const tools = await discoverTools('praetorian-cli', 'asset');
console.log(\`Found \${tools.length} asset tools:\`);
for (const tool of tools) {
console.log(\`- \${tool.name}: \${tool.summary}\`);
}
// Dynamically import and use first tool
const { assetsList } = await import(tools[0].importPath.replace('.ts', '.js'));
const result = await assetsList.execute({ pages: 1 });
console.log('Result:', result.summary.total_count, 'assets');
})();" 2>/dev/null
Common Workflows
Workflow 1: Asset Discovery (Inline)
# Use Bash tool with stderr suppression
ROOT="$(git rev-parse --show-superproject-working-tree --show-toplevel | head -1)" && npx tsx -e "(async () => {
const { assetsList } = await import('$ROOT/.claude/tools/praetorian-cli/assets-list.ts');
const result = await assetsList.execute({
key_prefix: '#asset#example.com',
pages: 1
});
console.log(\`Found \${result.summary.total_count} assets\`);
console.log(\`Statuses:\`, JSON.stringify(result.summary.statuses));
console.log(\`First 20:\`, result.assets.map(a => a.name).join(', '));
})();" 2>/dev/null
Token usage: ~800 tokens (vs 10k+ without wrapper)
Workflow 2: Risk Analysis (Script File)
# Step 1: Write using Write tool (.claude/tools/scripts/risk-analysis.ts)
import { risksList, risksGet } from "../praetorian-cli/index.js";
async function analyzeRisks() {
const risks = await risksList.execute({
prefix_filter: "#risk#example.com",
pages: 1,
});
const critical = risks.risks.filter((r) => r.priority <= 10);
for (const risk of critical) {
const details = await risksGet.execute({
key: risk.key,
details: true,
});
console.log(`${risk.name}: ${details.affected_assets_summary?.total_count} assets`);
}
return { total: risks.summary.total_count, critical: critical.length };
}
analyzeRisks().then((result) => console.log(JSON.stringify(result)));
# Step 2: Execute using Bash tool
npx tsx .claude/tools/scripts/risk-analysis.ts
Token usage: ~1.5k tokens (vs 50k+ without wrapper)
Workflow 3: Linear Issue Tracking (Inline)
# Use Bash tool with stderr suppression
ROOT="$(git rev-parse --show-superproject-working-tree --show-toplevel | head -1)" && npx tsx -e "(async () => {
const { listIssues, createIssue } = await import('$ROOT/.claude/tools/linear/index.ts');
const bugs = await listIssues.execute({
team: 'Engineering',
state: 'Backlog',
label: 'bug'
});
console.log(\`Found \${bugs.issues.length} open bugs\`);
if (bugs.issues.length > 10) {
await createIssue.execute({
title: 'Bug Triage: High backlog count',
team: 'Engineering',
priority: 2,
description: \`Found \${bugs.issues.length} bugs in backlog\`
});
console.log('Created tracking issue');
}
})();" 2>/dev/null
Token usage: ~1k tokens (vs 15k+ without wrapper)
Verification Checklist
Before using external APIs:
- Check registry:
ls .claude/tools/ - Discover tools:
npx tsx .claude/tools/discover.ts {category} - Review README:
.claude/tools/{category}/README.md - Import wrapper: Use TypeScript import
- No mcp__ tools: Don't reference mcp__ prefixed tools (don't exist!)
Red Flags
Warning signs you're doing it wrong:
- โ "Let me use mcppraetorian-cli tools" (don't exist in context!)
- โ "I'll call the MCP server directly" (no progressive loading)
- โ "I'll import the TypeScript directly" (need Bash + npx tsx!)
- โ "Don't know if wrapper exists" (CHECK with ls/discover!)
- โ "Can't find .js file for .ts import" (.js is correct - ESM convention!)
Reality:
- โ All MCP tools removed from context (71.8k tokens saved)
- โ Wrappers are the ONLY way to use MCPs now
- โ
Must execute via Bash tool:
npx tsx -e "import..." - โ Source is .ts, imports use .js (ESM + "type": "module")
- โ
Discovery takes 5 seconds:
ls .claude/tools/
Quick Reference
List categories:
# Bash tool:
ls .claude/tools/
Discover tools in category:
# Bash tool:
npx tsx .claude/tools/discover.ts praetorian-cli
Execute wrapper (inline):
# Bash tool with stderr suppression:
ROOT="$(git rev-parse --show-superproject-working-tree --show-toplevel | head -1)" && npx tsx -e "(async () => {
const { toolName } = await import('$ROOT/.claude/tools/category/tool-name.ts');
const result = await toolName.execute({ params });
console.log(JSON.stringify(result, null, 2));
})();" 2>/dev/null
Execute wrapper (script file):
# Step 1: Write tool (Write tool) โ .claude/tools/scripts/my-script.ts
# Step 2: Execute (Bash tool) โ npx tsx .claude/tools/scripts/my-script.ts
Required agent tools:
Bash- Execute TypeScript (npx tsx)Read- Discover available toolsWrite- Optional (for script files)
Token cost:
- Session start: 0 tokens
- Per import: ~50-100 tokens
- Per execution: varies by tool (see result.estimated_tokens)
Troubleshooting
See references/troubleshooting.md for common issues:
- "forEach is not a function" errors
- Missing
next_offsetfor pagination - Empty results debugging
- Import path issues
Related Skills
- mcp-code-create - How to CREATE new wrappers (templates, patterns)
- mcp-code-test - How to TEST wrappers (validation, benchmarks)
- mcp-code-guide - Why progressive loading works (educational)
Further Reading
- Registry overview:
.claude/tools/README.md - Discovery tool:
.claude/tools/discover.ts - Example wrappers:
.claude/tools/praetorian-cli/ - MCP client:
.claude/tools/config/lib/mcp-client.ts
Migration Notes (Historical)
Before (October 2024):
- MCPs defined in
.mcp.json - Loaded at session start: 71.8k tokens
- Direct mcp__ tool calls
After (November 2024):
- MCPs removed from
.mcp.json - Session start: 0 tokens
- Import-based usage only
- 71.8k tokens freed (7.2% of context window)
This architecture is now PERMANENT.