{"id":41380415,"url":"https://github.com/hesreallyhim/diy-tools-mcp","last_synced_at":"2026-01-23T10:56:23.627Z","repository":{"id":310448118,"uuid":"1026623088","full_name":"hesreallyhim/diy-tools-mcp","owner":"hesreallyhim","description":"An MCP server that allows users to dynamically add custom tools/functions at runtime","archived":false,"fork":false,"pushed_at":"2026-01-05T18:56:43.000Z","size":717,"stargazers_count":33,"open_issues_count":2,"forks_count":9,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-11T17:46:25.375Z","etag":null,"topics":["agent-frameworks","agentic-ai","agentic-coding","anthropic","anthropic-claude","claude","claude-code","mcp","mcp-framework","mcp-server","mcp-tools"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hesreallyhim.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["hesreallyhim"],"patreon":["hesreallyhim"],"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":null,"thanks_dev":null,"custom":null}},"created_at":"2025-07-26T09:06:13.000Z","updated_at":"2025-12-19T05:19:45.000Z","dependencies_parsed_at":"2025-08-18T07:27:14.272Z","dependency_job_id":"f8e3827c-fb37-42c1-8c60-435f15695d15","html_url":"https://github.com/hesreallyhim/diy-tools-mcp","commit_stats":null,"previous_names":["hesreallyhim/diy-tools-mcp"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/hesreallyhim/diy-tools-mcp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hesreallyhim%2Fdiy-tools-mcp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hesreallyhim%2Fdiy-tools-mcp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hesreallyhim%2Fdiy-tools-mcp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hesreallyhim%2Fdiy-tools-mcp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hesreallyhim","download_url":"https://codeload.github.com/hesreallyhim/diy-tools-mcp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hesreallyhim%2Fdiy-tools-mcp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28689098,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-23T05:48:07.525Z","status":"ssl_error","status_checked_at":"2026-01-23T05:48:07.129Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent-frameworks","agentic-ai","agentic-coding","anthropic","anthropic-claude","claude","claude-code","mcp","mcp-framework","mcp-server","mcp-tools"],"created_at":"2026-01-23T10:56:22.830Z","updated_at":"2026-01-23T10:56:23.619Z","avatar_url":"https://github.com/hesreallyhim.png","language":"TypeScript","funding_links":["https://github.com/sponsors/hesreallyhim","https://patreon.com/[\"hesreallyhim\"]"],"categories":[],"sub_categories":[],"readme":"# DIY Tools MCP Server\n\n[![npm version](https://img.shields.io/npm/v/diy-tools-mcp.svg)](https://www.npmjs.com/package/diy-tools-mcp)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)\n[![Node.js Version](https://img.shields.io/node/v/diy-tools-mcp.svg)](https://nodejs.org/)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)\n[![CI](https://github.com/hesreallyhim/diy-tools-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/hesreallyhim/diy-tools-mcp/actions/workflows/ci.yml)\n[![Verified on MseeP](https://mseep.ai/badge.svg)](https://mseep.ai/app/a3f05c40-4cc1-432d-b081-f5b418d00fd1)\n\nA 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.\n\n## Overview\n\nThe 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.\n\nThis 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.\n\n## Features\n\n- **Dynamic Tool Registration**: Add new tools at runtime without restarting the server\n- **Multi-Language Support**: Write functions in Python, JavaScript, Bash, and more\n- **File-Based Functions**: Define functions in separate files for better maintainability\n- **Automatic Validation**: Functions are validated for syntax before registration\n- **Security Validation**: Comprehensive security checks for file-based functions\n- **Persistence**: Registered tools are saved and automatically loaded on server restart\n- **Type Safety**: Full JSON Schema validation for function parameters\n- **Error Handling**: Comprehensive error messages and timeout protection\n- **Source Code Viewer**: Built-in tool to inspect function source code\n\n## Installation\n\n```bash\n# Clone the repository\ngit clone https://github.com/yourusername/diy-tools-mcp.git\ncd diy-tools-mcp\n\n# Install dependencies\nnpm install\n\n# Build the project\nnpm run build\n```\n\n## Usage\n\n### Starting the Server\n\n```bash\n# Start the server\nnpm start\n\n# Or for development with auto-reload\nnpm run dev\n```\n\n### Adding Tools\n\nThe server provides four built-in tools:\n\n1. **`add_tool`** - Register a new custom function\n2. **`remove_tool`** - Remove a registered function\n3. **`list_tools`** - List all available custom tools\n4. **`view_source`** - View the source code of a registered tool\n\n### Example: Adding a Python Tool\n\n```json\n{\n  \"name\": \"calculate_factorial\",\n  \"description\": \"Calculate the factorial of a number\",\n  \"language\": \"python\",\n  \"code\": \"def main(n):\\n    if n \u003c= 1:\\n        return 1\\n    return n * main(n - 1)\",\n  \"parameters\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"n\": {\n        \"type\": \"integer\",\n        \"description\": \"The number to calculate factorial for\",\n        \"minimum\": 0\n      }\n    },\n    \"required\": [\"n\"]\n  },\n  \"returns\": \"The factorial of the input number\"\n}\n```\n\n### Example: Adding a JavaScript Tool\n\n```json\n{\n  \"name\": \"format_date\",\n  \"description\": \"Format a date string\",\n  \"language\": \"javascript\",\n  \"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}\",\n  \"parameters\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"date\": {\n        \"type\": \"string\",\n        \"description\": \"Date string to format\"\n      },\n      \"format\": {\n        \"type\": \"string\",\n        \"enum\": [\"short\", \"long\"],\n        \"default\": \"long\"\n      }\n    },\n    \"required\": [\"date\"]\n  }\n}\n```\n\n### Example: Adding a Bash Tool\n\n```json\n{\n  \"name\": \"system_info\",\n  \"description\": \"Get basic system information\",\n  \"language\": \"bash\",\n  \"code\": \"main() {\\n  echo '{\\\"os\\\": \\\"'$(uname -s)'\\\", \\\"kernel\\\": \\\"'$(uname -r)'\\\", \\\"arch\\\": \\\"'$(uname -m)'\\\"}'\\n}\",\n  \"parameters\": {\n    \"type\": \"object\",\n    \"properties\": {}\n  }\n}\n```\n\n### File-Based Functions\n\nYou can now define functions in separate files instead of inline code. This is useful for:\n- Complex functions that are easier to maintain in dedicated files\n- Functions you want to version control separately\n- Reusing existing code without modification\n\n#### Example: Adding a Python Function from File\n\n1. Create your function file `my_function.py`:\n```python\nfrom datetime import datetime\n\ndef main(name, age):\n    \"\"\"\n    Generate a personalized greeting.\n    \"\"\"\n    return {\n        \"greeting\": f\"Hello {name}!\",\n        \"message\": f\"You are {age} years old.\",\n        \"timestamp\": datetime.now().isoformat()\n    }\n```\n\n2. Register the function:\n```json\n{\n  \"name\": \"personalized_greeting\",\n  \"description\": \"Generate a personalized greeting with timestamp\",\n  \"language\": \"python\",\n  \"codePath\": \"./my_function.py\",\n  \"parameters\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"name\": {\n        \"type\": \"string\",\n        \"description\": \"Person's name\"\n      },\n      \"age\": {\n        \"type\": \"integer\",\n        \"description\": \"Person's age\"\n      }\n    },\n    \"required\": [\"name\", \"age\"]\n  }\n}\n```\n\n#### Example: Adding a JavaScript Function from File\n\n1. Create your function file `data_processor.js`:\n```javascript\nasync function main({ data, format }) {\n  // Process data based on format\n  if (format === 'csv') {\n    return processCSV(data);\n  } else if (format === 'json') {\n    return processJSON(data);\n  }\n  throw new Error(`Unsupported format: ${format}`);\n}\n\nfunction processCSV(data) {\n  // CSV processing logic\n  return { processed: true, format: 'csv', rows: data.split('\\n').length };\n}\n\nfunction processJSON(data) {\n  // JSON processing logic\n  const parsed = JSON.parse(data);\n  return { processed: true, format: 'json', keys: Object.keys(parsed) };\n}\n\nmodule.exports = { main };\n```\n\n2. Register the function:\n```json\n{\n  \"name\": \"data_processor\",\n  \"description\": \"Process data in various formats\",\n  \"language\": \"javascript\",\n  \"codePath\": \"./data_processor.js\",\n  \"parameters\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"data\": {\n        \"type\": \"string\",\n        \"description\": \"Raw data to process\"\n      },\n      \"format\": {\n        \"type\": \"string\",\n        \"enum\": [\"csv\", \"json\"],\n        \"description\": \"Data format\"\n      }\n    },\n    \"required\": [\"data\", \"format\"]\n  }\n}\n```\n\n### Configurable Entry Points (New in v1.2.0)\n\nYou can now specify any function name as the entry point, not just `main`. This allows you to:\n- Use existing code without renaming functions\n- Share a single file between multiple tools with different entry points\n- Better organize related functions\n\n#### Example: Multiple Tools from One File\n\n1. Create a file with multiple functions `math_utils.py`:\n```python\ndef calculate_tax(income, tax_rate):\n    \"\"\"Calculate tax amount.\"\"\"\n    return {\n        \"tax_amount\": income * tax_rate,\n        \"net_income\": income * (1 - tax_rate)\n    }\n\ndef compound_interest(principal, rate, time):\n    \"\"\"Calculate compound interest.\"\"\"\n    amount = principal * (1 + rate) ** time\n    return {\n        \"principal\": principal,\n        \"interest\": amount - principal,\n        \"total\": amount\n    }\n```\n\n2. Register multiple tools using different entry points:\n```json\n// Tax calculator tool\n{\n  \"name\": \"tax_calculator\",\n  \"description\": \"Calculate tax and net income\",\n  \"language\": \"python\",\n  \"codePath\": \"./math_utils.py\",\n  \"entryPoint\": \"calculate_tax\",  // Specify which function to use\n  \"parameters\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"income\": { \"type\": \"number\" },\n      \"tax_rate\": { \"type\": \"number\" }\n    },\n    \"required\": [\"income\", \"tax_rate\"]\n  }\n}\n\n// Interest calculator tool\n{\n  \"name\": \"interest_calculator\",\n  \"description\": \"Calculate compound interest\",\n  \"language\": \"python\",\n  \"codePath\": \"./math_utils.py\",\n  \"entryPoint\": \"compound_interest\",  // Different function from same file\n  \"parameters\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"principal\": { \"type\": \"number\" },\n      \"rate\": { \"type\": \"number\" },\n      \"time\": { \"type\": \"number\" }\n    },\n    \"required\": [\"principal\", \"rate\", \"time\"]\n  }\n}\n```\n\nIf no `entryPoint` is specified, the system defaults to looking for a function named `main` for backward compatibility.\n\n### Viewing Function Source Code\n\nUse the `view_source` tool to inspect any registered function:\n\n```json\n{\n  \"name\": \"view_source\",\n  \"arguments\": {\n    \"name\": \"data_processor\",\n    \"verbose\": true\n  }\n}\n```\n\nThe `verbose` option includes full metadata about the tool in addition to the source code.\n\n## Supported Languages\n\n- **Python** (`python`) - Requires Python 3.x\n- **JavaScript** (`javascript` or `node`) - Requires Node.js\n- **Bash** (`bash`) - Requires Bash shell\n- **TypeScript** (`typescript`) - Transpiled and run as JavaScript\n- **Ruby** (`ruby`) - Requires Ruby\n\n## Function Requirements\n\n### Python Functions\n\n- Must define a `main` function that accepts keyword arguments\n- Should return JSON-serializable data\n- Example:\n  ```python\n  def main(x, y):\n      return x + y\n  ```\n\n### JavaScript Functions\n\n- Must define a `main` function (regular or async)\n- Receives parameters as a single object\n- Example:\n  ```javascript\n  function main({ x, y }) {\n    return x + y;\n  }\n  ```\n\n### Bash Functions\n\n- Must define a `main` function\n- Receives JSON arguments as first parameter\n- Should output JSON to stdout\n- Example:\n  ```bash\n  main() {\n    # Parse JSON args if needed\n    echo '{\"result\": \"success\"}'\n  }\n  ```\n\n### Ruby Functions\n\n- Must define a `main` method that accepts keyword arguments\n- Should return JSON-serializable data\n- Example:\n  ```ruby\n  def main(name:, age:)\n    { greeting: \"Hello #{name}, you are #{age} years old!\" }\n  end\n  ```\n\n## Configuration\n\n### Timeout Settings\n\nYou can specify a timeout for each function (in milliseconds):\n\n```json\n{\n  \"timeout\": 5000 // 5 seconds\n}\n```\n\nMaximum timeout is 300000ms (5 minutes).\n\n### Dependencies\n\nFor Python functions, you can specify required packages:\n\n```json\n{\n  \"dependencies\": [\"numpy\", \"pandas\"]\n}\n```\n\nNote: Automatic dependency installation is not yet implemented.\n\n## Error Handling\n\nThe server provides detailed error messages for:\n\n- Syntax errors in function code\n- Invalid parameter schemas\n- Runtime execution errors\n- Timeout violations\n- Missing dependencies\n\n## Storage\n\nFunctions are stored in the `functions/` directory as JSON files. Each file contains:\n\n- Function specification\n- Unique ID\n- Creation and update timestamps\n\n## Development\n\n```bash\n# Run in development mode with auto-reload\nnpm run dev\n\n# Run tests\nnpm test\n\n# Build for production\nnpm run build\n\n# Clean build artifacts\nnpm run clean\n```\n\n## Security Considerations\n\n### File-Based Functions Security\n\nWhen using file-based functions, the server implements multiple security layers:\n\n1. **Path Traversal Protection**: Prevents access to files outside the intended directories\n2. **Symbolic Link Detection**: Blocks symbolic links to prevent unauthorized file access\n3. **System Directory Protection**: Restricts access to critical system directories including:\n   - `/etc`, `/usr/bin`, `/System` (macOS), `C:\\Windows`\n   - User-specific sensitive directories (`~/.ssh`, `~/.aws`, etc.)\n4. **File Size Limits**: Files are limited to 10MB to prevent resource exhaustion\n5. **Dangerous Pattern Detection**: Scans for potentially malicious code patterns:\n   - `eval()` and `exec()` calls\n   - Dynamic imports and requires\n   - Dangerous shell commands (`rm -rf /`, etc.)\n   - Subprocess calls with shell=True\n6. **File Extension Validation**: Only allows appropriate extensions for each language\n7. **Main Function Requirement**: Enforces that all functions have a proper `main` entry point\n\n### General Security Notes\n\n- Functions run with the same permissions as the server\n- No built-in sandboxing (use with trusted code only)\n- Network and file system access depends on the language runtime\n- Consider running in a containerized environment for production use\n- When copying function files, the server creates isolated copies to prevent external modifications\n\n## Best Practices\n\n### When to Use File-Based vs Inline Functions\n\n**Use file-based functions for:**\n- Complex logic that benefits from IDE features (syntax highlighting, linting, debugging)\n- Functions you want to unit test separately\n- Shared utilities across multiple tools\n- Functions that require multiple helper functions\n- Code that you want to version control independently\n\n**Use inline functions for:**\n- Simple, single-purpose operations (\u003c 20 lines)\n- Quick prototypes and experiments\n- Functions that are tightly coupled to their tool definition\n- One-off utilities that don't need separate maintenance\n\n### Directory Organization\n\nOrganize your functions for maintainability:\n\n```\nmy-mcp-tools/\n├── functions/          # Auto-managed metadata (don't edit)\n├── function-code/      # Auto-managed copies (don't edit)\n└── my-functions/       # Your source files\n    ├── data/\n    │   ├── processor.js\n    │   └── validator.py\n    ├── ml/\n    │   ├── predictor.py\n    │   └── trainer.py\n    └── tests/\n        ├── test_processor.js\n        └── test_validator.py\n```\n\n### Function Design Guidelines\n\n1. **Keep functions focused**: Each function should do one thing well\n2. **Use clear parameter names**: Make your API intuitive\n3. **Provide comprehensive descriptions**: Help users understand what your tool does\n4. **Handle errors gracefully**: Return meaningful error messages\n5. **Validate inputs early**: Check parameters before processing\n6. **Document edge cases**: Use the `returns` field to explain output format\n\n## Migration Guide\n\n### Migrating from Inline to File-Based Functions\n\nExisting inline functions continue to work without changes. To migrate an inline function to file-based:\n\n1. **Extract the code to a new file:**\n   ```python\n   # Before (inline)\n   \"code\": \"def main(x, y):\\n    return x + y\"\n   \n   # After (calculator.py)\n   def main(x, y):\n       return x + y\n   ```\n\n2. **Update the tool definition:**\n   ```json\n   // Before\n   {\n     \"name\": \"calculator\",\n     \"code\": \"def main(x, y):\\n    return x + y\",\n     ...\n   }\n   \n   // After\n   {\n     \"name\": \"calculator\",\n     \"codePath\": \"./my-functions/calculator.py\",\n     ...\n   }\n   ```\n\n3. **Re-register the tool:**\n   - Use `remove_tool` to remove the old inline version\n   - Use `add_tool` with the new file-based definition\n\nThe server automatically handles the transition, copying the file to its managed directory and preserving all functionality.\n\n### Gradual Migration Strategy\n\n1. **Start with new functions**: Write all new functions as files\n2. **Migrate complex functions first**: Move functions that would benefit most from IDE support\n3. **Keep simple functions inline**: Don't migrate unless there's a clear benefit\n4. **Test after migration**: Ensure functions work identically after migration\n\n## Roadmap\n\n### Completed Features ✅\n- [x] Allow users to write functions in stand-alone files, as opposed to inline in the tool definition\n- [x] Add tool to view function source code (`view_source`)\n- [x] Enforce single `main` function entry point with comprehensive validation\n- [x] Comprehensive security validation for file-based functions\n\n### Future Enhancements\n- [ ] Configurable entry points (use any function name instead of requiring `main`)\n- [ ] Support for multiple entry points per file (share code between tools)\n- [ ] File watching and hot-reload for development workflow\n- [ ] Dependency resolution for local imports\n- [ ] Version tracking and rollback capabilities\n- [ ] Streaming output for long-running functions\n- [ ] Function composition and chaining\n\n## Contributing\n\nContributions are welcome! Please:\n\n1. Fork the repository\n2. Create a feature branch\n3. Add tests for new functionality\n4. Submit a pull request\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhesreallyhim%2Fdiy-tools-mcp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhesreallyhim%2Fdiy-tools-mcp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhesreallyhim%2Fdiy-tools-mcp/lists"}