Your First ThoughtGate Proxy
This tutorial covers the HTTP sidecar mode for server deployments. For local development with the CLI wrapper, see Wrap Your First Agent.
In this tutorial, you'll set up ThoughtGate to proxy requests between an AI agent and an MCP server, with governance rules that require approval for destructive operations.
What You'll Learn
- How to run ThoughtGate as a proxy
- How to write YAML governance rules
- How to configure Slack approvals
- How the SEP-1686 task flow works
Prerequisites
- Rust 1.87+ installed
- An MCP server running (or use the mock server)
- A Slack workspace with bot token (for approvals)
Step 1: Build ThoughtGate
Clone the repository and build the release binary:
git clone https://github.com/thoughtgate/thoughtgate
cd thoughtgate
cargo build --release -p thoughtgate-proxy
The binary will be at target/release/thoughtgate-proxy.
Step 2: Start a Mock MCP Server
For testing, ThoughtGate includes a mock MCP server:
cargo build --release -p thoughtgate-proxy --features mock --bin mock_mcp
MOCK_MCP_PORT=3000 ./target/release/mock_mcp
This server responds to MCP tool calls with mock data.
Step 3: Create a Configuration File
Create thoughtgate.yaml with governance rules:
schema: 1
sources:
- id: upstream
kind: mcp
url: http://localhost:3000
governance:
defaults:
action: forward # Allow by default
rules:
# Require approval for destructive operations
- match: "delete_*"
action: approve
- match: "drop_*"
action: approve
# Block admin tools entirely
- match: "admin_*"
action: deny
approval:
default:
adapter: slack
channel: "#thoughtgate-approvals"
timeout: 5m
Step 4: Configure Slack Integration
- Create a Slack app at api.slack.com/apps
- Add Bot Token Scopes:
chat:write— Post approval messagesreactions:read— Detect approval reactionschannels:history— Poll channel for reactionsusers:read— Resolve user display names
- Install to your workspace
- Copy the Bot OAuth Token
- Create a channel for approvals (e.g.,
#thoughtgate-approvals) - Invite the bot:
/invite @YourBotName
Step 5: Start ThoughtGate
Set environment variables and start the proxy:
export THOUGHTGATE_CONFIG=./thoughtgate.yaml
export THOUGHTGATE_SLACK_BOT_TOKEN=xoxb-your-token
./target/release/thoughtgate-proxy
You should see:
ThoughtGate listening on 0.0.0.0:7467
Admin server on 0.0.0.0:7469
Upstream: http://localhost:3000
Step 6: Test the Proxy
Send a tools/list request (should pass through):
curl -X POST http://localhost:7467 \
-H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "method": "tools/list", "id": 1}'
Send a safe tools/call request (should pass through):
curl -X POST http://localhost:7467 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {"name": "get_weather", "arguments": {"city": "London"}},
"id": 2
}'
Step 7: Trigger an Approval
Send a destructive tools/call request:
curl -X POST http://localhost:7467 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {"name": "delete_user", "arguments": {"user_id": "12345"}},
"id": 3
}'
With SEP-1686 async tasks, you'll receive a task ID immediately:
{
"jsonrpc": "2.0",
"result": {
"taskId": "tg_abc123xyz",
"status": "working"
},
"id": 3
}
Check your Slack channel — you should see a message like:
🔒 Approval Required: delete_user
Tool: delete_user
Principal: unknown
Arguments:
{
"user_id": "12345"
}
React with 👍 to approve or 👎 to reject
Task ID: tg_abc123xyz • Expires: 2024-01-15 10:30 UTC
Step 8: Poll for the Result
The agent polls for the result using tasks/get:
curl -X POST http://localhost:7467 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tasks/get",
"params": {"taskId": "tg_abc123xyz"},
"id": 4
}'
While waiting for approval:
{
"jsonrpc": "2.0",
"result": {
"taskId": "tg_abc123xyz",
"status": "working",
"statusMessage": "Waiting for human approval"
},
"id": 4
}
React with 👍 in Slack, then poll again:
{
"jsonrpc": "2.0",
"result": {
"taskId": "tg_abc123xyz",
"status": "completed"
},
"id": 4
}
Then fetch the result with tasks/result:
curl -X POST http://localhost:7467 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tasks/result",
"params": {"taskId": "tg_abc123xyz"},
"id": 5
}'
Step 9: Verify Health
Check the health endpoints on the admin port:
curl http://localhost:7469/health
curl http://localhost:7469/ready
What's Next?
- Learn how to write more complex policies
- Understand the 4-Gate architecture
- Deploy to Kubernetes for production