The MCP Server Handler supports custom tools that allow you to create
sophisticated MCP
(Model Context Protocol) tools
using OpenAPI specifications and custom handler functions. This provides the
flexibility to build complex workflows that can invoke multiple API routes,
implement custom business logic, and provide rich responses to AI systems
without having to sacrifice the governance and power of an OpenAPI
configuration.
Custom tools give you full programmatic control over tool behavior within an
MCP Server Handler. Define tools using standard
OpenAPI patterns with custom TypeScript handlers for complex multi-step
workflows and custom logic.
Key Features
OpenAPI Standard: Define tools using standard OpenAPI specifications
Custom Handlers: Implement complex logic using TypeScript functions
Complex Workflows: Chain multiple API calls, implement business logic, and
handle complex data transformations
Type Safety: Built-in JSON Schema validation for LLM tool arguments and
inputs
Runtime Integration: Access to context.invokeRoute(), logging, and other
Zuplo runtime features
Quick Start
1. Define Your API Specification
Create an OpenAPI specification that defines your tools:
# Test with MCP Inspectornpx @modelcontextprotocol/inspector# Or test with curlcurl https://your-gateway.zuplo.dev/mcp \ -X POST \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": "0", "method": "tools/list" }'
Advanced Usage
Complex Multi-Step Workflows
Create sophisticated workflows that chain multiple operations:
This utilizes context.invokeRoute to invoke various routes on a gateway. This
powerful workflow lets you create composite routes and tools that call many
different routes on your gateway.
context.invokeRoutewill utilize the full inbound and outbound policy
pipeline but does not go back out to HTTP: requests stay within the gateway.
This means that policies you set on your MCP server route will be invoked
alongside policies that are associated with any calls made through
context.invokeRoute.
A corresponding OpenAPI definition makes this custom route available on your
gateway.
Handle errors gracefully by throwing standard JavaScript errors. These then get
caught by the MCP Server and are served back to the LLM client so it can take
action:
Code
// modules/validate-user.tsimport { ZuploContext, ZuploRequest } from "@zuplo/runtime";export default async function (request: ZuploRequest, context: ZuploContext) { const args = await request.json(); if (args.shouldFail) { throw new Error(args.errorMessage || "Validation failed"); } return { valid: true, userId: args.userId };}
Accessing Request Headers
Access the original request headers through the standard ZuploRequest object.
These headers are piped through the MCP server on your gateway into your route.
This means you can handle headers from MCP clients like you would on any other
route:
By default, the MCP Server handler serializes the downstream response body into
a single text content block and, when includeStructuredContent is enabled,
derives structuredContent from it. The ZuploMcpSdk class gives you
programmatic control over what the AI client receives — without changing the
response your handler returns.
The most common use case is a compact summary: replace the full serialized
body in content with a short human-readable summary, while the gateway still
auto-derives structuredContent from the downstream response. This reduces the
token count in the model's context without losing data.
Code
import { ZuploContext, ZuploMcpSdk, ZuploRequest } from "@zuplo/runtime";export default async function (request: ZuploRequest, context: ZuploContext) { const response = await context.invokeRoute("/todos"); new ZuploMcpSdk(context).setRawCallToolResult({ content: [{ type: "text", text: "Fetched the todo list" }], }); return response;}
You can also read metadata from the incoming tools/call request using
getRawCallToolRequest(), and override structuredContent and _meta
independently. For the full API reference, see
ZuploMcpSdk.
Best Practices
Tool Design
Clear Operation IDs: Use descriptive, action-oriented operation IDs
(addNumbers, processOrder)
Detailed Descriptions: Help AI systems understand what your tool does