Skip to main content

MCP Getting Started

Deploy your first MCP tool and invoke it from an AI agent β€” in 5 minutes.

No cluster? Use the hosted cloud instead

This guide covers the self-hosted path using kubectl and CRDs. If you just want to connect Claude to STOA's hosted cloud without any infrastructure, see MCP for Developers.

Prerequisites​

RequirementDetails
STOA PlatformRunning instance (Quick Start)
kubectlConfigured for your cluster
TenantAt least one active tenant (e.g., oasis)
KeycloakToken for authentication
Configure your environment

The examples below use environment variables. Set them for your STOA instance:

export STOA_API_URL="https://api.gostoa.dev"       # Replace with your domain
export STOA_AUTH_URL="https://auth.gostoa.dev" # Keycloak OIDC provider
export STOA_GATEWAY_URL="https://mcp.gostoa.dev" # MCP Gateway endpoint

Self-hosted? Replace gostoa.dev with your domain.

Step 1 β€” Define a Tool (CRD)​

Create a Tool custom resource that wraps an existing API as an MCP tool:

# my-tool.yaml
apiVersion: gostoa.dev/v1alpha1
kind: Tool
metadata:
name: get-weather
namespace: stoa-system
spec:
displayName: Get Weather
description: "Returns current weather for a given city. Useful for travel planning and logistics."
endpoint: https://api.weatherapi.com/v1/current.json
method: GET
inputSchema:
type: object
properties:
q:
type: string
description: "City name or coordinates"
required:
- q
auth: none
rateLimit: "60/min"
annotations:
readOnly: true
destructive: false
idempotent: true
openWorld: true

Apply it:

kubectl apply -f my-tool.yaml

Verify registration:

kubectl get tools.gostoa.dev -n stoa-system
NAME          DISPLAY NAME   ENDPOINT                                    REGISTERED
get-weather Get Weather https://api.weatherapi.com/v1/current.json true
Tool Annotations

Annotations help AI agents make safe decisions:

  • readOnly: Tool only reads data, no side effects
  • destructive: Tool deletes or modifies data irreversibly
  • idempotent: Calling twice produces the same result
  • openWorld: Tool interacts with external systems

Step 2 β€” Discover Tools​

Option A: REST API​

curl -s -X POST "${STOA_GATEWAY_URL}/mcp/v1/tools" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" | jq '.tools[].name'

Option B: MCP Protocol (JSON-RPC)​

curl -s -X POST "${STOA_GATEWAY_URL}/mcp/tools/list" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{}' | jq '.tools[] | {name, description}'

Both return the same tools. REST is simpler for scripts; JSON-RPC follows the MCP spec for AI agent integration.

Step 3 β€” Invoke a Tool​

REST Invocation​

curl -s -X POST "${STOA_GATEWAY_URL}/mcp/v1/tools/invoke" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "get-weather",
"arguments": {"q": "Paris"}
}' | jq '.content[0].text'

MCP Protocol Invocation (JSON-RPC)​

curl -s -X POST "${STOA_GATEWAY_URL}/mcp/tools/call" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "get-weather",
"arguments": {"q": "Paris"}
}'

Response:

{
"content": [
{
"type": "text",
"text": "{\"location\":{\"name\":\"Paris\"},\"current\":{\"temp_c\":12.0}}"
}
],
"isError": false
}

Step 4 β€” Connect via SSE (Streaming)​

For AI agents that need persistent connections, use Server-Sent Events:

# 1. Initialize a session
curl -s -X POST "${STOA_GATEWAY_URL}/mcp/sse" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": { "tools": {} },
"clientInfo": { "name": "my-agent", "version": "1.0" }
},
"id": 1
}'

Response includes:

{
"jsonrpc": "2.0",
"result": {
"protocolVersion": "2025-03-26",
"capabilities": {
"tools": { "listChanged": true },
"tokenOptimization": {
"supported": true,
"levels": ["none", "moderate", "aggressive"]
}
},
"serverInfo": { "name": "stoa-gateway", "version": "0.1.0" }
},
"id": 1
}

After initialization, send tool calls on the same endpoint:

# 2. Call a tool via SSE
curl -s -X POST "${STOA_GATEWAY_URL}/mcp/sse" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "get-weather",
"arguments": {"q": "London"}
},
"id": 2
}'
Protocol Versions

STOA supports MCP 2025-03-26 (latest) and 2024-11-05 (backward compatible). The gateway negotiates the highest mutually supported version during initialization.

Step 5 β€” Connect a ToolSet (External MCP Server)​

To connect an existing MCP server (e.g., a team's internal tools), use the ToolSet CRD:

# my-toolset.yaml
apiVersion: gostoa.dev/v1alpha1
kind: ToolSet
metadata:
name: internal-tools
namespace: stoa-system
spec:
upstream:
url: https://tools.internal.example.com/mcp
transport: sse
auth:
type: bearer
secretRef: internal-tools-token
timeoutSeconds: 30
tools: [] # Empty = expose all discovered tools
prefix: "int" # Tools become int_tool_name (avoids collisions)

Apply and check:

kubectl apply -f my-toolset.yaml
kubectl get toolsets.gostoa.dev -n stoa-system
NAME             UPSTREAM                                    TOOLS   CONNECTED
internal-tools https://tools.internal.example.com/mcp 5 true

The gateway automatically discovers tools from the upstream MCP server and exposes them.

What's Next​

TopicLink
Build custom tools with CRDsMCP Tools Development
MCP API referenceMCP Gateway API
MCP protocol deep-diveMCP Protocol Fiche
Manage subscriptionsSubscriptions Guide
Tool and ToolSet CRD specsKubernetes CRDs

Troubleshooting​

ProblemCauseFix
Tool not listedCRD not in stoa-system namespaceCheck namespace: kubectl get tools -A
401 UnauthorizedMissing or expired tokenRefresh Keycloak token
registered: false on ToolGateway hasn't syncedCheck gateway logs: kubectl logs deploy/stoa-gateway -n stoa-system
ToolSet connected: falseUpstream unreachableVerify URL + auth secret: kubectl describe toolset <name>
SSRF blocked on invokeBackend URL resolves to private IPUse public URL or container name (not localhost)