
Mcp Management
- 20 installs
- 95 repo stars
- Updated July 7, 2026
- binjuhor/shadcn-lar
mcp-management is a Claude skill for discovering, analyzing, and executing tools, prompts, and resources from configured MCP servers in a context-efficient way.
About
This skill manages Model Context Protocol servers by discovering, analyzing, and executing their tools, prompts, and resources. A developer uses it to list available MCP capabilities, select relevant tools for a task, execute tools programmatically, and coordinate across multiple servers. It emphasizes context-efficient progressive disclosure so MCP discovery does not pollute the main context window.
- Discovers, analyzes, and executes tools from configured MCP servers
- Context-efficient progressive disclosure via subagents and a tool catalog
- Multi-server orchestration and intelligent tool selection
Mcp Management by the numbers
- 20 all-time installs (skills.sh)
- Ranked #10,427 of 16,556 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
mcp-management capabilities & compatibility
- Capabilities
- orchestration
- Use cases
- orchestration
What mcp-management says it does
Skill for managing and interacting with Model Context Protocol (MCP) servers.
Progressive disclosure of MCP capabilities (load only what's needed)
Coordinate tools across multiple servers. Each tool knows its source server for proper routing.
npx skills add https://github.com/binjuhor/shadcn-lar --skill mcp-managementAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 20 |
|---|---|
| repo stars | ★ 95 |
| Last updated | July 7, 2026 |
| Repository | binjuhor/shadcn-lar ↗ |
What it does
Discover, select, and execute tools from configured MCP servers across multiple servers without polluting the main context.
Who is it for?
Developers integrating or orchestrating multiple MCP servers who need context-efficient tool discovery and execution.
Skip if: Developers authoring a new MCP server from scratch (use an MCP builder instead).
When should I use this skill?
Discovering MCP capabilities, selecting tools for a task, executing MCP tools programmatically, or building MCP client functionality.
What you get
MCP capabilities are discovered, cataloged, and executed through subagents with progressive disclosure, keeping the main context clean.
By the numbers
- CLI commands: list-tools, list-prompts, list-resources, call-tool
Files
MCP Management
Skill for managing and interacting with Model Context Protocol (MCP) servers.
Overview
MCP is an open protocol enabling AI agents to connect to external tools and data sources. This skill provides scripts and utilities to discover, analyze, and execute MCP capabilities from configured servers without polluting the main context window.
Key Benefits:
- Progressive disclosure of MCP capabilities (load only what's needed)
- Intelligent tool/prompt/resource selection based on task requirements
- Multi-server management from single config file
- Context-efficient: subagents handle MCP discovery and execution
- Persistent tool catalog: automatically saves discovered tools to JSON for fast reference
When to Use This Skill
Use this skill when: 1. Discovering MCP Capabilities: Need to list available tools/prompts/resources from configured servers 2. Task-Based Tool Selection: Analyzing which MCP tools are relevant for a specific task 3. Executing MCP Tools: Calling MCP tools programmatically with proper parameter handling 4. MCP Integration: Building or debugging MCP client implementations 5. Context Management: Avoiding context pollution by delegating MCP operations to subagents
Core Capabilities
1. Configuration Management
MCP servers configured in .claude/.mcp.json.
Gemini CLI Integration (recommended): Create symlink to .gemini/settings.json:
mkdir -p .gemini && ln -sf .claude/.mcp.json .gemini/settings.jsonSee references/configuration.md and references/gemini-cli-integration.md.
GEMINI.md Response Format: Project root contains GEMINI.md that Gemini CLI auto-loads, enforcing structured JSON responses:
{"server":"name","tool":"name","success":true,"result":<data>,"error":null}This ensures parseable, consistent output instead of unpredictable natural language. The file defines:
- Mandatory JSON-only response format (no markdown, no explanations)
- Maximum 500 character responses
- Error handling structure
- Available MCP servers reference
Benefits: Programmatically parseable output, consistent error reporting, DRY configuration (format defined once), context-efficient (auto-loaded by Gemini CLI).
2. Capability Discovery
npx tsx scripts/cli.ts list-tools # Saves to assets/tools.json
npx tsx scripts/cli.ts list-prompts
npx tsx scripts/cli.ts list-resourcesAggregates capabilities from multiple servers with server identification.
3. Intelligent Tool Analysis
LLM analyzes assets/tools.json directly - better than keyword matching algorithms.
4. Tool Execution
Primary: Gemini CLI (if available)
# IMPORTANT: Use stdin piping, NOT -p flag (deprecated, skips MCP init)
echo "Take a screenshot of https://example.com" | gemini -y -m gemini-2.5-flashSecondary: Direct Scripts
npx tsx scripts/cli.ts call-tool memory create_entities '{"entities":[...]}'Fallback: mcp-manager Subagent
See references/gemini-cli-integration.md for complete examples.
Implementation Patterns
Pattern 1: Gemini CLI Auto-Execution (Primary)
Use Gemini CLI for automatic tool discovery and execution. Gemini CLI auto-loads GEMINI.md from project root to enforce structured JSON responses.
Quick Example:
# IMPORTANT: Use stdin piping, NOT -p flag (deprecated, skips MCP init)
# Add "Return JSON only per GEMINI.md instructions" to enforce structured output
echo "Take a screenshot of https://example.com. Return JSON only per GEMINI.md instructions." | gemini -y -m gemini-2.5-flashExpected Output:
{"server":"puppeteer","tool":"screenshot","success":true,"result":"screenshot.png","error":null}Benefits:
- Automatic tool discovery
- Structured JSON responses (parseable by Claude)
- GEMINI.md auto-loaded for consistent formatting
- Faster than subagent orchestration
- No natural language ambiguity
See references/gemini-cli-integration.md for complete guide.
Pattern 2: Subagent-Based Execution (Fallback)
Use mcp-manager agent when Gemini CLI unavailable. Subagent discovers tools, selects relevant ones, executes tasks, reports back.
Benefit: Main context stays clean, only relevant tool definitions loaded when needed.
Pattern 3: LLM-Driven Tool Selection
LLM reads assets/tools.json, intelligently selects relevant tools using context understanding, synonyms, and intent recognition.
Pattern 4: Multi-Server Orchestration
Coordinate tools across multiple servers. Each tool knows its source server for proper routing.
Scripts Reference
scripts/mcp-client.ts
Core MCP client manager class. Handles:
- Config loading from
.claude/.mcp.json - Connecting to multiple MCP servers
- Listing tools/prompts/resources across all servers
- Executing tools with proper error handling
- Connection lifecycle management
scripts/cli.ts
Command-line interface for MCP operations. Commands:
list-tools- Display all tools and save toassets/tools.jsonlist-prompts- Display all promptslist-resources- Display all resourcescall-tool <server> <tool> <json>- Execute a tool
Note: list-tools persists complete tool catalog to assets/tools.json with full schemas for fast reference, offline browsing, and version control.
Quick Start
Method 1: Gemini CLI (recommended)
npm install -g gemini-cli
mkdir -p .gemini && ln -sf .claude/.mcp.json .gemini/settings.json
# IMPORTANT: Use stdin piping, NOT -p flag (deprecated, skips MCP init)
# GEMINI.md auto-loads to enforce JSON responses
echo "Take a screenshot of https://example.com. Return JSON only per GEMINI.md instructions." | gemini -y -m gemini-2.5-flashReturns structured JSON: {"server":"puppeteer","tool":"screenshot","success":true,"result":"screenshot.png","error":null}
Method 2: Scripts
cd .claude/skills/mcp-management/scripts && npm install
npx tsx cli.ts list-tools # Saves to assets/tools.json
npx tsx cli.ts call-tool memory create_entities '{"entities":[...]}'Method 3: mcp-manager Subagent
See references/gemini-cli-integration.md for complete guide.
Technical Details
See references/mcp-protocol.md for:
- JSON-RPC protocol details
- Message types and formats
- Error codes and handling
- Transport mechanisms (stdio, HTTP+SSE)
- Best practices
Integration Strategy
Execution Priority
1. Gemini CLI (Primary): Fast, automatic, intelligent tool selection
- Check:
command -v gemini - Execute:
echo "<task>" | gemini -y -m gemini-2.5-flash - IMPORTANT: Use stdin piping, NOT
-pflag (deprecated, skips MCP init) - Best for: All tasks when available
2. Direct CLI Scripts (Secondary): Manual tool specification
- Use when: Need specific tool/server control
- Execute:
npx tsx scripts/cli.ts call-tool <server> <tool> <args>
3. mcp-manager Subagent (Fallback): Context-efficient delegation
- Use when: Gemini unavailable or failed
- Keeps main context clean
Integration with Agents
The mcp-manager agent uses this skill to:
- Check Gemini CLI availability first
- Execute via
geminicommand if available - Fallback to direct script execution
- Discover MCP capabilities without loading into main context
- Report results back to main agent
This keeps main agent context clean and enables efficient MCP integration.
MCP Management Skill
Intelligent management and execution of Model Context Protocol (MCP) servers.
Overview
This skill enables Claude to discover, analyze, and execute MCP server capabilities without polluting the main context window. Perfect for context-efficient MCP integration using subagent-based architecture.
Features
- Multi-Server Management: Connect to multiple MCP servers from single config
- Intelligent Tool Discovery: Analyze which tools are relevant for specific tasks
- Progressive Disclosure: Load only necessary tool definitions
- Execution Engine: Call MCP tools with proper parameter handling
- Context Efficiency: Delegate MCP operations to
mcp-managersubagent
Quick Start
1. Install Dependencies
cd .claude/skills/mcp-management/scripts
npm install2. Configure MCP Servers
Create .claude/.mcp.json:
{
"mcpServers": {
"memory": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-memory"]
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/allowed/path"]
}
}
}See .claude/.mcp.json.example for more examples.
3. Test Connection
cd .claude/skills/mcp-management/scripts
npx ts-node cli.ts list-toolsUsage Patterns
Pattern 1: Discover Available Tools
npx ts-node scripts/cli.ts list-tools
npx ts-node scripts/cli.ts list-prompts
npx ts-node scripts/cli.ts list-resourcesPattern 2: LLM-Driven Tool Selection
The LLM reads assets/tools.json and intelligently selects tools. No separate analysis command needed - the LLM's understanding of context and intent is superior to keyword matching.
Pattern 3: Execute MCP Tools
npx ts-node scripts/cli.ts call-tool memory add '{"key":"name","value":"Alice"}'Pattern 4: Use with Subagent
In main Claude conversation:
User: "I need to search the web and save results"
Main Agent: [Spawns mcp-manager subagent]
mcp-manager: Discovers brave-search + memory tools, reports back
Main Agent: Uses recommended tools for implementationArchitecture
Main Agent (Claude)
↓ (delegates MCP tasks)
mcp-manager Subagent
↓ (uses skill)
mcp-management Skill
↓ (connects via)
MCP Servers (memory, filesystem, etc.)Benefits:
- Main agent context stays clean
- MCP discovery happens in isolated subagent context
- Only relevant tool definitions loaded when needed
- Reduced token usage
File Structure
mcp-management/
├── SKILL.md # Skill definition
├── README.md # This file
├── scripts/
│ ├── mcp-client.ts # Core MCP client manager
│ ├── analyze-tools.ts # Intelligent tool selection
│ ├── cli.ts # Command-line interface
│ ├── package.json # Dependencies
│ ├── tsconfig.json # TypeScript config
│ └── .env.example # Environment template
└── references/
├── mcp-protocol.md # MCP protocol reference
└── configuration.md # Config guideScripts Reference
mcp-client.ts
Core client manager class:
- Load config from
.claude/.mcp.json - Connect to multiple MCP servers
- List/execute tools, prompts, resources
- Lifecycle management
cli.ts
Command-line interface:
list-tools- Show all tools and save to assets/tools.jsonlist-prompts- Show all promptslist-resources- Show all resourcescall-tool <server> <tool> <json>- Execute tool
Note: Tool analysis is performed by the LLM reading assets/tools.json, which provides better context understanding than algorithmic matching.
Configuration
Environment Variables
Scripts check for variables in this order:
1. process.env (runtime) 2. .claude/skills/mcp-management/.env 3. .claude/skills/.env 4. .claude/.env
MCP Config Format
{
"mcpServers": {
"server-name": {
"command": "executable", // Required
"args": ["arg1", "arg2"], // Required
"env": { // Optional
"VAR": "value",
"API_KEY": "${ENV_VAR}" // Reference env vars
}
}
}
}Common MCP Servers
Install with npx:
@modelcontextprotocol/server-memory- Key-value storage@modelcontextprotocol/server-filesystem- File operations@modelcontextprotocol/server-brave-search- Web search@modelcontextprotocol/server-puppeteer- Browser automation@modelcontextprotocol/server-fetch- HTTP requests
Integration with mcp-manager Agent
The mcp-manager agent (.claude/agents/mcp-manager.md) uses this skill to:
1. Discover: Connect to MCP servers, list capabilities 2. Analyze: Filter relevant tools for tasks 3. Execute: Call MCP tools on behalf of main agent 4. Report: Send concise results back to main agent
This architecture keeps main context clean and enables efficient MCP integration.
Troubleshooting
"Config not found"
Ensure .claude/.mcp.json exists and is valid JSON.
"Server connection failed"
Check:
- Server command is installed (
npxpackages installed?) - Server args are correct
- Environment variables are set
"Tool not found"
List available tools first:
npx ts-node scripts/cli.ts list-toolsResources
- MCP Specification
- MCP TypeScript SDK
- Official MCP Servers
- Skill References
License
MIT
MCP Configuration Guide
Configuration File Structure
MCP servers are configured in .claude/.mcp.json:
{
"mcpServers": {
"server-name": {
"command": "executable",
"args": ["arg1", "arg2"],
"env": {
"API_KEY": "value"
}
}
}
}Common Server Configurations
Memory Server
Store and retrieve key-value data:
{
"memory": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-memory"]
}
}Filesystem Server
File operations with restricted access:
{
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/allowed/path"
]
}
}Brave Search Server
Web search capabilities:
{
"brave-search": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-brave-search"],
"env": {
"BRAVE_API_KEY": "${BRAVE_API_KEY}"
}
}
}Puppeteer Server
Browser automation:
{
"puppeteer": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-puppeteer"]
}
}Environment Variables
Reference env vars with ${VAR_NAME} syntax:
{
"api-server": {
"command": "node",
"args": ["server.js"],
"env": {
"API_KEY": "${MY_API_KEY}",
"BASE_URL": "${API_BASE_URL}"
}
}
}Configuration Loading Order
Scripts check for config in this order:
1. process.env (runtime environment) 2. .claude/skills/mcp-management/.env 3. .claude/skills/.env 4. .claude/.env
Validation
Config must:
- Be valid JSON
- Include
mcpServersobject - Each server must have
commandandargs envis optional but must be object if present
Gemini CLI Integration Guide
⚠️ CRITICAL: Use Stdin Piping, NOT -p Flag
The -p flag is deprecated and skips MCP server initialization, causing tools to be unavailable:
# ❌ WRONG - This skips MCP connections!
gemini -y -m gemini-2.5-flash -p "Take a screenshot"
# ✅ CORRECT - This initializes MCP servers
echo "Take a screenshot" | gemini -y -m gemini-2.5-flashWhy: The -p flag runs in "quick mode" and bypasses MCP server connection initialization. Always use stdin piping (echo + pipe) to ensure MCP tools are available.
Overview
Gemini CLI provides automatic MCP tool discovery and execution via natural language prompts. This is the recommended primary method for executing MCP tools.
Installation
npm install -g gemini-cliVerify installation:
gemini --versionConfiguration
Symlink Setup
Gemini CLI reads MCP servers from .gemini/settings.json. Create a symlink to .claude/.mcp.json:
# Create .gemini directory
mkdir -p .gemini
# Create symlink (Unix/Linux/macOS)
ln -sf .claude/.mcp.json .gemini/settings.json
# Create symlink (Windows - requires admin or developer mode)
mklink .gemini\settings.json .claude\.mcp.jsonSecurity
Add to .gitignore:
.gemini/settings.jsonThis prevents committing sensitive API keys and server configurations.
Usage
Basic Syntax
# IMPORTANT: Use stdin piping, NOT -p flag (deprecated, skips MCP init)
echo "<prompt>" | gemini [flags]Essential Flags
-y: Skip confirmation prompts (auto-approve tool execution)-m <model>: Model selectiongemini-2.5-flash(fast, recommended for MCP)gemini-2.5-flash(balanced)gemini-pro(high quality)
Examples
Screenshot Capture:
echo "Take a screenshot of https://www.google.com.vn" | gemini -y -m gemini-2.5-flashMemory Operations:
echo "Remember that Alice is a React developer working on e-commerce projects" | gemini -y -m gemini-2.5-flashWeb Research:
echo "Search for latest Next.js 15 features and summarize the top 3" | gemini -y -m gemini-2.5-flashMulti-Tool Orchestration:
echo "Search for Claude AI documentation, take a screenshot of the homepage, and save both to memory" | gemini -y -m gemini-2.5-flashBrowser Automation:
echo "Navigate to https://example.com, click the signup button, and take a screenshot" | gemini -y -m gemini-2.5-flashHow It Works
1. Configuration Loading: Reads .gemini/settings.json (symlinked to .claude/.mcp.json) 2. Server Connection: Connects to all configured MCP servers 3. Tool Discovery: Lists all available tools from servers 4. Prompt Analysis: Gemini model analyzes the prompt 5. Tool Selection: Automatically selects relevant tools 6. Execution: Calls tools with appropriate parameters 7. Result Synthesis: Combines tool outputs into coherent response
Advanced Configuration
Trusted Servers (Skip Confirmations)
Edit .claude/.mcp.json:
{
"mcpServers": {
"memory": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-memory"],
"trust": true
}
}
}With trust: true, the -y flag is unnecessary.
Tool Filtering
Limit tool exposure:
{
"mcpServers": {
"chrome-devtools": {
"command": "npx",
"args": ["-y", "chrome-devtools-mcp@latest"],
"includeTools": ["navigate_page", "screenshot"],
"excludeTools": ["evaluate_js"]
}
}
}Environment Variables
Use $VAR_NAME syntax for sensitive data:
{
"mcpServers": {
"brave-search": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-brave-search"],
"env": {
"BRAVE_API_KEY": "$BRAVE_API_KEY"
}
}
}
}Troubleshooting
Check MCP Status
gemini
> /mcpShows:
- Connected servers
- Available tools
- Configuration errors
Verify Symlink
# Unix/Linux/macOS
ls -la .gemini/settings.json
# Windows
dir .gemini\settings.jsonShould show symlink pointing to .claude/.mcp.json.
Debug Mode
echo "Take a screenshot" | gemini --debugShows detailed MCP communication logs.
Comparison with Alternatives
| Method | Speed | Flexibility | Setup | Best For |
|---|---|---|---|---|
| Gemini CLI | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ | All tasks |
| Direct Scripts | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | Specific tools |
| mcp-manager | ⭐ | ⭐⭐ | ⭐⭐⭐ | Fallback |
Recommendation: Use Gemini CLI as primary method, fallback to scripts/subagent when unavailable.
Resources
Model Context Protocol (MCP) Reference
Protocol Overview
MCP is JSON-RPC 2.0 based protocol for AI-tool integration.
Version: 2025-03-26 Foundation: JSON-RPC 2.0 Architecture: Client-Host-Server
Connection Lifecycle
1. Initialize: Client sends initialize request with capabilities 2. Response: Server responds with its capabilities 3. Handshake: Client sends notifications/initialized 4. Active: Bidirectional messaging 5. Shutdown: Close connections, cleanup
Core Capabilities
Tools (Executable Functions)
Tools are functions that servers expose for execution.
List Tools:
{"method": "tools/list"}Call Tool:
{
"method": "tools/call",
"params": {
"name": "tool_name",
"arguments": {}
}
}Prompts (Interaction Templates)
Prompts are reusable templates for LLM interactions.
List Prompts:
{"method": "prompts/list"}Get Prompt:
{
"method": "prompts/get",
"params": {
"name": "prompt_name",
"arguments": {}
}
}Resources (Data Sources)
Resources expose read-only data to clients.
List Resources:
{"method": "resources/list"}Read Resource:
{
"method": "resources/read",
"params": {"uri": "resource://path"}
}Transport Types
stdio (Local)
Server runs as subprocess. Messages via stdin/stdout.
const transport = new StdioClientTransport({
command: 'node',
args: ['server.js']
});HTTP+SSE (Remote)
POST for requests, GET for server events.
const transport = new StreamableHTTPClientTransport({
url: 'http://localhost:3000/mcp'
});Error Codes
- -32700: Parse error
- -32600: Invalid request
- -32601: Method not found
- -32602: Invalid params
- -32603: Internal error
- -32002: Resource not found (MCP-specific)
Best Practices
1. Progressive Disclosure: Load tool definitions on-demand 2. Context Efficiency: Filter data before returning 3. Security: Validate inputs, sanitize outputs 4. Resource Management: Cleanup connections properly 5. Error Handling: Handle all error cases gracefully
# MCP Management Scripts Environment Variables
# Path to MCP configuration file (optional, defaults to .claude/.mcp.json)
MCP_CONFIG_PATH=.claude/.mcp.json
# Logging level (optional, defaults to info)
LOG_LEVEL=info
# Enable debug mode (optional, defaults to false)
DEBUG=false
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
node_modules
.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
coverage
# next.js
.next
out
# production
build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# package manager
package-lock.json
yarn.lock
pnpm-lock.yaml
# semantic-release
.nyc_output
# env files (can opt-in for committing if needed)
.env*
!.env.example
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
# flutter
.dart_tool
build
GoogleService-Info.plist
repomix-output.xml
.serena/cache
plans/**/*
!plans/templates/*
screenshots/*
docs/screenshots/*
docs/journals/*
docs/research/*
logs.txt
test-ck
__pycache__
prompt.md
#!/usr/bin/env node
/**
* MCP Management CLI - Command-line interface for MCP operations
*/
import { MCPClientManager } from './mcp-client.js';
import { writeFileSync, mkdirSync } from 'fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const GLOBAL_TIMEOUT_MS = parseInt(process.env.MCP_TIMEOUT || '120000', 10);
let globalManager: MCPClientManager | null = null;
function setupShutdownHandlers() {
const shutdown = async (signal: string) => {
console.log(`\nReceived ${signal}, cleaning up...`);
if (globalManager) {
await globalManager.cleanup();
}
process.exit(0);
};
process.on('SIGINT', () => shutdown('SIGINT'));
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGHUP', () => shutdown('SIGHUP'));
process.on('unhandledRejection', (reason) => {
console.error('Unhandled rejection:', reason);
process.exit(1);
});
}
async function main() {
const args = process.argv.slice(2);
const command = args[0];
// Setup shutdown handlers
setupShutdownHandlers();
// Check for help flags BEFORE connecting to servers
if (!command || command === '--help' || command === 'help') {
printUsage();
process.exit(0);
}
// Global timeout
const timeoutHandle = setTimeout(() => {
console.error('Global timeout exceeded, forcing exit');
process.exit(1);
}, GLOBAL_TIMEOUT_MS);
timeoutHandle.unref();
const manager = new MCPClientManager();
globalManager = manager;
try {
// Load config
await manager.loadConfig();
console.log('✓ Config loaded');
// Connect to all servers
await manager.connectAll();
console.log('✓ Connected to all MCP servers\n');
switch (command) {
case 'list-tools':
await listTools(manager);
break;
case 'list-prompts':
await listPrompts(manager);
break;
case 'list-resources':
await listResources(manager);
break;
case 'call-tool':
await callTool(manager, args[1], args[2], args[3]);
break;
default:
printUsage();
}
await manager.cleanup();
process.exit(0);
} catch (error) {
console.error('Error:', error);
process.exit(1);
}
}
async function listTools(manager: MCPClientManager) {
const tools = await manager.getAllTools();
console.log(`Found ${tools.length} tools:\n`);
for (const tool of tools) {
console.log(`📦 ${tool.serverName} / ${tool.name}`);
console.log(` ${tool.description}`);
if (tool.inputSchema?.properties) {
console.log(` Parameters: ${Object.keys(tool.inputSchema.properties).join(', ')}`);
}
console.log('');
}
// Save tools to JSON file
const assetsDir = join(__dirname, '..', 'assets');
const toolsPath = join(assetsDir, 'tools.json');
try {
mkdirSync(assetsDir, { recursive: true });
writeFileSync(toolsPath, JSON.stringify(tools, null, 2));
console.log(`\n✓ Tools saved to ${toolsPath}`);
} catch (error) {
console.error(`\n✗ Failed to save tools: ${error}`);
}
}
async function listPrompts(manager: MCPClientManager) {
const prompts = await manager.getAllPrompts();
console.log(`Found ${prompts.length} prompts:\n`);
for (const prompt of prompts) {
console.log(`💬 ${prompt.serverName} / ${prompt.name}`);
console.log(` ${prompt.description}`);
if (prompt.arguments && prompt.arguments.length > 0) {
console.log(` Arguments: ${prompt.arguments.map((a: any) => a.name).join(', ')}`);
}
console.log('');
}
}
async function listResources(manager: MCPClientManager) {
const resources = await manager.getAllResources();
console.log(`Found ${resources.length} resources:\n`);
for (const resource of resources) {
console.log(`📄 ${resource.serverName} / ${resource.name}`);
console.log(` URI: ${resource.uri}`);
if (resource.description) {
console.log(` ${resource.description}`);
}
if (resource.mimeType) {
console.log(` Type: ${resource.mimeType}`);
}
console.log('');
}
}
async function callTool(
manager: MCPClientManager,
serverName: string,
toolName: string,
argsJson: string
) {
if (!serverName || !toolName || !argsJson) {
console.error('Usage: cli.ts call-tool <server> <tool> <json-args>');
process.exit(1);
}
const args = JSON.parse(argsJson);
console.log(`Calling ${serverName}/${toolName}...`);
const result = await manager.callTool(serverName, toolName, args);
console.log('\nResult:');
console.log(JSON.stringify(result, null, 2));
}
function printUsage() {
console.log(`
MCP Management CLI
Usage:
cli.ts <command> [options]
Commands:
list-tools List all tools and save to assets/tools.json
list-prompts List all prompts from all MCP servers
list-resources List all resources from all MCP servers
call-tool <server> <tool> <json> Call a specific tool
Examples:
cli.ts list-tools
cli.ts call-tool memory create_entities '{"entities":[{"name":"Alice","entityType":"person"}]}'
cli.ts call-tool human-mcp playwright_screenshot_fullpage '{"url":"https://example.com"}'
Note: Tool analysis is done by the LLM reading assets/tools.json directly.
`);
}
main();
#!/usr/bin/env node
/**
* Tool Analyzer - Intelligently selects relevant MCP tools for tasks
*/
/**
* Analyze tools and return those relevant to the task
*/
export function analyzeToolsForTask(tools, taskDescription) {
const keywords = extractKeywords(taskDescription);
const scoredTools = tools.map(tool => ({
tool,
score: calculateRelevanceScore(tool, keywords, taskDescription),
reasons: explainScore(tool, keywords, taskDescription)
}));
// Sort by score descending
scoredTools.sort((a, b) => b.score - a.score);
// Filter tools with score above threshold
const threshold = 0.3;
const relevant = scoredTools.filter(st => st.score > threshold);
return {
relevantTools: relevant.map(st => st.tool),
reasoning: relevant.map(st => `${st.tool.name} (${st.tool.serverName}): ${st.reasons.join('; ')}`),
confidence: relevant.length > 0
? relevant[0].score
: 0
};
}
function extractKeywords(text) {
const stopWords = new Set([
'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at',
'to', 'for', 'of', 'with', 'by', 'from', 'as', 'is', 'was', 'are'
]);
return text
.toLowerCase()
.split(/\W+/)
.filter(word => word.length > 2 && !stopWords.has(word));
}
function calculateRelevanceScore(tool, keywords, taskDescription) {
let score = 0;
const toolText = `${tool.name} ${tool.description}`.toLowerCase();
// Keyword matching
for (const keyword of keywords) {
if (toolText.includes(keyword)) {
score += 0.2;
}
}
// Exact phrase matching
const taskLower = taskDescription.toLowerCase();
if (toolText.includes(taskLower) || taskLower.includes(tool.name.toLowerCase())) {
score += 0.5;
}
// Schema complexity bonus (more params = more specialized)
if (tool.inputSchema?.properties) {
const paramCount = Object.keys(tool.inputSchema.properties).length;
score += Math.min(paramCount * 0.05, 0.3);
}
return Math.min(score, 1.0);
}
function explainScore(tool, keywords, taskDescription) {
const reasons = [];
const toolText = `${tool.name} ${tool.description}`.toLowerCase();
const matchedKeywords = keywords.filter(k => toolText.includes(k));
if (matchedKeywords.length > 0) {
reasons.push(`matches keywords: ${matchedKeywords.join(', ')}`);
}
if (tool.description) {
reasons.push(`description: ${tool.description.slice(0, 100)}`);
}
return reasons;
}
#!/usr/bin/env node
/**
* MCP Management CLI - Command-line interface for MCP operations
*/
import { MCPClientManager } from './mcp-client.js';
import { writeFileSync, mkdirSync } from 'fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
async function main() {
const args = process.argv.slice(2);
const command = args[0];
const manager = new MCPClientManager();
try {
// Load config
await manager.loadConfig();
console.log('✓ Config loaded');
// Connect to all servers
await manager.connectAll();
console.log('✓ Connected to all MCP servers\n');
switch (command) {
case 'list-tools':
await listTools(manager);
break;
case 'list-prompts':
await listPrompts(manager);
break;
case 'list-resources':
await listResources(manager);
break;
case 'call-tool':
await callTool(manager, args[1], args[2], args[3]);
break;
default:
printUsage();
}
await manager.cleanup();
}
catch (error) {
console.error('Error:', error);
process.exit(1);
}
}
async function listTools(manager) {
const tools = await manager.getAllTools();
console.log(`Found ${tools.length} tools:\n`);
for (const tool of tools) {
console.log(`📦 ${tool.serverName} / ${tool.name}`);
console.log(` ${tool.description}`);
if (tool.inputSchema?.properties) {
console.log(` Parameters: ${Object.keys(tool.inputSchema.properties).join(', ')}`);
}
console.log('');
}
// Save tools to JSON file
const assetsDir = join(__dirname, '..', 'assets');
const toolsPath = join(assetsDir, 'tools.json');
try {
mkdirSync(assetsDir, { recursive: true });
writeFileSync(toolsPath, JSON.stringify(tools, null, 2));
console.log(`\n✓ Tools saved to ${toolsPath}`);
}
catch (error) {
console.error(`\n✗ Failed to save tools: ${error}`);
}
}
async function listPrompts(manager) {
const prompts = await manager.getAllPrompts();
console.log(`Found ${prompts.length} prompts:\n`);
for (const prompt of prompts) {
console.log(`💬 ${prompt.serverName} / ${prompt.name}`);
console.log(` ${prompt.description}`);
if (prompt.arguments && prompt.arguments.length > 0) {
console.log(` Arguments: ${prompt.arguments.map((a) => a.name).join(', ')}`);
}
console.log('');
}
}
async function listResources(manager) {
const resources = await manager.getAllResources();
console.log(`Found ${resources.length} resources:\n`);
for (const resource of resources) {
console.log(`📄 ${resource.serverName} / ${resource.name}`);
console.log(` URI: ${resource.uri}`);
if (resource.description) {
console.log(` ${resource.description}`);
}
if (resource.mimeType) {
console.log(` Type: ${resource.mimeType}`);
}
console.log('');
}
}
async function callTool(manager, serverName, toolName, argsJson) {
if (!serverName || !toolName || !argsJson) {
console.error('Usage: cli.ts call-tool <server> <tool> <json-args>');
process.exit(1);
}
const args = JSON.parse(argsJson);
console.log(`Calling ${serverName}/${toolName}...`);
const result = await manager.callTool(serverName, toolName, args);
console.log('\nResult:');
console.log(JSON.stringify(result, null, 2));
}
function printUsage() {
console.log(`
MCP Management CLI
Usage:
cli.ts <command> [options]
Commands:
list-tools List all tools and save to assets/tools.json
list-prompts List all prompts from all MCP servers
list-resources List all resources from all MCP servers
call-tool <server> <tool> <json> Call a specific tool
Examples:
cli.ts list-tools
cli.ts call-tool memory create_entities '{"entities":[{"name":"Alice","entityType":"person"}]}'
cli.ts call-tool human-mcp playwright_screenshot_fullpage '{"url":"https://example.com"}'
Note: Tool analysis is done by the LLM reading assets/tools.json directly.
`);
}
main();
#!/usr/bin/env node
/**
* MCP Client - Core client for interacting with MCP servers
*/
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { readFile } from 'fs/promises';
import { resolve } from 'path';
export class MCPClientManager {
config = null;
clients = new Map();
async loadConfig(configPath = '.claude/.mcp.json') {
const fullPath = resolve(process.cwd(), configPath);
const content = await readFile(fullPath, 'utf-8');
const config = JSON.parse(content);
this.config = config;
return config;
}
async connectToServer(serverName) {
if (!this.config?.mcpServers[serverName]) {
throw new Error(`Server ${serverName} not found in config`);
}
const serverConfig = this.config.mcpServers[serverName];
const transport = new StdioClientTransport({
command: serverConfig.command,
args: serverConfig.args,
env: serverConfig.env
});
const client = new Client({
name: `mcp-manager-${serverName}`,
version: '1.0.0'
}, { capabilities: {} });
await client.connect(transport);
this.clients.set(serverName, client);
return client;
}
async connectAll() {
if (!this.config) {
throw new Error('Config not loaded. Call loadConfig() first.');
}
const connections = Object.keys(this.config.mcpServers).map(name => this.connectToServer(name));
await Promise.all(connections);
}
async getAllTools() {
const allTools = [];
for (const [serverName, client] of this.clients.entries()) {
const response = await client.listTools({}, { timeout: 300000 });
for (const tool of response.tools) {
allTools.push({
serverName,
name: tool.name,
description: tool.description || '',
inputSchema: tool.inputSchema,
outputSchema: tool.outputSchema
});
}
}
return allTools;
}
async getAllPrompts() {
const allPrompts = [];
for (const [serverName, client] of this.clients.entries()) {
const response = await client.listPrompts({}, { timeout: 300000 });
for (const prompt of response.prompts) {
allPrompts.push({
serverName,
name: prompt.name,
description: prompt.description || '',
arguments: prompt.arguments
});
}
}
return allPrompts;
}
async getAllResources() {
const allResources = [];
for (const [serverName, client] of this.clients.entries()) {
const response = await client.listResources({}, { timeout: 300000 });
for (const resource of response.resources) {
allResources.push({
serverName,
uri: resource.uri,
name: resource.name,
description: resource.description,
mimeType: resource.mimeType
});
}
}
return allResources;
}
async callTool(serverName, toolName, args) {
const client = this.clients.get(serverName);
if (!client)
throw new Error(`Not connected to server: ${serverName}`);
return await client.callTool({ name: toolName, arguments: args }, undefined, { timeout: 300000 });
}
async getPrompt(serverName, promptName, args) {
const client = this.clients.get(serverName);
if (!client)
throw new Error(`Not connected to server: ${serverName}`);
return await client.getPrompt({ name: promptName, arguments: args }, { timeout: 300000 });
}
async readResource(serverName, uri) {
const client = this.clients.get(serverName);
if (!client)
throw new Error(`Not connected to server: ${serverName}`);
return await client.readResource({ uri }, { timeout: 300000 });
}
async cleanup() {
for (const client of this.clients.values()) {
await client.close();
}
this.clients.clear();
}
}
#!/usr/bin/env node
/**
* MCP Client - Core client for interacting with MCP servers
*/
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { readFile } from 'fs/promises';
import { resolve } from 'path';
interface MCPConfig {
mcpServers: {
[key: string]: {
command: string;
args: string[];
env?: Record<string, string>;
};
};
}
interface ToolInfo {
serverName: string;
name: string;
description: string;
inputSchema: any;
outputSchema?: any;
}
interface PromptInfo {
serverName: string;
name: string;
description: string;
arguments?: any[];
}
interface ResourceInfo {
serverName: string;
uri: string;
name: string;
description?: string;
mimeType?: string;
}
export class MCPClientManager {
private config: MCPConfig | null = null;
private clients: Map<string, Client> = new Map();
private transports: Map<string, StdioClientTransport> = new Map();
async loadConfig(configPath: string = '.claude/.mcp.json'): Promise<MCPConfig> {
const fullPath = resolve(process.cwd(), configPath);
const content = await readFile(fullPath, 'utf-8');
const config = JSON.parse(content) as MCPConfig;
this.config = config;
return config;
}
async connectToServer(serverName: string): Promise<Client> {
if (!this.config?.mcpServers[serverName]) {
throw new Error(`Server ${serverName} not found in config`);
}
const serverConfig = this.config.mcpServers[serverName];
const transport = new StdioClientTransport({
command: serverConfig.command,
args: serverConfig.args,
env: serverConfig.env
});
const client = new Client({
name: `mcp-manager-${serverName}`,
version: '1.0.0'
}, { capabilities: {} });
await client.connect(transport);
this.clients.set(serverName, client);
this.transports.set(serverName, transport); // Track transport!
return client;
}
async connectAll(): Promise<void> {
if (!this.config) {
throw new Error('Config not loaded. Call loadConfig() first.');
}
const serverNames = Object.keys(this.config.mcpServers);
console.log(`Connecting to ${serverNames.length} servers sequentially...`);
for (const serverName of serverNames) {
try {
await this.connectToServer(serverName);
console.log(`✓ ${serverName} connected`);
} catch (error) {
console.error(`✗ ${serverName} failed:`, error);
// Continue with other servers
}
}
}
async getAllTools(): Promise<ToolInfo[]> {
const allTools: ToolInfo[] = [];
for (const [serverName, client] of this.clients.entries()) {
try {
const response = await client.listTools({}, { timeout: 300000 });
for (const tool of response.tools) {
allTools.push({
serverName,
name: tool.name,
description: tool.description || '',
inputSchema: tool.inputSchema,
outputSchema: (tool as any).outputSchema
});
}
} catch (error: any) {
if (error?.code === -32601) {
console.warn(`${serverName} does not support listTools`);
} else {
console.error(`Error from ${serverName}:`, error);
}
// Continue with other servers!
}
}
return allTools;
}
async getAllPrompts(): Promise<PromptInfo[]> {
const allPrompts: PromptInfo[] = [];
for (const [serverName, client] of this.clients.entries()) {
try {
const response = await client.listPrompts({}, { timeout: 300000 });
for (const prompt of response.prompts) {
allPrompts.push({
serverName,
name: prompt.name,
description: prompt.description || '',
arguments: prompt.arguments
});
}
} catch (error: any) {
if (error?.code === -32601) {
console.warn(`${serverName} does not support listPrompts`);
} else {
console.error(`Error from ${serverName}:`, error);
}
// Continue with other servers!
}
}
return allPrompts;
}
async getAllResources(): Promise<ResourceInfo[]> {
const allResources: ResourceInfo[] = [];
for (const [serverName, client] of this.clients.entries()) {
try {
const response = await client.listResources({}, { timeout: 300000 });
for (const resource of response.resources) {
allResources.push({
serverName,
uri: resource.uri,
name: resource.name,
description: resource.description,
mimeType: resource.mimeType
});
}
} catch (error: any) {
if (error?.code === -32601) {
console.warn(`${serverName} does not support listResources`);
} else {
console.error(`Error from ${serverName}:`, error);
}
// Continue with other servers!
}
}
return allResources;
}
async callTool(serverName: string, toolName: string, args: any): Promise<any> {
const client = this.clients.get(serverName);
if (!client) throw new Error(`Not connected to server: ${serverName}`);
return await client.callTool(
{ name: toolName, arguments: args },
undefined,
{ timeout: 300000 }
);
}
async getPrompt(serverName: string, promptName: string, args?: any): Promise<any> {
const client = this.clients.get(serverName);
if (!client) throw new Error(`Not connected to server: ${serverName}`);
return await client.getPrompt({ name: promptName, arguments: args }, { timeout: 300000 });
}
async readResource(serverName: string, uri: string): Promise<any> {
const client = this.clients.get(serverName);
if (!client) throw new Error(`Not connected to server: ${serverName}`);
return await client.readResource({ uri }, { timeout: 300000 });
}
async cleanup(): Promise<void> {
// Close clients with timeout
const cleanupPromises: Promise<void>[] = [];
for (const [serverName, client] of this.clients.entries()) {
cleanupPromises.push(
(async () => {
try {
await client.close();
} catch (error) {
console.warn(`Warning closing ${serverName}:`, error);
}
})()
);
}
await Promise.race([
Promise.all(cleanupPromises),
new Promise<void>((resolve) => setTimeout(resolve, 5000))
]);
// CRITICAL: Close transports to kill subprocesses
for (const [serverName, transport] of this.transports.entries()) {
try {
await transport.close();
} catch (error) {
console.warn(`Warning closing ${serverName} transport:`, error);
}
}
this.clients.clear();
this.transports.clear();
}
}
{
"name": "mcp-management-scripts",
"version": "1.0.0",
"type": "module",
"description": "MCP client scripts for managing MCP servers",
"scripts": {
"build": "tsc",
"test": "node --loader ts-node/esm test.ts"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0"
},
"devDependencies": {
"@types/node": "^20.0.0",
"nodemon": "^3.1.11",
"ts-node": "^10.9.2",
"tsx": "^4.20.6",
"typescript": "^5.9.3"
}
}
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"outDir": "./dist",
"rootDir": "./",
"resolveJsonModule": true
},
"include": ["*.ts"],
"exclude": ["node_modules", "dist"]
}
Related skills
FAQ
How does mcp-management stay context-efficient?
It uses progressive disclosure, delegates MCP discovery and execution to subagents, and persists a tool catalog to JSON so only relevant tool definitions load when needed.
How are MCP servers configured?
MCP servers are configured in .claude/.mcp.json, optionally symlinked to .gemini/settings.json for Gemini CLI integration.