Skip to main content
This page describes the com.agentcat:agentcat-java-sdk package, version 1.x. For the first setup, see Java setup.

Options

AgentCat.track(server, projectId, options) takes an AgentCatOptions object. Each setter returns the object, so the calls chain. Every option is optional.
We recommend that you keep the defaults of setEnableToolCallContext, setEnableReportMissing, and setEnableTracing, because each one feeds a part of the dashboard. With setEnableToolCallContext(false), the dashboard has no intent data. With setEnableReportMissing(false), agents cannot report missing tools. With setEnableTracing(false), AgentCat records nothing.
UserIdentity has three fields: userId (a string), userName (optional), and userData (an optional Map<String, Object>). new UserIdentity(userId) sets only the ID. A UserIdentity with a null userId adds no identity to the event. A hook returns a CompletableFuture. A null future, a null result, or an exception means “no value”. AgentCat writes the exception to the log. The SDK reads one environment variable: DISABLE_DIAGNOSTICS (1, true, yes, or on), the same as setDisableDiagnostics(true). The core SDK does not read AGENTCAT_PROJECT_ID: your code passes the project ID to track(), and the framework integrations read it from agentcat.project-id.

Framework and transport details

MCP Java SDK

track() has an overload for each server type: McpSyncServer, McpAsyncServer, McpStatelessSyncServer, and McpStatelessAsyncServer. It returns the same server, so the call can stay inline. One agentcat-java-sdk jar works with MCP Java SDK 0.18.x, 1.x, and 2.x, and finds the version by itself. Every build of the SDK runs its tests against 2.0.x, and its compatibility tests against 1.1.4 and 0.18.4. HTTP headers reach the hooks only through the transport context. The servlet transports of the SDK (HttpServletStatelessServerTransport, HttpServletStreamableServerTransportProvider) start with an empty context, and their builders take a contextExtractor that copies the headers that you need. A stateless server of the SDK serves all requests from one instance. All tracked instances share one event queue, which AgentCat builds with the first event.

Spring Boot

AgentCat registers the Spring Boot auto-configuration com.agentcat.spring.AgentCatAutoConfiguration. It is active when agentcat.project-id has a value and the MCP Java SDK is on the classpath. A bean post-processor then calls track() on each McpSyncServer, McpAsyncServer, McpStatelessSyncServer, and McpStatelessAsyncServer bean, for example the server of the Spring AI MCP server starter. Hooks go in an AgentCatOptions bean. The post-processor then uses that bean, and the two enable-* properties have no effect: those options belong on the bean. The Spring AI starter (2.0) builds its transport with an empty transport context. The identity snippet on the setup page declares its own WebMvcStreamableServerTransportProvider bean with a contextExtractor. It replaces the starter’s bean only with spring.ai.mcp.server.protocol=STREAMABLE: without a protocol value, the starter also adds its SSE transport, and Spring then finds two transport beans. With protocol=STATELESS, declare a WebMvcStatelessServerTransport bean with the same contextExtractor.

Quarkus

The CDI bean com.agentcat.quarkus.AgentCatQuarkusListener runs at startup (StartupEvent). It calls track() on each McpSyncServer, McpAsyncServer, McpStatelessSyncServer, and McpStatelessAsyncServer bean of the MCP Java SDK. Your code makes these beans, for example with @Produces @Singleton methods. The Quarkus extension quarkus-mcp-server has its own MCP implementation, without these server types, so AgentCat cannot track it. The listener reads agentcat.project-id, agentcat.enable-tool-call-context, and agentcat.enable-agent-tracking through MicroProfile Config, so the AGENTCAT_PROJECT_ID environment variable sets the project ID too. Without a project ID, the listener writes [Quarkus] agentcat.project-id not set — skipping track() to the log. An AgentCatOptions bean replaces the options from the two enable-* properties. Because the listener runs at startup, it tracks the server beans that exist then. A server that your code builds later, for example for each request, needs its own AgentCat.track() call.

Micronaut

Bean-created listeners in com.agentcat.micronaut call track() on each McpSyncServer, McpAsyncServer, McpStatelessSyncServer, and McpStatelessAsyncServer bean when Micronaut creates it, for example the server of micronaut-mcp-server-java-sdk. They are active when agentcat.project-id has a value. The AGENTCAT_PROJECT_ID environment variable sets it too. The properties are the same as for Spring Boot. AgentCatMicronautDefaultOptions builds the default AgentCatOptions from the properties. For hooks, a factory of your own replaces it, with @Replaces(bean = AgentCatOptions.class, factory = AgentCatMicronautDefaultOptions.class). With micronaut.mcp.server.transport=HTTP, Micronaut MCP serves a stateless server, and its transport context holds no Authorization header. The setup page reads the header with ServerRequestContext.currentRequest(). This works because AgentCat starts each hook on the request thread, before the tool handler runs. Code inside the returned future runs on another thread, so it reads no request context.

What track() changes in your server

  • Each tool schema gets session_id and context (and agent_id with setEnableAgentTracking(true)), in its required list. AgentCat removes these values before your handler runs.
  • AgentCat removes additionalProperties: false from tracked input schemas.
  • A tool with oneOf, allOf, or anyOf at the top of its input schema gets no injected parameters, and AgentCat writes a warning to the log.
  • 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.
  • If your tool already declares session_id, agent_id, or context, AgentCat leaves that parameter alone and writes a warning to the log.
  • Tools that you add after track() are tracked too.
  • A second track() on the same server does nothing, and its options have no effect.

Runtimes, delivery, and shutdown

The identify, resolveSessionId, eventTags, and eventProperties hooks start when the tool call starts, and run at the same time as your tool handler. They never delay the tool call. AgentCat waits up to 5 seconds for each hook, then sends the event without that value. AgentCat puts each event in a queue in the process. A background thread sends it with the JDK HTTP client (java.net.http): the SDK adds no HTTP or JSON library to your classpath. It sends up to 5 events at the same time, and tries again 3 times (after 1, 2, and 4 seconds) when the server is unreachable or answers with a server error. Each request times out after 10 seconds. The queue holds up to 10,000 events. When the queue is full, AgentCat drops the oldest event. The SDK builds the queue, and adds a JVM shutdown hook, with the first event. On shutdown, the hook waits up to 5 seconds for the queue to empty. An event that is still waiting after that is lost, and the log says how many. If the process stops within about half a second of its first tool call, that first event can be lost before the queue exists.

Logging and debug mode

The SDK has no debug option. It always writes its log to ~/agentcat.log (from the user.home system property), and never to stdout, because stdout carries JSON-RPC on a stdio server. If the SDK cannot write the file, it writes the line to stderr. AgentCatLogger.setLogDir(dir) moves the file to another directory. Useful log lines:
  • AgentCat setup complete | project=... | tools=1+get_more_tools | tracing=true | context=true | reportMissing=true: track() worked.
  • Successfully sent event ... | mcp:tools/call | session ... | <project> | <duration> ms | <user>: an event reached AgentCat. The last value is the user ID from identify, or anonymous.
  • Warning: Failed to track server: track() failed. It returns the server untracked, and your server keeps working.

Testing

The SDK has no test mode. It sends events whenever it has a project ID. If you prefer that tests send no events, you can leave AGENTCAT_PROJECT_ID unset in the test environment. On Spring Boot, Quarkus, and Micronaut, AgentCat then stays off. 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. AgentCat keeps no identity cache.
  • A process that stops within about half a second of its first tool call can lose that first event. See Runtimes, delivery, and shutdown.
  • AgentCat waits up to 5 seconds for a hook. The TypeScript SDK waits up to 30 seconds.
  • The Java SDK has no exporters, no apiBaseUrl option, and no telemetry-only mode. Events always go to https://api.agentcat.com.
  • Each event carries the MCP request and, on a stateful server, the MCP session ID. It carries no HTTP headers.
  • 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. Image, audio, and binary content becomes a placeholder text.
  • AgentCat.publishCustomEvent(server, projectId, data) sends an event with no session, unless data.sessionId gives one. AgentCat.publishCustomEvent(sessionId, projectId, data) sends it to a known session. See Custom events.