---
title: MCP Server | Developer Documentation
description: Run the LlamaParse MCP server on a self-hosted LlamaCloud deployment: enabling it in Helm values, giving it its own ingress hostname and certificate, and how API key authentication changes file uploads.
---

## Self-Hosting Documentation Access

This section requires a password to access. Interested in self-hosting? [Contact sales](https://www.llamaindex.ai/contact) to learn more.

Password:

Access Documentation

Self-Hosting Documentation Access Granted Logout

The [LlamaParse MCP server](/llamaparse/for-agents/mcp/index.md) exposes your deployment’s document-processing capabilities as tools any MCP-compatible agent can call. Self-hosted deployments run their own copy, so documents and API keys stay inside your network — agents talk to your MCP server, and it talks to your LlamaCloud backend over the cluster’s internal network.

## Enable the server

values.yaml

```
config:
  mcp:
    enabled: true
```

The MCP server is released from its own repository, so its version does not track the chart’s. The chart pins a published tag for you, and there is normally nothing to set. Override it — under the top-level `mcp` key, not `config.mcp` — if you mirror images into your own registry, or to move to a newer MCP release than the one your chart version pins:

```
mcp:
  image: your-registry.example/llamacloud-mcp:<tag>
```

A pod reporting `ImagePullBackOff` is almost always this: a cluster that cannot reach the default registry, and no mirrored tag set here.

By default the server calls the LlamaCloud backend at its in-cluster address, so there is nothing to configure for it to reach the API. Set `config.mcp.llamaCloudBaseUrl` only if you need it to call a different endpoint; a public host there must use `https`.

## Give it a hostname

MCP needs a hostname of its own — it answers on paths the web UI already uses, so it cannot share the main one. Point a second DNS name at the same ingress and set it as the public URL:

```
ingress:
  enabled: true
  host: cloud.your-company.example
  tlsSecretName: llamacloud-tls
  ingressClassName: nginx


config:
  mcp:
    enabled: true
    publicUrl: https://mcp.your-company.example
```

The chart adds an ingress rule sending that host to the MCP service. Two things it cannot do for you:

- `mcp.your-company.example` has to resolve to the same ingress as `cloud.your-company.example`.
- `llamacloud-tls` has to cover **both** names. One certificate serves both, so it needs a subject alternative name for each, or a wildcard.

The MCP rule shares the Ingress object with the web UI, so everything in `ingress.annotations` applies to it as well — and the reverse. A tool call like `parseFile` holds one HTTP request open for the whole job rather than returning a job id, so an ingress controller’s usual 60-second read timeout would cut a long Parse off while the job kept running and still spent the credits. So when the chart routes MCP on a hostname of its own, it also sets `nginx.ingress.kubernetes.io/proxy-read-timeout: "600"` on the Ingress. Raise it if your documents take longer:

```
ingress:
  annotations:
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
```

Setting that key in `ingress.annotations` always wins, including setting it to `""` to drop the default on a controller that does not read it. It has to be `ingress.annotations` specifically — `commonAnnotations` is overridden by the default rather than winning over it.

If you route MCP yourself, the chart sets nothing, so put an equivalent timeout on whatever rule you add. That is the configuration most likely to hit the 60-second cut, because nothing warns you. Re-read any rewrite or auth annotation you set for the UI before assuming it suits MCP.

Set `publicUrl` to a full URL including the scheme, and without a path — clients append their own (`/mcp`, `/parse/mcp`, and the rest). Wherever the chart derives a route from it, a value with no scheme is refused at render time rather than quietly producing no route. Once it is set, the web UI starts offering ready-made MCP client configuration, because it finally knows a URL your users’ agents can reach.

The rule is derived from `publicUrl`, so the chart routes **any** `publicUrl` host that differs from `ingress.host`. Two shapes leave it out of the way: a `publicUrl` on `ingress.host`, which it never routes because MCP answers on paths the web UI already owns, and `ingress.enabled: false`, where it renders no Ingress at all. In either case send what you route to the `llamacloud-mcp` service on port 80.

What you cannot do today is keep the chart’s Ingress enabled, advertise MCP on a hostname of its own, and route that hostname yourself — the chart adds its own rule for it, and two Ingresses claiming one host resolve in an order you do not control.

## Authentication

Self-hosted MCP servers authenticate with a LlamaCloud API key, which users pass as a bearer token:

```
{
  "mcpServers": {
    "llamaparse": {
      "url": "https://mcp.your-company.example/mcp",
      "headers": { "Authorization": "Bearer ${LLAMA_CLOUD_API_KEY}" }
    }
  }
}
```

One tool behaves differently as a result. `getUploadUrl` hands back a pre-signed URL for uploading a local file, and it is **not available to API key callers** — it would leave a standing credential for anyone who reached that URL. Agents should call `uploadFileByUrl` instead, with a URL the server can reach. Everything else works the same.

### Documents hosted inside your network

`uploadFileByUrl` refuses private and link-local addresses by default, which stops a caller using the server to reach things it should not. If the documents your users upload genuinely live on an internal host, allow it explicitly:

```
config:
  mcp:
    allowPrivateUploadHosts: true
```

Only turn this on if you are comfortable with callers naming internal addresses, since it is what the default protects against.

## Check it is working

Terminal window

```
kubectl -n <your-namespace> get ingress
kubectl -n <your-namespace> get pods -l app.kubernetes.io/name=llamacloud-mcp
```

Then confirm the server is reachable at the hostname you configured:

Terminal window

```
curl -sS https://mcp.your-company.example/api/healthz
```

A pod that starts and an endpoint that answers do not prove a client can use it — connect an agent and call a tool. The [MCP documentation](/llamaparse/for-agents/mcp/index.md) covers client configuration and the full tool list, and applies to your deployment with your own hostname in place of `mcp.llamaindex.ai`.
