MCPServer
An MCPServer connects Ark to an external Model Context Protocol server, so the tools it exposes become available to agents. The controller resolves the server’s address, discovers its tools on a poll loop, and — for OAuth-protected servers — detects the authorization requirement and surfaces the discovered OAuth metadata in status. Agents don’t reference an MCPServer directly: it is wired in as MCPServer → Tool → Agent.
Spec
apiVersion: ark.mckinsey.com/v1alpha1
kind: MCPServer
metadata:
name: github-mcp
namespace: default
spec:
# --- Required ----------------------------------------------------------
address: # where the MCP server lives (ValueSource)
valueFrom:
serviceRef:
name: github-mcp-server
namespace: default
port: http
path: mcp
# --- Common optional fields --------------------------------------------
transport: http # http | sse (default 'http')
description: "GitHub repository operations via MCP protocol"
timeout: 30s # max duration for tool calls (default '30s')
pollInterval: 1m # tool re-discovery interval (default '1m')
headers: # extra HTTP headers sent to the server
- name: X-Api-Key
value:
valueFrom:
secretKeyRef:
name: github-mcp-credentials
key: api-key
# --- OAuth-protected servers (RFC 9728) --------------------------------
authorization:
tokenSecretRef:
name: notion-mcp-oauth # Secret holding OAuth tokens (same namespace)Fields
| Field | Type | Required | Description |
|---|---|---|---|
spec.address | ValueSource | yes | Where the MCP server is reachable. Supports a direct value, or valueFrom with serviceRef (in-cluster service), secretKeyRef, configMapKeyRef, or queryParameterRef. Resolved value is published to status.resolvedAddress. |
spec.transport | enum (http, sse) | yes | Transport protocol. Defaults to http. |
spec.description | string | no | Human-readable description of the server. |
spec.timeout | duration | no | Maximum duration for MCP tool calls to this server (30s, 5m, 10m). Defaults to 30s. Raise it for long-running operations. |
spec.pollInterval | duration | no | How often the controller re-discovers tools from the server. Defaults to 1m. |
spec.headers[] | list | no | HTTP headers added to requests to the server. Each entry has a name and a value (either a literal value or valueFrom — secretKeyRef, configMapKeyRef, queryParameterRef). |
spec.authorization | object | no | OAuth configuration for servers protected per RFC 9728 . When unset, the controller does not attempt to inject Authorization headers. |
spec.authorization.tokenSecretRef | object | yes (within authorization) | References the Secret holding OAuth tokens and client credentials. The Secret must exist in the same namespace as the MCPServer. |
spec.authorization.tokenSecretRef.name | string | yes | Name of the Secret. |
spec.authorization.tokenSecretRef.accessTokenKey | string | no | Secret key holding the access token. Defaults to access_token. |
spec.authorization.tokenSecretRef.refreshTokenKey | string | no | Secret key holding the refresh token. Defaults to refresh_token. |
spec.authorization.tokenSecretRef.expiresAtKey | string | no | Secret key holding the token expiry. Defaults to expires_at. |
spec.authorization.tokenSecretRef.clientIDKey | string | no | Secret key holding the OAuth client ID. Defaults to client_id. |
spec.authorization.tokenSecretRef.clientSecretKey | string | no | Secret key holding the OAuth client secret. Defaults to client_secret. |
Address
spec.address is a ValueSource, so the server location can come from a literal string or be resolved at reconcile time. The most common form is a serviceRef pointing at an in-cluster MCP service:
spec:
address:
valueFrom:
serviceRef:
name: github-mcp-server
namespace: default
port: http # port name; falls back to the service's only/first port
path: mcp # path component — typically 'mcp' or 'sse'
transport: httpFor a remote server, use a direct value:
spec:
address:
value: https://mcp.notion.com/mcp
transport: httpThe resolved endpoint is written to status.resolvedAddress.
Using MCP servers with agents
Agents never reference an MCPServer directly. A Tool of type: mcp binds a specific tool exposed by the server, and the agent references that Tool:
apiVersion: ark.mckinsey.com/v1alpha1
kind: Agent
metadata:
name: github-agent
spec:
prompt: You are a GitHub assistant with access to repository operations.
modelRef:
name: gpt-4-model
tools:
- type: mcp
name: github-get-repo # References a Tool that connects to this MCP serverThe controller discovers tools from the server every spec.pollInterval and publishes the count to status.toolCount. See Tools for creating Tool resources that connect to MCP servers.
OAuth-protected servers
Remote MCP servers that require OAuth (e.g. https://mcp.notion.com/mcp, GitHub Copilot MCP) respond to unauthenticated requests with HTTP 401 and a WWW-Authenticate: Bearer challenge per RFC 9728 and the MCP 2025-06-18 authorization spec .
When Ark detects this, it performs OAuth metadata discovery and populates status.authorization:
kubectl get mcpserver notion -o yamlstatus:
authorization:
state: Required # Required | DiscoveryFailed | Authorized
resource: https://mcp.notion.com/mcp
resourceName: Notion MCP (Beta)
authorizationServers: [https://mcp.notion.com]
authorizationEndpoint: https://mcp.notion.com/authorize
tokenEndpoint: https://mcp.notion.com/token
registrationEndpoint: https://mcp.notion.com/register
grantTypesSupported: [authorization_code, refresh_token]
conditions:
- type: Available
status: "False"
reason: AuthorizationRequired
message: "OAuth authorization required for Notion MCP (Beta)..."kubectl get mcpservers shows the state in the AUTH column:
NAME AVAILABLE TOOLS AUTH
notion False Required
github False Required
shell True 1state value | Meaning |
|---|---|
| (empty) | Server does not require OAuth. |
Required | Server returned 401 and metadata discovery succeeded. Ready for an authorize flow. A 401 from an authorized server (expiry, revocation, refresh failure) also collapses back to Required and emits a TokenRejected event. |
Authorized | The controller successfully listed tools using a Bearer token resolved from spec.authorization.tokenSecretRef. |
DiscoveryFailed | Server returned 401 but no usable RFC 9728 metadata was found. The CLI cannot drive an OAuth flow against this server. |
Prerequisites
Before running ark mcp auth login, spec.authorization.tokenSecretRef.name must be set on the MCPServer. ark-api returns 422 with an actionable message if the field is missing:
spec:
authorization:
tokenSecretRef:
name: notion-mcp-oauth # operator must declare thisToken writes go through ark-api
The interactive authorize flow (dynamic client registration, PKCE, token exchange, Secret write) is orchestrated by ark-api. The ark mcp auth login CLI is a thin client that POSTs to ark-api’s /api/v1/mcp-servers/{name}/auth/start and polls auth/status — it never touches the Kubernetes Secret directly. The CLI is the only client today; a dashboard authorize flow is not yet available. See MCP OAuth Callback for the operator-side callback URL configuration.
A successful flow leaves two annotations on the MCPServer:
metadata:
annotations:
ark.mckinsey.com/mcp-auth-authorized-by: cli # cli (dashboard not yet supported)
ark.mckinsey.com/mcp-auth-authorized-at: 2026-05-19T14:32:11Zauthorized-by is cli for this phase. A future per-user-tokens capability will surface the OIDC subject of the authenticated user here so multi-user clusters can distinguish whose credentials wrote the Secret. Until that ships, MCPServer tokens are effectively a per-server singleton — only one user can hold an authorized session at a time.
Status
status:
resolvedAddress: http://github-mcp-server.default.svc:8080/mcp
toolCount: 12
authorization:
state: Authorized
resource: https://mcp.notion.com/mcp
resourceName: Notion MCP (Beta)
resourceMetadataURL: https://mcp.notion.com/.well-known/oauth-protected-resource
authorizationServers: [https://mcp.notion.com]
scopesSupported: [read, write]
grantTypesSupported: [authorization_code, refresh_token]
registrationEndpoint: https://mcp.notion.com/register
authorizationEndpoint: https://mcp.notion.com/authorize
tokenEndpoint: https://mcp.notion.com/token
lastDiscovered: 2026-05-19T14:30:00Z
expiresAt: 2026-05-19T15:32:11Z
conditions:
- type: Available
status: "True"
reason: ToolsDiscovered
message: Discovered 12 toolsStatus fields
| Field | Description |
|---|---|
status.resolvedAddress | The actual address the controller resolved from spec.address. |
status.toolCount | Number of tools discovered from this MCP server. Shown in the TOOLS print column. |
status.authorization | OAuth 2.1 / RFC 9728 discovery metadata. Populated only when the server responds with HTTP 401; absent otherwise. |
status.authorization.state | One of Required, DiscoveryFailed, Authorized. Shown in the AUTH print column. Empty means authorization is not required. |
status.authorization.resource | Canonical URI of the protected MCP resource (RFC 9728 resource). |
status.authorization.resourceMetadataURL | resource_metadata URL parsed from the server’s WWW-Authenticate header (RFC 9728 §5.1). |
status.authorization.resourceName | Human-readable name of the protected resource (RFC 9728 resource_name). |
status.authorization.authorizationServers | Authorization server issuers the resource trusts (RFC 9728 authorization_servers). |
status.authorization.scopesSupported | OAuth scopes advertised by the authorization server (RFC 8414 scopes_supported). |
status.authorization.grantTypesSupported | OAuth grant types the authorization server supports (RFC 8414 grant_types_supported). |
status.authorization.registrationEndpoint | RFC 7591 dynamic client registration endpoint, when supported. |
status.authorization.authorizationEndpoint | OAuth 2.1 authorization endpoint (RFC 8414 authorization_endpoint). |
status.authorization.tokenEndpoint | OAuth 2.1 token endpoint (RFC 8414 token_endpoint). |
status.authorization.lastDiscovered | Timestamp of the most recent successful discovery probe. |
status.authorization.expiresAt | Absolute expiry time of the current access token, published for consumers that can get mcpservers but not secrets. |
status.conditions[] | Standard Kubernetes conditions. Available reflects readiness; Discovering reflects tool-discovery progress. |
Print columns
kubectl get mcpservers prints NAME, AVAILABLE (Available condition status), DISCOVERING (Discovering condition status), TOOLS (.status.toolCount), AUTH (.status.authorization.state), and AGE.
Related
- Tools — the
type: mcpTool that binds a server’s tool to an agent. - Agents — reference MCP tools via
spec.tools. - Query — runs work against agents that use MCP tools.
- MCP OAuth Callback — operator-side callback URL configuration for the authorize flow.