> ## 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.

# Build system

> How Horizon turns Python MCP and FastMCP source code into a deployable server.

When you deploy a hosted server, Horizon transforms your source code into
something it can run. For Horizon, that means taking a Python MCP or FastMCP
server from a specific repository commit, installing its dependencies,
inspecting its MCP surface area, and producing an immutable artifact that can be
deployed.

This page explains what happens during that transformation, from the moment
Horizon receives a build request to the moment the build artifact is ready for a
deployment.

<Info>
  Horizon builds Python MCP and FastMCP servers. The build system expects a
  Python entrypoint inside your repository.
</Info>

## Starting a build

A build begins when Horizon has a server revision to prepare. This can happen
when:

* you push to a connected Git repository
* Horizon creates or updates a preview deployment
* you redeploy from the dashboard
* another deployment action requests a new build

Before the build starts, Horizon resolves the request to an exact commit SHA.
That means a build is tied to immutable source code, even if it was triggered
from a branch name.

Horizon also gathers the server configuration needed to build that commit.

<CardGroup cols={2}>
  <Card title="Source" icon="code-branch">
    The repository and commit SHA Horizon should check out.
  </Card>

  <Card title="Entrypoint" icon="terminal">
    The Python file, and optional object name, that defines the server.
  </Card>

  <Card title="Dependencies" icon="box">
    A configured dependency file, or the dependency file Horizon discovers from
    the repository.
  </Card>

  <Card title="Environment" icon="sliders">
    Environment variables for the target deployment.
  </Card>
</CardGroup>

## Build environment

Each build starts from a clean checkout of the selected commit. Files generated
by previous builds are not reused, so anything your server needs must come from
the repository, dependency installation, or configured environment variables.

The build environment can access the network to download source code and install
packages. It is meant to package and inspect your server, not run application
workloads. Keep long setup scripts and import-time work small so builds stay
fast and predictable.

Builds time out after 15 minutes.

## Understanding your project

Before Horizon can package your server, it has to understand how your Python
project is laid out. It does this by resolving the entrypoint, Python version,
dependency file, and execution environment.

<Note>
  These rules are evaluated from the checked-out commit, so changing a branch
  after a build starts does not change that build.
</Note>

### Entrypoint

The entrypoint tells Horizon where your Python MCP or FastMCP server lives in
the repository. It must be a relative Python file path, optionally followed by
an object name:

```text theme={null}
main.py
server.py:app
services/weather/server.py:mcp
```

If an object name is provided after `:`, it must be a valid Python identifier.
Entrypoints cannot be absolute paths, cannot include `..`, and must point to a
Python file inside the repository.

If no entrypoint is configured, Horizon uses `main.py`.

### Python version

Horizon chooses the Python version from the server source tree. It starts in the
entrypoint directory and walks up toward the repository root, looking for a
version request in:

* `.python-version`
* `pyproject.toml` project metadata
* Python version requirements in `pyproject.toml`

Supported versions are Python `3.11`, `3.12`, `3.13`, and `3.14`. If Horizon
cannot find a supported version, it uses Python `3.12`.

### Dependencies

If you configure a dependency file, Horizon uses that file. The path must point
to a file inside the repository.

If you do not configure one, Horizon searches from the entrypoint directory up to
the repository root and uses the first supported file it finds:

| Search order | File                                                     | Install method                                     | Effect                                                                       |
| ------------ | -------------------------------------------------------- | -------------------------------------------------- | ---------------------------------------------------------------------------- |
| 1            | `requirements.txt`                                       | Installed as a dependency file.                    | Installs the listed requirements.                                            |
| 2            | `uv.lock` (only when a `pyproject.toml` sits next to it) | Installed with a frozen `uv sync`.                 | Uses the exact versions you committed.                                       |
| 3            | `pyproject.toml`                                         | Installed as a project when it has a build system. | Your package metadata and local modules are importable when the server runs. |

For reproducible builds, commit your lockfiles and make dependency versions as
explicit as your project needs.

### Environment variables

Environment variables configured for the deployment are included in the build
input and made available to the running server. Variable names must use uppercase
letters, numbers, and underscores, and must start with a letter or underscore.

The same configured values may be available during dependency installation and
server inspection. Avoid printing secret values from setup scripts or package
hooks.

## Build lifecycle

<Steps>
  <Step title="Install dependencies">
    Horizon installs FastMCP and your project dependencies into the server
    image. Dependency installation happens during the build, so package
    resolution errors, missing files, and incompatible Python versions surface
    before a deployment is promoted.

    In monorepos, keep dependency files close to the server entrypoint or
    configure an explicit dependency file path. Horizon resolves automatic
    dependency files by walking from the entrypoint directory toward the
    repository root.
  </Step>

  <Step title="Package the server">
    Horizon packages your source code, dependencies, and execution configuration
    into a deployable server artifact.
  </Step>

  <Step title="Inspect the MCP server">
    Horizon inspects the built server with FastMCP. Inspection loads the server
    entrypoint and produces a manifest of the tools, resources, and prompts
    exposed by the server. Import-time errors or side effects can fail the
    build, even if dependency installation succeeded.
  </Step>

  <Step title="Publish the artifact">
    If the build succeeds, Horizon publishes an immutable artifact and records
    the artifact reference on the build.
  </Step>
</Steps>

## Producing output

A successful build produces:

* an immutable server artifact
* a server manifest generated by FastMCP inspection
* build logs for the full build lifecycle
* a terminal build status

The artifact is what Horizon deploys. The manifest is what lets Horizon display,
route, and govern the MCP surface area of the server.

Failed builds do not produce a new deployable artifact.

## Finalizing deployment

Build success and deployment promotion are related, but they are separate steps.
After a build succeeds, Horizon can use the artifact to create or update a
deployment.

Whether the live endpoint changes depends on the deployment context. A preview
build may update only that preview deployment. A production build may be promoted
to the live deployment. A successful build by itself does not necessarily mean
the production endpoint changed.

<Tip>
  If a build succeeded but the endpoint did not change, check whether the build
  was for a preview deployment, whether promotion is required, and whether a
  newer build has already been selected for that deployment.
</Tip>

## Defaults and configuration

| Setting         | Default                                           | How to change it                                                     |
| --------------- | ------------------------------------------------- | -------------------------------------------------------------------- |
| Server language | Python MCP or FastMCP                             | Use a Python MCP/FastMCP server entrypoint                           |
| Entrypoint      | `main.py`                                         | Configure the server entrypoint                                      |
| Python version  | `3.12`                                            | Add `.python-version` or Python version metadata in `pyproject.toml` |
| Dependencies    | nearest `requirements.txt`, then `pyproject.toml` | Configure a dependency file path                                     |
| Build timeout   | 15 minutes                                        | Not configurable                                                     |

## Failures and logs

Build logs include lifecycle markers, dependency installation output, server
inspection output, and packaging output. Use them when a build fails because
they usually show the exact command or package step that failed.

<AccordionGroup>
  <Accordion title="Missing or invalid entrypoints">
    Horizon could not find the configured Python file, the path pointed outside
    the repository, or the object name after `:` was not a valid Python
    identifier.
  </Accordion>

  <Accordion title="Dependency file issues">
    The configured dependency file did not exist, pointed outside the
    repository, or dependency installation failed.
  </Accordion>

  <Accordion title="Environment variable issues">
    One or more environment variable names used unsupported characters. Names
    must use uppercase letters, numbers, and underscores, and must start with a
    letter or underscore.
  </Accordion>

  <Accordion title="Server inspection failures">
    FastMCP inspection could not load the server entrypoint or could not produce
    a server manifest. Import-time errors commonly appear here.
  </Accordion>

  <Accordion title="Timeouts">
    The build ran longer than 15 minutes. Large dependency installs, package
    hooks, network calls, or expensive import-time work can all contribute.
  </Accordion>
</AccordionGroup>

Platform-level failures happen when Horizon cannot complete the build workflow.
These failures are reported with a generic message and can usually be retried.

## What affects build speed

Build time usually depends on things inside your repository:

* dependency count and package size
* package install scripts
* large repositories or large files in the build context
* network calls during setup or import
* expensive work that runs when the server module is imported

Keep build-time work lightweight. Your server should do expensive application
work when it receives requests, not while Horizon is packaging and inspecting
it.

## Related docs

<CardGroup cols={2}>
  <Card title="GitHub" icon="github" iconType="brands" href="/git-integrations/github">
    Install the Horizon GitHub App and deploy servers from your repo.
  </Card>

  <Card title="Environment variables" icon="lock" href="/environment-variables">
    Configure deployment-scoped environment variables.
  </Card>

  <Card title="Deployments" icon="cloud-arrow-up" href="/deployments">
    Understand preview deployments, promotion, and rollback.
  </Card>

  <Card title="Compute model" icon="server" href="/platform/compute-model">
    Learn how Horizon runs the deployed build artifact.
  </Card>
</CardGroup>
