Skip to main content
Use this guide when you maintain the instrumentation yourself. The generated setup prompt fits the same boundaries to your repository. For Python use Manual Python setup. The examples below are integration fragments: retain your application’s existing request handler, model/tool loop, tool definitions, authenticated context and response sink. Do not replace a working agent with an example loop.

1. Install and load the connection

Use the git-ignored .env from your connection path. A CLI connection contains RIPPLETIDE=rippletide://…. The app/manual path supplies RIPPLETIDE_AGENT_ID and RIPPLETIDE_API_KEY.
This example chooses metadata capture deliberately. Without a capture setting, the TypeScript SDK defaults to full. See Privacy & capture. A client with no Connection key runs disabled; instrumented code alone is not proof of governance.

2. Declare inventory

At startup, register your existing system prompt, provider-compatible tool definitions and connection secret names:
systemPrompt and tools come from your application. Use the actual provider and environment variable names. Connection registration sends credential names/status, not their secret values. Inspect the inventory in Context & actions. For an MCP server, declare tools only and use the guarded dispatcher below; do not create a model loop or response-delivery event for the server.

3. Guard the actual tool dispatcher

Wire the existing tool loop to this dispatcher. toolHandlers is your actual handler registry. Resolve trusted identity from your authenticated application, never from model-generated tool arguments. Match its shape to the declared contextSchema. guardToolCall() remains supported. The equivalent general API is guard({ trigger: "before_action", action: "core/tool_name", ... }, execute). An enforced blocking decision raises RippletidePolicyBlockedError before the callback. Do not catch that error and execute the tool anyway. For MCP, convert it into an isError: true tool result without crashing the server.

4. Trace the run through final delivery

Wrap the existing model client with wrapOpenAI() when using OpenAI, or trace the actual provider call with traced(). The following example assumes runExistingAgent keeps your real tool loop and uses the guarded dispatcher above:
rawModelCall, runExistingAgent and sendToUser stand for your existing functions. The delivery callback must be the real caller-facing sink. Keep the run open until it completes. A model’s returned text is only a candidate, not evidence it reached the user. Let a blocking delivery error leave the run before an outer application handler turns it into a safe error response. Do not send the blocked candidate from an error handler. Always flush before a short-lived worker or script exits; long-running services should also flush on shutdown. For real delegated agents, add agent() spans without changing concurrency. The SDK propagates causal context and supports explicit dependency links. Use traceHeaders() only when forwarding context to another instrumented process belonging to the same connected agent; an incoming W3C traceparent can be passed to run().

5. Verify and extend

Run a real tool-using request that delivers a response, then check rippletide verify and rippletide events --wait. Inspect the run and delivery outcome in Traces. If before_response remains missing, check causal placement and the actual sink. For completed collections, use Result filtering. For an out-of-process executor, use authorize() followed by recordOutcome() when the real result arrives, rather than an empty guard callback that would falsely report execution. Keep the returned authorization inside your trusted application; it is plain JSON, not a signed capability.