aegis · Guides
Connect and secure MCP servers
Register upstream MCP servers in the aegis catalog, point clients at the gateway, add mTLS and brokered credentials, and discover unmanaged servers.
On this page
Your agents connect to aegis, and aegis connects to the MCP servers you register. This guide covers both sides of that connection. It also shows how to secure the connection to each server and how to find MCP servers you haven't registered yet.
Point an MCP client at aegis#
Send JSON-RPC requests to POST /mcp on port 8080, with Authorization: Bearer <jwt>. POST /v1/invoke runs the same handler at its original path.
- aegis answers
initializeandpingitself.initializeadvertises tools only. tools/listmerges the tools of every enabled server in your tenant, minus the tools RBAC denies you and any tool in integrity quarantine. Each entry carries extraserverandtenantfields.tools/callgoes to the server that owns the tool name.- Notifications get HTTP 202. Any other method returns
-32601.
aegis doesn't proxy resources or prompts.
Each successful response carries an X-Session-ID header. Send it back on later requests to keep one session. A session belongs to the subject that created it and expires one hour after aegis creates it. A session ID that belongs to another subject returns HTTP 403, and an expired or unknown one returns HTTP 401. Leave the header off to start a new session.
Keep tool names unique within a tenant. If two servers expose the same name, the server whose name sorts first owns it, and the shadow check reports the collision at GET /admin/v1/shadows.
Single upstream (no database)#
Without DATABASE_URL, aegis fronts one server named default at UPSTREAM_URL (default http://localhost:9000) in the default tenant, so use tokens that carry no tenant claim. There is no catalog, the admin API returns HTTP 503, and RBAC is off, so aegis allows every call it can route. Use this mode only to evaluate aegis on your own machine.
Register servers in the catalog#
With DATABASE_URL set, you manage servers through the admin API. You need catalog:write on the virtual server aegis-admin. A server belongs to the tenant of the token that registered it.
curl -s -X POST http://localhost:8080/admin/v1/servers -H "Authorization: Bearer $ADMIN" -H 'Content-Type: application/json' -d '{"name":"github","base_url":"http://mcp-github:9000/mcp"}'
A successful registration returns HTTP 201.
aegis reaches servers over MCP Streamable HTTP. It sends Accept: application/json, text/event-stream, runs the initialize handshake once per server, sends back any Mcp-Session-Id the server issues, and reads either a JSON or an SSE reply. It declares protocol version 2025-06-18 unless you set MCP_PROTOCOL_VERSION. It doesn't launch stdio servers. aegis builds each upstream request itself and doesn't copy your client's headers, including its Authorization.
During discovery, aegis reads the first page of each server's tools/list reply and doesn't follow nextCursor. Every tool in that reply needs name, description and inputSchema. If one is missing, aegis can't read that server's tools.
A base_url whose host is a loopback, link-local (including the cloud metadata range), unspecified, multicast or broadcast address is refused with HTTP 400. Octal, hex and IPv4-mapped IPv6 spellings of those addresses are refused too. RFC1918 addresses are allowed, because in-cluster servers use them. The check covers IP addresses written in the URL. For hostnames, restrict the gateway's egress with a NetworkPolicy (Deployment).
Each change rebuilds routing without a restart, and the response reports "reloaded": true or false. A reconciler also compares routing with the catalog every CATALOG_RECONCILE_SECS (default 30) and rebuilds it on drift.
PATCH /admin/v1/servers/:name changes base_url, enabled or secret_ref. DELETE /admin/v1/servers/:name also deletes the server's policies and returns policies_deleted. Registering the server again doesn't restore those policies, so prefer PATCH, including {"enabled":false} to take a server out of service.
Callers can use a new server's tools only after a policy allows them. Identity and access shows how to grant access.
Use mTLS to backends#
To have aegis present a client certificate to https:// servers, set these variables:
A _FILE path takes precedence over the inline _PEM value. aegis presents the same identity to every https:// server and ignores these settings for http:// servers. If the settings are invalid, the gateway exits with an error rather than fall back to plain TLS.
Keep upstream secrets away from agents (commercial)#
The credential broker is part of the commercial edition. It holds a server's credential so your agents never see it.
Set BROKER_ENABLED=1, then give the gateway AWS credentials through the standard AWS credential chain. Grant that identity secretsmanager:GetSecretValue only on the secrets it brokers. AWS_REGION defaults to us-east-1, and AWS_ENDPOINT_URL points the broker at LocalStack for testing. Then set the server's secret_ref to the name of its Secrets Manager secret:
curl -s -X PATCH http://localhost:8080/admin/v1/servers/github -H "Authorization: Bearer $ADMIN" -H 'Content-Type: application/json' -d '{"secret_ref":"acme/github-mcp"}'
- aegis sends the secret's string value as
Authorization: Bearer <value>on the handshake, on tool discovery and on everytools/call. - Outside the
defaulttenant,secret_refmust start with<tenant>/. - Values are cached for
BROKER_CACHE_TTL_SECS(default 300). - If aegis can't resolve the secret, the call is refused with
-32054. The same happens when a server has asecret_refbut the broker is off. - If a server echoes the credential back, aegis replaces it with
[REDACTED:UPSTREAM_CREDENTIAL].
PATCH can't clear a secret_ref. To stop brokering for a server, delete it, register it again and re-create its policies. To rotate the credential, update the secret in Secrets Manager. aegis uses the new value once the cached one expires.
Tune resilience per upstream#
Each tenant and server pair gets its own breaker and concurrency cap for tools/call.
An open breaker or a full cap returns -32063 immediately.
-32064 means aegis sent the call and the timeout expired before a reply, so the tool may have run. aegis audits the call as indeterminate and sets "safe_to_retry": false in the error data. Confirm the outcome before you retry a tool that changes state, and set the timeout above your slowest tool's normal run time.
Find unregistered MCP servers with aegis-discover#
aegis-discover is a separate CLI that finds MCP servers on hosts you list or referenced in GitHub code. Compare what it finds with your catalog and register the servers you keep. Build it from the aegis source:
cd discovery && cargo build --release
The binary is discovery/target/release/aegis-discover. To run it on the Compose stack's network instead, use docker compose -f deploy/docker-compose.yml run --rm discovery followed by a subcommand. Both subcommands print a JSON report to stdout.
aegis-discover net --targets host-a:9000,host-b:9000 --timeout 5 --concurrency 16
net probes only the host:port targets you list and doesn't expand CIDR or port ranges. --timeout is in seconds, and --https probes over HTTPS. It POSTs tools/list to each target's root path, doesn't follow redirects, and counts a target as MCP only if the JSON reply contains a result.tools array. Each MCP result lists the tool names and whether /healthz answered.
net sends no Accept header, skips the initialize handshake and reads JSON replies only. A server that requires any of those appears under non_mcp, so treat that list as unconfirmed.
aegis-discover repo --markers mcpServers,mcp.json --limit 30
repo searches GitHub code for each marker and reports up to --limit hits per marker (at most 100) as repository, path and link, never file contents. Set GITHUB_TOKEN so requests are authenticated and get higher rate limits. A failed or rate-limited search for one marker is reported on stderr and doesn't stop the others.