# How to Install an MCP Server from the Official Registry

> Source: <https://dev.to/nelson_amaya_16872e58232b/how-to-install-an-mcp-server-from-the-official-registry-589l>
> Published: 2026-09-01 08:00:05+00:00

SAFi comes already packaged to handle MCP tool installation, including its own

registry search, which looks for MCP servers published at the official MCP

registry, https://registry.modelcontextprotocol.io

To search for an MCP server is easy. For example, if you want to see whether

Smartsheet has published an MCP server in the registry, you search for it with

this command.

On Docker:

```
docker compose exec app python scripts/safi_mcp.py search smartsheet
```

On bare metal, first move into your SAFi directory and activate the virtual

environment, then run the same script:

```
cd /var/www/safi
source venv/bin/activate
python3 scripts/safi_mcp.py search smartsheet
```

Your prompt changes to show `(venv)`

once the environment is active. That is how

you know it worked. If you skip this step, Python will not find SAFi’s

dependencies and the script will fail before it does anything.

Every bare-metal command in this post assumes you have done those two lines

first. When you are finished, `deactivate`

puts your shell back.

Here is what came back:

```
io.github.christianclaudio/smartsheet-rm  (package, v1.0.2)  [pypi:mcp-server-smartsheet-rm@1.0.2]
    MCP server for Smartsheet Resource Management (10,000ft API) time tracking
    and scheduling.

Add one with:  scripts/safi_mcp.py add <name>
```

You may also see a few `faiss`

lines above that about AVX2 support. Ignore them.

That is the search index loading and it has nothing to do with MCP.

Four things in that one line tell you what you are dealing with.

** io.github.christianclaudio/smartsheet-rm** is the name you install by. Note

`io.github`

prefix: this is a server published by an individual developer,** (package, ...)** means it runs as a process on your own machine. The other

** v1.0.2** is the version, and it is pinned. Good. A server that updates can

** [pypi:...]** is the ecosystem.

`pypi`

means Python and SAFi will launch it`uvx`

. If it said `npm`

, SAFi would launch it with `npx`

. This detailBecause the registry knows the details, you install by name and SAFi works out

the command:

```
# Docker
docker compose exec app python scripts/safi_mcp.py add io.github.christianclaudio/smartsheet-rm

# Bare metal
python3 scripts/safi_mcp.py add io.github.christianclaudio/smartsheet-rm
```

**On a fresh install this will refuse, and that is the system working.** SAFi

checks that the launcher exists before it writes anything, so you get a message

naming the missing binary rather than a server that silently never starts:

```
'uvx' is not on PATH here, so this server could never start.
install uv (pip install uv), which provides uvx.
```

SAFi ships with Node and `npx`

, so `npm`

servers work out of the box. It does

not ship `uv`

, so a Python server needs it installed once:

```
# Bare metal, with the venv active
pip install uv

# Docker, add it to your Dockerfile and rebuild, otherwise it disappears
# on the next `docker compose up --build`
RUN pip install uv
```

That last point matters on Docker. Installing `uv`

inside a running container

works until the next rebuild, and then the server stops connecting for a reason

that looks like nothing to do with you. Put it in the image.

Then run the `add`

command again and it will save.

If your search returns a remote server rather than a package, there is nothing

to install locally. You point SAFi at the URL:

```
# Docker
docker compose exec app python scripts/safi_mcp.py add --url https://mcp.example.com/mcp \
    --key example --label "Example"

# Bare metal
python3 scripts/safi_mcp.py add --url https://mcp.example.com/mcp \
    --key example --label "Example"
```

Some remote servers support per-user sign-in, so each member connects their own

account and every call runs as them. Add `--auth oauth`

for those. To find out

whether a server offers it, ask the server:

```
curl -s https://mcp.example.com/.well-known/oauth-authorization-server
```

If that returns a document, use `--auth oauth`

. If it returns nothing, the server

takes one static credential shared by everybody.

Prefer per-user sign-in whenever the data belongs to a person, which is exactly

the case with project tools. Each member’s calls then inherit their own

permissions in the source system, appear under their own name in that system’s

audit log, and stop working when they are offboarded. One shared credential is

the right model for a shared resource, such as an internal pricing service, and

the wrong one for somebody’s projects.

Most servers need an API token. The server’s own documentation tells you which

environment variable it reads. This one wants `SMARTSHEET_RM_API_TOKEN`

.

Pass it as a reference, never as the value:

```
# Bare metal
python3 scripts/safi_mcp.py add io.github.christianclaudio/smartsheet-rm \
    --env SMARTSHEET_RM_API_TOKEN='${SMARTSHEET_RM_API_TOKEN}' \
    --env SMARTSHEET_RM_READONLY=true
```

Write `${SMARTSHEET_RM_API_TOKEN}`

literally, exactly as shown, braces and

quotes included. SAFi resolves it from the environment when it connects. The

real token goes in your `.env`

file, so the server list stays safe to copy, back

up and share with a colleague.

If the server offers a read-only switch, as this one does, turn it on for the

first install. You can relax it later once you know what the tools return.

This is the thing to understand before you give a tool to anybody.

A package server is a process on your machine holding one API token. Every

member’s agents act as whoever owns that token. Everyone sees whatever that

account can see, the source system’s audit log records that account rather than

the person who asked, and access does not end when someone leaves the company.

That is the correct model for a shared resource, such as an internal pricing

service or a company API. It is the wrong model for “show me my projects”,

because there is no “my” in it.

It does not matter that the service has user logins of its own. Smartsheet has

accounts and permissions, but a local server reaches it with one token, so all

of that collapses into a single identity on the way through.

Per-user sign-in exists in SAFi, and it is the answer to this, but it needs a

remote server that implements the MCP authorization specification. A local

process cannot take part. That is why some cards in the Tools Catalog have a**Sign in** button and others do not.

So choose deliberately. Use a service account with the narrowest permissions

that still make the tools useful, and enable only read tools to begin with. If

members genuinely need their own view of their own data, a shared token is not

the way to get there.

```
# Docker
docker compose exec app python scripts/safi_mcp.py check
docker compose exec app python scripts/safi_mcp.py list

# Bare metal
python3 scripts/safi_mcp.py check
python3 scripts/safi_mcp.py list
```

`check`

connects to every configured server and reports what it found. `list`

shows what is configured and where it came from.

You do not need to restart anything. Every write bumps a counter that the

running workers watch, so they pick up the new server on their next request.

**“Connected” does not mean authenticated.** When the Tools Catalog shows a

server as connected, it means the process started and answered SAFi’s question

about what tools it has. That is a local conversation between SAFi and that

process. Nothing has contacted the vendor yet.

So a server with a missing or wrong API token still shows as connected, with its

full tool list. You find out on the first real tool call, which comes back as an

authentication error from the vendor rather than as a connection problem in the

catalog. The green state is real, it just certifies less than it looks like: the

server is installed and running, not that it can reach anything.

This is the step people skip.

Installing a server puts it in Settings, Tools Catalog, where the server and its

tools are visible and **completely inactive**. Nothing can call them yet.

Discovery never grants anything.

Three steps make a tool usable:

Start with the read-only tools. Get those working, then decide about anything

that creates, updates, moves or deletes, and give that one a reviewer.

Two things to know before you enable a server you did not write. Its tool

descriptions are written by the publisher and go into the model’s context. And a

tool result becomes part of the governance record, so whatever it returns

inherits that record’s retention.

Installing an MCP server is done from the shell, by whoever controls the

deployment. That is the only way, for remote servers as much as for packages.

There is no admin screen for it and no API route, on purpose.

The browser installs nothing. Settings, Tools Catalog is where you see what is

installed, where members sign in to servers that support it, and where you

confirm what a server offers. It is not a place to add one.

This is deliberate rather than unfinished. Installing a server is a decision

about what code runs next to your data and what leaves your network, and whoever

makes it should already hold the rights that implies. On a deployment serving

several organizations, an admin who could install a server would be making that

decision for everybody on the host, not just for their own organization.

What an organization admin controls is the part that matters day to day: which

of the installed tools their policies enable, and which agents get them.

There are seven. Everything in this post is one of these.

```
scripts/safi_mcp.py search <term>     # find servers in the official registry
scripts/safi_mcp.py add <name>        # install one
scripts/safi_mcp.py list              # show what is configured, and where from
scripts/safi_mcp.py check             # connect to everything and report
scripts/safi_mcp.py disable <key>     # stop connecting, keep the definition
scripts/safi_mcp.py enable <key>      # start connecting again
scripts/safi_mcp.py remove <key>      # delete the definition entirely
```

`disable`

and `remove`

are the pair worth understanding. `disable`

keeps

everything you configured and just stops SAFi connecting to it, which is what

you want when a server is misbehaving and you are still working out why.`remove`

deletes the definition, so reinstalling means retyping the arguments,

the credentials and the org list.

Neither of them changes what a policy allows. If you remove a server, the tools

it provided simply stop existing, and the Will refuses any call naming them.

`check`

takes an optional `--key`

to test one server instead of all of them,

which is faster when you are debugging a single install.

The flags on `add`

you are most likely to need:

```
--url          a remote server's endpoint, instead of a registry name
--auth oauth   the server supports per-user sign-in
--command      the local program to run, for a package server
--args         comma-separated arguments for --command
--env          KEY=VALUE for the server process, repeatable
--key          the short name you will use in every other command
--label        the display name people see in the tool picker
--orgs         which organizations may use it, on a shared deployment
--force        install even if the check fails
```

One trap with `--args`

. If your first argument starts with a dash, use the

equals form or argparse reads it as another option:

```
--args="-y,@scope/package@1.2.3"
```

Use `--force`

sparingly. The check exists so a server that cannot start fails

now, while you are looking at it, rather than becoming a mystery next week.

Every SAFi install ships with a small demo MCP server so you can watch this work

end to end:

```
# Docker
docker compose exec app python scripts/safi_mcp.py add \
    --command python --args "/app/mcp/demo_server.py" --key demo --label "Demo Server"

# Bare metal
python3 scripts/safi_mcp.py add \
    --command python3 --args "/var/www/safi/mcp/demo_server.py" --key demo --label "Demo Server"
```

It has two tools. Install it, enable one of them in a policy, and watch an agent

get exactly that one. Then ask the agent to use the other one, and read the

governance record for that turn.

Run it and tell us where it fails.
