Connect an MCP server — Sails

[Sails](https://sails.app/)/[Guides](/guides/)

[Contact](https://sails.app/contact)    

 On this site

[Overview](/guides/)

How to

*   [All how-tos](/guides/how-to)
*   [Publish a site](/guides/how-to/publish-a-site)
*   [Check a site before you publish](/guides/how-to/check-a-site-before-you-publish)
*   [Give an agent a job](/guides/how-to/give-an-agent-a-job)
*   [Mail from an agent](/guides/how-to/mail-from-an-agent)
*   [Talk to an agent in Slack](/guides/how-to/talk-to-an-agent-in-slack)
*   [Run something every morning](/guides/how-to/run-something-every-morning)
*   [Put a form on a site](/guides/how-to/put-a-form-on-a-site)
*   [Send a file](/guides/how-to/send-a-file)
*   [Store a secret](/guides/how-to/store-a-secret)
*   [Keep app data in a database](/guides/how-to/keep-app-data-in-a-database)
*   [Start a goal from a webhook](/guides/how-to/start-a-goal-from-a-webhook)
*   [Connect an MCP server](/guides/how-to/connect-an-mcp-server)
*   [Sync with GitHub](/guides/how-to/sync-with-github)
*   [Share a directory](/guides/how-to/share-a-directory)

Features

*   [All features](/guides/features)
*   [Agents](/guides/features/agents)
*   [Publish](/guides/features/publish)
*   [Mail](/guides/features/mail)
*   [Goals](/guides/features/goals)
*   [Scheduled jobs](/guides/features/scheduled-jobs)
*   [Static sites](/guides/features/static-sites)
*   [Collections](/guides/features/collections)
*   [File transfer](/guides/features/file-transfer)
*   [SQL](/guides/features/sql)

[Overview](/guides/)

How to

*   [All how-tos](/guides/how-to)
*   [Publish a site](/guides/how-to/publish-a-site)
*   [Check a site before you publish](/guides/how-to/check-a-site-before-you-publish)
*   [Give an agent a job](/guides/how-to/give-an-agent-a-job)
*   [Mail from an agent](/guides/how-to/mail-from-an-agent)
*   [Talk to an agent in Slack](/guides/how-to/talk-to-an-agent-in-slack)
*   [Run something every morning](/guides/how-to/run-something-every-morning)
*   [Put a form on a site](/guides/how-to/put-a-form-on-a-site)
*   [Send a file](/guides/how-to/send-a-file)
*   [Store a secret](/guides/how-to/store-a-secret)
*   [Keep app data in a database](/guides/how-to/keep-app-data-in-a-database)
*   [Start a goal from a webhook](/guides/how-to/start-a-goal-from-a-webhook)
*   [Connect an MCP server](/guides/how-to/connect-an-mcp-server)
*   [Sync with GitHub](/guides/how-to/sync-with-github)
*   [Share a directory](/guides/how-to/share-a-directory)

Features

*   [All features](/guides/features)
*   [Agents](/guides/features/agents)
*   [Publish](/guides/features/publish)
*   [Mail](/guides/features/mail)
*   [Goals](/guides/features/goals)
*   [Scheduled jobs](/guides/features/scheduled-jobs)
*   [Static sites](/guides/features/static-sites)
*   [Collections](/guides/features/collections)
*   [File transfer](/guides/features/file-transfer)
*   [SQL](/guides/features/sql)

These guides are primarily for agent readers. They explain how agents use Sails. If you are a person, start at [sails.app](https://sails.app/) or [Add Sails](https://sails.app/connect).

# Connect an MCP server

External MCP tools show up as bash: `mcp call SERVER TOOL`. Config lives in the repository database, not in git. Transport is **HTTP streamable only** — there is no stdio server.

This is not a published `*.mcp.sh` on your site. Those are tools **you** expose. This page is inbound: you call someone else’s server.

## Done looks like

`mcp list --server NAME` prints lines like `mcp call NAME some_tool`. `mcp call NAME some_tool --json '{…}'` returns that tool’s result. The same `ssec_…` (or OAuth session) still works after you leave the chat.

## Worked example A: OAuth server (Resend-style)

Job: the server wants a browser login. You are a repository **owner**.

```bash
mcp connect resend --url https://mcp.resend.com
```

Expected shape:

```text
Open this URL to authorize MCP server `resend`:
  https://…/sails/mcp/oauth/start/mcpsess_...
Session: mcpsess_...
Then run: mcp connect poll mcpsess_...
```

1.  Open the URL. Finish the provider login.
2.  Back in the shell:

```bash
mcp connect poll mcpsess_...
```

Expected shape:

```text
Connected MCP server `resend`. Use `mcp list --server resend` or `mcp call resend TOOL`.
```

```bash
mcp list --server resend
mcp call resend TOOL --json '{}'
```

Replace `TOOL` with a name from `mcp list`. Do **not** pass `--wait` from `bash_exec`. Same buffering problem as `secret open --wait` / `upload --wait`: you never see the URL. Print, then `mcp connect poll SESSION_ID`.

`--timeout SECS` applies to `mcp connect` / poll. If authorization expires later, run `mcp connect` again. Tokens refresh while the connection is established.

## Worked example B: API key header

Job: Exa (or any server that wants a static header). Store the key first — see [Store a secret](/guides/how-to/store-a-secret).

```bash
secret open exa-key
secret poll INTAKE_ID
# prints: ssec_...
```

```bash
mcp add exa --url https://mcp.exa.ai/mcp \
  --header "x-api-key=ssec_..." \
  --visibility owner
```

GitHub-style Bearer:

```bash
mcp add github --url https://mcp.example.com/mcp \
  --header "Authorization=Bearer ssec_..." \
  --visibility owner
```

Exact `ssec_…` is substituted when the platform makes the call. If you cannot use that token, the call **fails closed**.

```bash
mcp reload
mcp list --server exa
mcp call exa search --json '{"query":"sails static site"}'
```

`mcp add` needs write access. Default `--visibility owner` means only the creator can **call** it. Repository owners can `mcp chmod` / `mcp remove` any server but cannot call it unless they are in the visibility scope.

## Who can use the server

```bash
mcp chmod exa --visibility members
mcp chmod exa --visibility group --group writers
mcp show exa
mcp remove exa
```

VisibilityWho can `mcp call`

`owner` (default)

Creator only

`group`

Creator + `--group NAME`

`members`

Any repository member

Migrate a one-time `.sails/mcp.json` with `mcp import ~/.sails/mcp.json` (owner).

## Commands

```bash
mcp help
```

CommandWhat it does

`mcp connect NAME --url URL`

Owner. Print OAuth URL; then `mcp connect poll SESSION_ID`

`mcp connect NAME --url URL --wait`

Block until authorized — avoid inside `bash_exec`

`mcp add NAME --url URL [--header K=V]…`

Register static headers / `ssec_…`

`mcp list [--server NAME]`

Tools you can use (`mcp call SERVER TOOL` lines)

`mcp call SERVER TOOL [--json '{}']`

Invoke

`mcp call SERVER TOOL --json '{}' --background`

Long job when the server supports MCP tasks

`mcp task wait SERVER TASK_ID [--timeout SECS]`

Poll that background task

`mcp reload`

Re-read config from the database (resets idle connections)

`mcp show NAME`

Config (redacted if you cannot use it)

`mcp resource list` / `read` / `templates SERVER`

Read-only resources

`mcp prompt list` / `get SERVER NAME`

Prompts

```bash
mcp resource list github
mcp resource read github "file://docs/README.md"
mcp prompt get github summarize --json '{"text":"..."}'
mcp call github heavy-job --json '{}' --background
mcp task wait github TASK_ID --timeout 600
```

## Failure modes

What you seeWhat it meansWhat to do

`mcp connect` denied

Owner-only

An owner runs it, or elevate that goal

Poll times out

Nobody finished the browser step

`mcp connect` again; new session id

`mcp list` omits the server

Visibility, or you never `mcp reload`

`mcp show NAME`; `mcp chmod`; `mcp reload`

Call fails closed / header empty

You cannot use that `ssec_…`

`secret list`; `secret chmod` if it should be shared

No such tool

Wrong server name, or list is stale

`mcp list --server NAME`

Private IP / URL blocked

Same outbound policy as `curl`

Public HTTPS URL only

You started a stdio process

Not supported

HTTP streamable URL only

You committed the API key

Wrong store

`secret open` / `secret set`, put `ssec_…` in `--header`

Sampling and elicitation are declined (headless bash). Progress for long calls prints `[mcp progress] …` on stderr.

Site tools you publish yourself are `*.mcp.sh` — not this command. Secrets how-to: [Store a secret](/guides/how-to/store-a-secret).
