Push Notifications
infrastrActure can push config-change events to registered clients instead of waiting for clients to poll. This is handled by services/webhookEmitter.ts.
How It Fits
The notification is fire-and-forget — it never blocks the primary operation and never surfaces errors to the caller.
Configuration
Set these env vars on the infrastrActure instance:
| Variable | Required | Notes |
|---|---|---|
WEBHOOK_CLIENT_URL | yes | Full URL to the client receiver endpoint |
WEBHOOK_CLIENT_SECRET | yes | Shared secret, sent as Authorization: Bearer <secret> |
If neither URL variable is set, the emitter is silently disabled and clients fall back to polling.
Payload
{
"event": "installation_created",
"instanceId": "my-instance-id",
"delta": {
"added": {
"github-mcp__user_42__install_abc": {
"type": "streamable",
"url": "http://host:port/mcp"
}
},
"removed": []
},
"meta": {
"userId": "42",
"toolId": "github-mcp"
}
}
| Field | Description |
|---|---|
event | What changed — see event types below |
instanceId | Target instance; receivers should ignore events for other instances |
delta.added | Map of server name → full MCP server config object |
delta.removed | List of server names to drop from local config |
meta | Optional context (userId, toolId, etc.) |
Event Types
| Event | Trigger |
|---|---|
installation_created | User installs a tool |
installation_deleted | User removes a tool |
installation_updated | Installation config changes |
shared_tool_started | Shared tool container comes up |
shared_tool_stopped | Shared tool container goes down |
sandbox_started | Sandbox runtime starts |
sandbox_stopped | Sandbox runtime stops |
sandbox_destroyed | Sandbox is deleted |
config_changed | Generic — full re-sync needed (no delta available) |
Receiver Contract
Any client can consume these events. The receiver must:
- Accept
POSTon the configured URL - Validate
Authorization: Bearer <secret> - Optionally check
instanceId— ignore events for other instances - Respond
200immediately - Apply the delta or trigger a re-sync asynchronously
delta is preferred over config_changed when the caller knows exactly which servers changed. Clients that support delta application can update their config without a round-trip to the integration endpoint.
Minimal Receiver (Express)
app.post("/mcp/webhook/config-changed", async (req, res) => {
const token = (req.headers.authorization || "").replace("Bearer ", "");
if (token !== process.env.WEBHOOK_CLIENT_SECRET) {
return res.status(401).json({ error: "Unauthorized" });
}
res.status(200).json({ ok: true }); // acknowledge before any async work
const { delta } = req.body;
if (delta?.added || delta?.removed) {
await hypervisor.applySyncDelta(delta);
} else {
await hypervisor.syncFromInfrastrActure(/* ... */);
}
});
Relation to Polling
Push notifications are the primary sync path. Clients should still run a periodic fallback poll (15-minute interval recommended) to catch any missed events caused by network blips or service restarts.
Client-Side Reload Safety
The point of sending a delta is to let clients reload their MCP connections without interrupting active tool calls. This section explains what a safe reload looks like so you can implement it correctly in your own stack — language and framework are up to you.
The problem with a naïve reload
If you disconnect all MCP servers and reconnect from scratch, any tool call that is mid-flight at that moment will fail. In a multi-user system that may be someone else's request entirely.
What safe reload looks like
A safe reload follows this sequence:
Key properties:
- Build first, swap second. The old client stays alive while the new one connects. If the new client fails to connect any server, the old one is still serving requests.
- Atomic cache swap. All internal references (client, tool map, toolset map) are replaced in one synchronous step. There is no moment where a caller can observe a partial state.
- Tool references are self-contained. Tool objects returned to callers close over their own server connection, not the parent client. An in-flight call holding a reference to an old tool object will still complete after the client reference has been replaced — as long as the underlying connection hasn't been closed yet.
- Grace period before disconnect. Wait long enough for in-flight calls to finish before closing old connections. 10 seconds covers the overwhelming majority of MCP tool calls (calendar reads, searches, filesystem ops). If your stack has longer-running tools, increase it or track active call counts and drain to zero before disconnecting.
What your receiver needs to do
- Acknowledge the webhook
200 OKbefore starting the reload. Never block the response on reconnection work. - Apply the delta to your local config store.
- Build a new client against the updated config.
- Atomically swap your shared client/tool references.
- Schedule disconnection of the old client after your grace period.