Custom Servers
Overview
Section titled “Overview”You can create custom MCP servers and use them with MCP Gateway in your AgentBox sandboxes. Custom servers allow you to extend MCP Gateway with your own tools and resources.
Create a custom MCP server
Section titled “Create a custom MCP server”Create a custom MCP server using the MCP SDK:
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
# Create a server instance
server = Server("my-custom-server")
# Define a tool
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="custom_tool",
description="A custom tool that does something useful",
inputSchema={
"type": "object",
"properties": {
"input": {
"type": "string",
"description": "Input for the tool"
}
},
"required": ["input"]
}
)
]
# Implement the tool
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
if name == "custom_tool":
input_value = arguments.get("input", "")
result = f"Processed: {input_value}"
return [TextContent(type="text", text=result)]
else:
raise ValueError(f"Unknown tool: {name}")
# Run the server
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream, server.create_initialization_options())
if __name__ == "__main__":
import asyncio
asyncio.run(main())import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
// Create a server instance
const server = new Server(
{
name: "my-custom-server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// Define a tool
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "custom_tool",
description: "A custom tool that does something useful",
inputSchema: {
type: "object",
properties: {
input: {
type: "string",
description: "Input for the tool",
},
},
required: ["input"],
},
},
],
}));
// Implement the tool
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "custom_tool") {
const input = request.params.arguments?.input as string;
return {
content: [
{
type: "text",
text: `Processed: ${input}`,
},
],
};
} else {
throw new Error(`Unknown tool: ${request.params.name}`);
}
});
// Run the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Custom MCP server running on stdio");
}
main().catch(console.error);Package your custom server
Section titled “Package your custom server”Create a package for your custom server:
# Create a package structure
mkdir my-custom-mcp-server
cd my-custom-mcp-server
# Create setup.py or pyproject.toml
cat > pyproject.toml << EOF
[project]
name = "my-custom-mcp-server"
version = "1.0.0"
requires-python = ">=3.9"
dependencies = ["mcp"]
[project.scripts]
my-custom-server = "my_custom_server:main"
EOF
# Install in development mode
pip install -e .# Create a package structure
mkdir my-custom-mcp-server
cd my-custom-mcp-server
# Initialize npm package
npm init -y
# Install dependencies
npm install @modelcontextprotocol/sdk
# Add to package.json scripts
# "start": "node dist/index.js"Use custom server in sandbox
Section titled “Use custom server in sandbox”To use your custom server, you need to:
- Install the server in the sandbox template
- Configure it in the MCP Gateway configuration
Option 1: Install in template
Section titled “Option 1: Install in template”Add your custom server to a template:
FROM agentbox/base:latest
# Install your custom MCP server
RUN pip install my-custom-mcp-server
# Or copy and install from source
COPY my-custom-mcp-server/ /opt/my-custom-mcp-server/
RUN cd /opt/my-custom-mcp-server && pip install -e .Option 2: Use command-based server
Section titled “Option 2: Use command-based server”Configure a custom server that runs as a command:
from agentbox import Sandbox
sandbox = Sandbox.create(
api_key="ab_xxxxxxxxxxxxxxxxxxxxxxxx",
mcp={
"my-custom-server": {
"command": "python",
"args": ["-m", "my_custom_server"],
"env": {
"CUSTOM_VAR": "value"
}
}
}
)from agentbox import AsyncSandbox
sandbox = await AsyncSandbox.create(
api_key="ab_xxxxxxxxxxxxxxxxxxxxxxxx",
mcp={
"my-custom-server": {
"command": "python",
"args": ["-m", "my_custom_server"],
"env": {
"CUSTOM_VAR": "value"
}
}
}
)Best practices
Section titled “Best practices”- Follow MCP protocol: Ensure your server follows the MCP protocol specification
- Handle errors gracefully: Implement proper error handling in your tools
- Document your tools: Provide clear descriptions and input schemas
- Test thoroughly: Test your server with various clients
- Version your server: Use semantic versioning for your server
Example: Simple calculator server
Section titled “Example: Simple calculator server”Here’s a complete example of a simple calculator MCP server:
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
server = Server("calculator-server")
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="add",
description="Add two numbers",
inputSchema={
"type": "object",
"properties": {
"a": {"type": "number"},
"b": {"type": "number"}
},
"required": ["a", "b"]
}
),
Tool(
name="multiply",
description="Multiply two numbers",
inputSchema={
"type": "object",
"properties": {
"a": {"type": "number"},
"b": {"type": "number"}
},
"required": ["a", "b"]
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
if name == "add":
result = arguments["a"] + arguments["b"]
return [TextContent(type="text", text=str(result))]
elif name == "multiply":
result = arguments["a"] * arguments["b"]
return [TextContent(type="text", text=str(result))]
else:
raise ValueError(f"Unknown tool: {name}")
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream, server.create_initialization_options())
if __name__ == "__main__":
import asyncio
asyncio.run(main())