AXONN Vantis logo
AXONN VantisAgentic eXperience, Open Neural Network,Complete Governance
Contact SalesEN
On this page← User Guides

DOCS · USER GUIDE

Connect tools to your agents — the MCP gateway

A hands-on guide to giving isolated agents external tools, safely. Aggregate multiple backend tool servers into one catalog behind the host MCP gateway, keep every credential on the host, and scope what each profile can see.

Last updated · 2026-07-28

1 · Overview

For an agent inside an isolated VM to use external tools — a wiki search, a code repository, a database — it needs that tool’s credentials. But the moment you put an API key inside the VM, isolation and zero-trust break down: if the agent is compromised, the key leaks with it. The MCP gateway exists to resolve exactly this dilemma.

The gateway is a single MCP server that runs on the host. It aggregates several backend MCP servers and exposes to the in-VM agent only one namespaced, profile-filtered catalog of tools. Backend credentials stay on the host and are never injected into a VM, under any circumstance.

The shape of it

There are three layers: agents inside isolated VMs at the top, the gateway on the host in the middle, and the backend servers that provide the actual tools at the bottom. A VM talks only to the gateway — never directly to the credentialed backends.

Isolated guests — identified by source IP

VM · Agent Ano secrets
VM · Agent Bno secrets
POST http://vantisso-gw:3001/mcpbridge-only
MCP GATEWAY · HostCredentials live here · one namespaced, profile-filtered catalog
Bearer
stdio
Backend · HTTPremote server
Backend · stdiolocal host subprocess
A VM talks only to the gateway; backend credentials never leave the host.

The gateway uses no login token. A caller’s identity is the request’s source IP: the gateway maps it to the VM, learns which profile that VM runs under, and uses that profile’s policy to decide what it may see.

What this guide covers

  1. Turn the gateway on — it’s off by default, so you enable it explicitly.
  2. A public HTTP backend — attach a no-auth server and confirm it works right away.
  3. Credentials — attach an authenticated backend while keeping the key on the host only.
  4. A local stdio backend — have the gateway run an MCP server process on the host itself.
  5. Per-profile scope — narrow which role sees which tools.
  6. Identity, audit, and rate limiting — record who called what, and cap abuse.
  7. Operate and troubleshoot — what takes effect when, and how to diagnose a missing tool.

Configuring the gateway — what to know

The gateway has no separate admin console: you configure it by editing two files on the host (servers.yaml and secrets.yaml) and calling the control-plane API. Registering backends and managing credentials is therefore an administrator (operator) task that requires direct host access. This guide assumes the simplest setup — one person working locally with API authentication disabled — so the example curl commands run as-is, with no credentials. Where API authentication is enabled, calling these configuration APIs requires that same administrator-level authorization.

2 · Turn the gateway on

First, Vantisso must be installed and running on your host, with at least one profile. A profile is the bundle of settings an agent runs under — provider, model, sizing, tools, and system prompt — and the trial already ships with a default profile, so this requirement is met out of the box. If Vantisso isn’t installed yet, follow the Getting Started guide to complete the install.

The gateway reads two config files: configs/mcp/servers.yaml (the list of backend servers) and configs/mcp/secrets.yaml (their credentials). You have no backends yet, so start with an empty server list.

# /opt/vantisso/configs/mcp/servers.yaml   — backends (added in later steps)
servers: []
# /opt/vantisso/configs/mcp/secrets.yaml   (mode 0600) — credentials, host-only
# key: token   (filled in step 4). Both files are gitignored — never commit them.

You enable the gateway with the VANTISSO_MCP_ENABLED environment variable. It is read once, at daemon startup, so a change takes effect only after you (re)start the daemon.

# systemd service: add a drop-in, then restart.
#   under [Service]:  Environment=VANTISSO_MCP_ENABLED=1
sudo systemctl edit vantisso
sudo systemctl restart vantisso

# Or, when running the daemon directly:
VANTISSO_MCP_ENABLED=1 sudo ./vantisso-daemon

Confirm it’s on through the control-plane API. With no backends yet, server_count is 0.

curl -s http://localhost:3000/config/mcp
# → {"enabled":true,"endpoint":"http://vantisso-gw:3001/mcp","server_count":0}

Two addresses, don’t mix them up

http://vantisso-gw:3001/mcp is the gateway address reachable only from inside a VM (it’s an alias for the bridge gateway IP 10.0.1.1, unreachable externally). You inspect and configure the gateway from the host via the control-plane API (`localhost:3000`). Every curl in this guide uses the latter.

3 · Your first tool: a public HTTP backend

Start with the simplest kind of backend — a public MCP server that requires no authentication. For this example we use DeepWiki, a public server that answers questions about public GitHub repositories. Add one entry to servers.yaml.

# /opt/vantisso/configs/mcp/servers.yaml
servers:
  - id: deepwiki            # stable identifier and default namespace
    namespace: deepwiki     # tool-name prefix in the catalog (defaults to id)
    transport: http         # MCP Streamable HTTP (the default)
    url: https://mcp.deepwiki.com/mcp
    profiles: []            # empty = every profile may use it

servers.yaml is read once at daemon startup, so after adding or editing a backend, restart the daemon.

sudo systemctl restart vantisso

Now check that the backend is registered and reachable. GET /config/mcp/servers returns the configured backends plus a live health probe (up). Credentials are never included — only has_credential.

curl -s http://localhost:3000/config/mcp/servers
# → [
#     {
#       "id": "deepwiki", "namespace": "deepwiki", "transport": "http",
#       "url": "https://mcp.deepwiki.com/mcp", "command": "",
#       "profiles": [], "has_credential": false,
#       "up": true, "error": ""
#     }
#   ]

How tools are cataloged and exposed

The gateway merges every backend’s tools into one catalog. To avoid name clashes it prefixes each tool name with its server’s namespace, using __ (a double underscore) as the separator. DeepWiki’s ask_question tool, for instance, appears in the catalog as deepwiki__ask_question.

When the gateway is on and a profile is allowed to use this backend, the gateway’s connection details are injected automatically into that role’s VM. The runtime inside the VM connects to it as an MCP client and adds the catalog’s tools to its own tool set. When the agent calls one, the gateway relays the request to the backend and returns the response.

Only the tool catalog reaches the VM. The backend’s real URL, and the credential you add next, are never visible inside the VM. The agent knows what a tool is, but not where it lives or how it authenticates.

4 · Keep credentials on the host

Real backends usually require authentication. You put the credential in secrets.yaml, not servers.yaml, and reference it by key name from servers.yaml. The token value itself never appears in the server config, nor in any API response.

# /opt/vantisso/configs/mcp/servers.yaml
servers:
  - id: my-api
    namespace: myapi
    transport: http
    url: https://api.example.com/mcp
    credential: my_api_token     # a key into secrets.yaml (not the token itself)
    profiles: []
# /opt/vantisso/configs/mcp/secrets.yaml   (mode 0600, gitignored)
my_api_token: sk-your-real-token-here

On every call to this backend the gateway automatically attaches an Authorization: Bearer <token> header. The token lives on the host and never travels to a VM.

If the credential key referenced in servers.yaml is missing or empty in secrets.yaml, the gateway disables itself over that error — a fail-safe, so it never starts without the authentication it was configured to use.

What reaches the VM, and what doesn’t

ItemHostInside the VM
Backend URLyesno
Credential tokenyesno
Namespaced tool catalog (name, description, schema)yesyes

This is the heart of zero-trust: even if the agent is compromised, the backend credential was never present in that environment, so there is nothing to leak.

5 · Run a local tool: a stdio backend

This step is an optional, advanced path. Most setups are fine with the HTTP backends above (steps 3–4); you only need this when you want to run a tool server as a local process on the host.

Instead of a remote HTTP server, you can have the gateway run an MCP server process itself on the host. With transport: stdio, the gateway spawns the command you name as a subprocess and speaks newline-delimited JSON-RPC over its stdin/stdout.

# /opt/vantisso/configs/mcp/servers.yaml
servers:
  - id: localtools
    namespace: localtools
    transport: stdio
    command: /usr/local/bin/my-mcp-server   # or a bare name on the daemon's PATH
    args: ["--flag"]                         # NEVER put secrets here (cmdline is public)
    credential: localtools_token             # optional; requires credential_env
    credential_env: MY_SERVER_TOKEN          # env var the token is injected as
    profiles: [leader]

A stdio backend is handled as follows.

  • Lazy spawn — if the subprocess exits abnormally it is respawned, but at most once every 5 seconds (a crash-loop cooldown).
  • Privilege isolation — when the daemon runs as root (production), the subprocess runs as an unprivileged user — nobody by default, overridable via VANTISSO_MCP_STDIO_USER.
  • Resource limits — kernel-enforced caps (rlimits) bound how many file descriptors it can open and processes it can spawn, so the tool server cannot exhaust host resources.
  • Dedicated work dir — each server gets /var/lib/vantisso/mcp-stdio/<id> as its working directory and HOME (kept across restarts, so it can cache).
  • Minimal environment — the child inherits only a minimal env (PATH, HOME, LANG, plus credential_env if set). It does not inherit the daemon’s own environment.

Credentials and the executable

Never put a secret in args; the command line is world-readable via /proc/<pid>/cmdline. If a backend needs a credential, supply it through credential/credential_env rather than args — the token is injected only as the child’s environment variable. The executable must also be readable and runnable by the unprivileged user, so place it somewhere like /usr/local/bin, not under /root. If the server is fetched on first run via npx/uvx, that can exceed the startup handshake budget — prefer pre-installing it and using an absolute path.

HTTP backendstdio backend
transporthttp (default)stdio
Where it runsa remote servera local subprocess on the host
Required fieldurlcommand
Credential injectionAuthorization: Bearer headerchild env var (credential_env)

6 · Scope tools per profile

Not every agent needs every tool. Exposing only the tools a role actually needs improves security, and it shortens the tool list sent with each request, which lowers call cost. You control tool exposure with the three filters below, and the effective result is the intersection of their conditions: a tool is included only if all three filters allow it.

FilterWhereEffectTakes effect
profiles:servers.yamlwhich profiles may use this server (empty = all)restart
tools_allow / tools_denyservers.yamlwhich tools within a server to expose (allow-list) or hide (deny-list)restart
Profile bindingPUT /config/profiles/{name}/mcpthe set of servers a profile actually usesimmediately

First, in the servers.yaml file, you can set two things: which profiles may use a server (profiles:), and which of the server’s tools to expose or hide (tools_allow / tools_deny). Both live in the config file, so a change takes effect only after you restart the daemon.

  - id: deepwiki
    transport: http
    url: https://mcp.deepwiki.com/mcp
    profiles: [leader, researcher]        # only these profiles may use it
    tools_allow: [read_wiki_structure, ask_question]   # expose only these tools

Then, at runtime, you can narrow a profile’s actual set of servers further through an API binding. It applies on top of servers.yaml’s profiles: as an intersection, so it can never open a server that wasn’t already allowed — it only shrinks the allowed set. Unlike the config file, it is re-read on every request, so it takes effect immediately, with no restart.

# Restrict the 'researcher' profile to just deepwiki:
curl -s -X PUT http://localhost:3000/config/profiles/researcher/mcp \
  -H 'Content-Type: application/json' \
  -d '{"servers":["deepwiki"]}'
# → {"servers":["deepwiki"],"bound":true}

# Read it back:
curl -s http://localhost:3000/config/profiles/researcher/mcp
# → {"servers":["deepwiki"],"bound":true}

Binding rules

  • No binding (bound: false) — the profile follows servers.yaml as-is.
  • Empty list ({"servers":[]}) — the profile uses no MCP servers at all.
  • Unknown server — an id not in the config is rejected with 400, and nothing is saved.
  • Zero usable servers — if a profile ends up with no allowed server, that role’s VM does not even connect to the gateway.

7 · Identity, audit, and rate limiting

How the caller is identified

The gateway has no login token; a caller’s identity is the request’s source IP. The gateway looks the source IP up in the VM registry to learn the VM and its profile, then decides access with that profile’s policy. For this identity to be trustworthy, IP spoofing must be prevented: VANTISSO_NET_ANTISPOOF (on by default) pins each VM’s bridge port to its assigned MAC and IP, so one VM cannot impersonate another’s address.

Audit log

Every call through the gateway — tool, resource, or prompt — is recorded. Each is written as a single line to {workDir}/audit/mcp.jsonl and also counted as a metric. To protect privacy, the actual arguments and results are never stored — only metadata is kept.

{"ts":"2026-07-21T09:12:03Z","vm":"vm-3f9a","profile":"researcher","server":"deepwiki","kind":"tool","tool":"ask_question","outcome":"ok","ms":412}

outcome is one of four values: ok (success), forbidden (denied by policy), rate_limited (over the rate cap), and fail (a backend error).

Rate limiting

To cap runaway loops or cost spikes, you can set a per-minute budget for each (VM, backend) pair. VANTISSO_MCP_RATE is the allowed calls per minute (0 = unlimited, the default), and VANTISSO_MCP_BURST is the token-bucket burst (defaults to the rate when unset). A call over budget is returned as a JSON-RPC error, metered as rate_limited, and can be retried shortly after.

# 60 calls/min per (VM, backend), bursts up to 10.
# Rate settings are read at startup, so set them then restart the daemon.
#   Environment=VANTISSO_MCP_RATE=60
#   Environment=VANTISSO_MCP_BURST=10

8 · Operate and troubleshoot

What takes effect when

What you changedHow it applies
servers.yaml · secrets.yaml (add/edit a backend or credential)restart the daemon
VANTISSO_MCP_* environment variablesrestart the daemon
Profile binding (PUT …/mcp)immediately (no restart)

When a tool doesn’t show up

  1. Is the gateway on? Check GET /config/mcp for enabled: true and server_count greater than 0.
  2. Is the backend reachable? In GET /config/mcp/servers, check up: true, or read error if not.
  3. May the profile use that server? Confirm it’s in the server’s profiles: list, or that the list is empty.
  4. Has a binding narrowed it away? Check GET /config/profiles/{name}/mcp — because it intersects, a binding may have excluded the server.
  5. Is the tool hidden by tools_allow/tools_deny?
  6. Does the tool name carry its namespace prefix (<namespace>__<tool>)?

Common config mistakes

  • A __ inside a namespace — it clashes with the separator and is rejected.
  • A duplicate id or namespace — the gateway disables itself.
  • command/args on an http server, or url on a stdio server — config validation fails.
  • For stdio, credential without credential_env — the two must be set together.
  • Editing servers.yaml without restarting the daemon — nothing changes.

Stopping the daemon shuts the gateway down in order: the listener first stops accepting new requests and lets any in-flight calls finish, and then all the stdio subprocesses it had spawned are terminated.

Next steps

See the API Reference for the details of the GET endpoints used to inspect your setup. For a commercial license or an extended evaluation, contact sales@ax-onn.com.

© 2026 AXONN Vantis Inc. All rights reserved.