# Agent feedback: what agents needed and couldn't find (opt in)

An AI agent working through your MCP server sometimes gives up because the tool it needs isn't there, a field is missing, or a step doesn't work. Agent feedback lets it tell you, in its own words, in one or two sentences. Each note shows in **Issues** (category **Agent feedback**), on the MCP session in **Sessions**, and in the bell.

This is the one place Telltale keeps text an agent wrote, so it is **off by default** in two places, and both must agree:

1. **A workspace owner or admin turns it on** in **Settings › Data & privacy › Agent feedback**. The change is recorded in the audit log.
2. **Your MCP server asks for it**: Telltale for MCP servers 1.3.0 or later with `feedback: true`. The SDK checks `GET /api/v1/agent-feedback/state` when it starts (and again at most every five minutes) and lists the tool only while the workspace has it on.

## Turn it on

[Download the MCP adapter and its companion files](/docs/server-adapters) into `lib/telltale/` first.

```js
import {withTelltale} from './lib/telltale/mcp.js';

withTelltale(server, {
  endpoint: 'https://app.trytelltale.dev',
  serverKey: process.env.TELLTALE_SERVER_KEY,
  identify: ctx => ({accountId: ctx.authInfo?.extra?.accountId}),
  feedback: true, // or {name: 'report_missing_capability'}
});
```

Your server's `tools/list` then ends with one more tool, `agent_feedback` (or the name you chose), with a single required argument, `needed`: "What you needed and could not find, in one or two sentences." The SDK answers that tool itself. It never reaches your own handlers, and it is never counted as a call in Tools, Issues or chargeable use. If your server already has a tool with that name, yours wins and the SDK adds nothing.

## What is kept, and what isn't

- **The note**, at most 500 characters, with the tool it was about (when the agent says), the account and user IDs your `identify` function returns, the agent app as it declared itself, and the MCP session it came from.
- **Cleaned twice**: in your SDK before it leaves your server, and again when it reaches Telltale. E-mail addresses become `[email]`, credential-like strings (Bearer values, JWTs, API keys, `password=…` style assignments) become `[redacted]`, long digit runs (card, phone and account numbers) become `[number]`, and the query part of a web address is removed.
- **Kept 30 days**, then deleted. Erasure and subject export include it.
- **Turning it off** deletes every note the workspace kept, and their bell notices, at once. While it is off, a note sent by an older setup is answered `{"stored": false, "reason": "off"}` and nothing is kept.

## What it never does

- It never feeds a verdict, a rule, a confidence band or billing. An agent's note is what it said, not evidence about who was driving.
- It is never sent to an AI provider: [AI summaries](/docs/ai-summaries) leave agent feedback out of the problems they group.
- Webhooks get the shape only: `agent_feedback.received` carries the tool, channel, account, agent app and the note's length, never the note itself (see [Webhooks](/docs/webhooks)).

## Where you see it

- **Issues**: notes with the same words (ignoring case and punctuation) are one issue, so ten agents asking for the same missing export are one line with ten times and the accounts they came from. Agent feedback has its own category and never changes the error counts, the "Open issues rise" alert or the weekly agent report.
- **Sessions**: the MCP session's timeline shows the note at the moment the agent wrote it, beside its calls.
- **The bell**: "An agent left feedback: …", which opens Issues.

## Sending it yourself

If you don't use the SDK, send a note with your server key:

```http
POST /api/v1/agent-feedback
Authorization: Bearer <server key>
Content-Type: application/json

{"eventId": "fb_7d1c2e9a40", "text": "I needed a bulk refund tool", "accountId": "acct_123"}
```

`eventId` makes a retry the same note. Optional fields: `tool`, `at`, `client` `{name, version}`, `userId` and `session` `{key, source}` (the same SHA-256 grouping key the SDK sends with calls). Unknown fields are refused. The answer is `{"stored": true}` or `{"stored": false, "reason": "off"}`.
