Custom MCP servers
An MCP server from an archive, a GitHub repository or by address: what will run, your consent, and only read-only tools — as you mark them.
Besides integrations from the catalogue, the connector can run an MCP server you brought yourself: your vendor’s server, a server from an open repository, a server your team wrote for an internal system. The model gets its tools the same way as the tools of catalogue integrations — under the same rules and along the same path.
There is one difference: nobody has checked this code. Catalogue integrations are signed by us, go to the network only through the connector’s core and only to the addresses you specified. Your own server runs as is — under a separate OS user, but it goes to the network by itself, and the connector neither sees nor limits its requests. So you confirm such a connection explicitly, it is marked as custom everywhere, and which of its tools count as read-only is decided by you, not by the server itself.
Three ways to bring a server
All three are on the connector’s local page, in the “Your own MCP” section. The code ends up on the connector’s machine and nowhere else; the installation is confirmed by whoever sits at this page.
Archive. Drop a file onto the page — the connector unpacks it and reads how this server is started from its own packaging:
| Format | What is read |
|---|---|
.mcpb, .dxt — MCP Bundle | manifest.json: the command, arguments, environment variables, settings the server asks the user for (user_config), the list of tools. The dependencies are already in the bundle |
.zip, .tar.gz, .tgz with Node.js source | package.json: the entry point (bin, otherwise main); dependencies are installed with npm install (or npm ci if there is a lockfile); if the entry point needs building — npm run build |
| an archive with Python source | pyproject.toml: the server is installed into its own .venv environment and started with its script from [project.scripts] or as a module |
an archive with an MCP registry server.json | name, version, the environment variables the server requires; how to start it — from the package.json/pyproject.toml lying next to it, and without them — the registry package via npx/uvx |
The connector removes the top-level folder added by GitHub or an archiver itself. Links, absolute paths and escaping the directory are not allowed in an archive — such an archive is rejected as a whole rather than “cleaned up”.
GitHub repository. Paste the address — with a branch, tag or commit if needed
(…/tree/release-2). A private repository needs a token: it is used once for
the download and not saved. Then everything is as with an archive.
Remote server. A server that is already running somewhere at an address (the
Streamable HTTP transport). Give the address and, if the server requires it, an Authorization header —
it is kept in the connector’s secrets directory and sent with every request. No code reaches your
machine in this case. OAuth sign-in for remote servers is not supported.
What the machine needs
A Node.js server needs Node.js, a Python server needs Python 3 (or uv). The connector
looks for them in PATH the way the connection’s user will see them — an interpreter in
root’s home directory is not available to it. The page says what was found before you
drop anything; if what is needed is missing, the draft says so right away, and nothing is
saved. For Python servers the runtime the connector keeps for
catalogue integrations will also do.
Dependencies are installed as the connection’s user, in its directory, not as
the connector: npm install of someone else’s project runs someone else’s scripts, and that is the first place
where foreign code gets control. The installation log — word for word what
npm or pip printed — stays in the draft.
What will be run
The draft shows everything that will run: the command, arguments, environment variables, the settings the server asks for. This is a starting point, not a verdict: any field can be edited, and exactly what is written in the form will run.
- The packaging’s words
node,npx,python,uvxare replaced by the connector with what it found on this machine. ${__dirname}is the directory with the server’s code,${user_config.name}is the value of a setting from the form. This is how MCP Bundle writes its manifests; you can write it by hand the same way.- A setting the packaging marked as secret is stored as a secret — in a file with
0600permissions, with only a reference left in the settings file — and reaches the server only where there is a${user_config.…}for it. A secret the packaging said nothing about (a token for an API that the server reads from the environment) is added in the “Secrets in the environment” field — it reaches the server as a variable under its own name. - A missing setting is a start-up error naming it, not an empty string passed to the server.
The model cannot see a name in Cyrillic — the
slug goes into tool names, and for such a name it
is taken from the server’s name in its packaging (weather-mcp → weather_mcp).
Consent
A connection can be saved only with the checkbox ticked: you accept that the code has not been checked by
Buff, that the connector does not limit its network requests, and that the model will get only
the tools you mark as read-only. This is not an interface formality: the consent
is written into the settings file as the
accepted field, and without it the connector will not start such a connection — even if it was written
into the YAML by hand.
The server is isolated like any connection: its own process under its own OS user, its own working directory; it sees neither the connector’s key nor other secrets. Without the privilege to switch users (not root, Windows) your own servers, like catalogue integrations, do not start.
Read-only is your decision
Tools reach the model only if they read —
in “Ask” and in deep research, as with catalogue integrations.
For a catalogue integration, its signed manifest says which tool is read-only.
Your own server has no signature — only its own word (readOnlyHint in the tool’s
description), and the word of unchecked code about its own harmlessness is exactly what
the “reading allowed, writing not” rule must not believe.
That is why your own server’s tools are checkboxes. As soon as the server has started and declared them, tick those that only read and click “Save read marks”. The “Mark as the server suggests” button ticks the boxes according to its hint — check them with your own eyes. The marks apply immediately, without restarting the server; an unmarked tool is not given to the model at any stage.
Connectivity check and log
“Check connection” for your own server is a successful handshake: the name and version the server introduced itself with, and the number of tools. “Log” is what the server writes to standard error; a server that failed at start-up leaves the reason there.
What it does not do
- Events, polling, webhooks — none: your own server gives the model tools, and only them. Requests from a tracker are a job for catalogue integrations.
- MCP resources and prompts — are not read; only
toolsare used. - Updates — are not tracked. A new version is brought in the same way as the first; the old connection can be deleted.
- OAuth for remote servers and the outdated HTTP+SSE transport — are not supported.
- Limiting the network of foreign code — is not promised. That is exactly the reason for the mark.