This document provides developers with detailed information on how to set up, run, and develop the ToolSDK MCP Registry project.
- ToolSDK MCP Registry Developer Guide
Before you begin, ensure your development environment meets the following requirements:
- Docker (recommended) - For quick start deployment
- Node.js >= 18.x (latest LTS version recommended) - Required for local development only
- pnpm >= 8.x (package manager) - Required for local development only
- Bun >= 1.x - Required for running build scripts
- Runtime Environment: Node.js (ESM modules)
- Package Manager: pnpm
- Language: TypeScript
- Web Framework: Hono.js + OpenAPI (Zod)
- Architecture: Domain-Driven Design (DDD) + Service Object Pattern
- Search Service: MeiliSearch (optional)
- Sandbox Providers: LOCAL / Sandock / Daytona / E2B
- Build Tool: TypeScript Compiler (tsc)
- Code Formatting: Biome
- Testing: Vitest
This project has two main purposes:
- MCP Registry - Collects and indexes various MCP servers, providing search functionality
- MCP Server - Deployed as a server to remotely call various MCP servers
- 📦 Package Management - Registry of 6000+ MCP servers with metadata and validation
- 🔍 Search Service - Full-text search powered by MeiliSearch (optional)
- 🛡️ Sandbox Execution - Secure MCP tool execution in isolated environments:
- LOCAL - Direct local execution (default)
- Sandock - Lightweight Docker sandbox for AI agents
- Daytona - Cloud development environments
- E2B - Code interpreter sandbox
- 🌐 RESTful API - Complete API with OpenAPI/Swagger documentation
- ⚡ Performance - Async execution with connection pooling
Additionally, we have deployed a website ToolSDK.ai that can search for and run MCP Servers. We also provide a tool called toolsdk to help integrate these MCP Servers.
Docker Compose allows you to quickly deploy the complete MCP Registry with search functionality and SANDOCK remote execution environment.
Step 1: Clone the Repository
git clone https://github.com/toolsdk-ai/toolsdk-mcp-registry.git
cd toolsdk-mcp-registryStep 2: Get Sandock API Key
Visit Sandock website to register and obtain your API Key.
Step 3: Configure Environment Variables
In the .env file, you only need to modify this line:
SANDOCK_API_KEY=your-sandock-api-key-here # Replace with your actual API KeyStep 4: Start Services
docker compose up -d
# Or use the shortcut:
make up
⚠️ Note: This command will build and install all 6000+ MCP packages and their dependencies, which may take 10-15 minutes on first run. If you only need specific packages for your use case, consider pruning unwanted packages first by following the guide in Section 5: Package Management for Private Deployment to significantly reduce build time and image size.Common Docker commands:
make upordocker compose up -d- Start all servicesmake downordocker compose down- Stop all servicesmake restartordocker compose restart- Restart all services
This will start two services:
mcp-registry- MCP Registry main application (port 3003)meilisearch- Search engine service (port 7700)
Step 5: Initialize Search Index (Optional)
Wait for services to start (about 30-60 seconds), then initialize the search index:
# Initialize search service
curl -X POST http://localhost:3003/api/v1/search/manage/init
# Index MCP data
curl -X POST http://localhost:3003/api/v1/search/manage/indexStep 6: Access Services
- 🌐 Homepage: http://localhost:3003
- 📚 API Documentation: http://localhost:3003/swagger
- 🔍 Search Engine Management: http://localhost:7700
List all MCP Servers:
curl http://localhost:3003/api/v1/packagesSearch MCP Servers:
curl "http://localhost:3003/api/v1/search/packages?q=github&limit=5"Execute MCP Tool (using SANDOCK remote execution):
curl -X POST http://localhost:3003/api/v1/packages/run \
-H "Content-Type: application/json" \
-d '{
"packageName": "mcp-starter",
"toolKey": "hello_tool",
"inputData": {
"name": "World"
},
"envs": {}
}'Issue 1: Port Already in Use
# Check port usage
lsof -i :3003
lsof -i :7700
# Modify port in .env file
MCP_SERVER_PORT=3004Issue 2: SANDOCK_API_KEY Not Configured
Error message:
Error: SANDOCK_API_KEY is required when using SANDOCK provider
Solution:
- Check if
.envfile exists - Confirm
SANDOCK_API_KEYis correctly filled in - Restart services:
docker compose restart
Issue 3: Search Function Unavailable
# Check if MeiliSearch is running
docker compose ps meilisearch
# Reinitialize indexes
curl -X POST http://localhost:3003/api/v1/search/manage/init
curl -X POST http://localhost:3003/api/v1/search/manage/indexIssue 4: Long Build Time
First build may take 10-15 minutes, which is normal. The Dockerfile needs to:
- Install Python 3.13 and pyenv
- Install Node.js dependencies (large number of packages)
- Install Python dependencies
- Build TypeScript code
Subsequent builds will be much faster using Docker cache.
If you're deploying this project privately, you probably don't need all 6000+ MCP packages. Here's how to keep only the packages you need to significantly reduce build time and dependencies.
All MCP packages are stored in the packages/ directory, organized by category:
packages/
├── developer-tools/ # Development related tools
├── databases/ # Database integrations
├── cloud-platforms/ # Cloud service integrations
├── version-control/ # Git, GitHub, etc.
├── communication/ # Slack, Discord, etc.
└── ... # 30+ other categories
Each package is defined by a JSON configuration file.
Option 1: Remove Specific Packages
To remove individual packages within a category, simply delete their JSON configuration files:
- Navigate to the category folder (e.g.,
packages/version-control/) - Delete the
.jsonfiles for packages you don't need - Run the rebuild process using
make build(see Section 5.3)
Example: Keep only GitHub-related packages in version-control by removing other .json files like gitlab.json, bitbucket.json, etc.
Option 2: Remove Entire Categories
To remove entire categories (e.g., gaming, sports), edit the config/categories.mjs file:
- Open
config/categories.mjs - Remove the category objects you don't need. For example, to remove gaming and sports:
// Remove these entries from the array:
{
key: "gaming",
name: "Gaming",
description: "Connect with gaming data, engines, and related services.",
},
{
key: "sports",
name: "Sports",
description: "Access sports data, results, and stats with ease.",
}- Run the rebuild process using
make build- the build script will automatically remove the corresponding directories and their packages
Option 3: Keep Only What You Need (Minimal Setup)
For a minimal deployment with only essential categories:
- Open
config/categories.mjs - Remove all category entries except the ones you need (e.g.,
developer-tools,databases,cloud-platforms,version-control,communication,file-systems) - Run the rebuild process using
make build
This approach is recommended as it ensures consistency between your configuration and the actual packages.
After removing packages, rebuild the indexes:
For Linux/macOS:
make buildFor Windows:
The make command is not available by default on Windows. You'll need to run the build commands manually. Open the Makefile file in the project root and execute the commands in the build target one by one:
bun scripts/cat-dirs.ts
pnpm install --no-frozen-lockfile
bun scripts/indexing-lists.ts
bun scripts/check-config.ts
pnpm install --no-frozen-lockfile
npx tsx scripts/test-mcp-clients.ts
pnpm install --no-frozen-lockfile
pnpm prune
bun scripts/readme-gen.ts
pnpm run sort
pnpm run check
pnpm run buildThis will:
- Scan the remaining packages in
packages/directory - Validate them and install only required dependencies
- Generate new indexes in
indexes/ - Update
package.jsonwith only the necessary Node.js dependencies
By keeping only the packages you need, you'll get:
- ✅ Faster Build Time - From 10-15 minutes to 2-3 minutes
- ✅ Smaller Dependencies - From thousands to dozens of packages
- ✅ Smaller Docker Image - Reduced image size by 50-80%
- ✅ Faster Deployment - Less data to transfer and install
- ✅ Easier Maintenance - Focus only on packages you actually use
For a typical private deployment, we recommend keeping only the most commonly used categories.
Edit config/categories.mjs and keep only these essential categories:
developer-tools- Development workflow toolsdatabases- Database integrationscloud-platforms- Cloud service integrationsversion-control- Git, GitHub, etc.communication- Slack, Discord, etc.file-systems- File management tools
To do this, open config/categories.mjs and remove all other category entries (like gaming, sports, marketing, travel, etc.). After rebuilding using make build, this will give you a practical set of ~200-300 packages instead of 6000+.
This section is for developers who want to contribute to the project or need faster development iteration.
When to use local development:
- Contributing code to the project
- Debugging and testing changes with fast reload
- Developing new features
Quick command reference:
make build- Build the project and install dependenciesmake search- Start MeiliSearch container (for search functionality)make dev- Start the development server with hot reload
pnpm installmake buildThis will perform the following operations:
- Validate all MCP server configurations
- Install all necessary dependencies
- Build TypeScript code
This is the simplest way to start, suitable for scenarios where only API functionality is needed:
- Ensure
ENABLE_SEARCH=falseis set in the.envfile:
ENABLE_SEARCH=false
MCP_SERVER_PORT=3003- Start the development server:
make dev- Access the following endpoints:
- API Documentation: http://localhost:3003/swagger
If you need full search functionality:
- Set up the
.envfile:
ENABLE_SEARCH=true
MCP_SERVER_PORT=3003
MEILI_HTTP_ADDR=http://localhost:7700- Start the MeiliSearch service (only the search container):
make searchThis command starts only the MeiliSearch container for local development. The main application will be run separately with make dev.
- Build the project and start the development server:
make build
make dev- Initialize search indexes:
Call the following endpoints via API:
POST /api/v1/search/manage/init- Initialize search servicePOST /api/v1/search/manage/index- Index data
- Access:
- Search Page: http://localhost:3003
- API Documentation: http://localhost:3003/swagger
When executing the make build command, you may see error messages similar to the following:
Error reading MCP Client for package: claude-prompts... ENOENT: no such file or directory
This is normal! The reason for these errors is:
- This project includes thousands of MCP packages
- The build process attempts to test all packages through the test-mcp-clients.ts script
- Due to the large number, the testing process may take several hours
- Not all packages need to be installed and tested, as most packages are not essential for running the registry
These errors can be ignored as long as the build process continues to execute. After the build is complete, you can still use the API and search functionality (if search is enabled) normally.
This project follows Domain-Driven Design (DDD) architecture with Service Object pattern:
.
├── config/ # Configuration files
├── indexes/ # Generated index files
├── packages/ # MCP server configuration files (categorized by domain)
├── scripts/ # Build and maintenance scripts
├── docker/ # Docker related files
│ ├── sandock-mcp.Dockerfile # Sandock custom image
│ ├── build-and-push.sh # Image build script
│ └── QUICKSTART.md # Docker quick start guide
├── docs/ # Documentation
│ └── SANDOCK_BEST_PRACTICES.md # Sandock usage guide
└── src/ # Source code (Domain-Driven Design)
├── api/ # API entry point
│ └── index.ts # Server initialization and route registration
├── domains/ # Business domains (core logic)
│ ├── config/ # Configuration management
│ ├── executor/ # Tool execution (local-executor, sandbox-executor)
│ ├── mcp-gateway/ # MCP Gateway — proxies packages as Streamable HTTP endpoints
│ ├── package/ # Package management (SO, handler, routes)
│ ├── sandbox/ # Sandbox management (pooling, providers)
│ └── search/ # Search service integration
└── shared/ # Shared infrastructure
├── config/ # Environment configuration (environment.ts)
├── schemas/ # Common Zod schemas
├── types/ # Shared TypeScript types
└── utils/ # Utility functions
- Service Object (SO) Pattern: Business logic encapsulated in reusable Service Objects
- Handler Layer: Thin HTTP request/response handlers
- Repository Pattern: Data access abstraction
- Factory Pattern: Dynamic object creation (Executor, Sandbox providers)
- Dependency Injection: Loose coupling through constructor injection
For Docker deployment, you only need to configure SANDOCK_API_KEY:
# 🔑 Required: Get it from https://sandock.ai
SANDOCK_API_KEY=your-sandock-api-key-hereAll other configurations have reasonable default values and do not need to be modified.
If you need to customize, you can configure the following variables:
| Variable | Description | Default |
|---|---|---|
ENABLE_SEARCH |
Enable search functionality | true |
MCP_SERVER_PORT |
Service port | 3003 |
Click to expand complete environment variable list (usually no need to modify)
| Variable | Description | Default |
|---|---|---|
MCP_SANDBOX_PROVIDER |
Sandbox type | SANDOCK |
SANDOCK_API_URL |
Sandock service URL | https://sandock.ai |
MEILI_HTTP_ADDR |
MeiliSearch address | http://meilisearch:7700 |
MEILI_MASTER_KEY |
MeiliSearch master key | - |
DAYTONA_API_KEY |
Daytona API Key (required when switching Provider) | - |
DAYTONA_API_URL |
Daytona service URL | - |
E2B_API_KEY |
E2B API Key (required when switching Provider) | - |
- SANDOCK ⭐ - Default recommendation, lightweight Docker sandbox designed for AI Agents
- LOCAL - Direct local execution, no isolation (for development and testing)
- DAYTONA - Cloud development environment (team collaboration)
- E2B - Code interpreter sandbox (specific scenarios)
.env- Main configuration file (copy from.env.example).env.local- Local override configuration (not committed to Git)
All environment variables are managed centrally through src/shared/config/environment.ts.
This section explains how to integrate OAuth authentication for MCP servers that require user authorization (e.g., GitHub, Slack, etc.).
The Registry encapsulates OAuth protocol complexity (Discovery/DCR/PKCE). Third-party platforms only need to implement the following standard flow to replace the demo implementation:
Flow Summary:
- Prepare - Initiate authorization and get auth URL
- Callback - Receive tokens after user authorization
- Run - Execute tools with access token
- Refresh - Refresh expired tokens
Endpoint: POST {REGISTRY_URL}/api/v1/oauth/prepare
Request:
{
"packageName": "github-mcp",
"callbackBaseUrl": "https://api.your-platform.com/internal/mcp-oauth/complete"
}Response:
{
"authUrl": "https://github.com/login/oauth/authorize?...",
"sessionId": "session-uuid-here"
}Implementation:
- Retrieve
authUrlandsessionIdfrom the response - Frontend redirects user via
window.open(authUrl)to initiate third-party login
Mechanism: After user authorization, Registry will automatically POST data to the callbackBaseUrl provided in Step 1.
Received Data:
{
"sessionId": "session-uuid-here",
"tokens": {
"access_token": "gho_xxxx",
"refresh_token": "ghr_xxxx",
"expires_in": 28800
},
"clientInfo": {
"client_id": "Iv1.xxxx"
}
}Implementation:
- Your backend must implement this callback endpoint
- Receive and persist the tokens, associating them with the current user
- Important: Registry does NOT store tokens - your platform is responsible for token storage
Endpoint: POST {REGISTRY_URL}/api/v1/packages/run
Request:
{
"packageName": "github-mcp",
"toolKey": "list_repositories",
"inputData": {
"owner": "toolsdk-ai"
},
"accessToken": "gho_xxxx"
}Implementation:
- Add
accessTokenfield to the existing request body - Registry automatically injects the token into MCP Server request headers
Endpoint: POST {REGISTRY_URL}/api/v1/oauth/refresh
Request:
{
"packageName": "github-mcp",
"refreshToken": "ghr_xxxx",
"clientId": "Iv1.xxxx"
}Response:
{
"access_token": "gho_new_xxxx",
"refresh_token": "ghr_new_xxxx",
"expires_in": 28800
}Implementation:
- When tokens expire, use stored
refreshTokenandclientIdto obtain new tokens - Update stored tokens with the new values
Happy coding! 🚀
For questions or issues, please open an issue or join our community discussions.