Expose services as MCP
Let AI applications use existing Zato services under the access controls you define.

An MCP gateway lets AI applications invoke selected Zato services as tools. The gateway authenticates each request and enforces its configured controls.
This page builds a patient lookup that queries the clinical audit log. The lookup is one example, and the same process applies to other services that can be exposed through an MCP gateway.
Before you begin
You need:
- A running Zato environment. The Docker installation guide sets one up.
- An MLLP channel with message auditing enabled. The HL7 v2 tutorial sends the example message for MRN
12345with control IDMSG00001, which is what we'll use here. The point is, we need some real data for AI to make use of via MCP. - An MCP client, such as Claude Code
Search for 12345 under Monitoring > Audit log before continuing. The result should contain the received message and its ACK, as the clinical audit log explains.
Write the service
The service searches the clinical audit log for received messages that mention the requested MRN. The source, event_type and query filters combine, so acknowledgments from the same trace remain outside the service response.
The response contains the control ID, event type and arrival time of each matching message:
# -*- coding: utf-8 -*-
# Zato
from zato.common.audit_log.api import AuditEvent, AuditSource
from zato.server.service import Service
# #####################################################################
# #####################################################################
class PatientMessageStatus(Service):
""" Returns whether HL7 messages for a given patient MRN arrived,
with the control ID, event type and arrival time of each.
"""
name = 'patient.message-status'
input = 'patient_mrn'
output = 'has_arrived', 'messages'
def handle(self) -> 'None':
# Read the MRN the caller asked about ..
patient_mrn = self.request.input.patient_mrn
# .. search for received MLLP messages that mention it ..
events = self.audit.search(
source=AuditSource.MLLP_Channel,
event_type=AuditEvent.Message_Received,
query=patient_mrn,
)
# .. copy only the fields needed by the caller ..
messages = []
for event in events:
messages.append({
'control_id': event['msg_id'],
'event_type': event['event_type'],
'received_at': event['event_time_iso'],
})
# .. determine whether at least one message arrived ..
has_arrived = len(messages) > 0
# .. and return the minimized result.
self.response.payload = {
'has_arrived': has_arrived,
'messages': messages,
}
The docstring becomes the description used during model tool selection. The declared input and output fields become its MCP schema, as tool schemas explains.
Deploy the class as patient.message-status using the Zato IDE or the deployment process used in your environment.
Create the client identity
Every MCP gateway requires authentication. Create the API key used in this example:
- Open Security > API keys in the Dashboard.
- Create an API key named
clinical-agents. - Set its key to
abc123. - Click OK.

The security definition becomes the client identity in MCP sessions, rate-limit counters and audit records. Give each client its own credential when they require separate identity or rate accounting, as MCP gateway security explains.
Configure the gateway
The Dashboard steps and the GitOps YAML below create the same gateway - named clinical at /mcp/clinical, with the patient.message-status service assigned, the clinical-agents API key allowed, tool arguments validated and handled MCP requests recorded.
- Open AI > MCP gateways and click Create a new MCP gateway.
- Enter
clinicalas the name and/mcp/clinicalas the URL path. - Assign
patient.message-statusin the Services picker. - Assign
clinical-agentsin the Security picker. - On What do they receive?, open More options and Gateway options.
- Enable Validate input against each tool's schema and Record each request in the audit log.
- Click OK.

Define the API key, the gateway security group and the gateway in the same Enmasse YAML:
security:
- name: clinical-agents
type: apikey
password: Zato_Enmasse_Env.ClinicalAgentsApiKey
groups:
- name: mcp.clinical
members:
- clinical-agents
mcp_gateway:
- name: clinical
url_path: /mcp/clinical
services:
- patient.message-status
security_groups:
- mcp.clinical
validate_input: true
is_audit_log_active: true
Zato_Enmasse_Env.ClinicalAgentsApiKey reads the API key from the ClinicalAgentsApiKey environment variable. Set that variable to abc123 for this example.
You can import the file from System > Config > Import enmasse. A file mounted at /opt/hot-deploy/enmasse/enmasse.yaml is imported at startup. Matching Enmasse files inside a mounted project are also imported when they change, as automatic imports explains.
Connect the MCP client
The MCP endpoint for your gateway will be http://localhost:11223/mcp/clinical, and here's how you can connect Claude Code:
claude mcp add --transport http --header "X-API-Key: abc123" zato-clinical http://localhost:11223/mcp/clinical
With other MCP clients, just point them to the same URL and X-API-Key header.
Verify the lookup
You can now ask your AI:
Behind this exchange, the model selected patient.message-status and called it with:
For the message sent by the HL7 v2 tutorial, the tool returned:
{
"has_arrived": true,
"messages": [{
"control_id": "MSG00001",
"event_type": "message-received",
"received_at": "2026-03-15T12:00:00"
}]
}
And this is the same lookup for an MRN with no messages:
Behind the scenes, the tool returned an empty result, which is how AI knew there were no messages for 99999.
Add more MCP tools
You can create and expose other useful services in a similar way, and here are some demo examples to get you started:
Schedule a meaningful demo
Book a demo with an expert who will help you build meaningful systems that match your ambitions
"We evaluated 12 integration platforms and Zato was the only one to score 100%."
Philip Zuñiga, Assistant Professor, University of the Philippines