Skip to content

Test Kavrynt

If you did the Quickstart, you have done steps 1 to 5: continue at step 6.

This runbook takes you from an empty Kubernetes cluster to a routed, policy-checked, audited MCP server in about 30 minutes, and then asks for your feedback. It works on any conformant Kubernetes cluster, local or managed. Everything comes from published, signed images and the Helm chart; you do not need to build anything.

Part What you check Time
1. Get a cluster Any Kubernetes 1.29+ 2–5 min
2. Install Kavrynt The chart installs and both components run 3 min
3. Register an MCP server The Operator validates it and discovers its tools 3 min
4. Call tools through the Gateway MCP routing and protocol checks 5 min
5. Open the console Servers, tools, and traffic in the UI 3 min
6. Test policy and audit A tool is denied and hidden; every call is audited 7 min
7. Try your own MCP server Your server, your tools 5 min
8. Clean up 1 min
9. Tell us what happened 5 min

A test without authentication

This runbook installs Kavrynt without MCP client authentication (gateway.auth.enabled=false) so you can test without an identity provider. Anyone who can reach the Gateway can then call every routed tool. Use a disposable or private cluster, and keep the Gateway Service internal. For a real install, see Install on Kubernetes.

1. Get a cluster

You need kubectl 1.29+ and Helm 3.14+, and nodes that can pull images from docker.io. Check your tools:

kubectl version --client
helm version --short

Use any cluster you can create resources in, including CRDs and ClusterRoles. If you have none, create a local one:

Platform Create a cluster
Kind kind create cluster --name kavrynt-try
k3d k3d cluster create kavrynt-try
minikube minikube start -p kavrynt-try
Docker Desktop, Rancher Desktop Enable Kubernetes in the settings
EKS, AKS, GKE, OpenShift, or your own Use your cluster's kubeconfig

Make sure kubectl points at the cluster you mean to use:

kubectl config current-context
kubectl auth can-i create customresourcedefinitions   # expect: yes

2. Install Kavrynt

export KAVRYNT_VERSION=1.0.0-beta.5

helm upgrade --install kavrynt oci://registry-1.docker.io/kavrynt/kavrynt \
  --version "$KAVRYNT_VERSION" \
  --namespace kavrynt-system --create-namespace \
  --set gateway.auth.enabled=false \
  --wait --timeout 5m

The chart installs the MCPServer CRD, the Operator, and the Gateway. Check both are running:

kubectl get deployments -n kavrynt-system
NAME               READY   UP-TO-DATE   AVAILABLE
kavrynt-gateway    1/1     1            1
kavrynt-operator   1/1     1            1

Check: leave out --set gateway.auth.enabled=false on a second, dry run. The install should stop with a message that names the authorization server settings and the opt-out:

helm template kavrynt oci://registry-1.docker.io/kavrynt/kavrynt --version "$KAVRYNT_VERSION" >/dev/null

3. Register an MCP server

sample-mcp-server.yaml runs a small MCP server in a new kavrynt-try namespace, with two tools: echo, and admin_reset, a harmless stand-in for a dangerous tool. It runs on the public python image, so nothing needs building. The file also registers the server with Kavrynt as an MCPServer.

kubectl apply -f https://raw.githubusercontent.com/kavrynt/kavrynt/develop/examples/try/sample-mcp-server.yaml
kubectl -n kavrynt-try rollout status deployment/sample-mcp-server --timeout=180s
kubectl -n kavrynt-try wait mcpserver/sample --for=condition=Ready --timeout=60s
kubectl -n kavrynt-try wait mcpserver/sample --for=condition=ToolsDiscovered --timeout=120s
kubectl get mcpservers -A
NAMESPACE     NAME     TRANSPORT   VERSION   REPORTED   READY   AGE
kavrynt-try   sample   http        1.0.0     1.0.0      True    31s

Checks:

  • READY is True: the Operator validated the server and the Gateway routes it.
  • REPORTED is the version the server itself reported.
  • The Operator listed the tools:
kubectl -n kavrynt-try get mcpserver sample -o jsonpath='{.status.tools[*].name}'; echo

Expected: echo admin_reset. - The API server rejects an unsafe endpoint at admission:

kubectl -n kavrynt-try apply --dry-run=server -f - <<'EOF'
apiVersion: kavrynt.io/v1alpha1
kind: MCPServer
metadata:
  name: unsafe
spec:
  version: 0.0.1
  transport: http
  endpoint: http://user:password@sample-mcp-server:8080
EOF

Expected: an error saying the endpoint must be a URL without credentials.

On OpenShift, if the sample pod is refused by the restricted security context constraint, delete the runAsUser: 65532 line from the manifest and apply it again.

4. Call tools through the Gateway

Forward the Gateway to your machine. If port 18080 is in use, pick another one and use it in the commands below.

kubectl -n kavrynt-system port-forward service/kavrynt-gateway 18080:8080 &
GW=http://127.0.0.1:18080/mcp/kavrynt-try.sample

Every server is routed at /mcp/<namespace>.<name>. Speak MCP to it:

# Start a session
curl -s -X POST "$GW" -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'

# List the tools
curl -s -X POST "$GW" -H 'Content-Type: application/json' -H 'MCP-Protocol-Version: 2025-06-18' \
  --data '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'

# Call one
curl -s -X POST "$GW" -H 'Content-Type: application/json' -H 'MCP-Protocol-Version: 2025-06-18' \
  --data '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"echo","arguments":{"message":"hello from Kavrynt"}}}'

Expected for the call: {"jsonrpc": "2.0", "id": 3, "result": {"content": [{"type": "text", "text": "hello from Kavrynt"}]}}.

Checks:

  • Malformed requests are refused before they reach the server. A JSON-RPC batch gets 400:
curl -s -X POST "$GW" -H 'Content-Type: application/json' --data '[{"jsonrpc":"2.0","id":4,"method":"tools/list"}]'
  • Every call is counted in Prometheus metrics, without arguments:
curl -s http://127.0.0.1:18080/metrics | grep '^kavrynt_gateway_mcp_requests_total'

Expect one line per method and tool, including outcome="protocol_error" for the batch. - The Gateway never passes your credentials on. A call with -H 'Authorization: Bearer my-token' still works, and the token never reaches the MCP server.

To use the Gateway from an MCP client such as an agent framework or IDE, point it at http://127.0.0.1:18080/mcp/kavrynt-try.sample with the Streamable HTTP transport.

5. Open the console

Open http://127.0.0.1:18080/ui/ and sign in as admin with the password the chart generated:

kubectl -n kavrynt-system get secret kavrynt-gateway-console-admin \
  -o jsonpath='{.data.password}' | base64 -d; echo

Checks:

  • Overview: the sample server is routed, and the console warns that MCP routes are not authenticated.
  • Servers and Tools: both tools appear with their input schemas.
  • Playground: call echo from the browser.
  • Traffic: your calls appear with method, tool, outcome, and latency, but never the arguments.

6. Test tool authorization policy and audit

Write a policy that denies every tool whose name starts with admin_:

cat > authz.rego <<'EOF'
package kavrynt.authz

default decision := {"allow": false, "reason": "admin tools are not allowed"}

decision := {"allow": true} if input.mcp_method != "tools/call"

decision := {"allow": true} if {
    input.mcp_method == "tools/call"
    not startswith(input.tool, "admin_")
}
EOF
kubectl -n kavrynt-system create configmap kavrynt-policy --from-file=authz.rego

Turn on the policy, the filtered tool list, and audit events:

helm upgrade kavrynt oci://registry-1.docker.io/kavrynt/kavrynt --version "$KAVRYNT_VERSION" \
  -n kavrynt-system --reset-then-reuse-values \
  --set gateway.policy.enabled=true --set gateway.policy.configMap=kavrynt-policy \
  --set gateway.policy.filterToolsList=true --set gateway.audit.enabled=true \
  --wait --timeout 3m

The Gateway restarts, so start the port-forward from step 4 again. Then:

# admin_reset is denied with a JSON-RPC error
curl -s -w ' HTTP %{http_code}\n' -X POST "$GW" -H 'Content-Type: application/json' -H 'MCP-Protocol-Version: 2025-06-18' \
  --data '{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"admin_reset","arguments":{}}}'

# echo still works
curl -s -X POST "$GW" -H 'Content-Type: application/json' -H 'MCP-Protocol-Version: 2025-06-18' \
  --data '{"jsonrpc":"2.0","id":6,"method":"tools/call","params":{"name":"echo","arguments":{"message":"still allowed"}}}'

# tools/list now shows only echo
curl -s -X POST "$GW" -H 'Content-Type: application/json' -H 'MCP-Protocol-Version: 2025-06-18' \
  --data '{"jsonrpc":"2.0","id":7,"method":"tools/list","params":{}}'

Expected for the denied call: {"error":{"code":-32001,"message":"denied by policy: admin tools are not allowed"},...} HTTP 200. MCP client SDKs show the agent that message. Releases up to 1.0.0-beta.3 answer HTTP 403 with the same body.

Audit: every MCP request is now one JSON line on the Gateway's output, chained with SHA-256. Install kavryctl and verify the chain:

curl -fsSL https://raw.githubusercontent.com/kavrynt/kavrynt/v1.0.0-beta.5/scripts/install.sh \
  | KAVRYNT_VERSION=v1.0.0-beta.5 sh
export PATH="$HOME/.kavrynt/bin:$PATH"

kubectl -n kavrynt-system logs -l app.kubernetes.io/name=gateway --prefix=false --tail=-1 \
  | kavryctl audit verify

Expected: ... every chain verifies.

To see the events themselves:

kubectl -n kavrynt-system logs -l app.kubernetes.io/name=gateway --prefix=false --tail=-1 \
  | grep '"type":"kavrynt.audit.v1"' | tail -3

They show the decision (deny for admin_reset, allow for echo), the tool, and the outcome, never the arguments.

To test client authentication, you need an OAuth 2.1 authorization server (for example Keycloak, Auth0, Okta, or Entra ID) that issues JWT access tokens. See Security: client authentication. Policies can then decide per caller (input.client_id, input.subject).

7. Try your own MCP server

Any MCP server that speaks Streamable HTTP works; stdio servers are not routed yet. Run it in the cluster as a normal Deployment and Service, then register it:

kubectl apply -f - <<'EOF'
apiVersion: kavrynt.io/v1alpha1
kind: MCPServer
metadata:
  name: my-server
  namespace: default
spec:
  version: 0.1.0
  transport: http
  endpoint: http://my-server.default.svc.cluster.local:8080/mcp   # your Service and MCP path
EOF
kubectl get mcpservers -A
kubectl describe mcpserver my-server

If ToolsDiscovered is False, the condition message says why. If the server needs an API key, see spec.credential in the Security guide.

8. Clean up

kill %1 2>/dev/null                    # the port-forward
kubectl delete namespace kavrynt-try
helm uninstall kavrynt -n kavrynt-system
kubectl delete crd mcpservers.kavrynt.io   # Helm keeps it on purpose
kubectl delete namespace kavrynt-system

Or delete the whole local cluster, for example kind delete cluster --name kavrynt-try.

9. Tell us what happened

Your feedback decides what we build next. Please open a tester feedback issue, even if everything worked. It helps to include:

  • your Kubernetes platform and version, and the Kavrynt version;
  • which steps worked and which did not, with the command and its output;
  • the output of kavryctl doctor --anonymize, which hashes names and never includes endpoints or Secret data, so it is safe to paste into a public issue;
  • for a problem, kubectl -n kavrynt-system logs deploy/kavrynt-gateway --tail=100, with secrets removed;
  • what you would need before using Kavrynt for real.

Report security vulnerabilities privately instead: security advisories.