Convert MCP servers to typed TypeScript APIs for efficient code execution. Reduces token usage by 98%+ by transforming tool calls into programmatic access. Use when building agents that need to interact with multiple MCP servers efficiently, when context window is a concern, or when native control flow (loops, conditionals) would simplify multi-step workflows.
/plugin marketplace add cameronsjo/claude-marketplace/plugin install mcp@cameronsjoThis skill inherits all available tools. When active, it can use any tool Claude has access to.
resources/example-workflow.tsresources/generate-server.tsresources/mcp-transport.tsTransform MCP servers from discrete tool invocations into typed TypeScript APIs that agents interact with programmatically. This approach dramatically reduces token usage and enables native control flow.
Traditional MCP tool usage has two inefficiencies:
Context Overload: Tool definitions occupy significant context window space. Agents connected to many servers process thousands of tokens before reading requests.
Intermediate Result Duplication: Data retrieved through tool calls traverses the model multiple times. A meeting transcript fetched, summarized, and stored means processing the transcript tokens repeatedly.
Present MCP servers as filesystem-organized code APIs. Agents discover, load, and use tools via native TypeScript instead of discrete tool calls.
Before (150,000+ tokens for a simple workflow):
1. Tool call: gdrive.getDocument → Model processes result
2. Tool call: summarize → Model processes result
3. Tool call: salesforce.updateRecord → Model processes result
After (2,000 tokens):
const transcript = (await gdrive.getDocument({ documentId: 'abc123' })).content;
const summary = extractKeyPoints(transcript);
await salesforce.updateRecord({
objectType: 'SalesMeeting',
recordId: '00Q5f000001abcXYZ',
data: { Notes: summary }
});
servers/
├── {server-name}/
│ ├── index.ts # Re-exports all tools
│ ├── types.ts # Shared types and interfaces
│ ├── {tool-name}.ts # Individual tool modules
│ └── README.md # Server documentation
└── index.ts # Server discovery/registry
Each MCP tool becomes a typed TypeScript module:
// servers/google-drive/getDocument.ts
import type { DocumentResult } from './types';
export interface GetDocumentInput {
/** Google Drive document ID */
documentId: string;
/** Format to retrieve (default: 'text') */
format?: 'text' | 'html' | 'markdown';
}
export interface GetDocumentOutput {
content: string;
title: string;
lastModified: string;
mimeType: string;
}
/**
* Retrieves a document from Google Drive by ID.
*
* @example
* const doc = await getDocument({ documentId: 'abc123' });
* console.log(doc.content);
*/
export async function getDocument(input: GetDocumentInput): Promise<GetDocumentOutput> {
// Implementation calls underlying MCP transport
return await mcpCall('google-drive', 'getDocument', input);
}
// servers/google-drive/index.ts
export { getDocument } from './getDocument';
export { listFiles } from './listFiles';
export { createDocument } from './createDocument';
export { updateDocument } from './updateDocument';
export * from './types';
// servers/index.ts
export * as gdrive from './google-drive';
export * as salesforce from './salesforce';
export * as slack from './slack';
export * as notion from './notion';
List available tools from the MCP server:
const tools = await mcpClient.listTools();
// Extract: name, description, inputSchema, outputSchema
Convert JSON schemas to TypeScript interfaces:
// From JSON Schema
{
"type": "object",
"properties": {
"query": { "type": "string", "description": "Search query" },
"limit": { "type": "number", "default": 10 }
},
"required": ["query"]
}
// To TypeScript
export interface SearchInput {
/** Search query */
query: string;
/** Maximum results (default: 10) */
limit?: number;
}
For each tool, create a module with:
Export all tools from server index, then all servers from root index.
Agents navigate the filesystem naturally, loading only needed definitions:
// Agent discovers available servers
const servers = await glob('servers/*/index.ts');
// Agent loads specific server when needed
const gdrive = await import('./servers/google-drive');
// Agent uses specific tool
const doc = await gdrive.getDocument({ documentId: 'abc123' });
Large datasets filter client-side before model exposure:
// Fetch 10,000 rows
const allRows = await sheets.getRows({ spreadsheetId: 'xyz' });
// Filter to relevant subset (never exposed to model)
const relevantRows = allRows.filter(row => row.status === 'active');
// Only log/return filtered results
console.log(`Found ${relevantRows.length} active records`);
Replace sequential tool calls with native programming:
// Process multiple items efficiently
for (const item of items) {
const data = await source.getData({ id: item.id });
if (data.needsUpdate) {
await target.updateRecord({
id: item.targetId,
data: transform(data)
});
}
}
Native try/catch instead of tool call error parsing:
try {
const result = await api.riskyOperation({ id });
return { success: true, result };
} catch (error) {
if (error.code === 'NOT_FOUND') {
return { success: false, reason: 'Record not found' };
}
throw error;
}
Maintain workspace files across executions:
// Save reusable functions to skills directory
await fs.writeFile('./skills/summarize.ts', summarizeFunction);
// Load in future executions
const { summarize } = await import('./skills/summarize');
The typed wrappers call through a transport abstraction:
// lib/mcp-transport.ts
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
const clients = new Map<string, Client>();
export async function mcpCall<T>(
serverName: string,
toolName: string,
input: unknown
): Promise<T> {
const client = await getOrCreateClient(serverName);
const result = await client.callTool({
name: toolName,
arguments: input
});
if (result.isError) {
throw new MCPError(result.content);
}
return parseResult<T>(result.content);
}
async function getOrCreateClient(serverName: string): Promise<Client> {
if (!clients.has(serverName)) {
const client = await initializeClient(serverName);
clients.set(serverName, client);
}
return clients.get(serverName)!;
}
Use this template to generate tool modules:
// Template for generating tool modules
function generateToolModule(tool: MCPTool): string {
const inputInterface = schemaToInterface(tool.inputSchema, `${tool.name}Input`);
const outputInterface = schemaToInterface(tool.outputSchema, `${tool.name}Output`);
return `
import { mcpCall } from '../../lib/mcp-transport';
${inputInterface}
${outputInterface}
/**
* ${tool.description}
*
* @example
* const result = await ${tool.name}(input);
*/
export async function ${tool.name}(input: ${tool.name}Input): Promise<${tool.name}Output> {
return await mcpCall('${tool.serverName}', '${tool.name}', input);
}
`;
}
Code execution requires secure sandboxing:
Validate at the typed wrapper layer:
export async function updateRecord(input: UpdateRecordInput): Promise<void> {
// Validate before MCP call
if (!input.recordId.match(/^[A-Za-z0-9]+$/)) {
throw new ValidationError('Invalid record ID format');
}
await mcpCall('salesforce', 'updateRecord', input);
}
Intermediate results stay in execution environment:
// PII never leaves sandbox by default
const userData = await crm.getUser({ id: userId });
// Only sanitized summary returned to model
return {
summary: `User ${userData.firstName} has ${userData.orderCount} orders`,
// Raw PII stays in sandbox
};
Good fit:
Traditional MCP better for:
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.