# Directory Structure ``` ├── .github │ └── workflows │ └── npm-publish.yml ├── .gitignore ├── .npmignore ├── build │ ├── index.d.ts │ └── index.js ├── build.sh ├── Dockerfile ├── docs │ └── images │ └── screenshot.png ├── LICENSE ├── package-lock.json ├── package.json ├── README.md ├── smithery.yaml ├── src │ └── index.ts └── tsconfig.json ``` # Files -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- ``` 1 | # Development files 2 | .git 3 | .github 4 | .vscode 5 | src/ 6 | test/ 7 | tests/ 8 | *.test.js 9 | *.spec.js 10 | tsconfig.json 11 | .eslintrc 12 | .prettierrc 13 | .editorconfig 14 | .gitignore 15 | .travis.yml 16 | .circleci/ 17 | jest.config.js 18 | 19 | # Build scripts 20 | build.sh 21 | scripts/ 22 | 23 | # Documentation files that aren't needed in the package 24 | docs/ 25 | test-instructions.md 26 | 27 | # Node.js 28 | node_modules/ 29 | npm-debug.log 30 | yarn-debug.log 31 | yarn-error.log 32 | 33 | # macOS 34 | .DS_Store 35 | ``` -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` 1 | # Dependency directories 2 | node_modules/ 3 | 4 | # Build artifacts 5 | dist/ 6 | 7 | # dotenv environment variables file 8 | .env 9 | .env.local 10 | .env.development.local 11 | .env.test.local 12 | .env.production.local 13 | 14 | # OS-specific files 15 | .DS_Store 16 | .DS_Store? 17 | ._* 18 | .Spotlight-V100 19 | .Trashes 20 | ehthumbs.db 21 | Thumbs.db 22 | 23 | # Logs 24 | logs 25 | *.log 26 | npm-debug.log* 27 | yarn-debug.log* 28 | yarn-error.log* 29 | 30 | # Yarn integrity file 31 | .yarn-integrity 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Editor directories and files 37 | .idea 38 | .vscode 39 | *.swp 40 | *.swo ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown 1 | # MCP Easy Copy 2 | 3 | [](https://modelcontextprotocol.io) 4 | [](https://claude.ai/download) 5 | [](https://www.npmjs.com/package/@fishes/mcp-easy-copy) 6 | [](https://nodejs.org) 7 | [](https://smithery.ai/server/@fisheepx/mcp-easy-copy) 8 | [](LICENSE) 9 | 10 | A Model Context Protocol server that makes it easy to discover and copy available MCP services in Claude Desktop. 11 | 12 | <img src="docs/images/screenshot.png" alt="MCP Easy Copy in action" width="400"/> 13 | 14 | ## Purpose 15 | 16 | This MCP server is designed to be your first stop when working with Claude Desktop. It solves the problem of having to remember MCP service names or looking them up in configuration files by: 17 | 18 | 1. Automatically reading the Claude Desktop configuration file 19 | 2. Extracting the names of all configured MCP services 20 | 3. Presenting them in an easy-to-copy format at the top of the tools list 21 | 22 | Although Claude can now automatically select the appropriate MCP services in most scenarios, there are still situations where users need to explicitly specify an MCP service name. These situations include: 23 | 24 | - When you have many MCP services configured, making the tools list long and difficult to navigate 25 | - When specific MCP services offer multiple callable actions, further increasing the list length 26 | - When you need to direct Claude to use a specific service rather than relying on its automatic selection 27 | - When troubleshooting or comparing results between different MCP services 28 | 29 | This tool bridges that gap, making all available services easily accessible without having to search through configuration files. 30 | 31 | ## Features 32 | 33 | - **Appears at the top of tools list**: Uses special name formatting to always appear first 34 | - **Dynamic updates**: Always shows the latest available services 35 | - **Copy-friendly format**: Numbered list for easy reference 36 | - **Zero external dependencies**: Just needs Node.js 37 | 38 | ## Installation 39 | 40 | ### Option 1: Install via npm (Recommended) 41 | 42 | ```bash 43 | npm install -g @fishes/mcp-easy-copy 44 | ``` 45 | 46 | Then add to your Claude Desktop configuration: 47 | 48 | ```json 49 | { 50 | "mcpServers": { 51 | "mcp-easy-copy": { 52 | "command": "npx", 53 | "args": [ 54 | "-y", 55 | "@fishes/mcp-easy-copy" 56 | ] 57 | } 58 | } 59 | } 60 | ``` 61 | 62 | ### Option 2: Installing via Smithery 63 | 64 | To install Easy Copy for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@fisheepx/mcp-easy-copy): 65 | 66 | ```bash 67 | npx -y @smithery/cli install @fisheepx/mcp-easy-copy --client claude 68 | ``` 69 | 70 | ### Option 3: Manual Installation 71 | 72 | 1. Clone the repository: 73 | ```bash 74 | git clone https://github.com/f-is-h/mcp-easy-copy.git 75 | cd mcp-easy-copy 76 | ``` 77 | 78 | 2. Install dependencies and build: 79 | ```bash 80 | npm install 81 | npm run build 82 | ``` 83 | 84 | 3. Configure Claude Desktop: 85 | 86 | **For macOS:** 87 | Edit `~/Library/Application Support/Claude/claude_desktop_config.json` 88 | 89 | **For Windows:** 90 | Edit `%APPDATA%\Claude\claude_desktop_config.json` 91 | 92 | Add the following configuration: 93 | ```json 94 | { 95 | "mcpServers": { 96 | "mcp-easy-copy": { 97 | "command": "node", 98 | "args": [ 99 | "/ABSOLUTE/PATH/TO/mcp_easy_copy/build/index.js" 100 | ] 101 | } 102 | } 103 | } 104 | ``` 105 | 106 | 4. Restart Claude Desktop 107 | 108 | 109 | 110 | ## Using the Tool 111 | 112 | Once installed, you can use the service in two ways: 113 | 114 | 1. **Via the tools menu:** Click the hammer icon in Claude Desktop and select the service at the top of the list (it will show all available services in its description) 115 | 116 | 2. **Via a prompt:** Ask Claude something like: 117 | ``` 118 | Please list all MCP services that are available to you 119 | ``` 120 | or 121 | ``` 122 | Please use _________mcp-easy-copy_________ to show me all available MCP services 123 | ``` 124 | 125 | ## Development 126 | 127 | MCP Easy Copy is built with TypeScript and uses the Model Context Protocol SDK. 128 | 129 | ```bash 130 | # Install dependencies 131 | npm install 132 | 133 | # Build the project 134 | npm run build 135 | 136 | # Test with the MCP Inspector 137 | npm run inspector 138 | ``` 139 | 140 | ## Troubleshooting 141 | 142 | If the tool doesn't work as expected: 143 | 144 | 1. **Check logs**: Look at the log files 145 | - macOS: `~/Library/Logs/Claude/mcp-server-mcp-easy-copy.log` 146 | - Windows: `%APPDATA%\Claude\logs\mcp-server-mcp-easy-copy.log` 147 | 148 | 2. **Verify configuration**: Make sure your `claude_desktop_config.json` is valid JSON 149 | 150 | 3. **Check Node.js**: Ensure Node.js is properly installed (`node --version`) 151 | 152 | 4. **Restart Claude**: Always restart Claude Desktop after making configuration changes 153 | 154 | 5. **Use the Inspector**: Run `npm run inspector` to debug with the MCP Inspector 155 | 156 | ## Other Badges 157 | 158 | ### Glama 159 | <a href="https://glama.ai/mcp/servers/@fisheepx/mcp-easy-copy"> 160 | <img width="380" height="200" src="https://glama.ai/mcp/servers/@f-is-h/mcp-easy-copy/badge" alt="Easy Copy MCP server" /> 161 | </a> 162 | 163 | ### MseeP.ai 164 | [](https://mseep.ai/app/f-is-h-mcp-easy-copy) 165 | 166 | ## License 167 | 168 | [MIT License](LICENSE) 169 | 170 | ## Future Vision 171 | 172 | While we have no specific knowledge of Anthropic's roadmap, we imagine that future versions of Claude's client could potentially implement features like autocomplete when using the '@' symbol. Such a feature might display a dropdown list of available MCP services, making it much easier for users to explicitly instruct Claude to utilize specific services. 173 | 174 | Even if such improvements eventually make this project obsolete, we'd be delighted to see Claude's interface evolve in ways that improve user experience. After all, the goal of this tool is to make MCP services more accessible, and having that functionality built directly into Claude would be the ultimate success. 175 | ``` -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- ```bash 1 | #!/bin/bash 2 | 3 | # Change to the directory where the script is located 4 | cd "$(dirname "$0")" 5 | 6 | # Build the project 7 | npm run build 8 | 9 | # Show success message 10 | echo "Build completed successfully!" 11 | echo "To use this MCP service, configure it in Claude Desktop." 12 | 13 | ``` -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "NodeNext", 5 | "moduleResolution": "NodeNext", 6 | "esModuleInterop": true, 7 | "outDir": "./build", 8 | "strict": true, 9 | "declaration": true, 10 | "skipLibCheck": true, 11 | "forceConsistentCasingInFileNames": true 12 | }, 13 | "include": ["src/**/*"], 14 | "exclude": ["node_modules", "**/*.test.ts"] 15 | } ``` -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- ```dockerfile 1 | # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile 2 | FROM node:lts-alpine 3 | 4 | WORKDIR /app 5 | 6 | # Copy package files and install dependencies 7 | COPY package*.json ./ 8 | RUN npm install --ignore-scripts 9 | 10 | # Copy the rest of the source code and build 11 | COPY . . 12 | RUN npm run build 13 | 14 | # Expose any required ports (if applicable) 15 | # EXPOSE 3000 16 | 17 | CMD ["npm", "start"] 18 | ``` -------------------------------------------------------------------------------- /smithery.yaml: -------------------------------------------------------------------------------- ```yaml 1 | # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml 2 | 3 | startCommand: 4 | type: stdio 5 | configSchema: 6 | # JSON Schema defining the configuration options for the MCP. 7 | type: object 8 | properties: {} 9 | commandFunction: 10 | # A JS function that produces the CLI command based on the given config to start the MCP on stdio. 11 | |- 12 | (config) => ({ 13 | command: 'node', 14 | args: ['build/index.js'] 15 | }) 16 | exampleConfig: {} 17 | ``` -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- ```yaml 1 | name: Node.js Package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: actions/setup-node@v2 13 | with: 14 | node-version: 16 15 | - run: npm ci 16 | - run: npm test 17 | - run: npm run build 18 | 19 | publish-npm: 20 | needs: build 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v2 24 | - uses: actions/setup-node@v2 25 | with: 26 | node-version: 16 27 | registry-url: https://registry.npmjs.org/ 28 | - run: npm ci 29 | - run: npm run build 30 | - run: npm publish --access public 31 | env: 32 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 33 | ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "@fishes/mcp-easy-copy", 3 | "version": "1.0.1", 4 | "description": "A MCP server that lists all available MCP services for easy copying and usage", 5 | "main": "build/index.js", 6 | "type": "module", 7 | "bin": { 8 | "mcp-easy-copy": "build/index.js" 9 | }, 10 | "scripts": { 11 | "build": "tsc", 12 | "start": "node build/index.js", 13 | "inspector": "npx @modelcontextprotocol/inspector node build/index.js", 14 | "prepublishOnly": "npm run build" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/fisheepx/mcp-easy-copy.git" 19 | }, 20 | "keywords": [ 21 | "mcp", 22 | "claude", 23 | "service-list", 24 | "tools", 25 | "utility" 26 | ], 27 | "author": "fisheep", 28 | "license": "MIT", 29 | "dependencies": { 30 | "@modelcontextprotocol/sdk": "^1.7.0" 31 | }, 32 | "devDependencies": { 33 | "@types/node": "^20.8.0", 34 | "typescript": "^5.2.2" 35 | }, 36 | "engines": { 37 | "node": ">=14.0.0" 38 | } 39 | } ``` -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * MCP Easy Copy Server 5 | * 6 | * This MCP server lists all configured MCP services in the Claude Desktop application. 7 | * It makes it easy for users to copy service names for use in their prompts. 8 | */ 9 | 10 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 11 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; 12 | import fs from "fs/promises"; 13 | import path from "path"; 14 | import os from "os"; 15 | 16 | // Create an MCP server that will appear at the top of the tools list 17 | const server = new McpServer({ 18 | name: "mcp-easy-copy", // Use hyphens consistently for npm naming convention 19 | version: "1.0.0" 20 | }); 21 | 22 | // Configure logging to stderr (doesn't interfere with the MCP protocol) 23 | const logDebug = (message: string) => { 24 | // Uncomment for debugging 25 | // console.error(`[DEBUG] ${message}`); 26 | }; 27 | 28 | /** 29 | * Configuration file paths for different operating systems 30 | * - macOS: ~/Library/Application Support/Claude/claude_desktop_config.json 31 | * - Linux: ~/.config/Claude/claude_desktop_config.json 32 | * - Windows: %APPDATA%/Claude/claude_desktop_config.json 33 | */ 34 | const possibleConfigPaths = [ 35 | path.join(os.homedir(), "Library/Application Support/Claude/claude_desktop_config.json"), 36 | path.join(os.homedir(), ".config/Claude/claude_desktop_config.json"), 37 | path.join(os.homedir(), "AppData/Roaming/Claude/claude_desktop_config.json") 38 | ]; 39 | 40 | /** 41 | * Finds the Claude Desktop configuration file 42 | * @returns The path to the config file if found, null otherwise 43 | */ 44 | async function findConfigFile(): Promise<string | null> { 45 | for (const configPath of possibleConfigPaths) { 46 | try { 47 | await fs.access(configPath); 48 | logDebug(`Found config file at: ${configPath}`); 49 | return configPath; 50 | } catch (error) { 51 | logDebug(`Config file not found at: ${configPath}`); 52 | } 53 | } 54 | return null; 55 | } 56 | 57 | /** 58 | * Retrieves all MCP service names from the Claude Desktop configuration file 59 | * @returns Array of MCP service names 60 | */ 61 | async function getMcpServices(): Promise<string[]> { 62 | try { 63 | // Find the config file 64 | const configPath = await findConfigFile(); 65 | if (!configPath) { 66 | return []; 67 | } 68 | 69 | // Read and parse the config file 70 | const configContent = await fs.readFile(configPath, 'utf-8'); 71 | const config = JSON.parse(configContent); 72 | 73 | // Extract and return MCP service names 74 | return config.mcpServers ? Object.keys(config.mcpServers) : []; 75 | } catch (error) { 76 | logDebug(`Error getting MCP services: ${error}`); 77 | return []; 78 | } 79 | } 80 | 81 | /** 82 | * Resource that returns the list of configured MCP services 83 | * This resource is exposed through URI: mcp-services://list 84 | */ 85 | server.resource( 86 | "mcp-services-list", 87 | "mcp-services://list", 88 | async (uri) => { 89 | try { 90 | // Find the config file 91 | const configPath = await findConfigFile(); 92 | if (!configPath) { 93 | return { 94 | contents: [{ 95 | uri: uri.href, 96 | text: "Error: Claude Desktop configuration file not found." 97 | }] 98 | }; 99 | } 100 | 101 | // Read and parse the config file 102 | const configContent = await fs.readFile(configPath, 'utf-8'); 103 | const config = JSON.parse(configContent); 104 | 105 | // Extract MCP service names 106 | const services = config.mcpServers ? Object.keys(config.mcpServers) : []; 107 | 108 | // Format the output in a user-friendly way 109 | const formattedList = services.length > 0 110 | ? "📋 AVAILABLE MCP SERVICES:\n" + services.map((name) => `- ${name}`).join("\n") + "\n\nCopy a service name to use in prompts like:\n" + 111 | "• Can you use [service name] to...\n" + 112 | "• Please call [service name] to..." 113 | : "No MCP services configured."; 114 | 115 | return { 116 | contents: [{ 117 | uri: uri.href, 118 | text: formattedList 119 | }] 120 | }; 121 | } catch (error) { 122 | console.error("Error reading MCP config:", error); 123 | return { 124 | contents: [{ 125 | uri: uri.href, 126 | text: `Error reading MCP configuration: ${error instanceof Error ? error.message : String(error)}` 127 | }] 128 | }; 129 | } 130 | } 131 | ); 132 | 133 | /** 134 | * Initializes and starts the MCP server 135 | */ 136 | async function main() { 137 | try { 138 | // Get initial MCP services to include in the tool description 139 | let toolDescription = "List all MCP services available in this Claude instance"; 140 | const services = await getMcpServices(); 141 | 142 | if (services.length > 0) { 143 | // Format service names with separators for better visibility in the UI 144 | // (newlines don't work well in Claude's tool descriptions) 145 | const servicesList = services.map(name => `${name}`).join(" │ "); 146 | toolDescription = `│ ${servicesList} │`; 147 | } 148 | 149 | // Register the tool with underscores to appear at the top of tool listings 150 | server.tool( 151 | "_________available_mcp_services_for_easy_copy_________", 152 | toolDescription, 153 | {}, // No parameters needed 154 | async () => { 155 | // Always fetch the latest services when the tool is called 156 | const currentServices = await getMcpServices(); 157 | 158 | if (currentServices.length === 0) { 159 | return { 160 | content: [{ 161 | type: "text", 162 | text: "No MCP services configured." 163 | }] 164 | }; 165 | } 166 | 167 | // Format the output with numbered list and usage instructions 168 | const formattedList = "📋 AVAILABLE MCP SERVICES:\n" + 169 | currentServices.map((name, index) => `${index + 1}. ${name}`).join("\n") + 170 | "\n\nCopy a service name to use in prompts like:\n" + 171 | "• Can you use [service name] to...\n" + 172 | "• Please call [service name] to..."; 173 | 174 | return { 175 | content: [{ 176 | type: "text", 177 | text: formattedList 178 | }] 179 | }; 180 | } 181 | ); 182 | 183 | // Connect the server using stdio transport (standard for MCP) 184 | const transport = new StdioServerTransport(); 185 | await server.connect(transport); 186 | console.error("MCP Easy Copy server running..."); 187 | } catch (error) { 188 | console.error("Failed to start server:", error); 189 | process.exit(1); 190 | } 191 | } 192 | 193 | // Start the server 194 | main(); ```