Skip to Content

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 Secret GitHub Personal Access Token
  • An MCPServer that routes authenticated requests to the externally hosted Official GitHub MCP Server (at https://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-mcp

See 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 Secret that contains the GitHub token used to talk to GitHub
  • A Deployment that creates a single Pod that runs the GitHub MCP server
  • A Service that exposes the MCP server to the cluster
  • An MCPServer resource that ensures the MCP server is easily discoverable by other resources

There are a few resources here, that provide some essential operational capabilities:

Diagram of Hosted MCP Server

Now take a look at your MCP server:

kubectl describe mcpserver github

To 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/mcp

You’ll be able to list the tools using the MCP Inspector:

Screenshot: Show MCP tools

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:

Screenshot: MCP Tool Call with Authentication

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-run

This 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:9090

You can quickly check the server with the MCP Inspector:

npx @modelcontextprotocol/inspector http://localhost:30001/mcp

Now 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.yaml

You can check to see your new MCP server, tools and agents:

kubectl describe mcpservers kubectl describe tools kubectl describe agents

Finally, 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

--- 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."
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 dev If you are deploying the controller using devspace 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.internal to reach localhost from docker containers

Create agents to test the MCP server tools

--- 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-settings annotations
  • 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-filesystem

The deployment creates:

  • A Deployment running the MCP server
  • A Service exposing the HTTP endpoint
  • An MCPServer resource for ARK integration
  • A PersistentVolumeClaim for 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 class

Creating 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: custom

Apply the agent configuration:

kubectl apply -f samples/agents/filesystem.yaml

Example 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 query command 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:

ToolDescription
mcp-filesystem-read-fileRead complete file contents
mcp-filesystem-write-fileWrite or overwrite a file
mcp-filesystem-edit-fileApply edits to existing file
mcp-filesystem-create-directoryCreate a directory
mcp-filesystem-list-directoryList directory contents
mcp-filesystem-list-directory-with-sizesList with file sizes
mcp-filesystem-treeShow directory tree structure
mcp-filesystem-move-fileMove or rename files
mcp-filesystem-search-filesFind files by name pattern
mcp-filesystem-get-file-infoGet file metadata
mcp-filesystem-set-base-directorySet 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: filesystem

This 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 /data

Local 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 │ └─────────────────────────────────────────┘
Last updated on