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: