DIY Tools MCP Server

DIY Tools MCP Server

Enables dynamic creation and execution of custom tools/functions in multiple programming languages at runtime, exposing them to MCP clients like Claude.

Category
访问服务器

README

DIY Tools MCP Server

npm version License: MIT Node.js Version TypeScript CI Verified on MseeP

A Model Context Protocol (MCP) server that allows you to create custom tools/functions at runtime in any programming language and expose them to Claude or other MCP clients.

Overview

The DIY Tools MCP server enables you to dynamically add custom tools without needing to write a full MCP server. Simply provide the function code, parameters schema, and the server handles the rest - validation, execution, persistence, and MCP protocol integration.

This server bridges the gap between simple function definitions and the MCP protocol, making it easy to extend Claude's capabilities with custom tools written in Python, JavaScript, Bash, Ruby, or TypeScript.

Features

  • Dynamic Tool Registration: Add new tools at runtime without restarting the server
  • Multi-Language Support: Write functions in Python, JavaScript, Bash, and more
  • File-Based Functions: Define functions in separate files for better maintainability
  • Automatic Validation: Functions are validated for syntax before registration
  • Security Validation: Comprehensive security checks for file-based functions
  • Persistence: Registered tools are saved and automatically loaded on server restart
  • Type Safety: Full JSON Schema validation for function parameters
  • Error Handling: Comprehensive error messages and timeout protection
  • Source Code Viewer: Built-in tool to inspect function source code

Installation

# Clone the repository
git clone https://github.com/yourusername/diy-tools-mcp.git
cd diy-tools-mcp

# Install dependencies
npm install

# Build the project
npm run build

Usage

Starting the Server

# Start the server
npm start

# Or for development with auto-reload
npm run dev

Adding Tools

The server provides four built-in tools:

  1. add_tool - Register a new custom function
  2. remove_tool - Remove a registered function
  3. list_tools - List all available custom tools
  4. view_source - View the source code of a registered tool

Example: Adding a Python Tool

{
  "name": "calculate_factorial",
  "description": "Calculate the factorial of a number",
  "language": "python",
  "code": "def main(n):\n    if n <= 1:\n        return 1\n    return n * main(n - 1)",
  "parameters": {
    "type": "object",
    "properties": {
      "n": {
        "type": "integer",
        "description": "The number to calculate factorial for",
        "minimum": 0
      }
    },
    "required": ["n"]
  },
  "returns": "The factorial of the input number"
}

Example: Adding a JavaScript Tool

{
  "name": "format_date",
  "description": "Format a date string",
  "language": "javascript",
  "code": "function main({ date, format }) {\n  const d = new Date(date);\n  if (format === 'short') {\n    return d.toLocaleDateString();\n  }\n  return d.toLocaleString();\n}",
  "parameters": {
    "type": "object",
    "properties": {
      "date": {
        "type": "string",
        "description": "Date string to format"
      },
      "format": {
        "type": "string",
        "enum": ["short", "long"],
        "default": "long"
      }
    },
    "required": ["date"]
  }
}

Example: Adding a Bash Tool

{
  "name": "system_info",
  "description": "Get basic system information",
  "language": "bash",
  "code": "main() {\n  echo '{\"os\": \"'$(uname -s)'\", \"kernel\": \"'$(uname -r)'\", \"arch\": \"'$(uname -m)'\"}'\n}",
  "parameters": {
    "type": "object",
    "properties": {}
  }
}

File-Based Functions

You can now define functions in separate files instead of inline code. This is useful for:

  • Complex functions that are easier to maintain in dedicated files
  • Functions you want to version control separately
  • Reusing existing code without modification

Example: Adding a Python Function from File

  1. Create your function file my_function.py:
from datetime import datetime

def main(name, age):
    """
    Generate a personalized greeting.
    """
    return {
        "greeting": f"Hello {name}!",
        "message": f"You are {age} years old.",
        "timestamp": datetime.now().isoformat()
    }
  1. Register the function:
{
  "name": "personalized_greeting",
  "description": "Generate a personalized greeting with timestamp",
  "language": "python",
  "codePath": "./my_function.py",
  "parameters": {
    "type": "object",
    "properties": {
      "name": {
        "type": "string",
        "description": "Person's name"
      },
      "age": {
        "type": "integer",
        "description": "Person's age"
      }
    },
    "required": ["name", "age"]
  }
}

Example: Adding a JavaScript Function from File

  1. Create your function file data_processor.js:
async function main({ data, format }) {
  // Process data based on format
  if (format === 'csv') {
    return processCSV(data);
  } else if (format === 'json') {
    return processJSON(data);
  }
  throw new Error(`Unsupported format: ${format}`);
}

function processCSV(data) {
  // CSV processing logic
  return { processed: true, format: 'csv', rows: data.split('\n').length };
}

function processJSON(data) {
  // JSON processing logic
  const parsed = JSON.parse(data);
  return { processed: true, format: 'json', keys: Object.keys(parsed) };
}

module.exports = { main };
  1. Register the function:
{
  "name": "data_processor",
  "description": "Process data in various formats",
  "language": "javascript",
  "codePath": "./data_processor.js",
  "parameters": {
    "type": "object",
    "properties": {
      "data": {
        "type": "string",
        "description": "Raw data to process"
      },
      "format": {
        "type": "string",
        "enum": ["csv", "json"],
        "description": "Data format"
      }
    },
    "required": ["data", "format"]
  }
}

Configurable Entry Points (New in v1.2.0)

You can now specify any function name as the entry point, not just main. This allows you to:

  • Use existing code without renaming functions
  • Share a single file between multiple tools with different entry points
  • Better organize related functions

Example: Multiple Tools from One File

  1. Create a file with multiple functions math_utils.py:
def calculate_tax(income, tax_rate):
    """Calculate tax amount."""
    return {
        "tax_amount": income * tax_rate,
        "net_income": income * (1 - tax_rate)
    }

def compound_interest(principal, rate, time):
    """Calculate compound interest."""
    amount = principal * (1 + rate) ** time
    return {
        "principal": principal,
        "interest": amount - principal,
        "total": amount
    }
  1. Register multiple tools using different entry points:
// Tax calculator tool
{
  "name": "tax_calculator",
  "description": "Calculate tax and net income",
  "language": "python",
  "codePath": "./math_utils.py",
  "entryPoint": "calculate_tax",  // Specify which function to use
  "parameters": {
    "type": "object",
    "properties": {
      "income": { "type": "number" },
      "tax_rate": { "type": "number" }
    },
    "required": ["income", "tax_rate"]
  }
}

// Interest calculator tool
{
  "name": "interest_calculator",
  "description": "Calculate compound interest",
  "language": "python",
  "codePath": "./math_utils.py",
  "entryPoint": "compound_interest",  // Different function from same file
  "parameters": {
    "type": "object",
    "properties": {
      "principal": { "type": "number" },
      "rate": { "type": "number" },
      "time": { "type": "number" }
    },
    "required": ["principal", "rate", "time"]
  }
}

If no entryPoint is specified, the system defaults to looking for a function named main for backward compatibility.

Viewing Function Source Code

Use the view_source tool to inspect any registered function:

{
  "name": "view_source",
  "arguments": {
    "name": "data_processor",
    "verbose": true
  }
}

The verbose option includes full metadata about the tool in addition to the source code.

Supported Languages

  • Python (python) - Requires Python 3.x
  • JavaScript (javascript or node) - Requires Node.js
  • Bash (bash) - Requires Bash shell
  • TypeScript (typescript) - Transpiled and run as JavaScript
  • Ruby (ruby) - Requires Ruby

Function Requirements

Python Functions

  • Must define a main function that accepts keyword arguments
  • Should return JSON-serializable data
  • Example:
    def main(x, y):
        return x + y
    

JavaScript Functions

  • Must define a main function (regular or async)
  • Receives parameters as a single object
  • Example:
    function main({ x, y }) {
      return x + y;
    }
    

Bash Functions

  • Must define a main function
  • Receives JSON arguments as first parameter
  • Should output JSON to stdout
  • Example:
    main() {
      # Parse JSON args if needed
      echo '{"result": "success"}'
    }
    

Ruby Functions

  • Must define a main method that accepts keyword arguments
  • Should return JSON-serializable data
  • Example:
    def main(name:, age:)
      { greeting: "Hello #{name}, you are #{age} years old!" }
    end
    

Configuration

Timeout Settings

You can specify a timeout for each function (in milliseconds):

{
  "timeout": 5000 // 5 seconds
}

Maximum timeout is 300000ms (5 minutes).

Dependencies

For Python functions, you can specify required packages:

{
  "dependencies": ["numpy", "pandas"]
}

Note: Automatic dependency installation is not yet implemented.

Error Handling

The server provides detailed error messages for:

  • Syntax errors in function code
  • Invalid parameter schemas
  • Runtime execution errors
  • Timeout violations
  • Missing dependencies

Storage

Functions are stored in the functions/ directory as JSON files. Each file contains:

  • Function specification
  • Unique ID
  • Creation and update timestamps

Development

# Run in development mode with auto-reload
npm run dev

# Run tests
npm test

# Build for production
npm run build

# Clean build artifacts
npm run clean

Security Considerations

File-Based Functions Security

When using file-based functions, the server implements multiple security layers:

  1. Path Traversal Protection: Prevents access to files outside the intended directories
  2. Symbolic Link Detection: Blocks symbolic links to prevent unauthorized file access
  3. System Directory Protection: Restricts access to critical system directories including:
    • /etc, /usr/bin, /System (macOS), C:\Windows
    • User-specific sensitive directories (~/.ssh, ~/.aws, etc.)
  4. File Size Limits: Files are limited to 10MB to prevent resource exhaustion
  5. Dangerous Pattern Detection: Scans for potentially malicious code patterns:
    • eval() and exec() calls
    • Dynamic imports and requires
    • Dangerous shell commands (rm -rf /, etc.)
    • Subprocess calls with shell=True
  6. File Extension Validation: Only allows appropriate extensions for each language
  7. Main Function Requirement: Enforces that all functions have a proper main entry point

General Security Notes

  • Functions run with the same permissions as the server
  • No built-in sandboxing (use with trusted code only)
  • Network and file system access depends on the language runtime
  • Consider running in a containerized environment for production use
  • When copying function files, the server creates isolated copies to prevent external modifications

Best Practices

When to Use File-Based vs Inline Functions

Use file-based functions for:

  • Complex logic that benefits from IDE features (syntax highlighting, linting, debugging)
  • Functions you want to unit test separately
  • Shared utilities across multiple tools
  • Functions that require multiple helper functions
  • Code that you want to version control independently

Use inline functions for:

  • Simple, single-purpose operations (< 20 lines)
  • Quick prototypes and experiments
  • Functions that are tightly coupled to their tool definition
  • One-off utilities that don't need separate maintenance

Directory Organization

Organize your functions for maintainability:

my-mcp-tools/
├── functions/          # Auto-managed metadata (don't edit)
├── function-code/      # Auto-managed copies (don't edit)
└── my-functions/       # Your source files
    ├── data/
    │   ├── processor.js
    │   └── validator.py
    ├── ml/
    │   ├── predictor.py
    │   └── trainer.py
    └── tests/
        ├── test_processor.js
        └── test_validator.py

Function Design Guidelines

  1. Keep functions focused: Each function should do one thing well
  2. Use clear parameter names: Make your API intuitive
  3. Provide comprehensive descriptions: Help users understand what your tool does
  4. Handle errors gracefully: Return meaningful error messages
  5. Validate inputs early: Check parameters before processing
  6. Document edge cases: Use the returns field to explain output format

Migration Guide

Migrating from Inline to File-Based Functions

Existing inline functions continue to work without changes. To migrate an inline function to file-based:

  1. Extract the code to a new file:

    # Before (inline)
    "code": "def main(x, y):\n    return x + y"
    
    # After (calculator.py)
    def main(x, y):
        return x + y
    
  2. Update the tool definition:

    // Before
    {
      "name": "calculator",
      "code": "def main(x, y):\n    return x + y",
      ...
    }
    
    // After
    {
      "name": "calculator",
      "codePath": "./my-functions/calculator.py",
      ...
    }
    
  3. Re-register the tool:

    • Use remove_tool to remove the old inline version
    • Use add_tool with the new file-based definition

The server automatically handles the transition, copying the file to its managed directory and preserving all functionality.

Gradual Migration Strategy

  1. Start with new functions: Write all new functions as files
  2. Migrate complex functions first: Move functions that would benefit most from IDE support
  3. Keep simple functions inline: Don't migrate unless there's a clear benefit
  4. Test after migration: Ensure functions work identically after migration

Roadmap

Completed Features ✅

  • [x] Allow users to write functions in stand-alone files, as opposed to inline in the tool definition
  • [x] Add tool to view function source code (view_source)
  • [x] Enforce single main function entry point with comprehensive validation
  • [x] Comprehensive security validation for file-based functions

Future Enhancements

  • [ ] Configurable entry points (use any function name instead of requiring main)
  • [ ] Support for multiple entry points per file (share code between tools)
  • [ ] File watching and hot-reload for development workflow
  • [ ] Dependency resolution for local imports
  • [ ] Version tracking and rollback capabilities
  • [ ] Streaming output for long-running functions
  • [ ] Function composition and chaining

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Submit a pull request

License

MIT

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选