Remote MCP servers are reached the same way as any HTTPS API: a URL, optionally behind authentication (Module 4). What differs by deployment maturity and audience is how clients learn that URL in the first place, ranging from a hardcoded config value to full registry-based discovery, and what network path is appropriate depending on who is calling.
Direct Connection: The Common Enterprise Pattern
For most internal enterprise deployments, the simplest and most operationally transparent pattern is a direct, explicitly configured connection: the agent framework’s configuration lists the exact endpoint.
// Example client-side MCP server configuration
{
"mcpServers": {
"knowledge-base": {
"url": "https://mcp.acmecorp.internal/kb/v1",
"headers": { "X-Agent-API-Key": "${KB_MCP_API_KEY}" }
},
"crm": {
"url": "https://mcp.acmecorp.internal/crm/v2"
}
}
}
This requires no registry infrastructure at all, and is entirely adequate when the number of internal servers is small and changes infrequently, teams simply update the config when a URL changes.
Registry-Based Discovery
As the number of internal MCP servers grows past a handful, per-team or per-agent hardcoded lists drift out of sync. A registry (either the public MCP Registry, for servers meant to be externally discoverable, or a private internal mirror of the same pattern) centralizes this: servers register their name, endpoint, version, and capabilities once, and clients resolve a server by name at connection time rather than embedding a URL everywhere.
flowchart LR
subgraph without["Without a registry"]
T1["Team A config"] --> U1["hardcoded URL"]
T2["Team B config"] --> U2["hardcoded URL\n(possibly stale)"]
T3["Team C config"] --> U3["hardcoded URL\n(possibly stale)"]
end
subgraph with["With an internal registry"]
R["Internal MCP Registry\n(name -> endpoint, version, health)"]
TA["Team A"] --> R
TB["Team B"] --> R
TC["Team C"] --> R
R --> S1["kb-server v2.1.0"]
R --> S2["crm-server v1.3.0"]
end
style R fill:#f0fdf9,stroke:#0D9488,color:#0F172A
style S1 fill:#fff7ed,stroke:#f59e0b,color:#0F172A
style S2 fill:#fff7ed,stroke:#f59e0b,color:#0F172A
# A minimal internal registry client: resolve a server name to its current
# endpoint and required headers, looked up at connection time rather than
# baked into every team's static config.
import httpx
async def resolve_mcp_server(name: str) -> dict:
resp = await httpx.AsyncClient().get(f"https://mcp-registry.internal/servers/{name}")
resp.raise_for_status()
return resp.json() # {"endpoint": "...", "version": "2.1.0", "status": "healthy"}
Choosing a Network Exposure Model
Beyond how a client learns a server’s address lies a related, often under-considered question: what network path should actually reach the server, and this decision should be driven by who needs to call it. Purely internal servers (Lesson 8’s Kubernetes Service) never need to be reachable outside the cluster’s internal network at all, they should sit behind a ClusterIP Service with no external ingress, full stop. Servers that must be reachable by remote employees (a support agent running on a laptop, not inside the office network) typically sit behind a company VPN or zero-trust network access layer, so only devices already authenticated to the corporate network can reach the endpoint in the first place, before MCP-level authentication (Module 4) is even evaluated. Servers that must be reachable by an external partner organization’s own agents are the case requiring the most care: the correct pattern is a dedicated, narrowly-scoped ingress or API gateway route exposing only that specific server, with its own authentication and network policy, rather than granting the partner any path onto general internal infrastructure meant for trusted internal traffic. Conflating these tiers, most commonly, putting a partner-facing server on the same general VPN used for internal engineering traffic, is a common and avoidable source of excess network reachability that has nothing to do with MCP itself but everything to do with standard network segmentation discipline applied to this new class of service.
flowchart TD
Need{"Who needs to\nreach this server?"}
Need -->|"Only other services\ninside the cluster"| Internal["ClusterIP Service only,\nno external ingress"]
Need -->|"Company employees,\nremote or on-site"| VPN["Behind corporate VPN /\nzero-trust network access"]
Need -->|"An external partner\norganization's agents"| Gateway["Dedicated, narrowly-scoped\nAPI gateway route,\nisolated from internal VPN"]
style Need fill:#fff7ed,stroke:#f59e0b,color:#0F172A
style Internal fill:#f0fdf9,stroke:#0D9488,color:#0F172A
style VPN fill:#f0fdf9,stroke:#0D9488,color:#0F172A
style Gateway fill:#f0fdf9,stroke:#0D9488,color:#0F172A
Publishing to the Public MCP Registry
If a server is meant to be used by external partners or the broader community (an open-source tool wrapper, for instance), it can be published to the public MCP Registry with a server.json manifest, as introduced in Lesson 3. Enterprise-internal servers exposing proprietary data should not be published there; they belong in the private registry pattern above, which Module 6 builds out fully alongside versioning and namespacing across many servers.
With deployment, addressing, and network topology covered, Module 4 turns to securing these now-reachable servers: OAuth 2.1, API keys, and scoped per-tool permissions.