External integrations (MCP)
Security model
The MCP integration sits on the visitor-facing hot path and reaches third-party servers. The security model below is the defence-in-depth applied at every layer.
SSRF protection
- The server URL is rejected at form-validation time if it points at a private, loopback, link-local, or cloud-metadata address.
- The same guard runs again at every outbound call with DNS rebind protection —
an attacker who controls DNS for a public hostname they own cannot redirect
the call to
127.0.0.1. - Only
http://andhttps://schemes are accepted.
Credential isolation
- Stored credentials are encrypted at rest via Laravel's
encrypted:arraycast. - The credential resolver is the only code path that touches decrypted credentials.
Every other layer works with an assembled
Authorizationheader string on a per-call DTO. - Octane workers never hold credentials between requests — the DTO is constructed per call.
Prompt-injection defence
Every tool result is wrapped in <tool-result trusted="false">
tags. The system prompt explicitly tells the LLM that anything inside such tags
is data, not instructions, and not to follow links or commands found inside.
Same pattern as the existing <source> RAG wrapping.
Output budget enforcement
Tool outputs are truncated to a per-tool token budget (default 1200) before being fed back to the LLM. A misbehaving server returning multi-megabyte responses is capped at 1MB by the transport layer; the token truncator caps oversized but legitimate responses.
Rate limit + circuit breaker
- Per-workspace rate limit: 60 MCP calls per minute by default. Excess attempts return a structured "rate_limited" tool result to the LLM.
- Per-server circuit breaker: 5 failures in 60s open the breaker for 60s. While open, calls return a structured "unavailable" tool result without hitting the server.
Tenancy
Servers, tools, grants, and call logs are scoped by workspace_id
via the BelongsToWorkspace trait. The widget hot path resolves the
agent from the signed JWT, then queries through the registry which never reads
CurrentWorkspace directly. Cross-tenant attempts return 404 on
admin routes (existence-leak-safe).