A minimal Helio configuration that logs everything, denies destructive operations, and allows everything else. This is the best starting point for understanding how Helio works.
- Transparent MCP proxying (all tool calls pass through Helio)
- Annotation-based policy matching (
readOnlyHint,destructiveHint) - Deny rules with structured self-repair feedback
- The Helio dashboard for real-time visibility
- Audit trail recording every tool call
- Node.js 22+
jq(optional) for pretty-printing JSON command output. If unavailable, remove| jqfrom curl commands.- Build the proxy from the repo root:
pnpm install && pnpm buildcd examples/basic
pnpm startThis starts:
- A local MCP echo server on port 8080 (5 demo tools)
- The Helio proxy on port 3000
- The dashboard on port 3100
Note: All examples use the same ports (8080, 3000, 3100). Stop any running example before starting another.
If
jqis not installed, remove| jqfrom the command snippets below.
curl -s -X POST http://localhost:3000/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jqcurl -s -X POST http://localhost:3000/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_weather","arguments":{"city":"London"}}}' | jqThis passes through because get_weather has readOnlyHint: true and matches the allow-reads rule.
curl -s -X POST http://localhost:3000/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"send_email","arguments":{"to":"alice@example.com","body":"Hello"}}}' | jqNo rule matches send_email specifically, so it falls through to default: allow.
curl -s -X POST http://localhost:3000/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"delete_record","arguments":{"id":"123"}}}' | jqThis is blocked because delete_record has destructiveHint: true and matches the block-destructive rule. The response includes structured feedback explaining why the action was denied.
Navigate to http://localhost:3100 to see (no login prompt in this local open-mode example):
- Feed: Real-time stream of all tool calls and policy decisions
- Audit: Searchable log of every action with full details
- Approvals: Pending approval queue (empty in this example)
- Limits: Rate and spend limit status (none configured in this example)
- Analytics: Charts showing action volume, decision breakdown, and top tools
version: '1'Required. Currently always "1".
upstream:
url: 'http://localhost:8080/mcp'
transport: streamable-httpThe MCP server to govern. All tool calls are forwarded here after policy evaluation. streamable-http is the default transport.
listen:
port: 3000
host: '127.0.0.1'Where the Helio proxy listens. Point your MCP client here instead of directly at the upstream server.
dashboard:
enabled: true
port: 3100
allow_open_mode: trueThe web dashboard for real-time visibility. Served on a separate port. This example intentionally uses local open mode (allow_open_mode: true) so pnpm start works without secret setup; keep this loopback-only.
policies:
default: allow
flag_destructive: logdefault: allow means any tool call that doesn't match a rule is permitted. flag_destructive: log adds an audit flag when tools have destructiveHint: true (even if they're explicitly denied by a rule).
rules:
- name: block-destructive
match:
annotations:
destructiveHint: true
action: deny
feedback:
message: 'Destructive operations are blocked by policy.'
suggestion: 'Use a non-destructive alternative or request manual action.'First rule evaluated. Any tool with destructiveHint: true in its MCP annotations is denied. The feedback block provides structured self-repair information to the calling agent.
- name: allow-reads
match:
annotations:
readOnlyHint: true
action: allowExplicitly allows read-only tools. This is redundant with default: allow but demonstrates the pattern — in production you'd typically use default: deny and explicitly allow specific tools.
approval:
timeout: 300s
default_on_timeout: deny
channels:
- type: dashboardApproval configuration. No rules in this example use require_approval, but the dashboard channel is set up so the Approvals page works. See the slack-approvals example for approval workflows in action.
audit:
storage: sqlite
path: ./helio-audit.db
retention: 90d
include_responses: trueEvery tool call is recorded to a local SQLite database. include_responses: true captures the full upstream response (set to false for privacy-sensitive deployments). Records older than 90 days are automatically cleaned up.
- Slack Approvals — Route sensitive actions to Slack for human approval
- Spend Limits — Cap monetary spend across payment tools