> ## Documentation Index
> Fetch the complete documentation index at: https://docs.horizon.prefect.io/llms.txt
> Use this file to discover all available pages before exploring further.

# External

> Bring an MCP server you already run into Horizon for managed discovery, access, and request observability.

export const externalServersPlans = "Developer and Enterprise";

<Note>
  External servers are available on {externalServersPlans}.
</Note>

External servers connect MCP servers that run outside Horizon to the Horizon
gateway. You keep the remote server in the runtime, network, and release process
it already uses. Horizon gives that server a managed entry, a protected MCP
endpoint, access policies, request logs, and a place in the same catalog as
hosted Horizon servers.

Use an external server when the server already exists, already has an owner, or
must stay where it runs today, but clients and administrators still need the
benefits of a shared Horizon access layer. The remote endpoint must accept HTTPS
connections initiated by Horizon. If it uses an IP allowlist, see
[Networking](/platform/networking#external-endpoints).

Horizon maintains [global connectors](/servers/global-connectors) for supported
provider-hosted MCP servers. Select one to use its preconfigured remote endpoint,
or create an organization connector for another endpoint.

<CardGroup cols={2}>
  <Card title="Adopt without redeploying" icon="plug">
    Register an existing MCP endpoint instead of moving its code into a Horizon
    hosted server first.
  </Card>

  <Card title="Give clients one front door" icon="route">
    Let clients call a Horizon endpoint while Horizon handles access checks and
    forwards accepted traffic to the remote server.
  </Card>

  <Card title="Keep remote credentials private" icon="lock">
    When the remote endpoint requires authentication, store its credentials in
    Horizon so clients do not need direct access to them.
  </Card>

  <Card title="Make remote traffic visible" icon="chart-line">
    Use Horizon request logs, clients, and users to understand how the external
    server is used through Horizon.
  </Card>
</CardGroup>

## When to use an external server

Choose an external server when the runtime should stay outside Horizon, but MCP
access should be managed through Horizon.

| Situation                                                                  | Why external helps                                                                                             |
| -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| The MCP server is already deployed and operated by another team            | Horizon can protect and expose the server without changing its ownership model.                                |
| The server must stay in a specific network or runtime environment          | Horizon forwards accepted requests while you keep the remote deployment where it is.                           |
| You want to standardize client access before migrating runtime             | Clients can move to a Horizon endpoint first; runtime migration can be a separate decision.                    |
| A sensitive remote server should not be connected to every client directly | Clients authenticate to Horizon, and Horizon uses the configured remote credentials when it forwards requests. |
| A remote server should become a backend for a remix server                 | Register it once, then include selected capabilities in a curated remix endpoint.                              |

If you want Horizon to build and run the server code, use a
[hosted server](/servers/hosted). If you want one endpoint made from selected
capabilities across multiple servers, use a [remix server](/servers/remix).

## User-visible contract

To clients, an external server behaves like a Horizon MCP server. The client
connects to the Horizon endpoint, authenticates to Horizon when required, and
discovers the capabilities that Horizon has learned from the remote server.

Horizon does not change the remote server's tool behavior. It governs the path
to the server, records gateway activity, and forwards accepted MCP requests to
the remote endpoint you configured.

| Concern               | Contract                                                                                                      |
| --------------------- | ------------------------------------------------------------------------------------------------------------- |
| Client endpoint       | Clients use the Horizon-served MCP URL, not the remote server URL.                                            |
| Capabilities          | Horizon discovers tools, resources, and prompts from the remote server.                                       |
| Access                | Horizon checks server and capability access before forwarding client traffic.                                 |
| Remote authentication | Horizon uses the configured remote credential, or no credential when the endpoint accepts anonymous requests. |
| Runtime behavior      | The remote server still executes the request and owns its application behavior.                               |
| Observability         | Horizon records gateway-level requests; remote runtime logs remain outside Horizon.                           |

<Info>
  If a request is rejected before it reaches the remote server, start with
  Horizon request logs. If the remote server receives the request and fails,
  debug the remote runtime where that server is operated.
</Info>

## Ownership boundary

External servers intentionally split platform responsibility from remote runtime
responsibility.

| Concern            | Horizon manages                                                                                       | You manage                                                                                                         |
| ------------------ | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| Client access      | Horizon endpoint, authentication, authorization, capability policies, and client-facing request logs. | Which users and service accounts should receive access.                                                            |
| Remote runtime     | Forwarding accepted requests to the configured endpoint.                                              | Deployment, uptime, scaling, dependencies, and application logs for the remote server.                             |
| Remote credentials | Secure storage and use of a configured remote credential.                                             | Whether the endpoint requires authentication, plus credential issuance, rotation timing, and remote-server policy. |
| Metadata snapshot  | Discovery, display, and policy attachment for remote capabilities.                                    | Keeping the remote capability lists valid, stable, and descriptive.                                                |
| Failures           | Gateway status, forwarding outcomes, and request metadata.                                            | Remote server errors, remote availability, and incorrect application responses.                                    |

This boundary is the main reason to choose an external server: Horizon becomes
the access and observability layer for MCP traffic without taking ownership of
the remote server's runtime.

## Request flow

<Steps>
  <Step title="Client calls the Horizon endpoint">
    The MCP client uses the Horizon server URL. If the server is protected, the
    client authenticates to Horizon.
  </Step>

  <Step title="Horizon checks access">
    The gateway identifies the caller and applies the external server's access
    settings, including capability policies when they are configured.
  </Step>

  <Step title="Horizon forwards to the remote server">
    Horizon sends the MCP request to the configured remote endpoint. It injects
    the configured remote credential, or sends no <code>Authorization</code>
    header when no authentication is selected.
  </Step>

  <Step title="The remote server handles the MCP call">
    The remote server executes the tool, resource, prompt, or template request
    and returns the MCP response.
  </Step>

  <Step title="Horizon returns the response">
    Horizon returns the remote response to the client and records gateway
    observability for the request.
  </Step>
</Steps>

## Authentication model

The MCP client signs in to the Horizon endpoint first. Horizon then presents a
separate OAuth token or API key to the remote MCP server, unless the remote
server accepts anonymous requests.

| Layer                                     | Credential                                                             | Failure boundary                                                                       |
| ----------------------------------------- | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| Client to Horizon                         | Horizon sign-in token or Horizon API key                               | The request stops at the gateway before the remote server is called.                   |
| Horizon to an authenticated remote server | Per-user OAuth token, per-user API key, or organization-shared API key | Horizon accepted the caller, but the forwarded request can fail remote authentication. |
| Horizon to a public remote server         | No credential                                                          | Horizon removes the caller's `Authorization` header before it forwards the request.    |

The external server's **Connection** page shows the current remote credential
status. For a public remote server, it shows that no authentication is required.
Users can authorize OAuth, add or revoke a personal token, and repair an expired
credential there. Server administrators can manage a shared key and its
injection format.

When the remote provider requires a pre-registered OAuth client, register
Horizon's callback URL exactly as
`https://horizon.prefect.io/oauth/external/callback`. Providers that support
Dynamic Client Registration receive the same callback URL during registration.

For OAuth discovery, Dynamic Client Registration, manual client fields, API-key
header formatting, and the complete setup flow, see
[External authentication](/platform/external-authentication).

## Register an external server

<Steps>
  <Step title="Choose external server">
    In Horizon, create a server and choose the external server type.
  </Step>

  <Step title="Enter the remote MCP URL">
    Provide the HTTP endpoint Horizon should call when clients use the server.
    The endpoint must be reachable by Horizon and speak a supported MCP HTTP
    transport.
  </Step>

  <Step title="Configure remote authentication">
    Choose no authentication, OAuth, or token authentication for the remote
    server. Select no authentication only when the endpoint accepts anonymous
    requests. Keep required remote credentials in Horizon instead of embedding
    them in MCP client setup.
  </Step>

  <Step title="Inspect capabilities">
    Horizon connects to the server, performs MCP discovery, and stores the
    metadata it will show in the dashboard and use for access policy setup.
  </Step>

  <Step title="Grant access">
    Configure server access and capability policies for the users and service
    accounts that should use the external server.
  </Step>
</Steps>

## How discovery works

When you register or refresh an external server, Horizon captures a snapshot of
the remote server's MCP metadata. Horizon initializes an MCP session with the
remote endpoint, then asks for tools, prompts, and resources. Those list requests
run independently, so one failed list method does not automatically discard
metadata returned by the others.

Horizon stores that snapshot with the external server metadata. The snapshot is
used for the dashboard, client setup surfaces, remix composition, and capability
policy setup.

If initialization fails, metadata capture fails. If every list response is empty
or unavailable, metadata capture fails because Horizon cannot tell whether the
server has no useful capabilities or discovery did not work. If at least one
list response succeeds, Horizon can store the metadata it received and leave the
missing capability types empty.

Runtime traffic still goes to the remote server. When a client calls
`tools/list`, `prompts/list`, or `resources/list` through the Horizon endpoint,
Horizon asks the remote server for the current list and applies access filtering
before returning the response. Playground, ChatMCP, and external clients still
depend on the runtime endpoint being able to reach the remote server.

<Info>
  If capability policy does not filter it, a newly added remote tool can appear
  in a runtime `tools/list` response before the dashboard snapshot has been
  refreshed. If capability policy is configured, refresh the external server
  metadata and update policy before expecting the new tool to appear.
</Info>

## Metadata and policy lifecycle

Treat remote metadata changes as compatibility changes. If the remote server
adds, removes, renames, or changes a capability schema, refresh discovery and
review policies before relying on the new shape in production clients.

| Remote change                | Horizon impact                                                                       | Recommended action                                                                    |
| ---------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- |
| Add a capability             | Runtime lists may show it before the dashboard snapshot or policy setup includes it. | Refresh metadata, inspect the capability, and update policy if it should be governed. |
| Remove a capability          | Clients or remixes that depend on it may fail or stop showing it.                    | Update clients, remixes, and policies before removing it from production use.         |
| Rename a capability          | Horizon treats the old and new names as different capabilities.                      | Update policies and client instructions that reference the old name.                  |
| Change input schema          | Existing client calls may become invalid.                                            | Test the updated schema in Playground before updating clients.                        |
| Change remote authentication | Horizon may fail to forward requests once the old credential stops working.          | Update the stored remote credential before rotating the old one out.                  |

## List and call failure behavior

An external server has one remote backend, so list behavior depends on that
backend's response.

| Event                                                                         | What clients see through Horizon                                                       |
| ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| The remote server does not support a list method                              | Horizon returns an empty list for that capability type.                                |
| The remote server is unavailable during a list request                        | Horizon can return an empty list for that capability type.                             |
| The remote server returns an authentication, authorization, or protocol error | The list request can fail instead of being treated as an empty list.                   |
| A caller is denied by Horizon capability policy                               | The capability is hidden from list responses and blocked when called directly.         |
| The remote server fails while handling a selected call                        | Horizon returns the remote failure to the client and records gateway request metadata. |

## Before clients use the server

An external server is ready for client use when Horizon can discover it, use
the expected remote authentication mode, and enforce the access boundary you
expect.

Check the server from Horizon before updating client configuration:

* Discovery shows the tools, resources, and prompts clients should see.
* Playground can call representative capabilities through the Horizon endpoint.
* A caller without access receives a gateway denial instead of reaching the
  remote server.
* Request logs show forwarded calls with the expected client and user metadata.
* The team that owns the remote server can debug runtime errors outside Horizon.

These checks matter because there are two systems in the request path. Horizon
can prove the gateway accepted, denied, or forwarded a request. The remote owner
still needs enough runtime visibility to explain what happened after forwarding.

## Troubleshooting

Start with where the request stopped. Horizon request logs show whether the
gateway denied, forwarded, or received an error from the remote server. If
forwarding succeeded, debug the remote runtime.

<AccordionGroup>
  <Accordion title="Horizon cannot discover capabilities">
    Confirm the remote URL is reachable from Horizon, uses a supported MCP HTTP
    transport, initializes successfully, and returns at least one non-empty
    capability list during discovery. If the remote endpoint requires
    authentication, confirm the remote credential configured in Horizon is
    current.
  </Accordion>

  <Accordion title="Clients receive 401">
    The client did not authenticate to Horizon successfully. Check the client
    credential and the external server's Horizon access mode.
  </Accordion>

  <Accordion title="Clients receive 403">
    The caller authenticated to Horizon, but server access or capability policy
    does not allow the requested operation. Review
    [Authorization](/platform/authorization).
  </Accordion>

  <Accordion title="The OAuth popup never completes">
    Allow popups for Horizon, then retry <code>Authorize with OAuth</code>. If
    the popup opens but returns an error, restart the OAuth flow so Horizon can
    create a fresh authorization request.
  </Accordion>

  <Accordion title="Horizon sign-in works but the remote server returns unauthorized">
    The client-to-Horizon credential is valid, but the Horizon-to-remote
    credential is missing, expired, revoked, or invalid. Reauthorize with OAuth
    or update the stored token for the external server.
  </Accordion>

  <Accordion title="Clients receive errors from the remote server">
    Check the remote credential configured in Horizon, then inspect the remote
    server's runtime logs. Horizon can show that forwarding occurred, but the
    remote server owns the application response.
  </Accordion>

  <Accordion title="Playground shows stale tools">
    Confirm Playground is connected to the intended target, then refresh its
    live capability list. Refresh external server metadata separately when the
    dashboard or capability policy setup is stale.
  </Accordion>

  <Accordion title="Horizon request logs succeed but the user sees bad data">
    Gateway forwarding succeeded. Inspect the remote server's application logs
    and the systems that remote server calls while handling the request.
  </Accordion>
</AccordionGroup>

## Related docs

<CardGroup cols={2}>
  <Card title="Gateway" icon="route" href="/gateway">
    Learn how Horizon routes and protects MCP traffic.
  </Card>

  <Card title="External authentication" icon="arrows-rotate" href="/platform/external-authentication">
    Configure whether the remote server needs a downstream credential and how
    Horizon supplies it.
  </Card>

  <Card title="Remix servers" icon="sliders" href="/servers/remix">
    Aggregate selected capabilities from multiple MCP backends behind one server.
  </Card>

  <Card title="Networking" icon="network-wired" href="/platform/networking">
    Configure reachability and source-address allowlisting for remote endpoints.
  </Card>
</CardGroup>
