Documentation
MCP

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/sdk that exposes a get_weather tool. 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 mcp package that exposes a get_weather tool. Implement list_tools and call_tool handlers. 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

PracticeWhy
Write clear descriptionsThe agent uses descriptions to decide when to call tools
Use specific parameter namescity_name is better than input
Return structured dataJSON is easier for the agent to parse than free text
Handle errors gracefullyReturn error messages, don't crash
Keep tools focusedOne tool per action, not mega-tools
Add input validationValidate 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

Docs last updated May 11, 2026