Gatana logoGatana Docs
Adding Servers

Runnable Code

Execute abitrary code and expose as tools

Introduction

Runnable code servers, also known as hosted servers, similar to servers based on executable apps, the difference is that instead of starting a pre-packaged executable application, you can provide source-code which methods can be exposed as MCP tools.

Two runtimes are supported: Node.js 24 and Python 3.13. You select the runtime when you create the server. You can change it later under Server Settings, which redeploys the server; the source package then has to match the new runtime.

Writing Source Code

There are three methods to upload your source code:

  • Directly in the editor: You can edit files directly in the embedded code editor above. Changes are saved and deployed when you click the "Deploy" button.
  • Upload ZIP archive: Prepare a ZIP file containing your source code and upload it using the "Upload Archive" button. This will replace the existing code with the contents of the ZIP file.
  • Gatana CLI: Use our command line tool to upload and manage your function's source code. See the GitHub repository for the CLI tool for details. The hosted init, hosted verify and hosted run commands work with Node.js packages; hosted upload works with both runtimes.

Quick-Start

npm install -g gatana
mkdir pkg
cd pkg
gatana hosted init
gatana hosted upload my-hosted-server --create
# new tools are now available in the Gateway

Validate and Test Locally

To ensure that the hosted server will be accepted by the Gateway, you can run:

gatana hosted verify .

You can test a tool as well:

$ gatana hosted run . add -p a=1 -p b=2
Running tool "add"...
3

Javascript Quick Start

Below is an example for a simple server which exposes two tools.

import z from 'zod';

export const schema = {
    whoami: {
        description: 'returns HTTP headers',
    },
    add: {
        description: 'adds two numbers',
        input: z.object({
            a: z.number(),
            b: z.number(),
        })
    },
};
export function whoami(args, credentials) {
    return JSON.stringify(credentials)
}
export function add({ a, b } = params) {
    return String(a + b + 10)
}

You can generate a valid hosted server by running:

gatana hosted init

Python Quick Start

The same server in Python. The file is named main.py; tool inputs are declared with a pydantic model or a JSON Schema dict.

import json

from pydantic import BaseModel


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


schema = {
    "whoami": {"description": "returns HTTP headers"},
    "add": {"description": "adds two numbers", "input": AddInput},
}


def whoami(args, credentials):
    return json.dumps(credentials)


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

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

Structure

Your source-code package needs one entry file in its root. The runtime decides which one is loaded:

RuntimeEntry fileMinimum content
Node.js 24index.jsexport const schema = {}
Python 3.13main.pyschema = {}

To add tools, expand the schema object. Each key names a tool, and the module must export a function with the same name:

export const schema = {
    helloworld: {
        description: 'returns hello world',
    },
};
export function helloworld(args, credentials) {
    return 'Hello World'
}

Each tool will be provided with two arguments:

The args argument is an object containing the arguments passed to the tool. In Python this is a dict; when the tool declares a pydantic model as input, the arguments are validated against it first.

The credentials argument is an object containing the credentials information. This is the structure:

{
  headers: {
    authorization: 'Bearer OAUTH_ACCESS_TOKEN' // Automatically injected in case of OAuth method
  }
  apikeys: [
    ["key1", "value1"],
    ["key2", "value2"],
  ],
  gatanaUserEmail: '[email protected]'
}

In Python the same object arrives as a dict with the keys headers, apikeys and gatanaUserEmail.

The gatanaUserEmail field holds the email address of the Gatana user the tool call is made for. Use it when your code needs to know who is calling, for example to look up the user in a downstream system. It is null (None in Python) when the call is not made on behalf of a user, for example when Gatana refreshes the tool list in the background.

Environment Variables

The environment variables you configure in the Server Settings will be available in the process.env object in Node.js, and in os.environ in Python.

Persistent Storage

The filesystem is reset on every deployment, so files your code writes do not survive a restart. To keep files across restarts and redeploys, enable persistent storage for the server and write to the directory named in the GATANA_DATA_DIR environment variable.

Private Networks

Your code reaches the public internet only. To reach a database or an API on your own network, enable Tailscale for the server. It then connects to hosts on your tailnet over ordinary sockets, with no change to your code.

Package Management (npm)

You can use npm to manage your packages. Run npm install to install dependencies and make sure your node_modules folder is part of the code package.

Package Management (Python)

Add a requirements.txt to the root of your package. It is installed with uv every time the server starts, before your code is loaded. Installation has to finish within about a minute, so keep the list to what the tools need.

The runtime already provides mcp, pydantic, starlette and uvicorn; your requirements are installed into the same environment, so a pin that conflicts with these can stop the server from starting. Check the deployment logs when a start fails.

As an alternative that needs no network access at start, place pure-Python packages in a vendor folder in the root of your package (for example with pip install --target vendor -r requirements.txt). The folder is added to the import path.

Deployment and Output Logs

When you create, update a server, or upload a new source-package, Gatana will automatically deploy the server. You can click on Deployment logs in the Gatana App to see the status of the current revision deployment.

The output logs can be viewed in the server details view, under the Server Logs section. In the dropdown, select a revision, and the browser will begin stream the logs to your browser.

Environment and Isolation

The operating system is Debian Bookworm. Each local server is being executed in virual machine environment using Kata Containers, which provides a very strong layer of security and isolation.

On this page