Skip to content

Commit 76586f4

Browse files
RobertCrupajirispilkaclaudealbatrossflyon-coderChris Brown
authored
feat: Add report-problem tool for agents to report issues (#1050)
Implements the `report-problem` MCP tool from #748 — a channel for agents to report problems with Apify's tools or Actors, so we get signal on where agents get stuck. ## What's included - **New tool `report-problem`**: returns a plain-text acknowledgement, always success. `readOnlyHint: true`, not payment-gated. Schema: - `message` — required string, 1–2000 chars. What happened. - `actorId` — optional string, ≤200 chars. - `actorRunId` — optional string, ≤200 chars. - `relatedTools` — optional string[], up to 20 items, each ≤100 chars. - **Default-enabled `report-problem` tool category** in the registry. - **Telemetry**: a successful call emits a dedicated `MCP Agent Feedback` Segment event (fields mapped to snake_case, absent optionals dropped) alongside the standard tool-call event. Identity handling mirrors the tool-call event. ## Gating — served only when all three hold 1. **Telemetry enabled.** The tool's only function is forwarding submissions via telemetry, so it is never served when telemetry is off (it would just fake an acknowledgement into the void). 2. **Client allowed.** Hidden from Anthropic surfaces (Claude.ai / Claude Desktop / Claude Code) pending the directory review, via a substring blocklist (`claude`, `anthropic`) matched against `clientInfo.name`. The tool cannot be judged until the client is known, so it is withheld until initialize and added on the flush if the client allows. 3. **Category selected.** The `report-problem` category is enabled (default on). All other tools are unconditionally servable, so recovery loads compose them eagerly and they survive a load that never sees an initialize; only `report-problem` is withheld until the client is known. ## Discovery - **Error nudge**: failed (`isError`) tool results get a report-problem nudge appended to their text, prompting the agent to report at the moment it decides what to do next. Full nudge for internal/unknown errors; a softer nudge for `INVALID_INPUT` (likely the agent's own mistake); suppressed for expected user-resolvable states (auth, payment/approval required). Also applied to failed long-running task results. Never appended when the tool isn't served or when `report-problem` itself is the failing tool. - **Server instructions**: a gentle line pointing agents at `report-problem`, emitted only when the tool is actually served. ## Testing - **Workflow eval** `report-problem-on-tool-error`: injects a `call-actor` failure and checks the agent proactively reports the blocker (rather than only telling the user it failed). Required adding tool-failure injection to the eval harness. - **Unit tests**: tool behavior/schema, gating, client blocklist, server instructions, telemetry event shaping. - **Integration suite**: `report-problem` listing and gating. - Manually verified with mcpc: schema, valid/invalid submissions, and the Anthropic block (filtered from the tool list for a `claude`-family client while remaining available to non-Anthropic clients). ## TODO - [ ] Get access to Segment so I can check the reported problems. Closes #748 --------- Co-authored-by: Jiří Spilka <jiri.spilka@apify.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: chris brown <albatrossflyon@gmx.com> Co-authored-by: Chris Brown <albatrossflyon1@gmail.com>
1 parent cbdcaf4 commit 76586f4

27 files changed

Lines changed: 1129 additions & 70 deletions

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ Here is an overview list of all the tools provided by the Apify MCP Server.
243243
Legend for the **Enabled by default** column:
244244
- ✅ — in the default tool set.
245245
- ⚡ — auto-injected when `call-actor`, `add-actor`, an Actor tool, or `get-actor-run` is present (which is true in the default configuration).
246+
- ✅¹ — served by default, but only when telemetry is enabled and the client is not withheld: Anthropic surfaces (Claude.ai / Claude Desktop / Claude Code) or `local-agent-mode-apify`. To disable, pass an explicit `tools=` list that omits it.
246247

247248
| Tool name | Category | Description | Enabled by default |
248249
| :--- | :--- | :--- | :---: |
@@ -256,6 +257,7 @@ Legend for the **Enabled by default** column:
256257
| `search-apify-docs` | docs | Search the Apify documentation for relevant pages. ||
257258
| `fetch-apify-docs` | docs | Fetch the full content of an Apify documentation page by its URL. ||
258259
| [`apify--rag-web-browser`](https://apify.com/apify/rag-web-browser) | Actor (see [tool configuration](#tools-configuration)) | An Actor tool to browse the web. ||
260+
| `report-problem` | dev | Report a problem with an Apify tool or Actor to the Apify team. | ✅¹ |
259261
| `get-actor-run-list` | runs | Get a list of an Actor's runs, filterable by status. | |
260262
| `get-actor-log` | runs | Retrieve the logs for a specific Actor run. | |
261263
| `get-dataset` | storage | Get metadata about a specific dataset. | |
@@ -292,6 +294,8 @@ When no query parameters are provided, the MCP server loads the following `tools
292294

293295
If the tools parameter is specified, only the listed tools or categories will be enabled – no default tools will be included.
294296

297+
`report-problem` is served by default (subject to the gating in the footnote above) but lives in the `dev` category, so an explicit `tools=dev` selects it too. To disable it, pass an explicit `tools=` list that omits it (e.g. `tools=actors,docs`).
298+
295299
> **Easy configuration:**
296300
>
297301
> Use the [UI configurator](https://mcp.apify.com/) to configure your server, then copy the configuration to your client.

evals/shared/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ export type WorkflowTestCase = {
4343
maxTurns?: number;
4444
/** Tools to enable for this test (optional, e.g., ["actors", "docs", "apify/rag-web-browser"]) */
4545
tools?: string[];
46+
/**
47+
* Tool names the harness force-fails with a synthetic INTERNAL_ERROR carrying the real
48+
* report-problem nudge (optional). Lets an eval deterministically throw a nudge-eligible error
49+
* that the live server + API cannot reproduce on demand. See mcp_client.ts.
50+
*/
51+
failTools?: string[];
4652
} & BaseTestCase;
4753

4854
/**

evals/workflows/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,7 @@ File: `test-cases.json`
461461
**Optional:**
462462
- `maxTurns` - Override default (10)
463463
- `tools` - List of tools to enable for this test (e.g., `["actors", "docs", "apify/rag-web-browser"]`). If omitted, all default tools are enabled. Passed to MCP server as `--tools` argument.
464+
- `failTools` - Tool names the harness force-fails with a synthetic `INTERNAL_ERROR` result carrying the real `report-problem` nudge, instead of calling the server (e.g. `["call-actor"]`). Use it to deterministically throw a nudge-eligible error that the live server + API cannot reproduce on demand, e.g. to test that the agent proactively calls `report-problem` after a failure. See `mcp_client.ts`.
464465

465466
## Performance
466467

evals/workflows/mcp_client.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
77
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
88

9+
import { REPORT_PROBLEM_NUDGE } from '../../src/tools/dev/report_problem.js';
910
import type { McpTool, McpToolCall, McpToolResult } from './types.js';
1011

1112
export class McpClient {
@@ -14,13 +15,16 @@ export class McpClient {
1415
private tools: McpTool[] = [];
1516
private instructions: string | null = null;
1617
private toolTimeoutMs: number;
18+
private failTools: Set<string>;
1719

1820
/**
1921
* Create MCP client
2022
* @param toolTimeoutSeconds - Timeout for tool calls in seconds (default: 60)
23+
* @param failTools - Tool names to force-fail with a synthetic INTERNAL_ERROR (see callTool)
2124
*/
22-
constructor(toolTimeoutSeconds = 60) {
25+
constructor(toolTimeoutSeconds = 60, failTools: string[] = []) {
2326
this.toolTimeoutMs = toolTimeoutSeconds * 1000;
27+
this.failTools = new Set(failTools);
2428
}
2529

2630
/**
@@ -112,6 +116,18 @@ export class McpClient {
112116
throw new Error('MCP client is not started');
113117
}
114118

119+
// Deterministic failure injection: force-fail listed tools with a synthetic INTERNAL_ERROR
120+
// carrying the real server nudge, so the agent sees exactly what production appends on a genuine
121+
// failure. The live server + API cannot reproduce an INTERNAL_ERROR on demand, so evals that
122+
// exercise error-driven behavior (e.g. report-problem) rely on this.
123+
if (this.failTools.has(toolCall.name)) {
124+
return {
125+
toolName: toolCall.name,
126+
success: false,
127+
error: `The ${toolCall.name} tool failed with an internal error.\n\n${REPORT_PROBLEM_NUDGE}`,
128+
};
129+
}
130+
115131
try {
116132
const response = await this.client.callTool(
117133
{

evals/workflows/run_workflow_evals.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ async function runSingleTest(
7777
logWithPrefix(testId, `[${index + 1}/${total}] Running...`);
7878

7979
// Create FRESH MCP instance per test for isolation
80-
const mcpClient = new McpClient(argv.toolTimeout);
80+
const mcpClient = new McpClient(argv.toolTimeout, testCase.failTools);
8181
const startTime = Date.now();
8282
let result: EvaluationResult;
8383

evals/workflows/test_cases.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,15 @@
197197
"reference": "The agent must find a Reddit scraper and call-actor it for the r/technology subreddit, then call get-dataset-items with the datasetId from that run. PASS only if get-dataset-items was called with a datasetId argument and the final answer lists several posts, each with a title and an upvote count.",
198198
"tools": ["actors", "get-dataset-items"],
199199
"maxTurns": 18
200+
},
201+
{
202+
"id": "report-problem-on-tool-error",
203+
"category": "report-problem",
204+
"query": "Use the apify/rag-web-browser Actor to scrape https://example.com and tell me the page title.",
205+
"reference": "While running the Actor, the call-actor tool fails with an internal error, so the task cannot be completed. The user did NOT ask for any problem to be reported. This tests whether the agent proactively reports the blocker after the error. PASS only if the agent calls the report-problem tool with a non-empty message describing what it was trying to do and what went wrong. FAIL if report-problem is not called (e.g., the agent only tells the user it could not complete the task).",
206+
"tools": ["actors", "report-problem"],
207+
"failTools": ["call-actor"],
208+
"maxTurns": 8
200209
}
201210
]
202211
}

src/const.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,20 @@ export const HELPER_TOOLS = {
5454
STORE_SEARCH_WIDGET: 'search-actors-widget',
5555
DOCS_SEARCH: 'search-apify-docs',
5656
DOCS_FETCH: 'fetch-apify-docs',
57+
PROBLEM_REPORT: 'report-problem',
5758
} as const;
5859
export type HelperToolName = (typeof HELPER_TOOLS)[keyof typeof HELPER_TOOLS];
5960

61+
/**
62+
* Client-name substrings (lowercased, matched against `clientInfo.name`) that `report-problem` is
63+
* hidden from. Applied once per connection in the compose step, where the client is known.
64+
* `report-problem` is hidden from Anthropic surfaces (Claude.ai / Claude Desktop / Claude Code /
65+
* `local-agent-mode-apify`) pending the directory review. Substring matching covers new client builds
66+
* without a maintained allowlist; over-matching only hides an optional tool, which is the safe failure
67+
* mode.
68+
*/
69+
export const REPORT_PROBLEM_BLOCKED_CLIENTS: string[] = ['claude', 'anthropic', 'local-agent-mode-apify'];
70+
6071
export const RAG_WEB_BROWSER = 'apify/rag-web-browser';
6172
export const RAG_WEB_BROWSER_WHITELISTED_FIELDS = ['query', 'maxResults', 'outputFormats'];
6273
export const RAG_WEB_BROWSER_ADDITIONAL_DESC = `Use this tool when user wants to GET or RETRIEVE actual data immediately (one-time data retrieval).

0 commit comments

Comments
 (0)