> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/dsx-exchange/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/dsx-exchange/_mcp/server.

# Publish an MCP Server

Use this guide to make a Model Context Protocol (MCP) server available through DSX Agent Gateway. You can discover a Kubernetes Service by labels or configure a server with a static address.

## Understand the Request Path

Agent Gateway authenticates the caller, derives a tenant ID, and checks access to the selected MCP target. The gateway then forwards the original bearer token to the selected server.

Your MCP server remains responsible for authorizing the caller within its domain. Do not use caller-supplied tenant headers as an authenticated identity.

Use the [architecture guide](/dsx-exchange/agent-gateway/architecture) for the complete direct and bridged request paths.

## Prepare the MCP Server

Your server must implement MCP over Streamable HTTP at `/mcp` and advertise only capabilities that it supports.

Static upstreams can use the legacy Server-Sent Events (SSE) transport when required. Use Streamable HTTP for new servers.

## Choose an Upstream Type

Choose the upstream type that matches how the gateway reaches the MCP server:

| Upstream Type | Use When                                                               | Runtime Target Name          | Required Configuration                                                                           |
| ------------- | ---------------------------------------------------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------ |
| Selector      | The gateway can discover a Kubernetes Service by namespace and labels. | `<service-name>-<port-name>` | A Service namespace, matching labels, and a named port with `appProtocol: agentgateway.dev/mcp`. |
| Static        | The server has a stable HTTP or HTTPS address.                         | The `upstreams` map key.     | A complete address and an optional transport protocol.                                           |

Use the selector workflow for a Kubernetes Service. Use the static workflow when Service discovery cannot represent the target.

## Publish a Kubernetes Service

Create a named Service port for the MCP endpoint. Set the port's `appProtocol` to `agentgateway.dev/mcp`.

```yaml
apiVersion: v1
kind: Service
metadata:
  name: inventory
  namespace: agent-services
  labels:
    app.kubernetes.io/name: inventory-mcp
spec:
  type: ClusterIP
  selector:
    app.kubernetes.io/name: inventory-mcp
  ports:
    - name: mcp
      port: 3001
      targetPort: mcp
      appProtocol: agentgateway.dev/mcp
```

The Service selector must identify the Pods that serve `/mcp` on the named target port. Expose a separate health endpoint for Kubernetes startup, readiness, and liveness probes, and configure the probes before you publish the workload. The health endpoint is not an MCP method and does not replace `/mcp`.

## Configure Service Discovery

Add a selector entry under `upstreams` in the Agent Gateway values. The `serviceLabels` map matches labels on the Service, not labels only on its Pods.

```yaml
upstreams:
  inventory-selector:
    namespace: agent-services
    serviceLabels:
      app.kubernetes.io/name: inventory-mcp
```

The upstream entry name identifies the selector configuration. The discovered MCP target name is `<service-name>-<port-name>`. The example Service is named `inventory` and its port is named `mcp`, so it creates the target `inventory-mcp`. Service labels select the Service, but they do not contribute to the target name.

The bundled Agent Gateway controller watches all namespaces by default. If the deployment restricts discovery with `agentgateway.discoveryNamespaceSelectors`, include the MCP server namespace.

The gateway skips an unavailable target when it builds a combined catalog, so healthy targets remain available. Use the [operations guide](/dsx-exchange/agent-gateway/operations) to monitor discovery and programming status.

## Configure a Static Upstream

Use a static upstream when the MCP server has a stable HTTP or HTTPS address. The map key becomes the target name.

```yaml
upstreams:
  inventory-external:
    mode: static
    address: https://inventory.example.com/mcp
    protocol: StreamableHTTP
    requestTimeout: 10s
```

The address must include the scheme, host, and path. The chart accepts `http` and `https` schemes. An HTTPS address enables Transport Layer Security from the gateway to the server.

Do not include credentials, a query string, or a fragment in the static address. Use `protocol: SSE` only for a server that requires the legacy SSE transport.

## Configure Timeouts

`upstreamRequestTimeout` sets the default deadline for receiving response headers from every MCP target. The default is `5s`. Streaming response bodies can continue after this deadline.

Set `upstreams.<name>.requestTimeout` when one target needs a different response-header deadline.

```yaml
upstreamRequestTimeout: 5s

upstreams:
  inventory-external:
    mode: static
    address: https://inventory.example.com/mcp
    requestTimeout: 10s
```

Set a timeout that covers the server's normal time to produce response headers. Do not use the response-header timeout as the maximum duration for a streaming tool call.

## Grant Tenant Access

The configured operator tenant can access every MCP target. Every other authenticated tenant can access only targets in `auth.cel.unprivilegedTenantMCPs`.

Add the runtime target name, not the selector configuration name.

```yaml
auth:
  cel:
    operatorTenantId: oidc:operator
    unprivilegedTenantMCPs:
      - inventory-mcp
      - inventory-external
```

In this example, `inventory-mcp` is the discovered Service target. `inventory-external` is the static target.

The current allowlist applies to all authenticated non-operator tenants. Apply finer operation-level authorization in the MCP server after it validates the forwarded token.

## Understand Published Names

When the gateway aggregates multiple targets, it prefixes tools and prompts with their target name. The published name has the form `<target>_<name>`.

For example, the `lookup` tool from `inventory-mcp` is published as `inventory-mcp_lookup`. Clients must use the complete published name in `tools/call`. An unprefixed tool or prompt name does not select a default target.

Choose stable, lowercase, hyphenated Service, port, and static target names. Changing a target name also changes its published tool and prompt names.

## Apply the Configuration

For a selector upstream, apply the MCP server workload and Service before you update the gateway release.

```bash
kubectl apply --filename inventory-mcp.yaml
```

Apply your updated values by following the [deployment guide](/dsx-exchange/agent-gateway/deployment).

## Verify the Published Server

Confirm that the Service exposes an MCP port with the required application protocol.

```bash
export MCP_NAMESPACE='agent-services'
export MCP_SERVICE='inventory'

kubectl get service "${MCP_SERVICE}" \
  --namespace "${MCP_NAMESPACE}" \
  --output jsonpath='{range .spec.ports[*]}{.name}{"\t"}{.appProtocol}{"\n"}{end}'
```

The MCP port must show `agentgateway.dev/mcp`.

Confirm that the gateway is programmed after the release update.

```bash
kubectl wait \
  --namespace dsx-agent-gateway \
  --for=condition=Programmed \
  gateway/dsx-agent-gateway \
  --timeout=5m
```

Follow the [client quickstart](/dsx-exchange/agent-gateway/client-quickstart) to initialize a session and call `tools/list`. Confirm that an authorized tenant sees the expected prefixed tool names. Confirm that a tenant without target access does not see or invoke the target.

If the target is absent, check the following configuration.

* The Service labels match `upstreams.<name>.serviceLabels`.
* The selector uses the Service namespace.
* The Service port is named and sets `appProtocol: agentgateway.dev/mcp`.
* The controller watches the server namespace.
* `auth.cel.unprivilegedTenantMCPs` contains the runtime target name for non-operator access.
* The MCP server responds at `/mcp` before the response-header deadline.

Use the [configuration reference](/dsx-exchange/agent-gateway/configuration-reference) for every chart-owned setting and default.