MCP Servers
All common MCP Server configurations are supported in the platform:
- Externally hosted MCP Servers
- MCP Servers with stdio transport running in-cluster
- MCP Servers with HTTP transport running in-cluster
- MCP Servers running on the local host (great for development and testing)
- Building your custom MCP servers
For locally running servers, you have full isolation as they run in containers, scalability, resource constraints, secret management, configuration, and more.
Accessing Externally Hosted MCP Servers
This sample shows how to create an MCPServer resource in your cluster that talks to an remote server hosted on another platform. We’ll use the official GitHub MCP Server and connect it to an agent.
Deploy the remote MCP server with your GitHub Personal Access Token:
export GITHUB_PAT="your_token"
envsubst < samples/mcp/github-external-mcp-server-sample.yaml | \
kubectl apply -f -This will substitute your GitHub PAT into the configuration and deploy the remote MCP server.
The samples/mcp/github-external-mcp-server-sample.yaml file contains:
- A
SecretGitHub Personal Access Token - An
MCPServerthat routes authenticated requests to the externally hosted Official GitHub MCP Server (athttps://api.githubcopilot.com/mcp/)
Create Tool resources for the MCP server, then reference them in your agent:
apiVersion: ark.mckinsey.com/v1alpha1
kind: Agent
metadata:
name: github-remote-agent
namespace: default
spec:
prompt: You are a helpful AI assistant that interacts with GitHub resources.
azureRuntime: azureruntime-sample
tools:
- type: custom
name: github-get-repo # References Tool that connects to github-remote-mcpSee Tools for creating Tool resources that connect to MCP servers.
Now run a query - if any tool calls are needed they’ll be made and you’ll get the results:
fark agent github-remote-agent 'whats my most popular repo?'Your most popular repository (by far) is hacker-laws – 26 k stars ⭐️ - 🧠 “Laws, Theories, Principles and Patterns for developers and technologists.”
The Query object will show all tool calls made.
Hosting a stdio MCP Server In-Cluster
This sample shows how to create an MCP server, the example we’ll use is the official GitHub MCP Server . Note that like many MCP servers, this only supports stdio when running locally - but we’ll work around this with a simple trick - using a proxy such as mcp-proxy.
Now deploy the samples/mcp/github-stdio-mcp-server-sample.yaml resources:
export GITHUB_PAT="your_token"
envsubst < samples/mcp/github-stdio-mcp-server-sample.yaml | \
kubectl apply -f -This YAML file contains the resources needed for a scalable and ready-to-go MCP server:
- A
Secretthat contains the GitHub token used to talk to GitHub - A
Deploymentthat creates a singlePodthat runs the GitHub MCP server - A
Servicethat exposes the MCP server to the cluster - An
MCPServerresource that ensures the MCP server is easily discoverable by other resources
There are a few resources here, that provide some essential operational capabilities:

Now take a look at your MCP server:
kubectl describe mcpserver githubTo test the MCP server out, you can expose it to your local machine and then connect the MCP Inspector;
# Expose the GitHub MCP server to your local host.
kubectl port-forward svc/github 3000:80
# Now run the MCP Inspector, pointing at the forwarded MCP server.
# Make sure to use the "Streamable HTTP Transport".
npx @modelcontextprotocol/inspector http://localhost:3000/mcpYou’ll be able to list the tools using the MCP Inspector:

You can make specific tool calls - the GITHUB_PAT that you provided has been set as a Secret in the cluster and is used by the hosted tool:

Hosting an HTTP MCP Server In-Cluster
Note: This sample is work-in-progress, but very similar to the above, expect that the mcp-proxy is not even needed.
Accessing a Local MCP Server
Accessing an MCP Server which is running on the local host can be extremely useful for debugging or quick-testing. You can have your MCP Server live-reload, update tools, and so on, without your cluster needing to know any of the details.
In this example, we’ll run the “AI Developer Guide” MCP server in local development mode, with live-reload. First, clone the repo and start the local apis:
git clone git@github.com:dwmkerr/ai-developer-guide
cd ai-developer-guide
make site-build && make site-runThis runs the AI Developer Guide APIs locally on port 9090. Now we need to run its MCP server.
The server uses stdio transport. It is generally much safer to connect agents to MCP server s via HTTP, so that the MCP server doesn’t run on the same host as the agent (which is a security and stability risk), so we can put a HTTP proxy in front of the server.
Run mcp-proxy and start the MCP server in HTTP SSE mode:
# Go to the MCP server folder.
cd mcp/ai-developer-guide-mcp
# Install dependencies and install the MCP proxy.
npm install
pipx install mcp-proxy
# Run the MCP server, point it to the local APIs.
mcp-proxy --port 30001 --host 0.0.0.0 --stateless --allow-origin '*' --pass-environment -- npm run dev -- start --base-url http://localhost:9090You can quickly check the server with the MCP Inspector:
npx @modelcontextprotocol/inspector http://localhost:30001/mcpNow deploy a new Agent that has access to the MCPServer. The samples/mcp/local-mcp-server.yaml file can be used:
kubectl apply -f samples/mcp/local-mcp-server.yamlYou can check to see your new MCP server, tools and agents:
kubectl describe mcpservers
kubectl describe tools
kubectl describe agentsFinally, send a query to your new agent - the appropriate tools will be executed via the MCP server on your local host:
fark agent devguide-agent 'tell me what are the golden rules for ai development'Here are the “Golden Rules” we follow when developing or improving any AI-related development…
Building your own MCP Servers
Pre-requisistes
Software
- Python >=3.11
- Docker
Install mcp library
RUN pip install 'mcp[cli]==1.9.2'Create a Streamable HTTP MCP server in python
from mcp.server.fastmcp import FastMCP
# Create an MCP server
# Stateless server (no session persistence)
mcp = FastMCP("MulDivMathServer", stateless_http=True, host="0.0.0.0")
# Add a multiply tool
@mcp.tool()
def multiply(a: int, b: int) -> int:
"""Multiply two numbers"""
print(f"Received {a} * {b}")
return a * b
# Add a divide tool
@mcp.tool()
def divide(a: float, b: float) -> float:
"""Divide two numbers"""
print(f"Received {a} / {b}")
return a / b
if __name__ == "__main__":
mcp.run(transport="streamable-http")Run the python file, output should be:
INFO: Started server process [62149]
INFO: Waiting for application startup.
[07/15/25 10:49:06] INFO StreamableHTTP streamable_http_manager.py:109
session manager
started
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)Create a ARK MCP server resource
- Open the custom-mcp and run it
---
apiVersion: ark.mckinsey.com/v1alpha1
kind: MCPServer
metadata:
name: simple-calculator-remote-http
spec:
address:
value: "http://localhost:8000/mcp" # replace with full K8s service URL for deployment - "http://calc-service.default.svc.cluster.local" or "http://host.docker.internal:8000" or "http://host.minikube.internal:8000/mcp" if you use minikube
timeout: "60s"
transport: http
description: "Remote server for performing calculations such as mul and div."- Please apply the above yaml (update to address value to http://host.minikube.internal:8000/mcp if you use minikube)
- Shell verification
k get mcpserver
NAME TOOLS PHASE ADDRESS AGE
simple-calculator-remote-http 2 ready http://localhost:8000 23m
k get tools
NAME AGE
simple-calculator-remote-http-divide 23m
simple-calculator-remote-http-multiply 23m- Successful ARK controller logs
2025-07-15T11:06:47+05:30 INFO mcp tools discover {"controller": "mcpserver", "controllerGroup": "ark.mckinsey.com", "controllerKind": "MCPServer", "MCPServer": {"name":"simple-calculator-remote-http","namespace":"default"}, "namespace": "default", "name": "simple-calculator-remote-http", "reconcileID": "599baab9-adf7-4462-a848-b4e4762df65a", "server": "simple-calculator-remote-http"}
2025-07-15T11:06:47+05:30 INFO MCP client connected successfully {"controller": "mcpserver", "controllerGroup": "ark.mckinsey.com", "controllerKind": "MCPServer", "MCPServer": {"name":"simple-calculator-remote-http","namespace":"default"}, "namespace": "default", "name": "simple-calculator-remote-http", "reconcileID": "599baab9-adf7-4462-a848-b4e4762df65a", "server": "http://localhost:8000", "attempts": 1}
2025-07-15T11:06:47+05:30 INFO tool crd created {"controller": "mcpserver", "controllerGroup": "ark.mckinsey.com", "controllerKind": "MCPServer", "MCPServer": {"name":"simple-calculator-remote-http","namespace":"default"}, "namespace": "default", "name": "simple-calculator-remote-http", "reconcileID": "599baab9-adf7-4462-a848-b4e4762df65a", "tool": "simple-calculator-remote-http-multiply", "mcpServer": "simple-calculator-remote-http"}
2025-07-15T11:06:47+05:30 INFO tool crd created {"controller": "mcpserver", "controllerGroup": "ark.mckinsey.com", "controllerKind": "MCPServer", "MCPServer": {"name":"simple-calculator-remote-http","namespace":"default"}, "namespace": "default", "name": "simple-calculator-remote-http", "reconcileID": "599baab9-adf7-4462-a848-b4e4762df65a", "tool": "simple-calculator-remote-http-divide", "mcpServer": "simple-calculator-remote-http"}
2025-07-15T11:06:47+05:30 INFO mcp tools discovered {"controller": "mcpserver", "controllerGroup": "ark.mckinsey.com", "controllerKind": "MCPServer", "MCPServer": {"name":"simple-calculator-remote-http","namespace":"default"}, "namespace": "default", "name": "simple-calculator-remote-http", "reconcileID": "599baab9-adf7-4462-a848-b4e4762df65a", "server": "simple-calculator-remote-http", "count": 2}
2025-07-15T11:06:47+05:30 DEBUG events tools discovered: 2 {"type": "Normal", "object": {"kind":"MCPServer","namespace":"default","name":"simple-calculator-remote-http","uid":"ea1392fe-b2c0-43c2-a98a-b8e499303c35","apiVersion":"ark.mckinsey.com/v1alpha1","resourceVersion":"1713"}, "reason": "ToolDiscovery"}- Note: The above MCP server URLs will be localhost when you run controller with
cd ark && make devIf you are deploying the controller usingdevspace dev, there are 2 options:- Please deploy the MCP servers in the cluster and use the kubernetes service URLs like “http://calc-service.default.svc.cluster.local ”
- Run the servers locally and use
host.docker.internalto reach localhost from docker containers
Create agents to test the MCP server tools
- Open the math agent and run it
---
apiVersion: ark.mckinsey.com/v1alpha1
kind: Agent
metadata:
name: math-agent
spec:
prompt: You're an agent helping with calculations such as div and mul.
tools:
- name: simple-calculator-remote-http-divide
type: custom
- name: simple-calculator-remote-http-multiply
type: custom- Please apply the above yaml
- Shell verification
k get agents
NAME AGE
math-agent 23m
query agent/math-agent "7*8/5"
querying: agent/math-agent....
math-agent: The calculation 7 * 8 / 5 is equal to 11.2.Filesystem MCP Server with Session Management
The Filesystem MCP Server provides persistent file operations with annotation-driven workspace configuration. All sessions share a common base directory (/data/) with explicitly named workspaces configured via ARK query annotations.
Features
- Annotation-Driven Workspaces: Workspaces configured via
ark.mckinsey.com/mcp-server-settingsannotations - Shared Base Directory: All operations under
/data/with user-specified workspace subdirectories - Persistent Sessions: Session metadata survives pod restarts via file-based storage
- LRU Eviction: Automatically evicts least recently used sessions when limit reached
- Comprehensive Tools: Read, write, search, list, tree, edit files and directories
- Security: Path validation prevents traversal attacks and unauthorized access
Deploying the Filesystem MCP Server
Deploy using the provided Helm chart:
# From the repository root
cd mcp/filesystem-mcp
# Build the Docker image
make build
# Deploy with Helm
make install
# Verify deployment
kubectl get pods -l app.kubernetes.io/name=mcp-filesystem
kubectl get mcpserver mcp-filesystemThe deployment creates:
- A
Deploymentrunning the MCP server - A
Serviceexposing the HTTP endpoint - An
MCPServerresource for ARK integration - A
PersistentVolumeClaimfor session storage (10Gi by default)
Configuration Options
Configure via Helm values or environment variables:
# values.yaml
env:
PORT: "8080"
BASE_DATA_DIR: "/data" # Shared base directory for all workspaces
SESSION_FILE: "/data/sessions/sessions.json"
MAX_SESSIONS: "1000" # Maximum concurrent sessions
persistence:
enabled: true
size: 10Gi # Storage for workspace files
storageClass: "" # Use default storage classCreating an Agent with Filesystem Tools
The filesystem MCP server exposes tools with the mcp-filesystem- prefix:
apiVersion: ark.mckinsey.com/v1alpha1
kind: Agent
metadata:
name: filesystem-agent
spec:
prompt: |
You are a helpful assistant that can manage files and directories.
You can read, write, search, and organize files for users.
tools:
# File operations
- name: mcp-filesystem-read-file
type: custom
- name: mcp-filesystem-write-file
type: custom
- name: mcp-filesystem-edit-file
type: custom
# Directory operations
- name: mcp-filesystem-list-directory
type: custom
- name: mcp-filesystem-create-directory
type: custom
- name: mcp-filesystem-tree
type: custom
# Search operations
- name: mcp-filesystem-search-files
type: customApply the agent configuration:
kubectl apply -f samples/agents/filesystem.yamlExample Queries
Write and read files:
# Create a file and read it back in a single query
ark query agent/filesystem "Create a file called notes.txt with the content 'Meeting at 3pm', then read it back to confirm"Directory and file operations:
# Create directory structure and list contents in one query
ark query agent/filesystem "Create a directory called projects, then list all files and directories"
# Create file and show directory tree
ark query agent/filesystem "Create a file hello.txt with content 'Hello World' and show me the directory tree"File information:
# Create multiple files and list them
ark query agent/filesystem "Create test1.md with content 'markdown file', test2.txt with content 'text file', and readme.md with content 'readme', then list all files"
# Get file information
ark query agent/filesystem "Create data.txt with content 'important data', then get the file info for data.txt"Edit files:
# Create and edit a file in one query
ark query agent/filesystem "Create notes.txt with content 'Meeting at 3pm', then change '3pm' to '4pm'"Important: Each
ark querycommand creates a new isolated session. To perform multiple operations on the same files, include all operations in a single query as shown above.
Workspace Configuration via Annotations
Automatic Session Creation: Each ark query command creates a new MCP session. The ARK controller manages session IDs automatically.
Workspace Isolation: Workspaces are configured via annotations, not sessions. Without annotation configuration, queries use the shared /data/ base:
# Query without workspace annotation - files go to /data/
ark query agent/filesystem "Create file data.txt with content 'test data'"
# Different query, no workspace configured - same /data/ base
ark query agent/filesystem "Read data.txt"
# Result: File found (same base directory, no isolation)To achieve isolation, configure workspaces via annotations (see example below).
Session Persistence: While sessions persist across pod restarts, each ark query invocation currently creates a fresh session. Session persistence is primarily useful for:
- Direct MCP client connections (via port-forward)
- Custom applications using the MCP protocol
- Reconnecting with explicit session IDs
Example of session persistence with direct MCP connection:
# Port forward to MCP server
kubectl port-forward svc/mcp-filesystem-server 8080:8080
# Initialize session (save the Mcp-Session-Id from response headers)
curl -i -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}},"id":1}'
# Use that session ID for subsequent requests
curl -X POST http://localhost:8080/mcp \
-H "Mcp-Session-Id: YOUR_SESSION_ID" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"write_file","arguments":{"path":"test.txt","content":"data"}},"id":2}'
# Pod can restart, session persists
kubectl delete pod -l app.kubernetes.io/name=mcp-filesystem
kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=mcp-filesystem
# Reconnect with same session ID after restart
curl -X POST http://localhost:8080/mcp \
-H "Mcp-Session-Id: YOUR_SESSION_ID" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"read_file","arguments":{"path":"test.txt"}},"id":3}'LRU Eviction: When MAX_SESSIONS is reached, the least recently used session is automatically evicted:
# Configure lower limit for testing
env:
MAX_SESSIONS: "100"Sessions are evicted based on lastAccessed time, not creation time. Active sessions are preserved.
Available Tools
The filesystem MCP server provides these tools:
| Tool | Description |
|---|---|
mcp-filesystem-read-file | Read complete file contents |
mcp-filesystem-write-file | Write or overwrite a file |
mcp-filesystem-edit-file | Apply edits to existing file |
mcp-filesystem-create-directory | Create a directory |
mcp-filesystem-list-directory | List directory contents |
mcp-filesystem-list-directory-with-sizes | List with file sizes |
mcp-filesystem-tree | Show directory tree structure |
mcp-filesystem-move-file | Move or rename files |
mcp-filesystem-search-files | Find files by name pattern |
mcp-filesystem-get-file-info | Get file metadata |
mcp-filesystem-set-base-directory | Set base directory for file operations |
Advanced: Workspace Configuration with Annotations
Use annotations to configure explicit workspaces under /data/ for isolation and organization:
apiVersion: ark.mckinsey.com/v1alpha1
kind: Query
metadata:
name: project-a-query
annotations:
"ark.mckinsey.com/mcp-server-settings": |
{
"default/mcp-filesystem": {
"toolCalls": [
{
"name": "set_base_directory",
"arguments": {"path": "project-a"}
}
]
}
}
spec:
input: Create a file called data.txt with content 'Project A data'
targets:
- type: agent
name: filesystemThis creates and configures /data/project-a/ as the working directory. Different queries can use different workspace names for isolation:
# Query for Project B - isolated from Project A
annotations:
"ark.mckinsey.com/mcp-server-settings": |
{"default/mcp-filesystem": {
"toolCalls": [{
"name": "set_base_directory",
"arguments": {"path": "project-b"}
}]
}}Files in /data/project-a/ and /data/project-b/ are completely isolated.
Monitoring and Troubleshooting
View session information:
# Check MCP server logs
kubectl logs -l app.kubernetes.io/name=mcp-filesystem
# View session metrics
kubectl exec -it $(kubectl get pod -l app.kubernetes.io/name=mcp-filesystem -o name) -- \
cat /data/sessions/sessions.json | jq .Session logs show:
- Session creation:
[Session] Creating new session <uuid> - Workspace creation:
[Directory] Created/verified workspace directory: /data/<workspace> - Eviction:
[Session] Evicting oldest session: <uuid> - Deletion:
[Session] Deleting session: <uuid>
Check storage usage:
# View PVC status
kubectl get pvc -l app.kubernetes.io/name=mcp-filesystem
# Check disk usage
kubectl exec -it $(kubectl get pod -l app.kubernetes.io/name=mcp-filesystem -o name) -- \
df -h /dataLocal Development and Testing
For local development, use port-forward to access the MCP server directly:
# Port forward MCP server
kubectl port-forward svc/mcp-filesystem-server 8080:8080
# Test with MCP Inspector
npx @modelcontextprotocol/inspector http://localhost:8080/mcp
# Or test with curl
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc":"2.0",
"method":"initialize",
"params":{
"protocolVersion":"2024-11-05",
"capabilities":{},
"clientInfo":{"name":"test","version":"1.0.0"}
},
"id":1
}'Architecture
The filesystem MCP server uses a wrapper + adapter architecture with clean separation:
┌─────────────────────────────────────────┐
│ Wrapper (src/index.ts) │
│ - Session management (generic) │
│ - File-based persistence │
│ - LRU eviction policy │
│ - Transport handling │
│ - NO filesystem knowledge │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Adapter (src/adapters/filesystem/) │
│ - MCP protocol implementation │
│ - File operations │
│ - Path validation & security │
│ - Tool definitions │
│ - Workspace management │
└─────────────────────────────────────────┘