Guide

How to Run Serverless Functions as MCP Tools

Not every system has an MCP server or an OpenAPI specification. A gateway that hosts code lets you write a function and expose it as a tool. This guide shows the three ways to do it. It also shows how Runnable Code servers work in Gatana and the limits to know before you select a method.

· Erik Jonsson Thorén, Founder, Gatana

Short answer: A gateway with hosted code runs your functions and exposes each one as an MCP tool. In Gatana, these are Runnable Code servers. You upload Node.js 24 or Python 3.13 source. A schema object names the tools. The gateway deploys the code in an isolated virtual machine. It injects the credentials of the caller into each call and records every call.

When a function is the right tool

Use a function when one of these statements is true:

  • The system has no MCP server and no OpenAPI specification.
  • One tool must combine two systems in one call, for example read a ticket and post a summary.
  • The data is in a private network, and the agent must not reach it directly.
  • A script exists already. The agent must run it under access rules and with an audit record.

Use an OpenAPI server instead when a specification exists. The gateway converts it without code. Use a standard MCP server when the vendor provides one.

Three ways to turn a function into a tool

Method You operate Identity, credentials and audit Fits when
Write an MCP server with a framework such as FastMCP and host it yourself The server, its hosting and its authentication You build them, for each server You need full control of the runtime
Deploy the server as a Cloudflare Worker. Cloudflare documents remote MCP servers on Workers The Worker and its authentication The tooling of Cloudflare You already build on Cloudflare
Upload the code to a gateway that hosts it, for example a Gatana Runnable Code server Only the code From the gateway: SSO identity, stored credentials, roles, audit log Your agents already go through the gateway

The first two give you a server that the gateway can then front like any other. The third removes the server from your list of things to operate.

How Runnable Code servers work in Gatana

The code

A package has one entry file in its root. The runtime decides which one:

Runtime Entry file Minimum content
Node.js 24 index.js export const schema = {}
Python 3.13 main.py schema = {}

Each key in schema names a tool. The module must export a function with the same name. The function receives the call arguments and a credentials object.

import z from 'zod';

export const schema = {
  add: { description: 'adds two numbers', input: z.object({ a: z.number(), b: z.number() }) },
};

export function add({ a, b }, credentials) {
  return String(a + b);
}

The same tool in Python:

from pydantic import BaseModel

class AddInput(BaseModel):
    a: float
    b: float

schema = {"add": {"description": "adds two numbers", "input": AddInput}}

def add(args, credentials):
    return str(args["a"] + args["b"])

A return value that is not a string is serialized to JSON. Python functions can be async def.

The credentials object

The gateway builds this object for every call:

{
  headers: { authorization: 'Bearer OAUTH_ACCESS_TOKEN' }, // injected for OAuth servers
  apikeys: [['key1', 'value1'], ['key2', 'value2']],      // API keys configured on the server
  gatanaUserEmail: '[email protected]'                     // the person the call is made for
}

gatanaUserEmail is null when no person is behind the call, for example when the gateway refreshes the tool list in the background.

Upload and deploy

Three methods exist:

  • The editor in the dashboard. Click Deploy to save and deploy.
  • A ZIP archive with the package. It replaces the current code.
  • The CLI:
npm install -g gatana
mkdir pkg && cd pkg
gatana hosted init                           # a Node.js package with two example tools
gatana hosted verify .                       # checks that the gateway will accept it
gatana hosted run . add -p a=1 -p b=2        # runs one tool locally
gatana hosted upload my-hosted-server --create

hosted init, verify and run work with Node.js packages. hosted upload works with both runtimes. The gateway deploys every upload. Deployment logs show the status of the revision. Server Logs stream the output of a revision.

Dependencies

For Node.js, run npm install and include node_modules in the package. For Python, add a requirements.txt. The runtime installs it with uv at every start, before it loads the code. The installation must finish within about one minute. The runtime provides mcp, pydantic, starlette and uvicorn. As an alternative that needs no network at start, put pure-Python packages in a vendor folder.

Isolation

Each server runs in its own virtual machine with Kata Containers, on Debian Bookworm. The code reaches the public internet only. Environment variables from Server Settings are available as process.env in Node.js and os.environ in Python.

Storage and private networks

The file system is reset on every deployment. Enable persistent storage to keep a volume mounted at the path in GATANA_DATA_DIR. An organization can claim 20 GiB across all its servers in Gatana Cloud. Enable Tailscale to reach hosts on your own network over ordinary sockets. The code does not change.

Access and audit

A Runnable Code server is a server like any other. Roles, teams and profiles decide who may call its tools. Tool firewall rules can deny or log calls by argument. Every call appears in the audit log with the person and the agent.

Limits to know

  • The code reaches the public internet only, unless Tailscale is enabled.
  • The Python dependency installation must finish within about one minute.
  • An idle server stops automatically. A server with persistent storage pauses for a moment on redeploy, because a volume attaches to one instance at a time.
  • Runnable Code servers are part of the Pro and Enterprise plans.

The Runnable Code page in the documentation has the full reference.

FAQ

Questions, answered.

What is a Runnable Code server?

A Runnable Code server is hosted by Gatana. You upload source code instead of a packaged application. A schema object in the code names the tools. Each named function becomes an MCP tool behind the gateway. The server runs in its own virtual machine. The same roles, profiles, firewall rules and audit log apply as for any other server.

Which languages can I use?

Node.js 24 and Python 3.13. You select the runtime when you create the server. A Node.js package needs an index.js file that exports a schema object. A Python package needs a main.py file with a schema dictionary. You declare tool inputs with zod in Node.js and with pydantic or a JSON Schema dictionary in Python.

How do credentials reach my function?

Each tool function receives two arguments: the call arguments and a credentials object. For a server with OAuth, the gateway injects the access token of the person as an authorization header. API keys configured on the server arrive as a list of key and value pairs. The object also contains the email address of the Gatana user that the call is made for.

Can the function reach a database on my private network?

Yes, with Tailscale. By default, the code reaches the public internet only. Enable Tailscale for the server and it connects to hosts on your tailnet over ordinary sockets. The code does not change. Environment variables set under Server Settings contain the connection details.

Does the file system persist between deployments?

Not by default. The file system is reset on every deployment, restart and idle stop. Enable persistent storage for the server to keep a data volume mounted at the path in the GATANA_DATA_DIR environment variable. An organization can claim 20 GiB across all its servers in Gatana Cloud.

Which plan includes Runnable Code servers?

The Pro plan, at 125 US dollars each month for each organization, and the Enterprise plan. Pro includes unlimited servers, Node.js and Python runtimes, 20 GiB of persistent storage and Tailscale. The Free plan covers remote MCP servers and OpenAPI servers only.

All guides