> ## 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.

# Java reference

> Options, framework integrations, delivery, logging, and limits of the AgentCat Java SDK.

This page describes the `com.agentcat:agentcat-java-sdk` package, version 1.x. For the first setup, see [Java setup](https://docs.agentcat.com/languages/java.md).

## Options

`AgentCat.track(server, projectId, options)` takes an `AgentCatOptions` object. Each setter returns the object, so the calls chain. Every option is optional.

| Setter | Default | What it does |
| - | - | - |
| `setIdentify` | none | `(request, extra) -> CompletableFuture<UserIdentity>`. Runs on every tool call and names the user of that call. `extra` is an `McpAsyncServerExchange` on a stateful server, or an `McpTransportContext` on a stateless server. See [Identifying users](https://docs.agentcat.com/languages/java.md#identifying-users). |
| `setEnableToolCallContext` | `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. |
| `setCustomContextDescription` | built-in text | Replaces the description of the `context` parameter, for example with domain-specific guidance. Works only when `enableToolCallContext` is `true`. |
| `setEnableReportMissing` | `true` | Adds the `get_more_tools` tool, so agents can report the tools that they looked for and did not find. |
| `setEnableTracing` | `true` | Records tool calls and injects the `session_id` parameter. |
| `setEnableAgentTracking` | `false` | Adds an `agent_id` parameter, in the `required` list of each tool schema. Each agent makes its own ID and sends it on every call, so parallel agents on one task stay apart. A strict client refuses to send a call without `agent_id`, so this option is off by default. |
| `setResolveSessionId` | none | `(request, extra) -> CompletableFuture<String>`. Gives your own task ID for each request. AgentCat then injects no `session_id` parameter. It combines your value with the project ID into one fixed `ses_` ID. A `null` result makes a new session that the agent never learns. |
| `setRedactSensitiveInformation` | none | `(text) -> CompletableFuture<String>`. Changes every text value in the parameters and the response before the event leaves your server. If the hook throws or times out, AgentCat drops the event. |
| `setRedactEvent` | none | `(event) -> CompletableFuture<Map<String, Object>>`. Gets the full event map before `redactSensitiveInformation`. The hook returns a changed map, or `null` to drop the event. If the hook throws or times out, AgentCat drops the event. |
| `setEventTags` | none | `(request, extra) -> CompletableFuture<Map<String, String>>`. Adds tags that you can filter on. Keys: 32 characters or fewer, from `[a-zA-Z0-9$_.:\- ]`. Values: 200 characters or fewer, with no newlines. At most 50 tags. AgentCat drops invalid entries and logs a warning. |
| `setEventProperties` | none | `(request, extra) -> CompletableFuture<Map<String, Object>>`. Adds JSON data to each event. |
| `setDisableDiagnostics` | `false` | Turns off anonymous error reports to AgentCat. The Java SDK 1.0 does not send these reports yet, so the option has no effect today. |

> [!NOTE]
>
> 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.

| Property | Default | What it does |
| - | - | - |
| `agentcat.project-id` | none | The project ID. Without a value, AgentCat stays off. The `AGENTCAT_PROJECT_ID` environment variable sets it too, through the relaxed binding of Spring Boot, as does `AGENTCAT_PROJECTID`, the form that the Spring Boot documentation uses. |
| `agentcat.enable-tool-call-context` | `true` | The same as `setEnableToolCallContext`. |
| `agentcat.enable-agent-tracking` | `false` | The same as `setEnableAgentTracking`. |

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](#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](https://docs.agentcat.com/sdk/custom-events.md).


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