# Connect your agent
Source: https://docs.agentcat.com/agent/connect
Use the AgentCat MCP server from Cursor, Claude, VS Code, and more.
The AgentCat MCP server lets you query your usage analytics and debug issues directly from your coding agent. It runs at:
```
https://mcp.agentcat.com/mcp
```
Authentication uses OAuth — **you never copy an API key**. The first time your client connects, it opens a browser to sign in to your AgentCat account, then caches the token.
## Claude Code
```bash theme={null}
claude mcp add --transport http agentcat https://mcp.agentcat.com/mcp
```
Run `/mcp` inside a session to complete the browser sign-in.
## Claude Desktop
Open **Settings → Connectors → Add custom connector**, paste `https://mcp.agentcat.com/mcp`, and sign in. The same custom-connector flow works across Claude's desktop apps (Claude Desktop and Cowork) — remote servers are supported directly, no proxy needed.
## Codex
```bash theme={null}
codex mcp add agentcat --url https://mcp.agentcat.com/mcp
```
## Cursor
[Add to Cursor](cursor://anysphere.cursor-deeplink/mcp/install?name=agentcat\&config=eyJ1cmwiOiJodHRwczovL21jcC5hZ2VudGNhdC5jb20vbWNwIn0=) — or add manually to `~/.cursor/mcp.json`:
```json theme={null}
{ "mcpServers": { "agentcat": { "url": "https://mcp.agentcat.com/mcp" } } }
```
## VS Code
Add to `.vscode/mcp.json` (or run `code --add-mcp '{"name":"agentcat","type":"http","url":"https://mcp.agentcat.com/mcp"}'`):
```json theme={null}
{
"servers": {
"agentcat": { "type": "http", "url": "https://mcp.agentcat.com/mcp" }
}
}
```
## Windsurf
Add to `~/.codeium/windsurf/mcp_config.json`:
```json theme={null}
{ "mcpServers": { "agentcat": { "serverUrl": "https://mcp.agentcat.com/mcp" } } }
```
## Other clients
Most clients accept the standard config:
```json theme={null}
{
"mcpServers": {
"agentcat": { "type": "http", "url": "https://mcp.agentcat.com/mcp" }
}
}
```
## Tools available
`list_projects`, `get_project_overview`, `search_sessions`, `get_session`, `search_issues`, `get_issue`, `get_metric_trend`, `get_metric_breakdown`, `list_skills`, `load_skill`.
## Example prompts
* "What are my top tools in the last 7 days?"
* "Investigate issue PROJ-42."
* "Summarize session ses\_abc123 and tell me why it errored."
# Deployment Options
Source: https://docs.agentcat.com/deployment/index
AgentCat offers flexible deployment models for organizations that need maximum control over their data.
Most teams use AgentCat's cloud platform out of the box — it's production-ready with row-level security, encryption at rest and in transit, and SOC 2-compliant infrastructure. For organizations with strict data sovereignty, compliance, or air-gapped requirements, we also offer single-tenant and self-hosted options.
Our fully managed multi-tenant platform with row-level security, encryption, and SOC 2-compliant infrastructure. The fastest way to get started.
A dedicated AgentCat instance hosted by us with custom DNS and complete infrastructure isolation.
Deploy AgentCat on your own infrastructure with full control over data residency, networking, and configuration.
## Not Sure Which Option Is Right for You?
| | Cloud | Single Tenant | Self-Hosted |
| ----------------- | -------------------------------------- | ------------------------------------------- | ---------------------------------------- |
| **Data location** | AgentCat shared infrastructure | Dedicated AgentCat infrastructure | Your infrastructure |
| **Isolation** | Row-level security | Full infrastructure isolation | Full infrastructure isolation |
| **Operations** | Fully managed by AgentCat | Fully managed by AgentCat | Managed by your team |
| **Custom DNS** | N/A | Custom subdomain included | You control DNS |
| **Compliance** | SOC 2, encryption at rest & in transit | Isolation + managed compliance posture | Full control — you own the audit scope |
| **Best for** | Most teams | Dedicated infrastructure without ops burden | Maximum control, air-gapped environments |
Book a call to discuss which deployment option is the best fit for your organization.
# Self-Hosted
Source: https://docs.agentcat.com/deployment/self-hosted
Deploy AgentCat on your own infrastructure with full control over data residency and security.
AgentCat Self-Hosted gives you a complete AgentCat deployment running entirely within your own infrastructure. Delivered as a Docker Compose package, it includes everything you need to capture, process, and analyze MCP agent interactions without any data leaving your environment.
## Infrastructure Requirements
### Minimum Resources
* **4 vCPUs**
* **16 GB memory**
* **400 GB storage** for approximately 30 million events per 3 months, or use a managed serverless database (e.g., Aurora Serverless, Neon) for elastic storage
### Included Dependencies
The Docker Compose deployment includes all required services:
* **Kafka** — Event queue for reliable message processing
* **PostgreSQL** — Primary data store
We recommend connecting an externally managed PostgreSQL instance (e.g., AWS RDS, GCP Cloud SQL) for production workloads to simplify backups and high availability.
* **Clickhouse** *(optional)* — High-performance analytics engine, recommended for deployments processing 1M+ events per day
* **SMTP service** — Your own email service for notifications and alerts
* **LLM API key** — Powers AI features like session summarization and intent detection. Built on [LiteLLM](https://docs.litellm.ai/), so any major LLM provider is supported including OpenAI, Anthropic, AWS Bedrock, Azure OpenAI, and more
If you use AWS Bedrock for your LLM provider, external network requirements are minimized since traffic stays within AWS.
## Management CLI
Every self-hosted deployment includes a purpose-built CLI for managing your AgentCat instance:
* **Health checks** — Run diagnostics across all services and dependencies. Verify database connectivity, Kafka broker status, worker health, and end-to-end event pipeline flow.
* **Log access** — Stream or export logs from any running service. Filter by severity, time range, or service name to quickly isolate issues.
* **Queue management** — Monitor consumer lag and throughput across Kafka topics. Run targeted commands to rebalance partitions or flush stuck messages when queue pressure builds.
* **Version upgrades** — Pull and apply the latest AgentCat images with a single command. The CLI handles service orchestration, migration execution, and rollback if needed.
* **Reporting** — Generate reports on system health, event throughput, storage utilization, and service uptime for operational reviews.
## Updates & Maintenance
New AgentCat versions are delivered as updated Docker images. The management CLI handles version upgrades, making it straightforward to stay current with the latest features and security patches.
## Network & Security
Self-hosted AgentCat is designed to run in locked-down environments:
* **No inbound external traffic required** — The deployment does not need to be exposed to the public internet. Your agents and SDKs only need internal network access to the AgentCat API endpoint
* **Runs behind your VPN or firewall** — Full compatibility with private network configurations
* **Minimal egress** — Outbound traffic is limited to pulling container images and LLM API calls. Using AWS Bedrock further reduces egress by keeping LLM traffic within your VPC
## Data Residency
Because you control the infrastructure, you choose where AgentCat runs. Deploy in any region or cloud provider that meets your compliance requirements — including on-premise data centers.
## Backup & Recovery
Database backup and recovery follows your existing infrastructure practices. We recommend configuring automated backups for your PostgreSQL instance, especially if using an externally managed database service.
## Forward Deployed Engineer
AgentCat offers an optional **Forward Deployed Engineer (FDE)** — a dedicated AgentCat engineer embedded with your team to own the operational side of your self-hosted deployment.
Your FDE handles:
* **Deployment & configuration** — End-to-end setup tailored to your infrastructure and security requirements
* **Ongoing operations** — Proactive monitoring, upgrades, and capacity planning so your team doesn't have to
* **Performance tuning** — Optimization for your specific event volumes and query patterns
* **Incident response** — Direct escalation path when issues arise
An FDE is ideal for organizations that want the control of self-hosting with the confidence of having a dedicated AgentCat expert managing it day-to-day.
***
Talk to our team about self-hosted AgentCat for your organization.
# Single Tenant
Source: https://docs.agentcat.com/deployment/single-tenant
A fully managed, dedicated AgentCat instance with complete infrastructure isolation and custom DNS.
AgentCat Single Tenant gives you a completely isolated copy of the AgentCat infrastructure, hosted and managed entirely by our team. You get all the benefits of a dedicated deployment — data isolation, custom DNS, and compliance alignment — without any of the operational overhead.
## Fully Managed
AgentCat handles everything for your single-tenant deployment:
* **Infrastructure management** — Provisioning, scaling, and monitoring
* **Updates** — Automatic upgrades to the latest AgentCat version
* **Security** — Patching, encryption, and access controls
* **Availability** — Uptime monitoring and incident response
Your team focuses on building great agent experiences. We handle the rest.
## Custom DNS
Every single-tenant deployment includes custom DNS resolution for both the dashboard and event ingestion endpoints:
* **Dashboard**: `mycompany.agentcat.com`
* **Event ingestion**: `events.mycompany.agentcat.com`
White-label URLs on your own domain are also supported, so your team and SDK integrations point to a URL you control.
## Compliance
A single-tenant deployment provides the infrastructure isolation that many compliance frameworks require or strongly recommend. Because your data is fully separated — dedicated databases, dedicated compute, no shared resources — the scope of compliance assessments is simplified significantly.
### Data Protection & Privacy
* **GDPR** — Clear data isolation and residency boundaries simplify compliance with data protection by design (Art. 25) and security of processing (Art. 32)
* **CCPA / CPRA** — Isolated infrastructure makes it straightforward to fulfill deletion and data access requests with no risk of cross-tenant data leakage
* **HIPAA** — Dedicated compute and storage satisfy the Security Rule's requirements for access controls, audit controls, and integrity controls for any PHI in agent interactions
If your compliance requirements mandate that no data leaves your own infrastructure, consider [self-hosted](/deployment/self-hosted) instead. For most compliance frameworks, single-tenant provides equivalent isolation with zero operational overhead.
***
Talk to our team about a single-tenant AgentCat deployment.
# Dashboard
Source: https://docs.agentcat.com/features/dashboard
See real-time usage and analytics for your MCP server.
When you have dozens of users and hundreds of tool calls flowing through your MCP server, you need a way to see the big picture. AgentCat's Dashboard gives you real-time KPIs, usage trends, and tool-level analytics so you can understand adoption and performance at a glance.
## KPI Cards
The top of the dashboard shows four key metrics, each with a trend comparison to the previous period:
| Metric | Description |
| ---------------------- | ---------------------------------------------------- |
| **Active Users** | Unique identified users with at least one session |
| **Active Sessions** | Total sessions with at least one event |
| **Avg Session Length** | Mean duration across all active sessions |
| **New Users** | Users seen for the first time in the selected period |
## Activity Chart
An overlaid area chart showing **users** and **sessions** over time. Use this to spot adoption trends, weekly patterns, and the impact of releases or announcements on usage.
## Users by Client
A donut chart showing how your users are distributed across AI clients — Claude Desktop, Cursor, Windsurf, and others. This helps you understand which clients to prioritize when testing and optimizing your tools.
## Most Used Tools
A horizontal bar chart ranking your top 7 tools by call volume. Each bar is split into **success** and **error** segments, so you can immediately see which tools are failing most often.
## Tool Latency
A line chart showing tool response time percentiles (**p50**, **p95**, **p99**) over time. Use this to catch performance regressions early and understand the latency experience for your typical and worst-case users.
## Agent Goals
If your project has enough data for [Agent Goals](/features/goals), the dashboard includes two additional charts:
* **Goal Distribution**: A donut chart showing the top 10 user intents across all sessions
* **Goal Success Rate**: A stacked bar chart showing how often each goal is accomplished vs failed, with color-coded progress bars
## Recent Sessions
A widget showing the 5 most recent sessions with activity. Each entry shows the user, timestamp, and session activity level. Click any session to jump directly into [Session Replay](/features/session-replay).
## Filter Controls
Use the controls at the top of the dashboard to narrow your view:
| Filter | Options |
| ----------------------- | ------------------------------------------------------- |
| **Date Range** | Last 7 days, last month, last 3 months |
| **Granularity** | Daily, weekly |
| **Actor Trait Filters** | Filter by user properties set via the identify callback |
# Data Export
Source: https://docs.agentcat.com/features/data-export
Export your AgentCat analytics data to S3 for custom analysis, data warehousing, or compliance.
AgentCat lets you export your analytics data to your own Amazon S3 bucket. Once configured, your data is exported as Parquet files: ready for your data warehouse, BI tools, or compliance archives.
Exports include two tables — sessions and events — with user and agent-goal details denormalized into each row. See [Privacy & Security](/privacy-security) for details on how AgentCat protects your data.
## Prerequisites
* Data export enabled for your organization
* An AWS account where you can create IAM roles
* An S3 bucket in your AWS account to receive exports
## Setting up a destination
Navigate to **Settings > Destinations > Add Destination** to start the setup wizard.
### Step 1: Choose destination type
Currently S3 is the only supported destination type. Select it and continue.
### Step 2: Configure your S3 bucket
| Field | Required | Description |
| -------------------- | -------- | ---------------------------------------------------------------------------------------- |
| **Destination name** | Yes | A friendly name (e.g., "Production Data Lake") |
| **Bucket name** | Yes | Your S3 bucket name |
| **AWS region** | Yes | The region where your bucket lives |
| **AWS account ID** | Yes | Your 12-digit AWS account ID |
| **IAM role name** | Yes | The name of the IAM role AgentCat will assume (e.g., `agentcat-s3-export`) |
| **Path prefix** | No | Optional prefix for the export path. Exports land at `s3://bucket/{prefix}/agentcat/...` |
| **KMS encryption** | No | If your bucket uses SSE-KMS, provide the KMS key ARN |
### Step 3: Set up the IAM role in your AWS account
AgentCat uses cross-account IAM role assumption — your data stays in your AWS account and AgentCat never sees your AWS credentials.
AgentCat generates the exact IAM policies you need. The wizard provides setup instructions in five formats:
* **AWS Console** — step-by-step manual instructions
* **AWS CLI** — copyable shell commands
* **Terraform** — HCL code you can add to your infrastructure
* **Pulumi** — TypeScript code for Pulumi users
* **CloudFormation** — one-click "Launch Stack" button that creates everything automatically
The generated IAM role includes:
* A **trust policy** that allows AgentCat's service role to assume it, with your organization ID as an external ID for security
* A **permissions policy** granting `s3:PutObject`, `s3:PutObjectAcl`, and `s3:DeleteObject` on your bucket path (and `kms:GenerateDataKey` / `kms:Decrypt` if KMS is configured)
### Step 4: Verify the connection
After creating the IAM role in your AWS account, return to AgentCat and click **Verify Connection**. AgentCat will:
1. Assume your IAM role via STS
2. Write a test object to your bucket
3. Delete the test object
If verification fails, AgentCat shows which step failed with a specific error message (e.g., "Role assumption failed", "Write denied", "KMS access denied") to help you troubleshoot.
## Assigning projects to a destination
After your destination is verified and active:
1. Navigate to **Settings > Projects** and select a project
2. In the **Exports** section, click **Add Destination**
3. Select from your verified destinations
4. Toggle the export on or off per project
A project can be assigned to multiple destinations, and a destination can receive data from multiple projects.
## What gets exported
AgentCat exports your data as two Parquet tables:
| Table | Contents |
| ------------ | ------------------------------------------------------------------------------------------------------------ |
| **Sessions** | One row per session, with its identified user, agent goal, timing, and client/server details denormalized in |
| **Events** | All captured MCP events (tool calls, resource reads, etc.) with session, user, and error details |
Sessions are exported once they finalize (a session finalizes after it has been inactive long enough to be considered complete). Each hourly export picks up newly finalized sessions and their events, and a cursor ensures every session is exported exactly once.
**Privacy:** By default, the event `parameters`, `response`, and `error` columns are excluded from exports — those columns are omitted from the Parquet files entirely. This is controlled by a per-destination column denylist, which you can edit to exclude additional columns or to include these three. All other columns are exported as stored, including IP addresses and user identification data, so the [server-side](/privacy-security/server-side-redaction) and [client-side](/privacy-security/client-side-redaction) redaction applied when events are ingested determines what sensitive values an export can contain.
## Export path structure
Exports are written as Hive-partitioned datasets:
```
s3://{bucket}/{prefix}/mcpcat/v1/{table}/project_id={project_id}/dt={YYYY-MM-DD}/hour={HH}/{file}.parquet
```
Example:
```
s3://my-data-lake/analytics/mcpcat/v1/sessions/project_id=proj_abc123/dt=2026-04-03/hour=14/part-0.parquet
s3://my-data-lake/analytics/mcpcat/v1/events/project_id=proj_abc123/dt=2026-04-03/hour=14/part-0.parquet
```
Each export batch also writes a manifest under `mcpcat/v1/_manifests/` recording the files and row counts it produced, and each table root carries a `_common_metadata` file so the datasets load directly in Spark, DuckDB, and other Parquet readers.
## Managing destinations
* **Enable/disable**: Toggle a destination on or off without deleting it
* **Edit**: Update the name or S3 configuration (changing config resets verification — you'll need to re-verify)
* **Delete**: Permanently remove a destination (also removes all project assignments)
* **Re-verify**: Test the connection again anytime from the destination detail page
# Agent Goals
Source: https://docs.agentcat.com/features/goals
Understand what the agent was trying to accomplish in your product.
## What are Agent Goals?
Tool call logs show you *what* happened in a session, but not *why*. Agent Goals use AI to classify the goal behind each session, turning raw event data into actionable insight about what your users are trying to do and whether they were successful.
Agent goals was built with a full product workflow in mind, helping you answer questions like:
* What are the most common use cases for agents using my product?
* How often are those use cases successful vs. not?
* What are new use cases?
## How Goals Work
AgentCat's AI analyzes patterns across your project's sessions to generate a set of goals. To start classifying goals, we need a minimum threshold of sessions and tool calls so our algorithm can begin clustering and classifying common goals:
| Requirement | Threshold |
| -------------- | ----------------------------- |
| **Sessions** | \~200 sessions in the project |
| **Tool calls** | \~1,000 tool call events |
Once these thresholds are met, AgentCat begins periodically discovering goals based on samples of sessions and classifying all new sessions into a goal once the session has completed.
## Session Goal
In each [Session Replay](/features/session-replay), you can find the session goal. The session goal includes information that helps you understand what the goal was for that session and whether it was achieved or not.
| Property | Description |
| --------------- | --------------------------------------------------------------------------------- |
| **Name** | A short label for the goal |
| **Description** | A longer explanation of the user intent |
| **Status** | Whether the goal was **accomplished** or **failed** for a given session |
| **Reasoning** | AI-generated explanation of why the goal was classified as accomplished or failed |
Goals go through different states depending on whether you're looking at a project or an individual session:
* **Learning:** The project doesn't have enough data to generate goals
* **Processing:** This individual session is still being analyzed or has not yet completed
* **Active:** Goals are available and ready to view
## Goal Success Rate
On the [Dashboard](/features/dashboard), you can see an overview of the agent goals across your entire project. You can see the distribution of goals across all recent sessions, how often a goal was succeeding or failing, and a graph of goal success rate over time.
Use this to identify goals where users frequently fail. These are your highest-impact improvement opportunities.
## Filtering by Goal
On the sessions list page, a **Goals** filter becomes available once goals are active for your project. Use it to find all sessions where agents had a specific goal. For example, filter to "Debug authentication flow" to see every session where an agent was trying to debug auth.
# Issues
Source: https://docs.agentcat.com/features/issues
Automatic error grouping, impact tracking, and trend analysis for your MCP server.
## What are Issues?
Errors scattered across individual sessions are hard to triage. You might see the same tool failing for dozens of users without realizing it's the same root cause. AgentCat's Issues feature automatically groups related errors, tracks their impact, and shows you trends over time so you can prioritize what to fix.
But agents also make lots of AI-specific errors that still show up as a "successful" tool call. Our error grouping also identifies hallucinations, tool schema mismatches, goal misalignment, and other AI errors so you can understand how often those problems are occuring.
## How Issues Are Captured
We capture issues by looking over every tool call in a session and building a broader picture of how that tool call fits into the session, what the goal of the session and that tool call was, and where in the session that tool call occured. The SDK automatically captures issues from multiple sources, including:
* Exception messages and types
* Stack traces (when available)
* Request parameters and response data
* The session and user context where the error occurred
Issues are grouped by error type, message, function name, and agent intent. When multiple sessions encounter the same error, they roll up into a single issue with an event count and user impact metric.
## Issues List
The issues list shows all detected issues with key metrics at a glance.
### Filters
| Filter | Description |
| -------------- | ------------------------------------------------------- |
| **Status** | Unresolved, resolved, or ignored |
| **Date range** | Filter by when errors occurred |
| **Sort** | Sort by events, users, impact, first seen, or last seen |
## Issue Detail
Click an issue to see its full breakdown, such as affected users, clients, and recent occurrences. You can also jump into [Session Replay](/features/session-replay) for any occurrence to see the full context.
### Status Management
| Action | Effect |
| ------------------------ | --------------------------------------------------------- |
| **Resolve** | Marks the issue as fixed. New occurrences will reopen it. |
| **Ignore** | Hides the issue from the default list view. |
| **Unresolve / Unignore** | Returns the issue to unresolved status. |
### Assigning an Issue
You can designate an **assignee** to each issue. This ensures that someone from your team is responsible for looking into an issue, identifying a fix, and ensuring it resolves the issue.
You can also assign a **priority** level to each issue to accurately prioritize its impact.
### Error History Chart
A bar chart showing error frequency over the last 14 days. Toggle between three metrics:
* **Events**: Total error occurrences per day
* **Users**: Unique affected users per day
* **Sessions**: Unique affected sessions per day
# Session Replay
Source: https://docs.agentcat.com/features/session-replay
Step through every event in a session to understand what happened and debug.
## What is Session Replay?
When a user reports an issue or you see an error spike, you need the full context, not just an error message. Session Replay lets you step through every event in a session, see what the user was trying to accomplish, and inspect the exact request and response data for each tool call.
## Session List
The sessions page shows all recorded sessions with filtering, sorting, and pagination.
### Filters
| Filter | Description |
| ------------------- | ------------------------------------------------------------------------------- |
| **User search** | Search by user name or ID |
| **Date range** | Filter sessions by time period |
| **User type** | Identified or anonymous users |
| **Server** | Filter by MCP server |
| **Client** | Filter by AI client |
| **Tools** | Filter sessions that used specific tools |
| **Goals** | Filter by session goal (visible when [Agent Goals](/features/goals) are active) |
| **Activity toggle** | Show only sessions with activity |
| **Actor traits** | Filter by user properties set via the identify callback |
Sessions can be sorted by date, activity count, or duration, with configurable page sizes.
## Replay View
The replay view uses a two-panel layout: a **timeline** on the left showing every event in sequence, and a **detail panel** on the right showing the full data for the selected event.
### Timeline
The timeline lists every event in chronological order, showing the timestamp, event type badge, and a description. Tool call events show the tool name; custom events show the resource name.
* Use the **Show/hide list tools** toggle to filter out `tools/list` events that add noise
* The timeline supports infinite scroll for sessions with many events
### Event Detail Panel
Click any event in the timeline to see its full details:
* **Agent Intent**: an explanation from the agent on why it called this tool (see [Tool Call Context](/sdk/tool-call-context))
* **Error Details**: if the tool call errored, the error message and stack trace
* **Parameters**: the full request arguments
* **Response**: the tool's response data
* **Event Metadata**: information about the event
* **Identify Data**: information about the user, if available
# Team Management
Source: https://docs.agentcat.com/features/team-management
Invite teammates and manage access to your organization.
AgentCat is built for teams. Every organization can have multiple members who share access to the same projects, sessions, and analytics. The **Members** page in **Settings** is where you manage who has access.
## Members Overview
Navigate to **Settings > Members** to see everyone in your organization. The members table shows each person's **name**, **email**, and **status** — either **Member** for active users or **Invited** for pending invitations. A dropdown menu on each row provides actions like removing the member or cancelling a pending invite.
## Inviting Team Members
### From Settings
Click the **Add member** button on the Members page to open the invite dialog. Enter a teammate's email address and click **Send Invite**. They will receive an email invitation to join your organization, and their entry will appear in the members table with an **Invited** status until they accept.
### During Setup
When you first create your organization, the setup flow includes a team invite step that supports batch entry. Type or paste multiple email addresses separated by commas or spaces, then send all invitations at once. You can always invite more members later from the Settings page.
## Accepting an Invite
When someone is invited, they see a banner on their dashboard: "You've been invited to an organization."
Clicking **Accept invite** opens a confirmation dialog that explains what will happen to their current organization:
| Scenario | What happens |
| -------------------------------------------------------- | ----------------------------------------------------------------------------- |
| **You are the only member** of your current organization | Your current organization and all its projects are permanently deleted |
| **Other members exist** in your current organization | You are removed from the organization, but it continues for remaining members |
After accepting, you are switched into the new organization and gain access to all of its projects.
## Removing Members
Open the dropdown menu on any member row and select **Remove** (or **Cancel invite** for pending invitations). A confirmation dialog appears before the action is carried out. Removed members immediately lose access to all projects in the organization.
You cannot remove yourself from an organization.
# How It Works
Source: https://docs.agentcat.com/how-it-works
The secret sauce behind AgentCat.
Here's what's going on under the hood — how the SDK captures data, how sessions work, and the key concepts you'll see throughout the docs.
## Architecture
AgentCat's SDK runs inside your MCP server, intercepting MCP protocol messages to capture analytics before passing them through to your tools. Your server works exactly as before. AgentCat just observes.
## Agent Intent Capture
The magic of AgentCat lies in understanding *why* agents call your tools, not just *what* they call.
When you install the SDK, AgentCat automatically injects a `context` parameter into your tool schemas. When AI assistants (like Claude Code or Cursor) call your tools, they see this additional parameter, they use it to explain their reasoning for making that tool call.
```json theme={null}
{
"tool": "search_files",
"arguments": {
"query": "authentication",
"context": "Looking for authentication logic to understand how user sessions are validated before adding a new permission check"
}
}
```
You can [customize or disable](/sdk/tool-call-context) this behavior.
This context is extracted as the agent's intent, giving you insights into:
* What users are trying to accomplish
* How tools fit into larger workflows
* Common patterns and use cases
* Missing functionality users need
## Request Tracing
AgentCat intercepts all MCP protocol messages by wrapping your server's handlers:
```python Python theme={null}
import agentcat
from mcp.server import FastMCP
# Your existing server
server = FastMCP(name="my-tools")
# One line to add analytics
agentcat.track(server, "proj_YOUR_PROJECT_ID")
```
```typescript TypeScript theme={null}
import { track } from 'agentcat';
import { Server } from '@modelcontextprotocol/sdk';
// Your existing server
const server = new Server();
// One line to add analytics
track(server, "proj_YOUR_PROJECT_ID");
```
```go Go theme={null}
import agentcat "go.agentcat.com/sdk/officialsdk"
// For mark3labs/mcp-go: import agentcat "go.agentcat.com/sdk/mcpgo"
// Your existing server
s := mcp.NewServer(&mcp.Implementation{Name: "my-tools", Version: "1.0.0"}, nil)
// One line to add analytics
agentcat.Track(s, "proj_YOUR_PROJECT_ID", nil)
```
Every request is captured with:
* **Event type** (tool call, resource access, prompt execution)
* **Timing data** (start time, duration)
* **Request parameters** and **response data**
* **Error states** and messages
* **Client information** (which AI tool made the request)
## How it Works Under the Hood
### 1. Schema Enhancement
When AgentCat wraps your server, it modifies tool schemas to include the context parameter:
**Before AgentCat:**
```json theme={null}
{
"name": "search_files",
"inputSchema": {
"type": "object",
"properties": {
"query": { "type": "string" }
}
}
}
```
**After AgentCat:**
```json theme={null}
{
"name": "search_files",
"inputSchema": {
"type": "object",
"properties": {
"query": { "type": "string" },
"context": {
"type": "string",
"description": "Describe why you are calling this tool..."
}
},
"required": ["query", "context"]
}
}
```
### 2. Transparent Interception
AgentCat intercepts requests without affecting your tool's functionality:
1. **Client calls tool** with arguments + context
2. **AgentCat extracts** the context parameter
3. **Your tool receives** original arguments (without context)
4. **Tool executes** normally
5. **AgentCat captures** the response
6. **Event is queued** for analytics
Your tools never see the context parameter—they work exactly as before.
### 3. Asynchronous Processing
Events are processed asynchronously to ensure zero performance impact:
* Thread-safe queue holds events (default: 10,000 capacity)
* Worker threads send events to AgentCat API
* Automatic retries with exponential backoff
* Graceful shutdown ensures no data loss
### 4. Privacy & Control
AgentCat is designed with [privacy](/privacy-security) in mind:
* **[Redaction functions](/privacy-security/client-side-redaction)** let you scrub sensitive data before sending
* **All data [encrypted](/privacy-security/certifications)** at rest
* **You control** what events are tracked
* **No PII required** - user identification is optional
## Sessions
Every interaction with your MCP server happens within a **session**. How sessions are created depends on your server's settings:
* **Stateful servers**: The SDK generates a session ID when a client connects. All events during that connection share the same session.
* **Stateless servers**: Each request is independent, so the SDK sends events without a session ID. AgentCat's backend groups these events into sessions automatically based on the identified user, the client used, and a 30-minute inactivity window. See [Stateless Servers](/sdk/stateless-servers) for setup details.
Sessions follow a simple lifecycle:
1. **Initialization**: A new session starts when a client initializes a connection to your MCP server
2. **Activity**: All tool invocations and interactions are associated with the session
3. **Inactivity**: Sessions remain active as long as the client maintains the connection
4. **Termination**: Sessions end when the client disconnects or after a period of inactivity (usually 30 minutes)
For stateless HTTP servers, sessions don't have an explicit initialization or termination. Instead, AgentCat groups events into sessions server-side based on user identity and timing. Learn more in [Stateless Servers](/sdk/stateless-servers).
### Session Construction
Sessions are constructed in two phases — first by the SDK on the client, then refined by the server.
#### Client-Side (SDK)
The SDK intercepts your MCP server's transport layer and automatically captures every interaction without requiring any code changes to your tools. When a client connects, the SDK:
1. **Generates a unique session ID** (stateful) or **signals stateless mode** so the server can group events — and associates all subsequent events accordingly
2. **Captures metadata** about the connection, like client name and version, server name and version, and SDK language and version
3. **Records each interaction** as a structured event with tool calls, resource reads, prompt requests, along with timing data, parameters, and responses
4. **Streams events asynchronously** to AgentCat's ingestion pipeline so there is zero impact on your server's response times
The SDK is designed to be resilient. Events are buffered and sent independently of your tool execution, so even if the network is momentarily unavailable, your MCP server continues to operate normally.
#### Server-Side Processing
Once events arrive, AgentCat's server reconstructs and enriches each session through several processing stages:
1. **Session assembly**: Events are matched to their session using atomic, concurrent-safe operations. If a session doesn't exist yet, it's created on the first event. Metadata fields are progressively backfilled: if the first event is missing client info, later events in the same session can fill in the gaps. For stateless servers, events arrive without a session ID, and AgentCat groups them by matching the identified user and AI client within a 30-minute window. This requires [user identification](/sdk/identifying-users) — anonymous stateless events each get their own isolated session.
2. **Session consolidation**: Some MCP clients (like Claude Desktop) reset session identifiers mid-conversation. AgentCat detects these inconsistencies and automatically merges fragmented sessions back together, so you see one continuous interaction instead of several broken ones.
3. **Counter denormalization**: As events arrive, session-level metrics (total events, tool calls, errors, missing tool indicators) are updated atomically. This means your dashboard queries are fast even across thousands of sessions, without needing to re-aggregate raw events on every page load.
4. **Error grouping**: When an error occurs during a tool call, AgentCat parses the error, computes a fingerprint, and groups it with similar errors across sessions. This surfaces recurring issues automatically rather than burying them in individual session logs.
5. **Goal categorization**: After a session becomes inactive, AgentCat analyzes the sessions contents to classify an agent goal, giving you insight into *what users were trying to accomplish*, not just what tools they called. For more information, see [Agent Goals](/features/goals)
This multi-stage pipeline means sessions in AgentCat are more than simple event logs. They're structured, deduplicated, enriched records that give you a complete picture of every user interaction.
## Projects
Projects are the top-level organizational unit for your analytics. Each project represents a distinct MCP server or deployment, identified by a unique project ID (like `proj_abc123xyz`) that you use when initializing AgentCat tracking.
Organize your projects to match your needs:
* **By server type**: Separate projects for different MCP servers (database tools, file system tools, API integrations)
* **By environment**: Keep development, staging, and production data isolated
* **By customer or team**: Track usage across different customer deployments or internal teams
Each project maintains its own session history, usage metrics, error tracking, user identification, and [custom event tracking](/sdk/custom-events).
## What Gets Tracked
AgentCat tracks all MCP protocol events:
* **Tool Operations**: `tools/list`, `tools/call`
* **Resource Operations**: `resources/list`, `resources/read`
* **Prompt Operations**: `prompts/list`, `prompts/get`
* **Completions**: `completion/complete`
* **Server Lifecycle**: `initialize`, `initialized`
* **Custom Events**: [Your own analytics events](/sdk/custom-events)
## Next Steps
Add analytics to your MCP server in minutes.
Explore real-time KPIs and usage analytics.
Step through individual sessions to debug issues.
Learn how AgentCat protects your users' data.
# Introduction
Source: https://docs.agentcat.com/index
AgentCat tells you exactly what agents are doing on your product and where they're getting stuck.
**AI agents are a new type of user.** They scale like traditional software but act like humans. And existing analytics tools fail to give you the insights and data you need to optimize your products for agents.
AgentCat helps you:
* **Replay every session:** step through exactly what your agents did and why they made each tool call
* **Understand agent goals:** see what agents were trying to accomplish and whether they were successful
* **Automatically capture issues**: track how often agents run into errors, hallucinations, and problems
* **Forward telemetry:** send enriched data to your existing tools like Datadog, Sentry, and PostHog via OTEL
* **Get a high-level overview:** visualize your traffic by client, server, location, model, and more
## What can AgentCat help with?
Replay agent sessions to debug issues and understand usage patterns
Understand *why* agents call your tools
Find the most common errors and hallucinations agents are making
Visualize usage trends, client distributions, and tool-level performance
# Datadog
Source: https://docs.agentcat.com/integrations/datadog
Integrate AgentCat with your Datadog observability stack.
## Overview
Integrate AgentCat with Datadog for monitoring and alerting. Events are sent as both logs and metrics for comprehensive observability.
## Prerequisites
1. A Datadog account with an API key
2. Your Datadog site (e.g., `datadoghq.com` for US, `datadoghq.eu` for EU)
## Configuration
```typescript TypeScript theme={null}
agentcat.track(server, null, {
exporters: {
datadog: {
type: "datadog",
apiKey: process.env.DD_API_KEY,
site: "datadoghq.com",
service: "my-mcp-server",
env: "production"
}
}
})
```
```python Python theme={null}
agentcat.track(server, None, agentcat.AgentCatOptions(
exporters={
"datadog": {
"type": "datadog",
"api_key": os.getenv("DD_API_KEY"),
"site": "datadoghq.com",
"service": "my-mcp-server",
"env": "production"
}
}
))
```
```go Go theme={null}
agentcat.Track(s, "", &agentcat.Options{
Exporters: map[string]any{
"datadog": map[string]any{
"type": "datadog",
"apiKey": os.Getenv("DD_API_KEY"),
"site": "datadoghq.com",
"service": "my-mcp-server",
"env": "production",
},
},
})
```
## Configuration Fields
| Field | Type | Required | Description |
| -------------------- | ------ | -------- | ---------------------------------------------------- |
| `api_key` / `apiKey` | string | Yes | Your Datadog API key |
| `site` | string | Yes | Datadog site (e.g., "datadoghq.com", "datadoghq.eu") |
| `service` | string | Yes | Service name for grouping |
| `env` | string | No | Environment name (default: "production") |
## Field Mapping
### Trace and Span IDs
| AgentCat Field | Datadog Field | Format |
| -------------- | ------------- | ------------------------------------- |
| `session_id` | `trace_id` | Numeric - last 64 bits of SHA256 hash |
| `event_id` | `span_id` | Numeric - SHA256 hash |
### Logs
Each event creates a log entry with the following structure:
| Field | Value | Description |
| ----------- | ---------------------------------- | -------------------------------------------------------------------- |
| `message` | `"{event_type} - {resource_name}"` | Combined event type and resource (e.g., "tools/call - get\_weather") |
| `service` | Your configured service name | From exporter configuration |
| `ddsource` | `"agentcat"` | Fixed source identifier |
| `ddtags` | Comma-separated string | Includes `env`, `event_type`, `resource`, and `error` tags |
| `timestamp` | Unix timestamp in milliseconds | Event occurrence time |
| `status` | `"error"` or `"info"` | Based on `is_error` flag |
#### MCP Event Data
All AgentCat event fields are preserved in the `mcp` object:
| Field | Description |
| -------------------- | ------------------------------- |
| `mcp.session_id` | Session identifier |
| `mcp.event_id` | Event identifier |
| `mcp.event_type` | Event type (e.g., "tools/call") |
| `mcp.resource` | Resource name |
| `mcp.duration_ms` | Duration in milliseconds |
| `mcp.user_intent` | User's intent (if captured) |
| `mcp.actor_id` | User identifier (if identified) |
| `mcp.actor_name` | User name (if identified) |
| `mcp.client_name` | MCP client name |
| `mcp.client_version` | MCP client version |
| `mcp.server_name` | Server name |
| `mcp.server_version` | Server version |
| `mcp.is_error` | Error flag (boolean) |
| `mcp.error` | Error details (if applicable) |
### Metrics
| Metric Name | Type | Description |
| -------------------- | ----- | ------------------------------ |
| `mcp.events.count` | count | Total number of events |
| `mcp.event.duration` | gauge | Event duration in milliseconds |
| `mcp.errors.count` | count | Number of error events |
All metrics include the following tags for filtering and grouping:
| Tag | Example Value | Source |
| ------------ | ----------------- | --------------------------------------- |
| `service` | `"my-mcp-server"` | Exporter configuration |
| `env` | `"production"` | Exporter configuration |
| `event_type` | `"tools.call"` | Event type (slashes replaced with dots) |
| `resource` | `"get_weather"` | Resource name |
# Third-Party Integrations
Source: https://docs.agentcat.com/integrations/index
Connect AgentCat with your existing observability platforms.
## Overview
AgentCat can integrate with your existing observability platforms, allowing you to monitor your MCP servers using familiar tools. You can use these integrations with or without an AgentCat account.
When you configure exporters, the `project_id` parameter becomes optional:
```typescript TypeScript theme={null}
// Forward to OTLP without an AgentCat account
agentcat.track(server, null, {
exporters: {
otlp: {
type: "otlp",
endpoint: "http://localhost:4318/v1/traces"
}
}
})
```
```python Python theme={null}
# Forward to OTLP without an AgentCat account
agentcat.track(server, None, agentcat.AgentCatOptions(
exporters={
"otlp": {
"type": "otlp",
"endpoint": "http://localhost:4318/v1/traces"
}
}
))
```
```go Go theme={null}
import agentcat "go.agentcat.com/sdk/officialsdk"
// For mark3labs/mcp-go: import agentcat "go.agentcat.com/sdk/mcpgo"
// Forward to OTLP without an AgentCat account
agentcat.Track(s, "", &agentcat.Options{
Exporters: map[string]any{
"otlp": map[string]any{
"type": "otlp",
"endpoint": "http://localhost:4318/v1/traces",
},
},
})
```
## Supported Platforms
AgentCat currently supports integration with the following observability platforms:
* **[OpenTelemetry (OTLP)](/integrations/open-telemetry)** - Connect to any OTLP-compatible backend (Jaeger, Tempo, New Relic, etc.)
* **[Datadog](/integrations/datadog)** - Send logs and metrics to Datadog
* **[Sentry](/integrations/sentry)** - Track errors and logs in Sentry
* **[PostHog](/integrations/posthog)** - Send product analytics events to PostHog
## Using Multiple Integrations
You can configure multiple integrations to send events to different platforms simultaneously:
```typescript TypeScript theme={null}
agentcat.track(server, "proj_YOUR_ID", {
exporters: {
otlp: {
type: "otlp",
endpoint: "http://localhost:4318/v1/traces"
},
datadog: {
type: "datadog",
apiKey: process.env.DD_API_KEY,
site: "datadoghq.com",
service: "my-mcp-server"
},
sentry: {
type: "sentry",
dsn: process.env.SENTRY_DSN,
environment: "production"
},
posthog: {
type: "posthog",
apiKey: process.env.POSTHOG_API_KEY,
host: "https://us.i.posthog.com"
}
}
})
```
```python Python theme={null}
agentcat.track(server, "proj_YOUR_ID", agentcat.AgentCatOptions(
exporters={
"otlp": {
"type": "otlp",
"endpoint": "http://localhost:4318/v1/traces"
},
"datadog": {
"type": "datadog",
"api_key": os.getenv("DD_API_KEY"),
"site": "datadoghq.com",
"service": "my-mcp-server"
},
"sentry": {
"type": "sentry",
"dsn": os.getenv("SENTRY_DSN"),
"environment": "production"
},
"posthog": {
"type": "posthog",
"api_key": os.getenv("POSTHOG_API_KEY"),
"host": "https://us.i.posthog.com"
}
}
))
```
```go Go theme={null}
agentcat.Track(s, "proj_YOUR_ID", &agentcat.Options{
Exporters: map[string]any{
"otlp": map[string]any{
"type": "otlp",
"endpoint": "http://localhost:4318/v1/traces",
},
"datadog": map[string]any{
"type": "datadog",
"apiKey": os.Getenv("DD_API_KEY"),
"site": "datadoghq.com",
"service": "my-mcp-server",
},
"sentry": map[string]any{
"type": "sentry",
"dsn": os.Getenv("SENTRY_DSN"),
"environment": "production",
},
"posthog": map[string]any{
"type": "posthog",
"apiKey": os.Getenv("POSTHOG_API_KEY"),
"host": "https://us.i.posthog.com",
},
},
})
```
## Performance
Platform integrations are non-blocking and asynchronous. Integration failures are logged but won't interrupt your MCP server's operation or affect performance.
# OpenTelemetry
Source: https://docs.agentcat.com/integrations/open-telemetry
Connect AgentCat with OpenTelemetry-compatible platforms.
## Overview
Integrate AgentCat with any OpenTelemetry Protocol (OTLP) compatible platform like Jaeger, Grafana Tempo, New Relic, or AWS X-Ray.
Learn more about OpenTelemetry at [opentelemetry.io](https://opentelemetry.io).
## Configuration
```typescript TypeScript theme={null}
agentcat.track(server, null, {
exporters: {
otlp: {
type: "otlp",
endpoint: "http://localhost:4318/v1/traces",
protocol: "http/protobuf",
headers: {
"api-key": "your-api-key"
}
}
}
})
```
```python Python theme={null}
agentcat.track(server, None, agentcat.AgentCatOptions(
exporters={
"otlp": {
"type": "otlp",
"endpoint": "http://localhost:4318/v1/traces",
"protocol": "http/protobuf",
"headers": {
"api-key": "your-api-key"
}
}
}
))
```
```go Go theme={null}
agentcat.Track(s, "", &agentcat.Options{
Exporters: map[string]any{
"otlp": map[string]any{
"type": "otlp",
"endpoint": "http://localhost:4318/v1/traces",
"protocol": "http/protobuf",
"headers": map[string]any{
"api-key": "your-api-key",
},
},
},
})
```
## Configuration Fields
| Field | Type | Required | Description |
| ---------- | ------ | -------- | --------------------------------------------------------------------------- |
| `endpoint` | string | Yes | OTLP endpoint URL |
| `protocol` | string | No | Protocol format: `"http/protobuf"` or `"grpc"` (default: `"http/protobuf"`) |
| `headers` | object | No | Additional headers to send with requests |
## Field Mapping
AgentCat events are mapped to OTLP spans with the following structure:
### Trace and Span IDs
| AgentCat Field | OTLP Field | Format |
| -------------- | ---------- | ------------------------------------------------ |
| `session_id` | `traceId` | 32-character hex (SHA256 hash, one per session) |
| `id` | `spanId` | 16-character hex (SHA256 hash, unique per event) |
### Resource Attributes
| AgentCat Internal Field | OTLP Attribute | Description |
| ----------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `server_name` | `service.name` | MCP server name |
| `server_version` | `service.version` | MCP server version |
| `sdk_language` | `telemetry.sdk.name` | `"agentcat-python"` (Python SDK only; the TypeScript SDK reports the SDK via the OTLP scope `name: "agentcat"` instead) |
| `agentcat_version` | `telemetry.sdk.version` | AgentCat SDK version (Python SDK only) |
### Span Attributes
| AgentCat Field | OTLP Attribute | Description |
| ------------------------- | -------------------- | --------------------------------- |
| `event_type` | `mcp.event_type` | Event type (e.g., "tools/call") |
| `session_id` | `mcp.session_id` | Session identifier |
| `project_id` | `mcp.project_id` | AgentCat project ID (if provided) |
| `resource_name` | `mcp.resource_name` | Tool/prompt/resource name |
| `user_intent` | `mcp.user_intent` | User's intent (if captured) |
| `identify_actor_given_id` | `mcp.actor_id` | User identifier (if identified) |
| `identify_actor_name` | `mcp.actor_name` | User name (if identified) |
| `client_name` | `mcp.client_name` | MCP client name |
| `client_version` | `mcp.client_version` | MCP client version |
Attributes with empty or falsy string values are automatically filtered out and will not appear in the exported span.
### Span Properties
| Property | Value | Description |
| ------------------- | --------------------------- | -------------------------------------- |
| `name` | Event type or `"mcp.event"` | Span display name |
| `kind` | `2` (`SPAN_KIND_SERVER`) | Indicates server-side span |
| `startTimeUnixNano` | Event timestamp | In nanoseconds |
| `endTimeUnixNano` | Start + duration | In nanoseconds (if duration available) |
| `status.code` | `1` (OK) or `2` (ERROR) | Based on `is_error` flag |
Timestamp values are serialized as strings, not integers.
# PostHog
Source: https://docs.agentcat.com/integrations/posthog
Send MCP analytics events to PostHog as product analytics.
## Overview
Integrate AgentCat with PostHog for product analytics. Events are sent as `capture` events via the PostHog `/batch` API, giving you full visibility into MCP tool usage within your existing PostHog dashboards.
## Prerequisites
1. A PostHog project with an API key (starts with `phc_...`)
2. Your PostHog host URL (default: `https://us.i.posthog.com`)
## Configuration
```typescript TypeScript theme={null}
agentcat.track(server, null, {
exporters: {
posthog: {
type: "posthog",
apiKey: process.env.POSTHOG_API_KEY,
host: "https://us.i.posthog.com"
}
}
})
```
```python Python theme={null}
agentcat.track(server, None, agentcat.AgentCatOptions(
exporters={
"posthog": {
"type": "posthog",
"api_key": os.getenv("POSTHOG_API_KEY"),
"host": "https://us.i.posthog.com"
}
}
))
```
```go Go theme={null}
agentcat.Track(s, "", &agentcat.Options{
Exporters: map[string]any{
"posthog": map[string]any{
"type": "posthog",
"apiKey": os.Getenv("POSTHOG_API_KEY"),
"host": "https://us.i.posthog.com",
},
},
})
```
## Configuration Fields
| Field | Type | Required | Description |
| -------------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `api_key` / `apiKey` | string | Yes | Your PostHog project API key (starts with `phc_...`) |
| `host` | string | No | PostHog host URL (default: `https://us.i.posthog.com`). Use `https://eu.i.posthog.com` for EU, or your own URL for self-hosted. |
## Field Mapping
### Event Names
AgentCat events are sent as PostHog `capture` events. Standard MCP event types are mapped to PostHog-friendly names. Any unmapped event types fall back to replacing colons and slashes with underscores.
| MCP Event Type | PostHog Event Name |
| -------------------- | -------------------- |
| `mcp:tools/call` | `mcp_tool_call` |
| `mcp:tools/list` | `mcp_tools_list` |
| `mcp:initialize` | `mcp_initialize` |
| `mcp:resources/read` | `mcp_resource_read` |
| `mcp:resources/list` | `mcp_resources_list` |
| `mcp:prompts/get` | `mcp_prompt_get` |
| `mcp:prompts/list` | `mcp_prompts_list` |
### Distinct ID
PostHog's `distinct_id` is set using the following priority:
1. **Identified user ID** — if the user was identified via the `identify` callback
2. **Session ID** — falls back to the AgentCat session ID
3. **`"anonymous"`** — if neither is available
### Event Properties
Each event includes the following properties:
| Property | Description |
| ---------------- | --------------------------------------- |
| `$session_id` | AgentCat session ID |
| `resource_name` | Resource or tool name |
| `tool_name` | Tool name (only for tool call events) |
| `duration_ms` | Event duration in milliseconds |
| `server_name` | MCP server name |
| `server_version` | MCP server version |
| `client_name` | AI client name (e.g., "Claude Desktop") |
| `client_version` | AI client version |
| `project_id` | AgentCat project ID |
| `user_intent` | Agent's stated intent for the tool call |
| `is_error` | Whether the event was an error |
| `parameters` | Request parameters |
| `response` | Tool response data |
### Person Properties
When a user is identified, PostHog person properties are set via `$set`:
* `name` — the identified user's name
* Any additional fields from `identifyActorData` are merged in
### Error Events
When `is_error` is `true`, AgentCat sends **two** events to PostHog:
1. **Regular capture event** — the standard event with all properties above
2. **`$exception` event** — a native PostHog exception event with:
| Property | Description |
| ----------------------- | ------------------------------------------ |
| `$exception_message` | Error message |
| `$exception_type` | Error type or class |
| `$exception_stacktrace` | Stack trace (when available) |
| `$exception_source` | `"backend"` — marks this as a server error |
This ensures errors appear in PostHog's native error tracking and can trigger alerts.
# Sentry
Source: https://docs.agentcat.com/integrations/sentry
Connect AgentCat with Sentry for error tracking and monitoring.
## Overview
Integrate AgentCat with Sentry for error tracking and performance monitoring. Events are sent as logs, and optionally as transactions for performance analysis.
## Prerequisites
1. A Sentry project with a DSN (Data Source Name)
2. DSN can be found in your Sentry project settings under "Client Keys (DSN)"
A DSN looks like `https://a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6@o123456.ingest.us.sentry.io/789`. The examples below read it from a `SENTRY_DSN` environment variable rather than hardcoding it.
## Configuration
```typescript TypeScript theme={null}
agentcat.track(server, null, {
exporters: {
sentry: {
type: "sentry",
dsn: process.env.SENTRY_DSN,
environment: "production",
release: "1.0.0",
enableTracing: true
}
}
})
```
```python Python theme={null}
agentcat.track(server, None, agentcat.AgentCatOptions(
exporters={
"sentry": {
"type": "sentry",
"dsn": os.getenv("SENTRY_DSN"),
"environment": "production",
"release": "1.0.0",
"enable_tracing": True
}
}
))
```
```go Go theme={null}
agentcat.Track(s, "", &agentcat.Options{
Exporters: map[string]any{
"sentry": map[string]any{
"type": "sentry",
"dsn": os.Getenv("SENTRY_DSN"),
"environment": "production",
"release": "1.0.0",
"enableTracing": true,
},
},
})
```
## Configuration Fields
| Field | Type | Required | Description |
| ---------------------------------- | ------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `dsn` | string | Yes | Sentry Data Source Name from project settings |
| `environment` | string | No | Environment name (default: "production") |
| `release` | string | No | Release version for tracking deployments |
| `enableTracing` / `enable_tracing` | boolean | No | Enable performance monitoring. When true, sends transaction events to Sentry's Performance tab in addition to logs, allowing you to track duration and performance metrics. |
## Field Mapping
### Trace and Event IDs
| AgentCat Field | Sentry Field | Format |
| -------------- | ------------ | ----------------------------------- |
| `session_id` | `trace_id` | SHA256 hash (one trace per session) |
| `event_id` | `event_id` | Double SHA256 hash for uniqueness |
### Log Events
All events are sent as logs with the following structure:
| Field | Value | Description |
| ----------- | ------------------------------------- | ------------------------ |
| `timestamp` | Event timestamp | Unix timestamp |
| `level` | `"error"` or `"info"` | Based on `is_error` flag |
| `body` | `"MCP {event_type}: {resource_name}"` | Formatted message |
### Transaction Events
Transaction events are only sent when `enableTracing` is set to `true` in the
configuration.
| Field | Value | Description |
| ----------------- | ---------------------------------- | ----------------------------------- |
| `transaction` | `"{event_type} - {resource_name}"` | Transaction name |
| `start_timestamp` | Timestamp - duration | Calculated start time |
| `end_timestamp` | Event timestamp | When the event completed |
| `op` | Event type | Operation type (e.g., "tools/call") |
| `status` | `"internal_error"` or `"ok"` | Based on `is_error` flag |
### Tags
Events include the following tags for filtering and grouping:
| AgentCat Field | Tag Name | Condition |
| ------------------------- | ------------- | ------------------ |
| Environment config | `environment` | Always included |
| Release config | `release` | If configured |
| `event_type` | `event_type` | Always included |
| `resource_name` | `resource` | If available |
| `server_name` | `server_name` | If available |
| `client_name` | `client_name` | If available |
| `identify_actor_given_id` | `actor_id` | If user identified |
### Extra Data
Additional context fields stored with events:
| AgentCat Field | Extra Field | Description |
| --------------------- | ---------------- | ------------------------ |
| `session_id` | `session_id` | Session identifier |
| `project_id` | `project_id` | AgentCat project ID |
| `user_intent` | `user_intent` | User's intent |
| `duration` | `duration_ms` | Duration in milliseconds |
| `identify_actor_name` | `actor_name` | User name |
| `identify_actor_data` | `actor_data` | User metadata |
| `client_version` | `client_version` | MCP client version |
| `server_version` | `server_version` | MCP server version |
### Error Events
When `is_error` is `true`, Sentry receives:
1. A log event (always sent)
2. A transaction event (if `enableTracing` is true)
3. A separate error event for the Issues tab
This ensures proper error tracking, alerting, and issue management in Sentry.
# Certifications
Source: https://docs.agentcat.com/privacy-security/certifications
AgentCat compliance certifications and infrastructure security measures.
## SOC 2 Compliant
We undergo annual SOC 2 Type II audits, demonstrating our commitment to:
* **Security**: Protecting data against unauthorized access
* **Availability**: Ensuring systems are operational and accessible
* **Processing Integrity**: Ensuring data processing is complete, valid, and authorized
* **Confidentiality**: Protecting confidential information
* **Privacy**: Collecting, using, and disclosing personal information in accordance with our privacy policy
## Infrastructure Security
* **Encryption**: All data encrypted in transit (TLS 1.3) and at rest (AES-256)
* **Access Control**: Role-based access control with principle of least privilege
* **Infrastructure Security**: Hosted on SOC 2 compliant cloud providers
* **Regular Audits**: Quarterly security assessments and annual penetration testing
* **Data Retention**: Data is retained while your project exists; deleting a project starts a permanent deletion, with a 30-day export window before the data is irreversibly removed
# Client-Side Redaction
Source: https://docs.agentcat.com/privacy-security/client-side-redaction
Sanitize sensitive data before it leaves your environment with custom redaction functions.
AgentCat provides powerful client-side redaction capabilities, allowing you to sanitize sensitive information before it ever leaves your environment. This ensures that sensitive data never reaches our servers.
## How it works
You can provide a custom redaction function when initializing AgentCat tracking:
```typescript Typescript theme={null}
import * as agentcat from "agentcat";
agentcat.track(mcpServer, "proj_YOUR_PROJECT_ID", {
redactSensitiveInformation: async (text) => {
let result = text;
result = result.replace(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g, "");
result = result.replace(/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g, "");
return result;
},
});
```
```python Python theme={null}
import agentcat
import re
def redact_sensitive_data(text):
result = text
result = re.sub(r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}', '', result)
result = re.sub(r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b', '', result)
return result
agentcat.track(server, "proj_YOUR_PROJECT_ID", agentcat.AgentCatOptions(
redact_sensitive_information=redact_sensitive_data,
))
```
```go Go theme={null}
import agentcat "go.agentcat.com/sdk/officialsdk"
// For mark3labs/mcp-go: import agentcat "go.agentcat.com/sdk/mcpgo"
agentcat.Track(s, "proj_YOUR_PROJECT_ID", &agentcat.Options{
RedactSensitiveInformation: func(text string) string {
result := text
result = regexp.MustCompile(`[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}`).ReplaceAllString(result, "")
result = regexp.MustCompile(`\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b`).ReplaceAllString(result, "")
return result
},
})
```
## Key Features
* **Recursive Application**: Redaction is applied to all string values in event data, including nested objects and arrays
* **Protected Fields**: Essential analytics fields (like `sessionId`, `projectId`, `eventType`) are preserved to maintain functionality
* **Type Preservation**: Non-string values (numbers, booleans, dates) are preserved without modification
* **Error Handling**: If redaction fails, the entire event is skipped to prevent accidental data leakage
# Privacy & Security
Source: https://docs.agentcat.com/privacy-security/index
How AgentCat protects your users' data with multiple layers of privacy and security.
AgentCat is built with privacy at every layer, from client-side redaction before data leaves your server, to automatic PII detection on ours.
AgentCat is co-founded by Naseem Al-Naji, creator of [Opal](https://opal.dev), bringing years of experience in building privacy-first developer tools that handle sensitive data with the utmost care.
Sanitize sensitive data before it leaves your environment with custom redaction functions.
Automatic pattern-based redaction of sensitive values in event payloads.
SOC 2 Type II compliance, encryption, and infrastructure security.
## Disabling Telemetry
While AgentCat provides valuable analytics, we understand that some users may want to disable telemetry entirely. You can implement this in your MCP server by conditionally calling the track function.
### Disabling Tracking Entirely
Add environment variable support to your MCP server so users can opt out:
```typescript Typescript theme={null}
import * as agentcat from "agentcat";
// Check for telemetry opt-out
const telemetryEnabled = process.env.DISABLE_USER_ANALYTICS !== 'true';
// Only initialize tracking if telemetry is enabled
if (telemetryEnabled) {
agentcat.track(mcpServer, "proj_YOUR_PROJECT_ID");
}
```
```python Python theme={null}
import os
import agentcat
# Check for telemetry opt-out
telemetry_enabled = os.environ.get("DISABLE_USER_ANALYTICS") != "true"
# Only initialize tracking if telemetry is enabled
if telemetry_enabled:
agentcat.track(server, "proj_YOUR_PROJECT_ID")
```
```go Go theme={null}
import agentcat "go.agentcat.com/sdk/officialsdk"
// For mark3labs/mcp-go: import agentcat "go.agentcat.com/sdk/mcpgo"
// Check for telemetry opt-out
if os.Getenv("DISABLE_USER_ANALYTICS") != "true" {
agentcat.Track(s, "proj_YOUR_PROJECT_ID", nil)
}
```
### Disabling Auto-Capture
By default, AgentCat automatically captures all MCP protocol events (tool calls, initialization, tool listings). If you only need [custom events](/sdk/custom-events) without automatic event capture, set `enableTracing` to `false`:
```typescript Typescript theme={null}
import * as agentcat from "agentcat";
agentcat.track(mcpServer, "proj_YOUR_PROJECT_ID", {
enableTracing: false
});
```
```python Python theme={null}
import agentcat
agentcat.track(server, "proj_YOUR_PROJECT_ID",
agentcat.AgentCatOptions(enable_tracing=False))
```
```go Go theme={null}
agentcat.Track(s, "proj_YOUR_PROJECT_ID", &agentcat.Options{
DisableTracing: true,
})
```
When tracing is disabled:
* **Tool call events** are not recorded
* **User identification** still works normally
* **Custom events** via `publishCustomEvent` are still sent
* **Exporters** (OpenTelemetry, Datadog, Sentry) still receive custom and identify events
### Anonymizing User Sessions
For users who want analytics without user identification, pass an `identify` function that returns `null`:
```typescript Typescript theme={null}
import * as agentcat from "agentcat";
agentcat.track(mcpServer, "proj_YOUR_PROJECT_ID", {
identify: async () => null
});
```
```python Python theme={null}
import agentcat
agentcat.track(server, "proj_YOUR_PROJECT_ID",
agentcat.AgentCatOptions(identify=lambda req, ctx: None))
```
```go Go theme={null}
agentcat.Track(s, "proj_YOUR_PROJECT_ID", &agentcat.Options{
Identify: func(ctx context.Context, req *mcp.CallToolRequest) *agentcat.UserIdentity {
return nil
},
})
```
### Implementation Best Practices
1. **Document Environment Variables**: Clearly document telemetry options in your MCP server's README
2. **Default to Privacy**: Consider making telemetry opt-in rather than opt-out
3. **Respect User Choice**: Always check environment variables before initializing tracking
4. **Provide Granular Control**: Allow users to disable specific types of tracking
Example implementation with multiple privacy options:
```typescript Typescript theme={null}
import * as agentcat from "agentcat";
const config = {
telemetry: process.env.DISABLE_USER_ANALYTICS !== 'true',
anonymize: process.env.ANONYMIZE_SESSIONS === 'true'
};
// Initialize AgentCat based on user preferences
if (config.telemetry) {
agentcat.track(mcpServer, "proj_YOUR_PROJECT_ID", {
identify: config.anonymize ? async () => null : undefined
});
}
```
```python Python theme={null}
import os
import agentcat
telemetry_enabled = os.environ.get("DISABLE_USER_ANALYTICS") != "true"
anonymize = os.environ.get("ANONYMIZE_SESSIONS") == "true"
# Initialize AgentCat based on user preferences
if telemetry_enabled:
agentcat.track(server, "proj_YOUR_PROJECT_ID", agentcat.AgentCatOptions(
identify=(lambda req, ctx: None) if anonymize else None
))
```
```go Go theme={null}
telemetryEnabled := os.Getenv("DISABLE_USER_ANALYTICS") != "true"
anonymize := os.Getenv("ANONYMIZE_SESSIONS") == "true"
// Initialize AgentCat based on user preferences
if telemetryEnabled {
opts := &agentcat.Options{}
if anonymize {
opts.Identify = func(ctx context.Context, req *mcp.CallToolRequest) *agentcat.UserIdentity {
return nil
}
}
agentcat.Track(s, "proj_YOUR_PROJECT_ID", opts)
}
```
# Server-Side Redaction
Source: https://docs.agentcat.com/privacy-security/server-side-redaction
Automatic pattern-based redaction of sensitive values in event payloads before storage.
In addition to [client-side redaction](/privacy-security/client-side-redaction), AgentCat redacts sensitive values on our servers before events are stored, catching data that client-side filters may have missed.
## What gets redacted
When redaction is enabled, the user intent, parameters, response, and error payloads of every incoming event are scanned, and matched values are replaced with placeholders:
* **Email addresses** → ``
* **IP addresses** (IPv4 and IPv6) → ``
* **URLs and hostnames** → ``
* **Credentials** — Bearer, Basic, Digest, Token, and API-key authorization values
* **Unique identifiers** — UUIDs and trace IDs
* Additional sensitive patterns, such as dates and long numeric identifiers
## How it works
1. **Pattern-based detection**: payload fields are scanned recursively — including nested objects and lists — against a curated set of patterns
2. **Applied before storage**: redaction happens during event processing, so matched values are replaced before the event is written
3. **Best effort**: pattern matching reduces the chance that sensitive data is stored, but it cannot guarantee catching everything. Keep sensitive data out of your events at the source where possible — see [client-side redaction](/privacy-security/client-side-redaction)
## Configuration
Server-side redaction is **enabled by default** for all projects. You can toggle this per project in **Settings > Projects > Edit > "Redact sensitive strings"**.
Toggling the setting only affects new events. Previously stored events are not modified.
# Quickstart
Source: https://docs.agentcat.com/quickstart
Install the AgentCat SDK in just 1 minute.
## Installation
Run the following command to install the open source [AgentCat SDK](https://github.com/agentcathq):
```bash npm theme={null}
npm i agentcat
```
```bash yarn theme={null}
yarn add agentcat
```
```bash pnpm theme={null}
pnpm add agentcat
```
```bash pip theme={null}
pip install agentcat
```
```bash poetry theme={null}
poetry add agentcat
```
```bash uv theme={null}
uv add agentcat
```
```bash go theme={null}
go get go.agentcat.com/sdk/officialsdk@latest
```
```typescript Typescript theme={null}
import * as agentcat from "agentcat";
/*
Example MCP server
const mcpServer = new Server({
name: "my-mcp-server",
version: "1.0.0"
});
mcpServer.tool("my-tool", ...)
*/
// Call .track() AFTER tool call registering
agentcat.track(mcpServer, "proj_YOUR_PROJECT_ID")
```
```python Python theme={null}
import agentcat
"""
Example MCP server (community FastMCP also works: from fastmcp import FastMCP)
server = FastMCP(
name="my-mcp-server",
version="1.0.0"
)
@server.tool()
async def my_tool(params):
pass
"""
# Call .track() AFTER tool call registering
agentcat.track(server, "proj_YOUR_PROJECT_ID")
```
```go Go theme={null}
import agentcat "go.agentcat.com/sdk/officialsdk"
// For mark3labs/mcp-go: import agentcat "go.agentcat.com/sdk/mcpgo"
/*
Example MCP server (official Go MCP SDK)
s := mcp.NewServer(&mcp.Implementation{
Name: "my-mcp-server",
Version: "1.0.0",
}, nil)
mcp.AddTool(s, ...)
*/
// Call Track() AFTER registering tools
agentcat.Track(s, "proj_YOUR_PROJECT_ID", nil)
```
You can grab your project ID from [AgentCat's dashboard](https://agentcat.com/app/settings/projects/).
## Using without an AgentCat account
You can forward events directly to your observability platform:
```typescript TypeScript theme={null}
agentcat.track(server, null, {
exporters: {
otlp: {
type: "otlp",
endpoint: "http://localhost:4318/v1/traces"
}
}
})
```
```python Python theme={null}
agentcat.track(server, None, agentcat.AgentCatOptions(
exporters={
"otlp": {
"type": "otlp",
"endpoint": "http://localhost:4318/v1/traces"
}
}
))
```
```go Go theme={null}
agentcat.Track(s, "", &agentcat.Options{
Exporters: map[string]any{
"otlp": map[string]any{
"type": "otlp",
"endpoint": "http://localhost:4318/v1/traces",
},
},
})
```
Learn more about [integration options →](/integrations)
## Next steps
Know which user is behind each session
Turn on extra logging when things aren't working right
Control what context AgentCat captures from each tool call
Track your own events alongside the ones AgentCat captures
# Custom Events
Source: https://docs.agentcat.com/sdk/custom-events
Track custom events beyond what AgentCat captures out of the box.
AgentCat automatically tracks MCP protocol events like tool calls and session initialization. Custom events let you go further and track actions specific to your application: feature usage, billing milestones, or anything else that matters to your product.
Custom events appear in the session replay timeline with a bolt icon, alongside automatic MCP events. The `resourceName` is used as the label. Click on any custom event to see its full details, including `parameters`, `response`, and `message`.
If `isError` is set to `true`, the event is styled as an error in the timeline.
## Publishing Events
After calling `agentcat.track()`, use `publishCustomEvent` to send events tied to the current session. Pass the tracked server instance as the first argument.
```typescript TypeScript theme={null}
import * as agentcat from "agentcat"
await agentcat.publishCustomEvent(mcpServer, "proj_abc123xyz", {
resourceName: "subscription_upgraded",
parameters: {
plan: "pro",
source: "settings_page"
}
})
```
```python Python theme={null}
import agentcat
agentcat.publish_custom_event(server, "proj_abc123xyz", {
"resource_name": "subscription_upgraded",
"parameters": {
"plan": "pro",
"source": "settings_page"
}
})
```
```go Go theme={null}
import agentcat "go.agentcat.com/sdk/officialsdk"
// For mark3labs/mcp-go: import agentcat "go.agentcat.com/sdk/mcpgo"
agentcat.PublishCustomEvent(s, "proj_abc123xyz", agentcat.EventData{
ResourceName: "subscription_upgraded",
Parameters: map[string]any{
"plan": "pro",
"source": "settings_page",
},
})
```
## Using a Session ID
If you have a session ID instead of the server instance, you can pass it directly as a string. AgentCat will derive a stable session from it.
```typescript TypeScript theme={null}
await agentcat.publishCustomEvent("user-session-12345", "proj_abc123xyz", {
resourceName: "report_exported"
})
```
```python Python theme={null}
agentcat.publish_custom_event("user-session-12345", "proj_abc123xyz", {
"resource_name": "report_exported"
})
```
```go Go theme={null}
agentcat.PublishCustomEvent("user-session-12345", "proj_abc123xyz", agentcat.EventData{
ResourceName: "report_exported",
})
```
## publishCustomEvent Reference
| Parameter | Type | Required | Description |
| ------------------- | ---------------- | -------- | ----------------------------------------------------- |
| `serverOrSessionId` | Server \| string | Yes | A tracked MCP server instance, or a session ID string |
| `projectId` | string | Yes | Your AgentCat project ID (e.g. `proj_abc123xyz`) |
| `eventData` | object | No | The event payload (see below) |
## Event Data
| Field | Type | Description |
| -------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `resourceName` | string | A name for the event. Shows as the label in the dashboard timeline. If omitted, the event shows as "Custom". |
| `parameters` | object | Arbitrary data to attach to the event. Visible in the event detail panel. |
| `response` | object | Response data to attach to the event. |
| `message` | string | A human-readable description of what happened. |
| `duration` | number | Duration of the event in milliseconds. |
| `isError` | boolean | Set to `true` to mark this event as an error. |
| `error` | object | Error details. Typically includes `message` and optionally `type` or `code`. |
| `tags` | Record\ | Indexed string key-value pairs for filtering and grouping. Subject to [tag validation rules](/sdk/event-tags-properties#tag-validation-rules). |
| `properties` | Record\ | Arbitrary JSON metadata. No client-side validation constraints. |
All fields in `eventData` are optional. You can call `publishCustomEvent` with just the server and project ID to record a minimal custom event.
You can also enrich every auto-captured event with metadata — see [Event Tags & Properties](/sdk/event-tags-properties).
## Examples
### Tracking Feature Usage
Record when users interact with specific features to understand adoption and usage patterns.
```typescript TypeScript theme={null}
await agentcat.publishCustomEvent(mcpServer, "proj_abc123xyz", {
resourceName: "feature_used",
parameters: {
feature: "data_export",
format: "json",
recordCount: 250
}
})
```
```python Python theme={null}
agentcat.publish_custom_event(server, "proj_abc123xyz", {
"resource_name": "feature_used",
"parameters": {
"feature": "data_export",
"format": "json",
"record_count": 250
}
})
```
```go Go theme={null}
agentcat.PublishCustomEvent(s, "proj_abc123xyz", agentcat.EventData{
ResourceName: "feature_used",
Parameters: map[string]any{
"feature": "data_export",
"format": "json",
"recordCount": 250,
},
})
```
### Tracking Errors
Capture application-level errors that aren't tied to a specific tool call.
```typescript TypeScript theme={null}
await agentcat.publishCustomEvent(mcpServer, "proj_abc123xyz", {
resourceName: "rate_limit_hit",
isError: true,
error: { message: "Rate limit exceeded", code: "RATE_LIMIT" },
parameters: {
endpoint: "/api/search",
limit: 100
}
})
```
```python Python theme={null}
agentcat.publish_custom_event(server, "proj_abc123xyz", {
"resource_name": "rate_limit_hit",
"is_error": True,
"error": {"message": "Rate limit exceeded", "code": "RATE_LIMIT"},
"parameters": {
"endpoint": "/api/search",
"limit": 100
}
})
```
```go Go theme={null}
agentcat.PublishCustomEvent(s, "proj_abc123xyz", agentcat.EventData{
ResourceName: "rate_limit_hit",
IsError: true,
Error: map[string]any{"message": "Rate limit exceeded", "code": "RATE_LIMIT"},
Parameters: map[string]any{
"endpoint": "/api/search",
"limit": 100,
},
})
```
### Tracking User Milestones
Track when users reach key points in their journey, with duration to measure how long it took.
```typescript TypeScript theme={null}
await agentcat.publishCustomEvent(mcpServer, "proj_abc123xyz", {
resourceName: "onboarding_completed",
message: "User finished all onboarding steps",
parameters: {
stepsCompleted: 5
},
duration: 200000
})
```
```python Python theme={null}
agentcat.publish_custom_event(server, "proj_abc123xyz", {
"resource_name": "onboarding_completed",
"message": "User finished all onboarding steps",
"parameters": {
"steps_completed": 5
},
"duration": 200000
})
```
```go Go theme={null}
agentcat.PublishCustomEvent(s, "proj_abc123xyz", agentcat.EventData{
ResourceName: "onboarding_completed",
Message: "User finished all onboarding steps",
Parameters: map[string]any{
"stepsCompleted": 5,
},
Duration: 200000,
})
```
# Debug Mode
Source: https://docs.agentcat.com/sdk/debug-mode
Enable debug logging to troubleshoot your AgentCat integration.
AgentCat can write detailed logs to `~/agentcat.log` to help you troubleshoot integration issues. Debug mode is off by default and needs to be enabled in your tracking configuration.
The log file includes information about event batching, session management, telemetry delivery, and any errors the SDK encounters.
## Enabling Debug Mode
Pass the debug option when calling `agentcat.track()`:
```typescript TypeScript theme={null}
import * as agentcat from "agentcat"
agentcat.track(mcpServer, "proj_abc123xyz", {
debug: true
})
```
```python Python theme={null}
import agentcat
agentcat.track(server, "proj_abc123xyz",
agentcat.AgentCatOptions(debug_mode=True))
```
```go Go theme={null}
import agentcat "go.agentcat.com/sdk/officialsdk"
// For mark3labs/mcp-go: import agentcat "go.agentcat.com/sdk/mcpgo"
agentcat.Track(s, "proj_abc123xyz", &agentcat.Options{
Debug: true,
})
```
### Environment Variable
You can also enable debug mode with an environment variable instead of changing your code:
```bash theme={null}
AGENTCAT_DEBUG_MODE=true
```
This accepts `true`, `1`, `yes`, or `on` (case-insensitive).
## Reading the Log File
Logs are written to `~/agentcat.log`. You can tail the file while your server is running to see output in real time:
```bash theme={null}
tail -f ~/agentcat.log
```
## When to Use Debug Mode
Debug mode is useful when:
* Events aren't showing up in the AgentCat dashboard
* You want to verify that tracking is set up correctly
* You need to confirm that user identification is working
* You're troubleshooting exporter or telemetry delivery issues
Debug mode is meant for development and troubleshooting. In production, leave it off to avoid unnecessary disk writes.
# Event Tags & Properties
Source: https://docs.agentcat.com/sdk/event-tags-properties
Attach structured tags and flexible metadata to every event AgentCat captures.
AgentCat's SDKs automatically capture rich telemetry on every tool call and major event in your MCP server. But sometimes, you need additional information to query or debug events. Event tags and properties let you attach that context:
* **Tags**: string key-value pairs designed for filtering and grouping in the dashboard and exported data
* **Properties**: flexible JSON for arbitrary context like feature flags, device info, or build metadata
Both are callback options on `agentcat.track()` that attach metadata to every auto-captured event - tool calls, tool lists, and session initialization. You set them up once and every event gets enriched automatically, without modifying individual tool handlers.
Both callbacks receive the same `(request, extra)` arguments as [`identify`](/sdk/identifying-users), and if they throw or return null, the event is still sent without metadata.
## Event Tags
Use `eventTags` to attach structured, filterable metadata to every event. Tags are ideal for values you'll want to filter or group by in the dashboard, such as environments, regions, trace IDs, or deployment versions.
```typescript TypeScript theme={null}
agentcat.track(mcpServer, "proj_abc123xyz", {
eventTags: async (request, extra) => ({
env: process.env.NODE_ENV ?? "development",
region: "us-east-1",
trace_id: extra?.requestContext?.traceId ?? "none",
}),
});
```
```python Python theme={null}
import os
import agentcat
def get_event_tags(request, context):
return {
"env": os.environ.get("APP_ENV", "development"),
"region": "us-east-1",
"trace_id": getattr(context, "trace_id", "none"),
}
agentcat.track(server, "proj_abc123xyz",
agentcat.AgentCatOptions(event_tags=get_event_tags))
```
```go Go theme={null}
import (
"os"
"github.com/modelcontextprotocol/go-sdk/mcp"
agentcat "go.agentcat.com/sdk/officialsdk"
)
agentcat.Track(s, "proj_abc123xyz", &agentcat.Options{
EventTags: func(ctx context.Context, req *mcp.CallToolRequest) map[string]string {
env := os.Getenv("APP_ENV")
if env == "" {
env = "development"
}
return map[string]string{
"env": env,
"region": "us-east-1",
}
},
})
```
## Event Properties
Use `eventProperties` to attach flexible JSON metadata. Properties have no client-side validation constraints beyond being valid JSON.
```typescript TypeScript theme={null}
agentcat.track(mcpServer, "proj_abc123xyz", {
eventProperties: async (request, extra) => ({
device: "desktop",
app_version: "2.1.0",
feature_flags: ["dark_mode", "beta_ui"],
build: {
commit: "abc123",
branch: "main",
},
}),
});
```
```python Python theme={null}
import agentcat
def get_event_properties(request, context):
return {
"device": "desktop",
"app_version": "2.1.0",
"feature_flags": ["dark_mode", "beta_ui"],
"build": {
"commit": "abc123",
"branch": "main",
},
}
agentcat.track(server, "proj_abc123xyz",
agentcat.AgentCatOptions(event_properties=get_event_properties))
```
```go Go theme={null}
agentcat.Track(s, "proj_abc123xyz", &agentcat.Options{
EventProperties: func(ctx context.Context, req *mcp.CallToolRequest) map[string]any {
return map[string]any{
"device": "desktop",
"app_version": "2.1.0",
"feature_flags": []string{"dark_mode", "beta_ui"},
"build": map[string]any{
"commit": "abc123",
"branch": "main",
},
}
},
})
```
## Using Both Together
Use tags for the values you'd filter by in a dashboard and properties for the detail you'd want when clicking into a specific event.
```typescript TypeScript theme={null}
agentcat.track(mcpServer, "proj_abc123xyz", {
identify: async (request, extra) => {
const userId = request.params?.arguments?.userId;
if (!userId) return null;
return { userId };
},
eventTags: async (request, extra) => ({
env: process.env.NODE_ENV ?? "development",
region: process.env.AWS_REGION ?? "unknown",
}),
eventProperties: async (request, extra) => ({
tool_name: request.params?.name,
server_version: process.env.npm_package_version,
}),
});
```
```python Python theme={null}
import os
import agentcat
from agentcat import UserIdentity
def identify_user(request, context):
user_id = request.params.arguments.get("userId")
if not user_id:
return None
return UserIdentity(user_id=user_id)
def get_event_tags(request, context):
return {
"env": os.environ.get("APP_ENV", "development"),
"region": os.environ.get("AWS_REGION", "unknown"),
}
def get_event_properties(request, context):
return {
"tool_name": getattr(request.params, "name", None),
"server_version": os.environ.get("APP_VERSION"),
}
agentcat.track(server, "proj_abc123xyz", agentcat.AgentCatOptions(
identify=identify_user,
event_tags=get_event_tags,
event_properties=get_event_properties,
))
```
```go Go theme={null}
import (
"os"
"github.com/modelcontextprotocol/go-sdk/mcp"
agentcat "go.agentcat.com/sdk/officialsdk"
)
agentcat.Track(s, "proj_abc123xyz", &agentcat.Options{
Identify: func(ctx context.Context, req *mcp.CallToolRequest) *agentcat.UserIdentity {
args := req.GetArguments()
userID, _ := args["userId"].(string)
if userID == "" {
return nil
}
return &agentcat.UserIdentity{UserID: userID}
},
EventTags: func(ctx context.Context, req *mcp.CallToolRequest) map[string]string {
env := os.Getenv("APP_ENV")
if env == "" {
env = "development"
}
return map[string]string{
"env": env,
"region": os.Getenv("AWS_REGION"),
}
},
EventProperties: func(ctx context.Context, req *mcp.CallToolRequest) map[string]any {
return map[string]any{
"tool_name": req.Params.Name,
"server_version": os.Getenv("APP_VERSION"),
}
},
})
```
## Examples
### Tagging by Environment
Tag events with your deployment environment and region to compare error rates and tool usage patterns across production, staging, and development.
```typescript TypeScript theme={null}
agentcat.track(mcpServer, "proj_abc123xyz", {
eventTags: async (request, extra) => ({
env: process.env.NODE_ENV ?? "development",
region: process.env.AWS_REGION ?? "unknown",
version: process.env.npm_package_version ?? "0.0.0",
}),
});
```
```python Python theme={null}
import os
import agentcat
agentcat.track(server, "proj_abc123xyz", agentcat.AgentCatOptions(
event_tags=lambda request, context: {
"env": os.environ.get("APP_ENV", "development"),
"region": os.environ.get("AWS_REGION", "unknown"),
"version": os.environ.get("APP_VERSION", "0.0.0"),
}
))
```
```go Go theme={null}
agentcat.Track(s, "proj_abc123xyz", &agentcat.Options{
EventTags: func(ctx context.Context, req *mcp.CallToolRequest) map[string]string {
return map[string]string{
"env": os.Getenv("APP_ENV"),
"region": os.Getenv("AWS_REGION"),
"version": os.Getenv("APP_VERSION"),
}
},
})
```
### Attaching Feature Flags
Attach feature flags and build metadata as properties so you can see exactly what configuration was active when a tool call happened — useful for debugging why a tool behaves differently for certain users.
```typescript TypeScript theme={null}
agentcat.track(mcpServer, "proj_abc123xyz", {
eventProperties: async (request, extra) => ({
feature_flags: getActiveFlags(),
build: {
commit: process.env.GIT_SHA,
branch: process.env.GIT_BRANCH,
ci: process.env.CI === "true",
},
}),
});
```
```python Python theme={null}
import os
import agentcat
agentcat.track(server, "proj_abc123xyz", agentcat.AgentCatOptions(
event_properties=lambda request, context: {
"feature_flags": get_active_flags(),
"build": {
"commit": os.environ.get("GIT_SHA"),
"branch": os.environ.get("GIT_BRANCH"),
"ci": os.environ.get("CI") == "true",
},
}
))
```
```go Go theme={null}
agentcat.Track(s, "proj_abc123xyz", &agentcat.Options{
EventProperties: func(ctx context.Context, req *mcp.CallToolRequest) map[string]any {
return map[string]any{
"feature_flags": getActiveFlags(),
"build": map[string]any{
"commit": os.Getenv("GIT_SHA"),
"branch": os.Getenv("GIT_BRANCH"),
"ci": os.Getenv("CI") == "true",
},
}
},
})
```
### Correlating with External Observability
Tag events with trace and span IDs from your existing observability stack to link AgentCat events directly to traces in Datadog, Sentry, or any OpenTelemetry-compatible platform.
```typescript TypeScript theme={null}
agentcat.track(mcpServer, "proj_abc123xyz", {
eventTags: async (request, extra) => ({
trace_id: extra?.requestContext?.traceId ?? "none",
span_id: extra?.requestContext?.spanId ?? "none",
service: "my-mcp-server",
}),
});
```
```python Python theme={null}
import agentcat
def get_event_tags(request, context):
return {
"trace_id": getattr(context, "trace_id", "none"),
"span_id": getattr(context, "span_id", "none"),
"service": "my-mcp-server",
}
agentcat.track(server, "proj_abc123xyz",
agentcat.AgentCatOptions(event_tags=get_event_tags))
```
```go Go theme={null}
agentcat.Track(s, "proj_abc123xyz", &agentcat.Options{
EventTags: func(ctx context.Context, req *mcp.CallToolRequest) map[string]string {
traceID := "none"
spanID := "none"
if sc := trace.SpanContextFromContext(ctx); sc.IsValid() {
traceID = sc.TraceID().String()
spanID = sc.SpanID().String()
}
return map[string]string{
"trace_id": traceID,
"span_id": spanID,
"service": "my-mcp-server",
}
},
})
```
## Tag Validation Rules
Tags are validated client-side before being attached to events. Invalid entries are silently dropped with a warning written to `~/agentcat.log`.
* **Keys**: Must be 32 characters or fewer and match `[a-zA-Z0-9_.:\- ]` (letters, digits, dots, underscores, colons, hyphens, spaces)
* **Values**: Must be strings, 200 characters or fewer, and cannot contain newline characters
* **Max entries**: 50 tags per event — entries beyond the limit are dropped
Event properties have no client-side validation constraints — any valid JSON
is accepted. Only tags are validated.
Tag validation applies to both `eventTags` callback results and inline `tags`
on [custom events](/sdk/custom-events). Invalid entries are dropped, but the
event is still sent.
# Identifying Users
Source: https://docs.agentcat.com/sdk/identifying-users
How to use AgentCat to identify your user's sessions.
AgentCat allows you to identify users and associate their sessions with specific user information. This lets you to track usage patterns, understand user behavior, and provide personalized experiences.
User identification happens automatically when a tool is invoked. In stateful mode (the default), this happens once on the first tool call and persists for the session. In [stateless mode](/sdk/stateless-servers), it runs on every request since there is no persistent session.
## Basic Implementation
To identify users, provide an `identify` callback function when tracking your MCP server. The simplest approach is to pull the user ID directly from the tool call arguments:
```typescript TypeScript theme={null}
agentcat.track(mcpServer, "proj_abc123xyz", {
identify: async (request, extra) => {
const userId = request.params?.arguments?.userId;
if (!userId) return null;
return {
userId,
userName: request.params?.arguments?.userName
};
}
});
```
```python Python theme={null}
import agentcat
from agentcat import UserIdentity
def identify_user(request, context):
user_id = request.params.arguments.get('userId')
if not user_id:
return None
return UserIdentity(
user_id=user_id,
user_name=request.params.arguments.get('userName')
)
agentcat.track(server, "proj_abc123xyz",
agentcat.AgentCatOptions(identify=identify_user))
```
```go Go theme={null}
import (
"github.com/modelcontextprotocol/go-sdk/mcp"
agentcat "go.agentcat.com/sdk/officialsdk"
// For mark3labs/mcp-go: agentcat "go.agentcat.com/sdk/mcpgo"
)
agentcat.Track(s, "proj_abc123xyz", &agentcat.Options{
Identify: func(ctx context.Context, req *mcp.CallToolRequest) *agentcat.UserIdentity {
args := req.GetArguments()
userID, _ := args["userId"].(string)
if userID == "" {
return nil
}
userName, _ := args["userName"].(string)
return &agentcat.UserIdentity{
UserID: userID,
UserName: userName,
}
},
})
```
## User Identity Object
The identify function should return a `UserIdentity` object with the following structure:
```typescript TypeScript theme={null}
interface UserIdentity {
userId: string; // Required: Unique identifier
userName?: string; // Optional: Display name
userData?: Record; // Optional: Additional metadata
}
```
```python Python theme={null}
from agentcat import UserIdentity
UserIdentity(
user_id="user123", # Required: Unique identifier
user_name="John Doe", # Optional: Display name
user_data={ # Optional: Additional metadata
"plan": "premium",
"company": "Acme Corp"
}
)
```
```go Go theme={null}
agentcat.UserIdentity{
UserID: "user123", // Required: Unique identifier
UserName: "John Doe", // Optional: Display name
UserData: map[string]any{ // Optional: Additional metadata
"plan": "premium",
"company": "Acme Corp",
},
}
```
## Common Patterns
### Token-Based Authentication
Extract user information from authentication tokens:
```typescript TypeScript theme={null}
agentcat.track(mcpServer, "proj_abc123xyz", {
identify: async (request, extra) => {
// Get token from request arguments or auth info
const token = request.params?.arguments?.token ||
extra.authInfo?.token;
// Validate token and fetch user
const user = await validateTokenAndGetUser(token);
return {
userId: user.id,
userName: user.email,
userData: {
role: user.role,
organization: user.orgId
}
};
}
});
```
```python Python theme={null}
from mcp.server.auth.middleware.auth_context import get_access_token
def identify_from_token(request, context):
# Try to get token from MCP auth context first
access_token = get_access_token()
if access_token:
token = access_token.token
else:
# Fall back to request arguments
token = request.params.arguments.get('token')
# Validate token and fetch user
user = validate_token_and_get_user(token)
return UserIdentity(
user_id=user.id,
user_name=user.email,
user_data={
'role': user.role,
'organization': user.org_id
}
)
agentcat.track(server, "proj_abc123xyz",
agentcat.AgentCatOptions(identify=identify_from_token))
```
```go Go theme={null}
agentcat.Track(s, "proj_abc123xyz", &agentcat.Options{
Identify: func(ctx context.Context, req *mcp.CallToolRequest) *agentcat.UserIdentity {
// Get token from request arguments or context
args := req.GetArguments()
token, _ := args["token"].(string)
// Validate token and fetch user
user, err := validateTokenAndGetUser(token)
if err != nil {
return nil
}
return &agentcat.UserIdentity{
UserID: user.ID,
UserName: user.Email,
UserData: map[string]any{
"role": user.Role,
"organization": user.OrgID,
},
}
},
})
```
### API Key Identification
Identify users based on API keys:
```typescript TypeScript theme={null}
agentcat.track(mcpServer, "proj_abc123xyz", {
identify: async (request, extra) => {
const apiKey = request.params?.arguments?.apiKey;
const account = await getAccountByApiKey(apiKey);
return {
userId: account.id,
userName: account.organizationName,
userData: {
tier: account.tier,
monthlyUsage: account.currentUsage,
region: account.region
}
};
}
});
```
```python Python theme={null}
def identify_from_api_key(request, context):
api_key = request.params.arguments.get('apiKey')
account = get_account_by_api_key(api_key)
return UserIdentity(
user_id=account.id,
user_name=account.organization_name,
user_data={
'tier': account.tier,
'monthly_usage': account.current_usage,
'region': account.region
}
)
agentcat.track(server, "proj_abc123xyz",
agentcat.AgentCatOptions(identify=identify_from_api_key))
```
```go Go theme={null}
agentcat.Track(s, "proj_abc123xyz", &agentcat.Options{
Identify: func(ctx context.Context, req *mcp.CallToolRequest) *agentcat.UserIdentity {
args := req.GetArguments()
apiKey, _ := args["apiKey"].(string)
account := getAccountByApiKey(apiKey)
return &agentcat.UserIdentity{
UserID: account.ID,
UserName: account.OrganizationName,
UserData: map[string]any{
"tier": account.Tier,
"monthlyUsage": account.CurrentUsage,
"region": account.Region,
},
}
},
})
```
### MCP Authentication
For servers using MCP's built-in authentication:
```typescript TypeScript theme={null}
agentcat.track(mcpServer, "proj_abc123xyz", {
identify: async (request, extra) => {
// Access auth token from extra parameter
const authToken = extra.authInfo?.token;
if (!authToken) {
return null; // Anonymous session
}
// Use the MCP auth token to identify user
const user = await getUserFromMCPToken(authToken);
return {
userId: user.id,
userName: user.email,
userData: {
authProvider: 'mcp',
scopes: extra.authInfo?.scopes
}
};
}
});
```
```python Python theme={null}
from mcp.server.auth.middleware.auth_context import get_access_token
def identify_from_mcp_auth(request, context):
# Get the MCP access token
access_token = get_access_token()
if not access_token:
return None # Anonymous session
# Use the MCP auth token to identify user
user = get_user_from_mcp_token(access_token.token)
return UserIdentity(
user_id=user.id,
user_name=user.email,
user_data={
'auth_provider': 'mcp',
'scopes': access_token.scopes
}
)
agentcat.track(server, "proj_abc123xyz",
agentcat.AgentCatOptions(identify=identify_from_mcp_auth))
```
```go Go theme={null}
agentcat.Track(s, "proj_abc123xyz", &agentcat.Options{
Identify: func(ctx context.Context, req *mcp.CallToolRequest) *agentcat.UserIdentity {
// Extract auth token from context
authToken := getAuthTokenFromContext(ctx)
if authToken == "" {
return nil // Anonymous session
}
// Use the auth token to identify user
user := getUserFromToken(authToken)
return &agentcat.UserIdentity{
UserID: user.ID,
UserName: user.Email,
UserData: map[string]any{
"authProvider": "mcp",
},
}
},
})
```
### Error Handling
Handle identification failures gracefully:
```typescript TypeScript theme={null}
agentcat.track(mcpServer, "proj_abc123xyz", {
identify: async (request, extra) => {
try {
const user = await fetchUserInfo(request);
return {
userId: user.id,
userName: user.name
};
} catch (error) {
console.error('User identification failed:', error);
// Return null to continue with anonymous tracking
return null;
}
}
});
```
```python Python theme={null}
def identify_with_error_handling(request, context):
try:
user = fetch_user_info(request)
return UserIdentity(
user_id=user.id,
user_name=user.name
)
except Exception as e:
print(f'User identification failed: {e}')
# Return None to continue with anonymous tracking
return None
agentcat.track(server, "proj_abc123xyz",
agentcat.AgentCatOptions(identify=identify_with_error_handling))
```
```go Go theme={null}
agentcat.Track(s, "proj_abc123xyz", &agentcat.Options{
Identify: func(ctx context.Context, req *mcp.CallToolRequest) *agentcat.UserIdentity {
user, err := fetchUserInfo(ctx, req)
if err != nil {
log.Printf("User identification failed: %v", err)
// Return nil to continue with anonymous tracking
return nil
}
return &agentcat.UserIdentity{
UserID: user.ID,
UserName: user.Name,
}
},
})
```
## Function Parameters
The identify function receives parameters that vary between the TypeScript, Python, and Go SDKs:
The request structure follows the MCP (Model Context Protocol) specification. The specific types and fields available depend on your SDK:
* TypeScript: Uses the [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
* Python: Uses the [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk)
* Go: Uses the [official Go MCP SDK](https://github.com/modelcontextprotocol/go-sdk) or [mark3labs/mcp-go](https://github.com/mark3labs/mcp-go)
```typescript TypeScript theme={null}
// TypeScript SDK parameters
identify: async (request, extra) => {
// request: MCP request object
// extra: RequestHandlerExtra object
}
```
```python Python theme={null}
# Python SDK parameters
def identify(request, context):
# request: MCP request object
# context: RequestContext object
pass
```
```go Go theme={null}
// Go SDK parameters
Identify: func(ctx context.Context, req *mcp.CallToolRequest) *agentcat.UserIdentity {
// ctx: context.Context — request context with cancellation and deadlines
// req: *mcp.CallToolRequest — the tool call request with arguments
return nil
}
```
### request
The MCP request object following the JSON-RPC format:
```typescript TypeScript theme={null}
{
method: string; // The tool/method being called (e.g., "tools/call")
params?: { // Optional parameters
_meta?: { // Optional metadata
progressToken?: string | number; // Token for progress notifications
};
name?: string; // Tool name being invoked
arguments?: { // Tool-specific arguments passed by the client
// Your custom arguments like userId, apiKey, token, etc. passed into tool calls
[key: string]: any;
};
};
}
```
```python Python theme={null}
# Python request object structure
request.method # The tool/method being called (e.g., "tools/call")
request.params # Optional parameters object
request.params.name # Tool name being invoked
request.params.arguments # Tool-specific arguments dict
# Access arguments like:
request.params.arguments.get('userId')
request.params.arguments.get('apiKey')
```
```go Go theme={null}
// Go request object structure (*mcp.CallToolRequest)
req.Method() // The tool/method being called
req.Params.Name // Tool name being invoked
req.GetArguments() // Tool-specific arguments map
// Access arguments like:
args := req.GetArguments()
userID, _ := args["userId"].(string)
apiKey, _ := args["apiKey"].(string)
```
### TypeScript: extra
The MCP RequestHandlerExtra object providing additional context:
```typescript theme={null}
{
signal: AbortSignal; // Abort signal for request cancellation
authInfo?: AuthInfo; // Validated access token information
sessionId?: string; // Session ID from the transport
_meta?: RequestMeta; // Metadata from the original request
requestId: RequestId; // JSON-RPC ID of the request (string | number)
// Functions for sending related messages (not typically used in identify)
sendNotification: (notification: Notification) => Promise;
sendRequest: (request: Request, resultSchema: U, options?: RequestOptions) => Promise>;
}
```
### Python: context
The MCP RequestContext object providing session and client information:
```python theme={null}
# Python context object
context.session # ServerSession object
context.session.client_params.clientInfo.name # Client name
context.session.client_params.clientInfo.version # Client version
context.lifespan_context # Application-specific context storage
```
In [stateless mode](/sdk/stateless-servers), `context.session` still exists but represents the per-request context rather than a long-lived session. The `clientInfo` fields are extracted from HTTP headers on each request.
**Python Authentication Access**: In Python MCP servers, authentication tokens are accessed via the `get_access_token()` function from `mcp.server.auth.middleware.auth_context`, not through the context parameter. This is different from the TypeScript SDK where auth info is available in the `extra` parameter.
### Go: ctx and req
The Go SDK identify function receives `context.Context` and `*mcp.CallToolRequest`:
```go theme={null}
// ctx: standard Go context with cancellation and deadline support
ctx.Done() // Channel closed when request is cancelled
ctx.Err() // Returns context.Canceled or context.DeadlineExceeded
// req: the MCP CallToolRequest
req.Params.Name // Tool name being invoked
args := req.GetArguments() // Tool arguments as map[string]any
```
**Go Authentication Access**: In Go MCP servers, authentication information is typically extracted from the `context.Context` parameter. The exact method depends on your authentication middleware and MCP library.
## Common Usage Patterns
```typescript TypeScript theme={null}
identify: async (request, extra) => {
// Access tool arguments
const userId = request.params?.arguments?.userId;
const apiKey = request.params?.arguments?.apiKey;
// Access auth information (if using MCP auth)
const authToken = extra.authInfo?.token;
// Tool name being called
const toolName = request.params?.name;
// Check if request was cancelled
if (extra.signal.aborted) {
return null;
}
// Your identification logic here...
}
```
```python Python theme={null}
from mcp.server.auth.middleware.auth_context import get_access_token
def identify(request, context):
# Access tool arguments
user_id = request.params.arguments.get('userId')
api_key = request.params.arguments.get('apiKey')
# Access auth token (if using MCP auth)
access_token = get_access_token()
if access_token:
auth_token = access_token.token
# Tool name being called
tool_name = request.params.name
# Access client information from context
client_name = None
try:
client_info = context.session.client_params.clientInfo
client_name = client_info.name if client_info else None
except:
pass
# Your identification logic here...
```
```go Go theme={null}
Identify: func(ctx context.Context, req *mcp.CallToolRequest) *agentcat.UserIdentity {
// Access tool arguments
args := req.GetArguments()
userID, _ := args["userId"].(string)
apiKey, _ := args["apiKey"].(string)
// Tool name being called
toolName := req.Params.Name
// Check if request was cancelled
if ctx.Err() != nil {
return nil
}
// Your identification logic here...
}
```
## Important Considerations
**Stateful servers (default):** The identify function is called **once per session** on the first tool invocation. This minimizes overhead while ensuring consistent user tracking throughout the session.
**[Stateless servers](/sdk/stateless-servers):** The identify function is called on **every request** since there is no persistent session. Keep your identify function lightweight — avoid expensive API calls or database lookups if possible. Without identification, stateless events cannot be grouped into sessions.
### Anonymous Sessions
If you don't provide an identify function or if it returns `null`, sessions will be tracked anonymously. This is useful for:
* Public APIs where user identification isn't required
* Development and testing environments
* Respecting user privacy preferences
# Report Missing Tools
Source: https://docs.agentcat.com/sdk/report-missing-tools
See what functionality users need but can't find on your MCP server.
When an AI agent can't find the right tool on your server, the user hits a dead end and you never hear about it. Report missing tools closes that feedback loop. AgentCat gives the agent a way to flag what it was looking for, so you can see exactly which capabilities are missing and how often users need them.
This feature is enabled by default — no configuration needed.
## What You'll See
Missing tool requests show up in two places:
**Sessions list** — The **Issues** column flags sessions where clients reported missing tools, alongside any errors. This gives you a quick sense of how often users are hitting gaps.
**Session Replay** — Missing tool events appear in the timeline with a warning badge. Click one to see what the client was looking for and the context behind the request. This is where you'll find the detail you need to decide what to build next.
## Disabling the Feature
If you don't want AgentCat to add the `get_more_tools` tool to your server, disable it:
```typescript TypeScript theme={null}
agentcat.track(mcpServer, "proj_abc123xyz", {
enableReportMissing: false
})
```
```python Python theme={null}
agentcat.track(server, "proj_abc123xyz",
agentcat.AgentCatOptions(enable_report_missing=False))
```
```go Go theme={null}
agentcat.Track(s, "proj_abc123xyz", &agentcat.Options{
DisableReportMissing: true,
})
```
# Stateless Servers
Source: https://docs.agentcat.com/sdk/stateless-servers
Set up AgentCat for a stateless MCP server for better horizontal scaling support.
## What are stateless MCP servers?
Today many MCP servers use a stateful implementation where the client maintains a persistent connection. AgentCat's SDK can track a session across that connection naturally since it maintains a session ID when the client connects and associates all events with it.
Stateless MCP servers don't maintain a session ID and each request is an independent HTTP call with no persistent connection. This is most commonly done to support horizontal scaling.
AgentCat works for stateless MCP servers that pass an identity function. It uses the user's identity and client to group sessions.
There are changes coming to the protocol and MCP SDKs to make servers stateless by default. For now, it's currently opt-in for all SDKs.
## Setup
Pass the `stateless` option when tracking your server:
```typescript TypeScript theme={null}
agentcat.track(mcpServer, "proj_YOUR_PROJECT_ID", {
stateless: true
});
```
```python Python theme={null}
agentcat.track(server, "proj_YOUR_PROJECT_ID",
agentcat.AgentCatOptions(stateless=True))
```
```go Go theme={null}
agentcat.Track(s, "proj_YOUR_PROJECT_ID", &agentcat.Options{
Stateless: true,
})
```
This is the recommended approach — it's clear, reliable, and works with any transport or framework.
If you're using FastMCP with `stateless_http=True` and don't set the `stateless` option explicitly, the Python SDK will attempt to detect stateless mode automatically. You can explicitly force stateless mode off by setting `stateless=False`.
## How sessions are grouped
When AgentCat receives events without a session ID, it groups them using three dimensions: **the identified user**, **the AI client** (e.g., Claude Desktop, Cursor), and **a 30-minute inactivity window**.
If two events come from the same user via the same client within 30 minutes of each other, they belong to the same session. Different AI clients get separate sessions, even for the same user. If someone is interacting via both Claude Desktop and Cursor simultaneously, those are distinct sessions.
Events without an identified user (anonymous) each get their own isolated session. AgentCat can't group what it can't identify.
| Event | User | Client | Timestamp | Assigned Session |
| ----- | ----- | -------------- | --------- | -------------------------------------------- |
| 1 | alice | Claude Desktop | 10:00 | ses\_001 |
| 2 | bob | Cursor | 10:02 | ses\_002 |
| 3 | alice | Claude Desktop | 10:15 | ses\_001 (reused, within 30 min) |
| 4 | alice | Cursor | 10:20 | ses\_003 (different client) |
| 5 | alice | Claude Desktop | 10:55 | ses\_004 (new session, >30 min from event 3) |
## Why identifying users matters
In stateless mode, every request is independent, so `identify` runs every time. Keep your identify function lightweight by avoiding expensive API calls or database lookups if possible.
Without an identify function, stateless tracking is severely limited. Every event becomes its own anonymous session with no way to group them.
Learn more about setting up user identification in [Identifying Users](/sdk/identifying-users).
# Tool Call Context
Source: https://docs.agentcat.com/sdk/tool-call-context
Configure how AgentCat captures agent intent from tool calls.
AgentCat automatically injects a `context` parameter into your tool schemas. When an AI agent calls one of your tools, it fills in this parameter with a short explanation of why it's making the call. AgentCat captures this as the event's agent intent and strips it from the arguments before your tool handler runs.
For a deeper look at how this works under the hood, see [How AgentCat Works](/how-it-works).
This feature is enabled by default. You can customize the prompt that guides the AI's response, or disable it entirely.
## Customizing the Description
The default description asks the AI to provide a 15-25 word explanation in third person. If you want to tailor the prompt to your domain, pass a `customContextDescription`:
```typescript TypeScript theme={null}
import * as agentcat from "agentcat"
agentcat.track(mcpServer, "proj_abc123xyz", {
customContextDescription: "Describe the business objective this tool call supports and which team requested it."
})
```
```python Python theme={null}
import agentcat
agentcat.track(server, "proj_abc123xyz",
agentcat.AgentCatOptions(
custom_context_description="Describe the business objective this tool call supports and which team requested it."
))
```
```go Go theme={null}
import agentcat "go.agentcat.com/sdk/officialsdk"
// For mark3labs/mcp-go: import agentcat "go.agentcat.com/sdk/mcpgo"
agentcat.Track(s, "proj_abc123xyz", &agentcat.Options{
CustomContextDescription: "Describe the business objective this tool call supports and which team requested it.",
})
```
The description you provide replaces the default prompt entirely. Write it as an instruction directed at the AI client.
## Tips for Writing a Good Description
A few things to keep in mind when writing your own context description:
* **Be specific to your domain.** Instead of "explain why you're calling this tool", try "describe which customer request or support ticket this action relates to."
* **Set a word count.** LLMs tend to be verbose. Asking for 15-25 words keeps the context concise and useful for analytics.
* **Ask for third person.** First-person context ("I need to...") is less useful for analytics than third-person ("Searching for...").
* **Remind about sensitive data.** If your tools handle credentials or personal information, instruct the AI not to include those in the context string.
## Disabling Context Collection
If you don't want AgentCat to modify your tool schemas, disable context collection:
```typescript TypeScript theme={null}
agentcat.track(mcpServer, "proj_abc123xyz", {
enableToolCallContext: false
})
```
```python Python theme={null}
agentcat.track(server, "proj_abc123xyz",
agentcat.AgentCatOptions(enable_tool_call_context=False))
```
```go Go theme={null}
agentcat.Track(s, "proj_abc123xyz", &agentcat.Options{
DisableToolCallContext: true,
})
```
Disabling context collection means you won't see user intent data in the dashboard or session replays. Tool calls will still be tracked, but without the "why" behind each call.