MCP: Building Custom Tools
Create your own MCP server to give Flapjack agents custom tool capabilities. TypeScript and Python examples.
Create your own MCP server to give Flapjack agents access to custom tools β your internal APIs, databases, or any functionality.
MCP Server Basics
An MCP server exposes tools via the Model Context Protocol. Each tool has:
- Name: Identifier (e.g.,
get_weather) - Description: When and how to use it (the agent reads this)
- Input schema: JSON Schema for parameters
- Handler: Function that executes the tool
TypeScript Example
Using the @modelcontextprotocol/sdk package:
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
const server = new McpServer({
name: 'my-tools',
version: '1.0.0',
});
// Define a tool
server.tool(
'get_weather',
'Get current weather for a city',
{ city: z.string().describe('City name') },
async ({ city }) => {
const weather = await fetchWeather(city);
return {
content: [{ type: 'text', text: JSON.stringify(weather) }],
};
}
);
// Start the server
const transport = new StdioServerTransport();
await server.connect(transport);
π Copy as prompt
Create a TypeScript MCP server using
@modelcontextprotocol/sdkthat exposes aget_weathertool. The tool takes a city name and returns weather data. Use stdio transport.
Python Example
Using the mcp package:
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
server = Server("my-tools")
@server.list_tools()
async def list_tools():
return [
Tool(
name="get_weather",
description="Get current weather for a city",
inputSchema={
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name"}
},
"required": ["city"]
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict):
if name == "get_weather":
weather = await fetch_weather(arguments["city"])
return [TextContent(type="text", text=str(weather))]
async def main():
async with stdio_server() as (read, write):
await server.run(read, write)
if __name__ == "__main__":
import asyncio
asyncio.run(main())
π Copy as prompt
Create a Python MCP server using the
mcppackage that exposes aget_weathertool. Implementlist_toolsandcall_toolhandlers. Use stdio transport.
Connecting to Flapjack
As a Remote Server (HTTP)
Deploy your MCP server with an HTTP endpoint, then add it to Flapjack:
curl -X POST https://api.flapjack.dev/api/mcps \
-H "Authorization: Bearer fj_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "My Tools",
"slug": "my-tools",
"transport": "streamable_http",
"url": "https://my-mcp-server.example.com/mcp"
}'
As a Local Tool (stdio)
For tools that run as local subprocesses:
curl -X POST https://api.flapjack.dev/api/mcps \
-H "Authorization: Bearer fj_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "My Tools",
"slug": "my-tools",
"transport": "stdio",
"command": "node",
"args": ["./my-mcp-server.js"]
}'
Tool Design Best Practices
| Practice | Why |
|---|---|
| Write clear descriptions | The agent uses descriptions to decide when to call tools |
| Use specific parameter names | city_name is better than input |
| Return structured data | JSON is easier for the agent to parse than free text |
| Handle errors gracefully | Return error messages, don't crash |
| Keep tools focused | One tool per action, not mega-tools |
| Add input validation | Validate parameters before executing |
Tool Naming Convention
In Flapjack, tools are namespaced as {slug}__{tool_name}. Choose slugs and tool names that are:
- Lowercase with underscores
- Descriptive but concise
- Unique within your organization
Next Steps
- MCP: Connecting Servers β add your server to Flapjack
- MCP: Overview β how MCP works
- Concepts: Tools β tool system overview