> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentcat.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Ruby reference

> Options, transports, delivery, logging, testing, and limits of the AgentCat Ruby SDK.

This page describes the `agentcat` gem, version 1.x, which tracks servers built on the official `mcp` gem. For the first setup, see [Ruby setup](https://docs.agentcat.com/languages/ruby.md).

## Options

`AgentCat.track(server, project_id, options)` takes an `AgentCat::Options` object as `options`. Every option is optional, and you pass options as keyword arguments to `AgentCat::Options.new`.

| Option | Default | What it does |
| - | - | - |
| `identify` | none | `(tool_call) -> AgentCat::UserIdentity \| nil`. Runs on every tool call and names the user of that call. See [Identifying users](https://docs.agentcat.com/languages/ruby.md#identifying-users). |
| `enable_tool_call_context` | `true` | Adds the `context` parameter to each tool. The agent explains in it why it calls the tool. This is the source of intent data. |
| `custom_context_description` | built-in text | Replaces the description of the `context` parameter, for example with domain-specific guidance. A `nil` or blank value keeps the default. |
| `enable_report_missing` | `true` | Registers the `get_more_tools` tool, so agents can report the tools that they looked for and did not find. If your server already has a tool of that name, AgentCat keeps yours. |
| `enable_tracing` | `true` | Records tool calls and custom events, and injects the `session_id` parameter. |
| `enable_agent_tracking` | `false` | Adds an `agent_id` parameter to each tool. Each agent makes its own ID and sends it on every call, so parallel agents on one task stay separate. A strict client can refuse a call without `agent_id`, so this option is off by default. |
| `resolve_session_id` | none | `(tool_call) -> String \| nil`. Gives your own task ID for each request. AgentCat then injects no `session_id` parameter, and turns your value into a fixed `ses_` ID with a SHA-256 hash, so the raw value never leaves your server. |
| `redact_sensitive_information` | none | `(string) -> String`. Changes every string before the event leaves your server. It runs after `redact_event`. |
| `redact_event` | none | `(event) -> event \| nil`. Gets the full event with raw values. Change the event and return it, or return `nil` to drop it. Return the event that you received, because the result replaces the whole event. If a redaction hook fails, AgentCat drops the event. |
| `event_tags` | none | `(tool_call) -> Hash`. Adds tags that you can filter on. Keys: strings of 32 characters or fewer, from letters, digits, spaces, and `_.:$-`. Values: strings of 200 characters or fewer, with no line breaks. At most 50 tags. AgentCat drops invalid entries and logs a warning. |
| `event_properties` | none | `(tool_call) -> Hash`. Adds JSON data to each event. |
| `exporters` | `[]` | Sends events to OpenTelemetry (`AgentCat::Exporters::Otlp`), Datadog, Sentry, or PostHog too, as objects or as hashes with a `type`. You can write your own with `AgentCat::Exporters::Base`. See [Integrations](https://docs.agentcat.com/integrations.md). |
| `hook_timeout` | `1` | The most seconds that each hook can run, from 1 to 5. A hook that runs longer costs that event its value, never the tool call. |
| `queue_size` | `10_000` | Events that the queue keeps before it drops the oldest. The first `track` call sets it for the process. |
| `max_retries` | `3` | Retries after a failed send, after about 1, 2, and 4 seconds, plus a small random delay. |
| `retry_base_delay` | `1` | The first wait between retries, in seconds. It doubles each time. |
| `request_timeout` | `10` | Seconds that each send can take. The connect timeout is 1 second. The first `track` call sets it for the process. |
| `api_base_url` | `https://api.agentcat.com` | The address that receives events. It wins over `AGENTCAT_API_URL`. The first `track` call sets it for the process. |
| `client` | built-in | Any object with `post_event(event)`, for a custom sender. |
| `disable_diagnostics` | `false` | Stops the anonymous error reports that the SDK sends to AgentCat. These reports never contain tool calls, responses, or user data. |
| `stateless` | `false` | Deprecated: it only logs a warning. Use `resolve_session_id` or `identify` instead. |

> [!NOTE]
>
> We recommend that you keep the defaults of `enable_tool_call_context`, `enable_report_missing`, `enable_tracing`, and `disable_diagnostics`, because each one feeds a part of the dashboard or helps us fix a broken install. With `enable_tool_call_context: false`, the dashboard has no intent data. With `enable_report_missing: false`, agents cannot report missing tools. With `enable_tracing: false`, AgentCat sends no events and injects no `session_id`.

A bad option value never raises: AgentCat logs one warning and uses the default. A `project_id` of `nil` turns on telemetry-only mode: events go to your `exporters` and not to AgentCat.

`AgentCat::UserIdentity` has three fields: `user_id` (a string), `user_name` (optional), and `user_data` (an optional hash).

The SDK reads these environment variables:

| Variable | What it does |
| - | - |
| `AGENTCAT_API_URL` | The address that receives events, when `api_base_url` is not set. |
| `AGENTCAT_API_KEY` | Sent as a bearer token to the events address, for a custom collector. |
| `AGENTCAT_ENABLED` | `false` turns AgentCat off everywhere. `true` turns it on in tests too. |
| `AGENTCAT_DISABLE_DIAGNOSTICS` | Stops the anonymous error reports, the same as `disable_diagnostics`. `DISABLE_DIAGNOSTICS` works too. |
| `AGENTCAT_DIAGNOSTICS_ENDPOINT`, `AGENTCAT_DIAGNOSTICS_TOKEN` | The address and token of a self-hosted diagnostics collector. |

The SDK does not read `AGENTCAT_PROJECT_ID`.

## Framework and transport details

### The `mcp` gem

The SDK supports `MCP::Server` from the `mcp` gem, 0.23.0 and later. The CI tests every minor release from 0.23.0 each night. The `mcp` gem has no login of its own: it can check bearer tokens when you pass `token_verifier:` to the HTTP transport, and your app can put its own data in the server's `server_context:`. Hooks see that context as `tool_call.server_context`. AgentCat sends the `auth_info:` entry with each event only when it is a hash that your app put in `server_context:` (such as `{"clientId" => ..., "scopes" => [...]}`), so keep raw tokens out of it. With `token_verifier:`, the gem stores an `MCP::Server::OAuth::AccessToken` object under `auth_info` instead, and AgentCat does not send that object.

### Stateless HTTP

`StreamableHTTPTransport.new(server, stateless: true)` serves each request on its own, and a per-request server needs one `track` call inside the factory. The event queue, exporters, and diagnostics start once and all instances share them. AgentCat holds each server weakly, so a server that you build for one request is freed with it.

### Long-lived HTTP and stdio

A long-lived server needs one `track` call, at start. Tools that you register after `track` are tracked too. The `mcp` gem keeps session state of the streamable HTTP transport in memory, so the gem's documentation advises one process.

### What `track` changes in your server

* Each tool schema gets `session_id` and `context` (and `agent_id` with `enable_agent_tracking`). AgentCat removes these values before your handler runs.
* On the first call of a task, the result starts with a text block that gives the agent its new `session_id`. For a tool with an output schema, AgentCat also puts the ID in `structuredContent` as `mcp_session`.
* AgentCat checks the `session_id` value: it rejects an ID that it did not issue and tells the agent to send the one that it got.
* Calls that the `mcp` gem rejects before a tool runs (an unknown tool, a missing argument, or arguments that fail the input schema) become events too.
* A second `track` on the same server does nothing. `AgentCat.tracked?(server)` tells you if a server is tracked.
* `track` never raises into your setup code. If setup fails, it logs an error and returns your server untracked.

## Runtimes, delivery, and shutdown

The hooks run on their own thread with a time limit, after AgentCat builds a frozen `AgentCat::ToolCall`. They never delay the tool call, and changing the arguments inside a hook raises `FrozenError` without any effect on your tool.

AgentCat puts each event in a queue in the process and sends it in the background. It retries a failed send 3 times. When the queue is full, AgentCat drops the oldest event. The queue restarts in a forked child, which covers Puma cluster mode, Unicorn, and Passenger.

AgentCat registers an `at_exit` hook that waits up to 5 seconds for the queue to empty. In a container or another short-lived process, we recommend that you also call `AgentCat.flush(timeout: 5)` on shutdown, because `at_exit` is best effort. It returns `true` when the queue emptied in time.

## Logging and debug mode

By default, the SDK writes WARN and ERROR lines to `$stderr` through `AgentCat.logger`, and the lines never contain your payload values. Repeated lines are held back for a minute. You can set your own logger, for example `AgentCat.logger = Rails.logger`.

`AgentCat.debug = true` also writes setup and diagnostics lines to `~/agentcat.log`, and never to stdout, because stdout carries JSON-RPC on a stdio server. If the process has no home directory, the SDK writes no file. The file does not list each sent event. Useful lines:

* `AgentCat setup started | project ...`: `track` ran.
* `AgentCat setup skipped | no adapter for ...`: the server shape is not supported. `track` returns the server untracked.
* `AgentCat runtime error | event publish failed after ... attempts`: events could not reach AgentCat.

## Testing

In a test environment, `AgentCat.track` returns your server untouched and sends no events. AgentCat treats a process as a test environment when Minitest or RSpec is loaded, or when `RAILS_ENV`, `RACK_ENV`, or `APP_ENV` is `test`. `AGENTCAT_ENABLED=true` turns it on in tests.

To check what your server tracks, you call `AgentCat.fake` before `AgentCat.track`. The fake keeps every event in memory and sends nothing:

```ruby theme={null}
class EchoServerTest < Minitest::Test
  def setup
    @agentcat = AgentCat.fake
    @server = build_server
  end

  def teardown
    AgentCat.reset!
  end

  def test_echo_is_tracked
    @server.handle({jsonrpc: "2.0", id: 1, method: "tools/call", params: {name: "echo", arguments: {msg: "hi"}}})

    @agentcat.assert_tracked("echo", count: 1)
  end
end
```

AgentCat changes your tool schemas. If your tests compare exact schemas, they see `session_id` and `context` after your own parameters.

## Differences and known limits

* `identify` runs on every tool call, and AgentCat keeps no identity cache. A hook has 1 second by default, where the TypeScript SDK waits 30 seconds, so we recommend your own cache for slow lookups.
* The SDK does not write a log line for each sent event. The dashboard or the [AgentCat MCP server](https://docs.agentcat.com/agent/connect.md) can confirm an event.
* AgentCat cuts each string to 32 KB, each event to 100 KB, and the intent text to 2,048 characters. Objects keep at most 10 levels and 100 keys or items for each level. Invalid UTF-8 becomes U+FFFD.
* `AgentCat.publish_custom_event(server, project_id, resource_name: ..., ...)` sends an `agentcat:custom` event with no session, unless you pass `session_id:`. See [Custom events](https://docs.agentcat.com/sdk/custom-events.md).
* If a release of the `mcp` gem removes an internal that AgentCat uses, AgentCat logs one error and turns off only the feature that needs it.
* The gem was never published under the old `mcpcat` name, so there is nothing to migrate.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.