Skip to main content
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.

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.
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: 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:
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 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.
  • 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.