AI & Agents

How to Build a Custom Fast.io MCP Tool

Building a custom Fast.io MCP tool lets developers inject proprietary business logic directly into an agent's file workspace. This guide covers the three mandatory components of a tool definition, authentication patterns for autonomous agents, implementation in Python and TypeScript, and how to deploy your tool alongside Fast.io's 19 built-in MCP tools.

Fast.io Editorial Team 10 min read
Extend your agent's capabilities with custom tool definitions.

What Is a Custom MCP Tool?

The Model Context Protocol (MCP) is an open standard that lets AI agents interact with external systems through a structured interface. An MCP server exposes tools, resources, and prompts. An MCP client (the agent host) discovers those capabilities at runtime and invokes them as needed.

Fast.io ships a managed MCP server at /storage-for-agents/ with 19 consolidated tools covering workspace management, file storage, AI/RAG queries, shares, uploads, and workflow primitives. These tools handle the common operations any agent needs when working with files and collaborating with humans.

A custom MCP tool fills the gap between those built-in capabilities and your organization's specific needs. Maybe your agent needs to pull records from an internal CRM, run a compliance check against uploaded documents, or trigger a deployment pipeline when certain files land in a workspace. Instead of modifying the LLM or chaining brittle prompts, you expose a structured tool interface. The agent reads the tool's description and parameter schema, then decides when to invoke it based on the user's request.

The architectural benefit is clean separation. Your business logic stays isolated, versioned, and testable. The LLM stays focused on reasoning. And Fast.io handles the file persistence, permissions, and collaboration layer that your tool operates within.

What to check before scaling How to build a custom Fast.io MCP tool

Every MCP tool definition requires exactly three components. Miss any of them and the agent either ignores the tool or misuses it.

1. Name

A unique string identifier that the protocol uses when the agent invokes the tool. Use lowercase alphanumeric characters with hyphens: fetch-customer-invoice, run-compliance-check, generate-quarterly-report. The name appears in JSON-RPC requests, so keep it descriptive but concise.

2. Description A natural language string that tells the LLM when to use the tool, what it does, and any constraints. This is the most important component for tool selection accuracy. A vague description like "handles data" leads to hallucinated parameters and wrong invocations. A specific description like "Retrieves PDF invoice metadata from the billing database when the user asks about payment history or charges" gives the agent clear selection criteria.

3. Input Schema A JSON Schema object that defines the parameters the tool accepts. It specifies types, required fields, enums, defaults, and per-property descriptions. The schema serves double duty: it guides the LLM on what arguments to construct, and it validates those arguments before your handler runs.

Here is a complete tool definition combining all three:

{
  "name": "fetch-customer-invoice",
  "description": "Retrieves PDF invoice metadata from the internal billing database. Use this when the user asks about payment history or recent charges.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "customer_id": {
        "type": "string",
        "description": "Alphanumeric customer ID (e.g., CUST-12345)"
      },
      "date_range": {
        "type": "string",
        "enum": ["last_30_days", "last_90_days", "all"],
        "description": "Time period for invoice retrieval",
        "default": "last_30_days"
      }
    },
    "required": ["customer_id"]
  }
}

The MCP specification also supports optional fields like title (human-readable display name), outputSchema (structured response format), and annotations (metadata like cost or privacy level). For most custom tools, the three core components are sufficient to get started.

Diagram showing the three components of an MCP tool definition flowing into an agent

Implementing a Custom Tool in Python

The fast path to a working custom tool is the Python MCP SDK's FastMCP class. It generates the JSON Schema automatically from your type hints and docstrings.

Install the SDK:

pip install "mcp[cli]"

Create a file called custom_tools.py:

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("fastio-custom-tools")

@mcp.tool()
async def fetch_customer_invoice(
    customer_id: str,
    date_range: str = "last_30_days"
) -> str:
    """Retrieve invoice metadata from the billing database.

Use this when the user asks about payment history,
    recent charges, or needs an invoice PDF.

Args:
        customer_id: Alphanumeric customer ID (e.g., CUST-12345)
        date_range: Time period - last_30_days, last_90_days, or all
    """
    ### Your database query logic here
    invoices = await query_billing_db(customer_id, date_range)
    return format_invoice_response(invoices)

@mcp.tool()
async def run_compliance_check(
    workspace_id: str,
    node_id: str,
    standard: str = "internal"
) -> str:
    """Check a document against compliance rules.

Use this when the user uploads a contract or policy
    document and asks whether it meets compliance standards.

Args:
        workspace_id: Fast.io workspace profile ID
        node_id: Storage node ID of the document to check
        standard: Compliance standard to check against
    """
    ### Fetch document from Fast.io, run checks
    result = await check_document(workspace_id, node_id, standard)
    return result

if __name__ == "__main__":
    mcp.run(transport="stdio")

The FastMCP decorator does the heavy lifting. Type hints become JSON Schema types. The docstring becomes the tool description. Required parameters (those without defaults) are marked as required in the schema. You focus on the business logic; the SDK handles protocol compliance.

To test your tool locally, use the MCP Inspector:

npx @modelcontextprotocol/inspector python custom_tools.py

This launches a web UI where you can browse your tools, inspect their schemas, and invoke them manually before connecting a real agent.

Fast.io features

Give Your Agents Custom Capabilities on Fast.io

Start with 50GB free storage, 19 built-in MCP tools, and the flexibility to add your own. No credit card required. Built for how build custom fast mcp tool workflows.

Implementing in TypeScript

If your stack is Node.js, the TypeScript MCP SDK uses Zod for schema validation. Install the dependencies:

npm install @modelcontextprotocol/sdk zod

Create custom-tools.ts:

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: "fastio-custom-tools",
  version: "1.0.0",
});

server.registerTool(
  "fetch-customer-invoice",
  {
    title: "Fetch Customer Invoice",
    description:
      "Retrieves invoice metadata from the billing database. " +
      "Use when the user asks about payment history or charges.",
    inputSchema: {
      customer_id: z.string()
        .describe("Alphanumeric customer ID"),
      date_range: z.enum([
        "last_30_days", "last_90_days", "all"
      ]).default("last_30_days")
        .describe("Time period for retrieval"),
    },
  },
  async ({ customer_id, date_range }) => {
    const invoices = await queryBillingDb(
      customer_id, date_range
    );
    return {
      content: [{
        type: "text",
        text: formatInvoiceResponse(invoices),
      }],
    };
  }
);

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
}

main().catch(console.error);

The TypeScript approach is more explicit. You define the schema using Zod objects, which gives you runtime validation and static type inference in the same declaration. The handler receives typed parameters directly, so there is no argument parsing boilerplate.

One critical note for stdio transport: never use console.log() in your tool handler. Stdout is the JSON-RPC communication channel. Logging to stdout corrupts the protocol. Use console.error() or write to a file instead.

Code editor showing TypeScript MCP tool implementation with schema validation

Authentication and Fast.io Integration

Custom tools that interact with Fast.io workspaces need authenticated access. Fast.io's MCP server supports four authentication methods, each suited to different deployment patterns.

API Key Authentication The simplest approach for server-to-server integrations. Generate an API key in the Fast.io dashboard, scope it to the relevant org or workspace, and pass it to your tool's environment:

import os
import httpx

FASTIO_API_KEY = os.environ["FASTIO_API_KEY"]

async def list_workspace_files(workspace_id: str):
    async with httpx.AsyncClient() as client:
        response = await client.get(
            f"https://api.fast.io/v1/workspaces/"
            f"{workspace_id}/storage",
            headers={
                "Authorization": f"Bearer {FASTIO_API_KEY}"
            }
        )
        return response.json()

API keys work as Bearer tokens with the same permissions as JWT tokens. They do not expire unless revoked, which makes them practical for long-running agent processes.

Autonomous Agent Signup

For agents that need their own identity, use the signup flow through the MCP server. The agent calls the auth tool with the signup action, which automatically registers with agent=true and assigns the free agent plan: 50GB storage, 5,000 monthly credits, 5 workspaces, no credit card required. After signup, email verification is required before most operations work.

PKCE Browser Login When your custom tool assists a human user who wants to connect their existing Fast.io account, the PKCE flow avoids passing credentials through the agent. The agent gets a login URL, the user opens it in a browser, approves access, and copies back an authorization code.

Choosing the Right Method

Use API keys when your custom tool runs as infrastructure (CI/CD pipelines, scheduled jobs, backend services). Use autonomous signup when the agent needs its own workspace to build deliverables and later transfer ownership to a human. Use PKCE when the agent acts on behalf of an authenticated user.

Deployment and Transport Options

How you deploy your custom tool depends on who needs to connect to it and where the agent runs.

Local Stdio Transport

Best for single-agent desktop setups. Your tool runs as a subprocess that communicates over stdin/stdout. Configure it in the agent's MCP settings:

{
  "mcpServers": {
    "my-custom-tools": {
      "command": "python",
      "args": ["custom_tools.py"]
    }
  }
}

This works with Claude Desktop, Cursor, VS Code with the MCP extension, and other local MCP clients. No network configuration needed, but it is limited to one client per server instance.

Remote HTTP Transport

For multi-agent deployments or cloud-hosted agents, expose your tool over HTTP. The MCP specification supports Streamable HTTP (the current standard) and legacy SSE. Fast.io's own MCP server uses Streamable HTTP at /mcp and legacy SSE at /sse.

To serve your custom tools over HTTP with the Python SDK:

if __name__ == "__main__":
    mcp.run(transport="sse", host="0.0.0.0", port=8080)

In production, put this behind a reverse proxy with TLS termination and add Bearer token authentication. The MCP specification supports standard HTTP auth headers, so your existing API gateway can handle token validation.

Running Alongside Fast.io's Built-in Tools

Your custom tools do not replace Fast.io's 19 built-in tools. They complement them. A typical agent configuration connects to both servers:

{
  "mcpServers": {
    "fastio": {
      "url": "/storage-for-agents/"
    },
    "my-custom-tools": {
      "command": "python",
      "args": ["custom_tools.py"]
    }
  }
}

The agent discovers tools from both servers at startup. When a user asks to "pull the latest invoices and save them to the project workspace," the agent calls your fetch-customer-invoice tool for the billing data, then calls Fast.io's upload tool to save the results. The tools compose naturally because they follow the same protocol.

For the full list of Fast.io's built-in capabilities, see the MCP skill guide.

Architecture diagram showing custom tools running alongside Fast.io MCP server

Error Handling and Debugging

Custom tools fail in predictable ways. Handling these failures well improves the agent's ability to recover and retry.

Validate Inputs Before Processing

Even though the JSON Schema catches type mismatches, your handler should validate business logic constraints. A customer ID that passes type validation might not exist in your database. Return a clear error message that the agent can act on:

@mcp.tool()
async def fetch_customer_invoice(customer_id: str) -> str:
    """Retrieve invoice metadata from the billing database."""
    if not customer_id.startswith("CUST-"):
        return "Error: customer_id must start with CUST-"

customer = await find_customer(customer_id)
    if not customer:
        return f"Error: No customer found with ID {customer_id}"

invoices = await get_invoices(customer)
    return format_response(invoices)

Notice that validation errors return as normal text responses, not exceptions. This lets the agent read the error message and adjust its approach. Throwing an unhandled exception produces a generic JSON-RPC error that gives the agent less to work with.

Timeout Long Operations

Custom tools that call external APIs or databases should set explicit timeouts. An agent waiting indefinitely on a hung database connection blocks the entire conversation. Set timeouts at the HTTP client level and return a descriptive message if the operation takes too long.

Use the MCP Inspector for Development

The MCP Inspector is the standard debugging tool for MCP servers. It shows you exactly what the agent sees: tool names, descriptions, schemas, and response payloads. Run it during development to verify your schema matches your intent before connecting a real agent.

npx @modelcontextprotocol/inspector python custom_tools.py

Check that parameter descriptions are specific enough for the agent to construct correct arguments. If the agent consistently passes wrong values for a parameter, the description needs work, not the agent.

Frequently Asked Questions

How do I extend an MCP server?

You extend an MCP server by adding new tool definitions with a name, description, and JSON input schema. In Python, use the FastMCP decorator to register tools from type-annotated functions. In TypeScript, use the McpServer.registerTool method with Zod schemas. The agent discovers new tools at startup through the tools/list protocol method.

What is the schema for a custom Fast.io MCP tool?

A custom MCP tool uses JSON Schema for its input definition. The schema specifies an object with named properties, each having a type (string, number, boolean, array), an optional description, and optional constraints like enums or defaults. Required parameters go in a separate "required" array. The MCP Python SDK generates this schema automatically from Python type hints.

Can I run custom tools alongside Fast.io's built-in tools?

Yes. Configure your agent to connect to both servers. Fast.io's MCP server runs at mcp.fast.io, and your custom tools run locally or on your own infrastructure. The agent discovers tools from all connected servers and composes them in a single conversation. Your custom tool handles business logic while Fast.io handles file storage, permissions, and collaboration.

What transport should I use for custom MCP tools?

Use stdio transport for local, single-agent development. It requires no network configuration and works with Claude Desktop, Cursor, and VS Code. Use Streamable HTTP or SSE for production deployments where multiple agents or remote clients need access. Put HTTP servers behind TLS and API gateway authentication.

Why build a custom tool instead of using pre-built ones?

Pre-built tools cover generic operations like file management, search, and sharing. Custom tools let you inject proprietary business logic, such as querying internal databases, running compliance checks, or triggering organization-specific workflows. The agent selects the right tool based on the user's request without any prompt engineering on your part.

Do I need to modify the LLM to add custom tools?

No. Custom MCP tools expand an agent's capabilities without modifying the core LLM. You define a tool with a clear description, and the agent decides when to use it based on that description. No fine-tuning, no model changes, no prompt hacking. The MCP protocol handles discovery and invocation.

Related Resources

Fast.io features

Give Your Agents Custom Capabilities on Fast.io

Start with 50GB free storage, 19 built-in MCP tools, and the flexibility to add your own. No credit card required. Built for how build custom fast mcp tool workflows.