Skip to content

fix(copilot): prioritize block discovery over MCP and sanitize HTML errors - #12394

Merged
majdyz merged 6 commits into
devfrom
otto/secrt-2116-copilot-mcp-block-discovery
Mar 14, 2026
Merged

fix(copilot): prioritize block discovery over MCP and sanitize HTML errors#12394
majdyz merged 6 commits into
devfrom
otto/secrt-2116-copilot-mcp-block-discovery

Conversation

@Otto-AGPT

Copy link
Copy Markdown
Contributor

Requested by @majdyz

When a user asks for Google Sheets integration, the CoPilot agent skips block discovery entirely (despite 55+ Google Sheets blocks being available), jumps straight to MCP, guesses a fake URL (https://sheets.googleapis.com/mcp), and gets a raw HTML 404 error page dumped into the conversation.

Changes:

  1. MCP guide (mcp_tool_guide.md): Added "Check blocks first" section directing the agent to use find_block before attempting MCP for any service not in the known servers list. Explicitly prohibits guessing/constructing MCP server URLs.

  2. Error handling (run_mcp_tool.py): Detects HTML error pages in HTTP responses (e.g. raw 404 pages from non-MCP endpoints) and returns a clean one-liner like "This URL does not appear to host an MCP server" instead of dumping the full HTML body.

Note: The main CoPilot system prompt (managed externally, not in repo) should also be updated to reinforce block-first behavior in the Capability Check section. This PR covers the in-repo changes.

Session reference: 9216df83-5f4a-48eb-9457-3ba2057638ae (turn 3)
Ticket: SECRT-2116


Co-authored-by: Zamil Majdy (@majdyz) majdyz@gmail.com

…rrors

- Add 'Check blocks first' section to MCP guide, directing the agent to
  search find_block before attempting MCP for any service not in the
  known servers list. Explicitly prohibit guessing MCP server URLs.

- Sanitize HTTP error responses in run_mcp_tool to detect HTML error
  pages (e.g. raw 404 pages from non-MCP endpoints) and return a clean
  one-liner instead of dumping the full HTML body.

Resolves SECRT-2116

---
Co-authored-by: Zamil Majdy (@majdyz) <majdyz@gmail.com>
@Otto-AGPT
Otto-AGPT requested a review from a team as a code owner March 13, 2026 04:34
@Otto-AGPT
Otto-AGPT requested review from Pwuts and Swiftyos and removed request for a team March 13, 2026 04:34
@github-project-automation github-project-automation Bot moved this to 🆕 Needs initial review in AutoGPT development kanban Mar 13, 2026
@github-actions github-actions Bot added platform/backend AutoGPT Platform - Back end size/m labels Mar 13, 2026
@coderabbitai

coderabbitai Bot commented Mar 13, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b7e0c588-7912-45b1-9e2c-f96b097392b3

📥 Commits

Reviewing files that changed from the base of the PR and between ccefed1 and 7346d81.

📒 Files selected for processing (1)
  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: types
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.12)
  • GitHub Check: Seer Code Review
  • GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (5)
autogpt_platform/backend/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development

Files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
autogpt_platform/backend/**/*.{py,txt}

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use poetry run prefix for all Python commands, including testing, linting, formatting, and migrations

Files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
autogpt_platform/backend/backend/**/*.py

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use Prisma ORM for database operations in PostgreSQL with pgvector for embeddings

Files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
autogpt_platform/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
autogpt_platform/backend/**/*test*.py

📄 CodeRabbit inference engine (AGENTS.md)

Run poetry run test for backend testing (runs pytest with docker based postgres + prisma)

Files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
🧠 Learnings (8)
📓 Common learnings
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12356
File: autogpt_platform/backend/backend/copilot/constants.py:9-12
Timestamp: 2026-03-10T08:39:13.707Z
Learning: In Significant-Gravitas/AutoGPT PR `#12356`, the `COPILOT_SYNTHETIC_ID_PREFIX = "copilot-"` check in `create_auto_approval_record` (human_review.py) is intentional and safe. The `graph_exec_id` passed to this function comes from server-side `PendingHumanReview` DB records (not from user input); the API only accepts `node_exec_id` from users. Synthetic `copilot-*` IDs are only ever created server-side in `run_block.py`. The prefix skip avoids a DB lookup for a `AgentGraphExecution` record that legitimately does not exist for CoPilot sessions, while `user_id` scoping is enforced at the auth layer and on the resulting auto-approval record.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:10030-10037
Timestamp: 2026-03-01T07:59:02.311Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — For MCP manual token storage, backend model autogpt_platform/backend/backend/api/features/mcp/routes.py defines MCPStoreTokenRequest.token as Pydantic SecretStr with a min length constraint, which generates OpenAPI schema metadata (format: "password", writeOnly: true, minLength: 1) in autogpt_platform/frontend/src/app/api/openapi.json. Prefer SecretStr (with length constraints) for sensitive request fields so generated TS clients and docs treat them as secrets.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:9983-9995
Timestamp: 2026-02-27T15:59:00.370Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — Backend/frontend OpenAPI codegen
Learning: For MCP schema models, required OpenAPI fields must have no defaults in Pydantic. Specifically, MCPToolInfo.input_schema must be required (no Field(default_factory=dict)) so openapi.json emits it in "required", ensuring generated TS types treat input_schema as non-optional.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:9983-9995
Timestamp: 2026-02-27T15:59:00.370Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — OpenAPI/codegen
Learning: Ensuring a field is required in generated TS types needs two sides: (1) no default value on the Pydantic field, and (2) the OpenAPI model's "required" array must list it. For MCPToolInfo, making input_schema required in OpenAPI and removing Field(default_factory=dict) in the backend prevents optional typing drift.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunMCPTool/helpers.tsx:23-24
Timestamp: 2026-02-27T10:45:55.700Z
Learning: As of PR `#12213`, MCP tool response types (MCPToolsDiscoveredResponse, MCPToolOutputResponse) are defined in openapi.json and frontend code in autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunMCPTool/helpers.tsx uses the generated types from `@/app/api/__generated__/`. Other tools like RunBlock still use inline TypeScript interfaces (e.g., BlockDetailsResponse) for SSE stream payloads that are not included in openapi.json schemas. The pattern is tool-specific: use generated types when available in openapi.json, use inline types only when the payload schema is truly SSE-stream-only and not exposed via OpenAPI.
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12284
File: autogpt_platform/frontend/src/app/api/openapi.json:11897-11900
Timestamp: 2026-03-04T23:58:18.476Z
Learning: Repo: Significant-Gravitas/AutoGPT — PR `#12284`
Backend/frontend OpenAPI codegen convention: In backend/api/features/store/model.py, the StoreSubmission and StoreSubmissionAdminView models define submitted_at: datetime | None, changes_summary: str | None, and instructions: str | None with no default. This is intentional to produce “required but nullable” fields in OpenAPI (properties appear in required[] and use anyOf [type, null]). This matches Prisma’s submittedAt DateTime? and changesSummary String?. Do not flag this as a required/nullable mismatch.
📚 Learning: 2026-02-27T10:45:55.700Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunMCPTool/helpers.tsx:23-24
Timestamp: 2026-02-27T10:45:55.700Z
Learning: As of PR `#12213`, MCP tool response types (MCPToolsDiscoveredResponse, MCPToolOutputResponse) are defined in openapi.json and frontend code in autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunMCPTool/helpers.tsx uses the generated types from `@/app/api/__generated__/`. Other tools like RunBlock still use inline TypeScript interfaces (e.g., BlockDetailsResponse) for SSE stream payloads that are not included in openapi.json schemas. The pattern is tool-specific: use generated types when available in openapi.json, use inline types only when the payload schema is truly SSE-stream-only and not exposed via OpenAPI.

Applied to files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
📚 Learning: 2026-02-27T15:59:00.370Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:9983-9995
Timestamp: 2026-02-27T15:59:00.370Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — Backend/frontend OpenAPI codegen
Learning: For MCP schema models, required OpenAPI fields must have no defaults in Pydantic. Specifically, MCPToolInfo.input_schema must be required (no Field(default_factory=dict)) so openapi.json emits it in "required", ensuring generated TS types treat input_schema as non-optional.

Applied to files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
📚 Learning: 2026-02-27T15:59:00.370Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:9983-9995
Timestamp: 2026-02-27T15:59:00.370Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — OpenAPI/codegen
Learning: Ensuring a field is required in generated TS types needs two sides: (1) no default value on the Pydantic field, and (2) the OpenAPI model's "required" array must list it. For MCPToolInfo, making input_schema required in OpenAPI and removing Field(default_factory=dict) in the backend prevents optional typing drift.

Applied to files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.

Applied to files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
📚 Learning: 2026-03-04T08:04:35.881Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12273
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:216-220
Timestamp: 2026-03-04T08:04:35.881Z
Learning: In the AutoGPT Copilot backend, ensure that SVG images are not treated as vision image types by excluding 'image/svg+xml' from INLINEABLE_MIME_TYPES and MULTIMODAL_TYPES in tool_adapter.py; the Claude API supports PNG, JPEG, GIF, and WebP for vision. SVGs (XML text) should be handled via the text path instead, not the vision path.

Applied to files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
📚 Learning: 2026-03-04T12:19:39.243Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12279
File: autogpt_platform/backend/backend/copilot/tools/base.py:184-188
Timestamp: 2026-03-04T12:19:39.243Z
Learning: In autogpt_platform/backend/backend/copilot/tools/, ensure that anonymous users always pass user_id=None to tool execution methods. The anon_ prefix (e.g., anon_123) is used only for PostHog/analytics distinct_id and must not be used as an actual user_id. Use a simple truthiness check on user_id (e.g., if user_id: ... else: ... or a dedicated is_authenticated flag) to distinguish anonymous from authenticated users, and review all tool execution call sites within this directory to prevent accidentally forwarding an anon_ user_id to tools.

Applied to files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
📚 Learning: 2026-03-05T15:42:08.207Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12297
File: .claude/skills/backend-check/SKILL.md:14-16
Timestamp: 2026-03-05T15:42:08.207Z
Learning: In Python files under autogpt_platform/backend (recursively), rely on poetry run format to perform formatting (Black + isort) and linting (ruff). Do not run poetry run lint as a separate step after poetry run format, since format already includes linting checks.

Applied to files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
🔇 Additional comments (1)
autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py (1)

583-624: Good regression coverage for sanitized HTTP error handling.

This test correctly validates the intended split: clean user-facing message and detailed error payload, while preventing raw HTML from leaking into message.


Walkthrough

Replaces registry lookup guidance with an API search example, adds explicit pre-checks (use find_block before run_mcp_tool), prohibits guessing MCP URLs, and updates MCP HTTP error handling to include status codes and a truncated error field; adds a test validating the new HTTP error payload behavior.

Changes

Cohort / File(s) Summary
MCP Tool Guidance
autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md
Rewrote registry lookup guidance to use GET https://registry.modelcontextprotocol.io/v0/servers?q=, added “Check blocks first” pre-checks, clarified when to use run_mcp_tool, and forbid guessing/constructing MCP URLs.
HTTP Error Handling
autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py
Log context uses local host variable; HTTP error responses now state the host and status code in the message and include a truncated error field with detail.
Error Handling Tests
autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
Added test test_http_error_returns_clean_message_with_collapsible_detail to assert user-facing message contains status code without raw HTML leakage and places detailed info into the error field.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

Review effort 2/5

Suggested reviewers

  • Swiftyos
  • Pwuts
  • ntindle

Poem

🐰 I hop through docs and logs with care,

I check the blocks before I dare,
I never guess a server's gate,
I show the code and hide the slate,
Safe hops, clear errors — celebrate! 🎉

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: prioritizing block discovery before MCP and sanitizing HTML error responses.
Description check ✅ Passed The description is directly related to the changeset, providing clear context for the motivation, changes made, and relevant issue tracking.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch otto/secrt-2116-copilot-mcp-block-discovery
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Reinforce in the shared supplement (appended to the Langfuse system
prompt) that the agent should always search find_block before
considering MCP for any integration request.
@github-actions

github-actions Bot commented Mar 13, 2026

Copy link
Copy Markdown
Contributor

🔍 PR Overlap Detection

This check compares your PR against all other open PRs targeting the same branch to detect potential merge conflicts early.

🟢 Low Risk — File Overlap Only

These PRs touch the same files but different sections (click to expand)

Summary: 0 conflict(s), 0 medium risk, 1 low risk (out of 1 PRs with file overlap)


Auto-generated on push. Ignores: openapi.json, lock files.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md (1)

31-33: Clarify fallback behavior when find_block fails (not just no-results).

The rule is clear for “no matching blocks,” but it doesn’t explicitly say what to do on find_block errors. Adding that guard will keep tool choice deterministic.

Proposed wording tweak
 Only use `run_mcp_tool` when:
 - The service is in the known hosted MCP servers list above, OR
 - You searched `find_block` first and found no matching blocks
+- If `find_block` returns an error, do not use `run_mcp_tool` yet; resolve the search issue first.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md` around lines
31 - 33, The guidance is missing the fallback for find_block failures; update
the text around run_mcp_tool and find_block to state that run_mcp_tool should be
used when the service is in the known hosted MCP servers list OR when find_block
either returns no matching blocks or fails with an error (treat errors the same
as no-results to keep tool selection deterministic); reference the symbols
run_mcp_tool and find_block and make the wording explicit that find_block errors
trigger the same fallback to run_mcp_tool.
autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py (1)

193-206: Avoid logging raw HTTP error bodies after sanitizing user output.

Line 193 still logs str(e) directly, which can include full HTML pages. Consider logging status/host plus an HTML flag instead.

Proposed log-sanitization refactor
-            logger.warning("MCP HTTP error for %s: %s", server_host(server_url), e)
             # Sanitize error: strip HTML bodies (e.g. raw 404 pages) to avoid
             # dumping entire HTML documents into the agent/user-facing message.
             error_body = str(e)
-            if _looks_like_html(error_body):
+            is_html_error = _looks_like_html(error_body)
+            logger.warning(
+                "MCP HTTP error for %s (status=%s, html_error=%s)",
+                server_host(server_url),
+                e.status_code,
+                is_html_error,
+            )
+            if is_html_error:
                 error_msg = (
                     f"MCP server at {server_host(server_url)} returned HTTP "
                     f"{e.status_code}. "
                     "This URL does not appear to host an MCP server."
                 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py` around lines
193 - 206, The logger currently prints the raw exception object (str(e)) which
can contain full HTML bodies; change the logging in run_mcp_tool.py so it only
logs the host, HTTP status (e.status_code or e.code), and whether the body looks
like HTML instead of the raw body string: capture error_body = str(e) only for
inspection, compute is_html = _looks_like_html(error_body), then call
logger.warning with a sanitized message like "MCP HTTP error for %s: status=%s
html=%s" using server_host(server_url), status, is_html (and if needed include
e.__class__.__name__), and keep the existing branch that builds error_msg using
error_body[:200] only for user-facing output but never log the unsanitized full
body.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py`:
- Around line 194-209: Add a unit test that mocks an HTTPClientError containing
an HTML-like body (e.g., "<!doctype html>...") to exercise the _looks_like_html
branch in run_mcp_tool.py: call the function that catches HTTPClientError (the
routine that returns ErrorResponse) with a mocked exception having .status_code
and string() returning the HTML body, then assert the returned
ErrorResponse.message contains "This URL does not appear to host an MCP server."
and preserves the session_id; reference symbols to locate code:
_looks_like_html, ErrorResponse, server_host/server_url, and HTTPClientError.

---

Nitpick comments:
In `@autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md`:
- Around line 31-33: The guidance is missing the fallback for find_block
failures; update the text around run_mcp_tool and find_block to state that
run_mcp_tool should be used when the service is in the known hosted MCP servers
list OR when find_block either returns no matching blocks or fails with an error
(treat errors the same as no-results to keep tool selection deterministic);
reference the symbols run_mcp_tool and find_block and make the wording explicit
that find_block errors trigger the same fallback to run_mcp_tool.

In `@autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py`:
- Around line 193-206: The logger currently prints the raw exception object
(str(e)) which can contain full HTML bodies; change the logging in
run_mcp_tool.py so it only logs the host, HTTP status (e.status_code or e.code),
and whether the body looks like HTML instead of the raw body string: capture
error_body = str(e) only for inspection, compute is_html =
_looks_like_html(error_body), then call logger.warning with a sanitized message
like "MCP HTTP error for %s: status=%s html=%s" using server_host(server_url),
status, is_html (and if needed include e.__class__.__name__), and keep the
existing branch that builds error_msg using error_body[:200] only for
user-facing output but never log the unsanitized full body.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cc7a4405-c3e5-495c-ad73-3035a97efc1a

📥 Commits

Reviewing files that changed from the base of the PR and between a083493 and b9a48d7.

📒 Files selected for processing (2)
  • autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md
  • autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: types
  • GitHub Check: Seer Code Review
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.13)
  • GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (4)
autogpt_platform/backend/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development

Files:

  • autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py
autogpt_platform/backend/**/*.{py,txt}

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use poetry run prefix for all Python commands, including testing, linting, formatting, and migrations

Files:

  • autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py
autogpt_platform/backend/backend/**/*.py

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use Prisma ORM for database operations in PostgreSQL with pgvector for embeddings

Files:

  • autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py
autogpt_platform/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py
🧠 Learnings (8)
📓 Common learnings
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12356
File: autogpt_platform/backend/backend/copilot/constants.py:9-12
Timestamp: 2026-03-10T08:39:13.707Z
Learning: In Significant-Gravitas/AutoGPT PR `#12356`, the `COPILOT_SYNTHETIC_ID_PREFIX = "copilot-"` check in `create_auto_approval_record` (human_review.py) is intentional and safe. The `graph_exec_id` passed to this function comes from server-side `PendingHumanReview` DB records (not from user input); the API only accepts `node_exec_id` from users. Synthetic `copilot-*` IDs are only ever created server-side in `run_block.py`. The prefix skip avoids a DB lookup for a `AgentGraphExecution` record that legitimately does not exist for CoPilot sessions, while `user_id` scoping is enforced at the auth layer and on the resulting auto-approval record.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:10030-10037
Timestamp: 2026-03-01T07:59:02.311Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — For MCP manual token storage, backend model autogpt_platform/backend/backend/api/features/mcp/routes.py defines MCPStoreTokenRequest.token as Pydantic SecretStr with a min length constraint, which generates OpenAPI schema metadata (format: "password", writeOnly: true, minLength: 1) in autogpt_platform/frontend/src/app/api/openapi.json. Prefer SecretStr (with length constraints) for sensitive request fields so generated TS clients and docs treat them as secrets.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:9983-9995
Timestamp: 2026-02-27T15:59:00.370Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — Backend/frontend OpenAPI codegen
Learning: For MCP schema models, required OpenAPI fields must have no defaults in Pydantic. Specifically, MCPToolInfo.input_schema must be required (no Field(default_factory=dict)) so openapi.json emits it in "required", ensuring generated TS types treat input_schema as non-optional.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:9983-9995
Timestamp: 2026-02-27T15:59:00.370Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — OpenAPI/codegen
Learning: Ensuring a field is required in generated TS types needs two sides: (1) no default value on the Pydantic field, and (2) the OpenAPI model's "required" array must list it. For MCPToolInfo, making input_schema required in OpenAPI and removing Field(default_factory=dict) in the backend prevents optional typing drift.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunMCPTool/helpers.tsx:23-24
Timestamp: 2026-02-27T10:45:55.700Z
Learning: As of PR `#12213`, MCP tool response types (MCPToolsDiscoveredResponse, MCPToolOutputResponse) are defined in openapi.json and frontend code in autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunMCPTool/helpers.tsx uses the generated types from `@/app/api/__generated__/`. Other tools like RunBlock still use inline TypeScript interfaces (e.g., BlockDetailsResponse) for SSE stream payloads that are not included in openapi.json schemas. The pattern is tool-specific: use generated types when available in openapi.json, use inline types only when the payload schema is truly SSE-stream-only and not exposed via OpenAPI.
📚 Learning: 2026-02-27T10:45:55.700Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunMCPTool/helpers.tsx:23-24
Timestamp: 2026-02-27T10:45:55.700Z
Learning: As of PR `#12213`, MCP tool response types (MCPToolsDiscoveredResponse, MCPToolOutputResponse) are defined in openapi.json and frontend code in autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunMCPTool/helpers.tsx uses the generated types from `@/app/api/__generated__/`. Other tools like RunBlock still use inline TypeScript interfaces (e.g., BlockDetailsResponse) for SSE stream payloads that are not included in openapi.json schemas. The pattern is tool-specific: use generated types when available in openapi.json, use inline types only when the payload schema is truly SSE-stream-only and not exposed via OpenAPI.

Applied to files:

  • autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py
  • autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md
📚 Learning: 2026-02-27T15:59:00.370Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:9983-9995
Timestamp: 2026-02-27T15:59:00.370Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — Backend/frontend OpenAPI codegen
Learning: For MCP schema models, required OpenAPI fields must have no defaults in Pydantic. Specifically, MCPToolInfo.input_schema must be required (no Field(default_factory=dict)) so openapi.json emits it in "required", ensuring generated TS types treat input_schema as non-optional.

Applied to files:

  • autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py
  • autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md
📚 Learning: 2026-02-27T15:59:00.370Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:9983-9995
Timestamp: 2026-02-27T15:59:00.370Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — OpenAPI/codegen
Learning: Ensuring a field is required in generated TS types needs two sides: (1) no default value on the Pydantic field, and (2) the OpenAPI model's "required" array must list it. For MCPToolInfo, making input_schema required in OpenAPI and removing Field(default_factory=dict) in the backend prevents optional typing drift.

Applied to files:

  • autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py
  • autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.

Applied to files:

  • autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py
  • autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md
📚 Learning: 2026-03-04T08:04:35.881Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12273
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:216-220
Timestamp: 2026-03-04T08:04:35.881Z
Learning: In the AutoGPT Copilot backend, ensure that SVG images are not treated as vision image types by excluding 'image/svg+xml' from INLINEABLE_MIME_TYPES and MULTIMODAL_TYPES in tool_adapter.py; the Claude API supports PNG, JPEG, GIF, and WebP for vision. SVGs (XML text) should be handled via the text path instead, not the vision path.

Applied to files:

  • autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py
📚 Learning: 2026-03-04T12:19:39.243Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12279
File: autogpt_platform/backend/backend/copilot/tools/base.py:184-188
Timestamp: 2026-03-04T12:19:39.243Z
Learning: In autogpt_platform/backend/backend/copilot/tools/, ensure that anonymous users always pass user_id=None to tool execution methods. The anon_ prefix (e.g., anon_123) is used only for PostHog/analytics distinct_id and must not be used as an actual user_id. Use a simple truthiness check on user_id (e.g., if user_id: ... else: ... or a dedicated is_authenticated flag) to distinguish anonymous from authenticated users, and review all tool execution call sites within this directory to prevent accidentally forwarding an anon_ user_id to tools.

Applied to files:

  • autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py
📚 Learning: 2026-03-05T15:42:08.207Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12297
File: .claude/skills/backend-check/SKILL.md:14-16
Timestamp: 2026-03-05T15:42:08.207Z
Learning: In Python files under autogpt_platform/backend (recursively), rely on poetry run format to perform formatting (Black + isort) and linting (ruff). Do not run poetry run lint as a separate step after poetry run format, since format already includes linting checks.

Applied to files:

  • autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py
🔇 Additional comments (1)
autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py (1)

37-40: HTML detection helper is a good addition.

This is a lightweight guard and fits the error-sanitization path well.

Comment thread autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py Outdated
…try URL

Address review feedback:
- Replace _looks_like_html heuristic with status code check
  (404/405/406 → 'not an MCP server'). Cleaner and more reliable.
- Put raw HTTP details in ErrorResponse.error field so the frontend
  renders them in the de-emphasised pre block, not the main message.
- Replace vague registry homepage link with the exact working API
  endpoint: GET /v0/servers?q=<search_term>, and instruct the agent
  to use remotes[].url from results instead of guessing paths.
Block-first guidance already lives in mcp_tool_guide.md (loaded via
get_mcp_guide). No need to duplicate it in the system prompt supplement.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py (1)

193-199: Prefer URL-level wording over host-level wording in not-found errors.

Using only host can be inaccurate when the domain is valid but the MCP path is wrong. Use server_url in the message for precision.

Proposed message tweak
-                error_msg = (
-                    f"No MCP server found at {host} (HTTP {e.status_code}). "
+                error_msg = (
+                    f"No MCP server found at {server_url} (HTTP {e.status_code}). "
                     "This URL does not appear to host an MCP server."
                 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py` around lines
193 - 199, The error message uses host which can be misleading when the MCP path
is wrong; update the construction of error_msg in run_mcp_tool.py (the if block
that checks e.status_code in _NOT_MCP_STATUS_CODES and the else branch building
error_msg) to reference server_url (the full URL variable) instead of host so
the message accurately reflects the URL that was queried; keep the existing
wording but replace host with server_url in those f-strings.
autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md (1)

24-26: Add a language to the fenced registry example.

The fenced block at Line 24 is missing a language tag (MD040).

Proposed doc-only fix
-```
+```http
 GET https://registry.modelcontextprotocol.io/v0/servers?q=<search_term>
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md around lines
24 - 26, The fenced code block containing the GET request example is missing a
language tag; update the fenced block that contains "GET
https://registry.modelcontextprotocol.io/v0/servers?q=<search_term>" to use a
language tag (e.g., add ```http at the opening fence) so the snippet is
correctly tagged and MD040 is resolved.


</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>

Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md:

  • Around line 24-26: The fenced code block containing the GET request example is
    missing a language tag; update the fenced block that contains "GET
    https://registry.modelcontextprotocol.io/v0/servers?q=<search_term>" to use a
    language tag (e.g., add ```http at the opening fence) so the snippet is
    correctly tagged and MD040 is resolved.

In @autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py:

  • Around line 193-199: The error message uses host which can be misleading when
    the MCP path is wrong; update the construction of error_msg in run_mcp_tool.py
    (the if block that checks e.status_code in _NOT_MCP_STATUS_CODES and the else
    branch building error_msg) to reference server_url (the full URL variable)
    instead of host so the message accurately reflects the URL that was queried;
    keep the existing wording but replace host with server_url in those f-strings.

</details>

---

<details>
<summary>ℹ️ Review info</summary>

<details>
<summary>⚙️ Run configuration</summary>

**Configuration used**: Organization UI

**Review profile**: CHILL

**Plan**: Pro

**Run ID**: `924a17b2-1787-4ac1-8063-ceb1fc1fc829`

</details>

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between d614ae94e3fa46425bfb4082d6e075312ace0595 and c88ac7d11c9b36550b53f3960f0d85d8b2334811.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md`
* `autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py`

</details>

</details>

<details>
<summary>📜 Review details</summary>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)</summary>

* GitHub Check: types
* GitHub Check: test (3.12)
* GitHub Check: test (3.11)
* GitHub Check: test (3.13)
* GitHub Check: Check PR Status

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (4)</summary>

<details>
<summary>autogpt_platform/backend/**/*.py</summary>


**📄 CodeRabbit inference engine (.github/copilot-instructions.md)**

> `autogpt_platform/backend/**/*.py`: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
> Always run 'poetry run format' (Black + isort) before linting in backend development
> Always run 'poetry run lint' (ruff) after formatting in backend development

Files:
- `autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py`

</details>
<details>
<summary>autogpt_platform/backend/**/*.{py,txt}</summary>


**📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)**

> Use `poetry run` prefix for all Python commands, including testing, linting, formatting, and migrations

Files:
- `autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py`

</details>
<details>
<summary>autogpt_platform/backend/backend/**/*.py</summary>


**📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)**

> Use Prisma ORM for database operations in PostgreSQL with pgvector for embeddings

Files:
- `autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py`

</details>
<details>
<summary>autogpt_platform/**/*.py</summary>


**📄 CodeRabbit inference engine (AGENTS.md)**

> Format Python code with `poetry run format`

Files:
- `autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py`

</details>

</details><details>
<summary>🧠 Learnings (10)</summary>

<details>
<summary>📓 Common learnings</summary>

```
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:9983-9995
Timestamp: 2026-02-27T15:59:00.370Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — Backend/frontend OpenAPI codegen
Learning: For MCP schema models, required OpenAPI fields must have no defaults in Pydantic. Specifically, MCPToolInfo.input_schema must be required (no Field(default_factory=dict)) so openapi.json emits it in "required", ensuring generated TS types treat input_schema as non-optional.
```

```
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:9983-9995
Timestamp: 2026-02-27T15:59:00.370Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — OpenAPI/codegen
Learning: Ensuring a field is required in generated TS types needs two sides: (1) no default value on the Pydantic field, and (2) the OpenAPI model's "required" array must list it. For MCPToolInfo, making input_schema required in OpenAPI and removing Field(default_factory=dict) in the backend prevents optional typing drift.
```

```
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:10030-10037
Timestamp: 2026-03-01T07:59:02.311Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — For MCP manual token storage, backend model autogpt_platform/backend/backend/api/features/mcp/routes.py defines MCPStoreTokenRequest.token as Pydantic SecretStr with a min length constraint, which generates OpenAPI schema metadata (format: "password", writeOnly: true, minLength: 1) in autogpt_platform/frontend/src/app/api/openapi.json. Prefer SecretStr (with length constraints) for sensitive request fields so generated TS clients and docs treat them as secrets.
```

```
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12356
File: autogpt_platform/backend/backend/copilot/constants.py:9-12
Timestamp: 2026-03-10T08:39:13.707Z
Learning: In Significant-Gravitas/AutoGPT PR `#12356`, the `COPILOT_SYNTHETIC_ID_PREFIX = "copilot-"` check in `create_auto_approval_record` (human_review.py) is intentional and safe. The `graph_exec_id` passed to this function comes from server-side `PendingHumanReview` DB records (not from user input); the API only accepts `node_exec_id` from users. Synthetic `copilot-*` IDs are only ever created server-side in `run_block.py`. The prefix skip avoids a DB lookup for a `AgentGraphExecution` record that legitimately does not exist for CoPilot sessions, while `user_id` scoping is enforced at the auth layer and on the resulting auto-approval record.
```

```
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunMCPTool/helpers.tsx:23-24
Timestamp: 2026-02-27T10:45:55.700Z
Learning: As of PR `#12213`, MCP tool response types (MCPToolsDiscoveredResponse, MCPToolOutputResponse) are defined in openapi.json and frontend code in autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunMCPTool/helpers.tsx uses the generated types from `@/app/api/__generated__/`. Other tools like RunBlock still use inline TypeScript interfaces (e.g., BlockDetailsResponse) for SSE stream payloads that are not included in openapi.json schemas. The pattern is tool-specific: use generated types when available in openapi.json, use inline types only when the payload schema is truly SSE-stream-only and not exposed via OpenAPI.
```

</details>
<details>
<summary>📚 Learning: 2026-02-27T10:45:55.700Z</summary>

```
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunMCPTool/helpers.tsx:23-24
Timestamp: 2026-02-27T10:45:55.700Z
Learning: As of PR `#12213`, MCP tool response types (MCPToolsDiscoveredResponse, MCPToolOutputResponse) are defined in openapi.json and frontend code in autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunMCPTool/helpers.tsx uses the generated types from `@/app/api/__generated__/`. Other tools like RunBlock still use inline TypeScript interfaces (e.g., BlockDetailsResponse) for SSE stream payloads that are not included in openapi.json schemas. The pattern is tool-specific: use generated types when available in openapi.json, use inline types only when the payload schema is truly SSE-stream-only and not exposed via OpenAPI.
```

**Applied to files:**
- `autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py`
- `autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md`

</details>
<details>
<summary>📚 Learning: 2026-02-27T15:59:00.370Z</summary>

```
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:9983-9995
Timestamp: 2026-02-27T15:59:00.370Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — Backend/frontend OpenAPI codegen
Learning: For MCP schema models, required OpenAPI fields must have no defaults in Pydantic. Specifically, MCPToolInfo.input_schema must be required (no Field(default_factory=dict)) so openapi.json emits it in "required", ensuring generated TS types treat input_schema as non-optional.
```

**Applied to files:**
- `autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py`
- `autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md`

</details>
<details>
<summary>📚 Learning: 2026-02-27T15:59:00.370Z</summary>

```
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:9983-9995
Timestamp: 2026-02-27T15:59:00.370Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — OpenAPI/codegen
Learning: Ensuring a field is required in generated TS types needs two sides: (1) no default value on the Pydantic field, and (2) the OpenAPI model's "required" array must list it. For MCPToolInfo, making input_schema required in OpenAPI and removing Field(default_factory=dict) in the backend prevents optional typing drift.
```

**Applied to files:**
- `autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py`
- `autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md`

</details>
<details>
<summary>📚 Learning: 2026-03-10T08:39:13.707Z</summary>

```
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12356
File: autogpt_platform/backend/backend/copilot/constants.py:9-12
Timestamp: 2026-03-10T08:39:13.707Z
Learning: In Significant-Gravitas/AutoGPT PR `#12356`, the `COPILOT_SYNTHETIC_ID_PREFIX = "copilot-"` check in `create_auto_approval_record` (human_review.py) is intentional and safe. The `graph_exec_id` passed to this function comes from server-side `PendingHumanReview` DB records (not from user input); the API only accepts `node_exec_id` from users. Synthetic `copilot-*` IDs are only ever created server-side in `run_block.py`. The prefix skip avoids a DB lookup for a `AgentGraphExecution` record that legitimately does not exist for CoPilot sessions, while `user_id` scoping is enforced at the auth layer and on the resulting auto-approval record.
```

**Applied to files:**
- `autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py`

</details>
<details>
<summary>📚 Learning: 2026-02-26T17:02:22.448Z</summary>

```
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.
```

**Applied to files:**
- `autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py`
- `autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md`

</details>
<details>
<summary>📚 Learning: 2026-03-04T08:04:35.881Z</summary>

```
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12273
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:216-220
Timestamp: 2026-03-04T08:04:35.881Z
Learning: In the AutoGPT Copilot backend, ensure that SVG images are not treated as vision image types by excluding 'image/svg+xml' from INLINEABLE_MIME_TYPES and MULTIMODAL_TYPES in tool_adapter.py; the Claude API supports PNG, JPEG, GIF, and WebP for vision. SVGs (XML text) should be handled via the text path instead, not the vision path.
```

**Applied to files:**
- `autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py`

</details>
<details>
<summary>📚 Learning: 2026-03-04T12:19:39.243Z</summary>

```
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12279
File: autogpt_platform/backend/backend/copilot/tools/base.py:184-188
Timestamp: 2026-03-04T12:19:39.243Z
Learning: In autogpt_platform/backend/backend/copilot/tools/, ensure that anonymous users always pass user_id=None to tool execution methods. The anon_ prefix (e.g., anon_123) is used only for PostHog/analytics distinct_id and must not be used as an actual user_id. Use a simple truthiness check on user_id (e.g., if user_id: ... else: ... or a dedicated is_authenticated flag) to distinguish anonymous from authenticated users, and review all tool execution call sites within this directory to prevent accidentally forwarding an anon_ user_id to tools.
```

**Applied to files:**
- `autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py`

</details>
<details>
<summary>📚 Learning: 2026-03-05T15:42:08.207Z</summary>

```
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12297
File: .claude/skills/backend-check/SKILL.md:14-16
Timestamp: 2026-03-05T15:42:08.207Z
Learning: In Python files under autogpt_platform/backend (recursively), rely on poetry run format to perform formatting (Black + isort) and linting (ruff). Do not run poetry run lint as a separate step after poetry run format, since format already includes linting checks.
```

**Applied to files:**
- `autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py`

</details>
<details>
<summary>📚 Learning: 2026-03-01T07:59:02.311Z</summary>

```
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:10030-10037
Timestamp: 2026-03-01T07:59:02.311Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — For MCP manual token storage, backend model autogpt_platform/backend/backend/api/features/mcp/routes.py defines MCPStoreTokenRequest.token as Pydantic SecretStr with a min length constraint, which generates OpenAPI schema metadata (format: "password", writeOnly: true, minLength: 1) in autogpt_platform/frontend/src/app/api/openapi.json. Prefer SecretStr (with length constraints) for sensitive request fields so generated TS clients and docs treat them as secrets.
```

**Applied to files:**
- `autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md`

</details>

</details><details>
<summary>🪛 markdownlint-cli2 (0.21.0)</summary>

<details>
<summary>autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md</summary>

[warning] 24-24: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (2)</summary><blockquote>

<details>
<summary>autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md (1)</summary><blockquote>

`29-40`: **Strong guardrails on block-first flow and URL provenance.**

This section is clear and correctly enforces `find_block` first, with explicit prohibition on guessed MCP URLs.

</blockquote></details>
<details>
<summary>autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py (1)</summary><blockquote>

`201-206`: **Good separation of primary error message vs raw HTTP detail.**

Moving HTTP detail into `error` while keeping `message` concise is a solid UX and safety improvement.

</blockquote></details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

- Drop _NOT_MCP_STATUS_CODES set; use single clean error message for
  all non-auth HTTP errors instead of guessing whether URL hosts MCP
- Sanitize logger to only log host and status code, not raw body
- Add test for HTML error body sanitization (404 with HTML body)
- Add language tag to fenced code block in mcp_tool_guide.md

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py (1)

616-622: Consider asserting the HTML body is preserved in the error field.

The test validates that HTML doesn't leak into response.message but doesn't verify the raw HTML body is present in response.error (for the collapsible detail feature). Per the implementation in run_mcp_tool.py:193, the error field should contain the truncated exception message (str(e)[:300]), which includes the HTML body.

💡 Suggested additional assertion
     # Raw detail goes in the collapsible `error` field
     assert response.error is not None
     assert "404" in response.error
+    # Verify raw HTML body is preserved in the collapsible error field
+    assert "<!doctype" in response.error.lower()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py` around
lines 616 - 622, Add an assertion in test_run_mcp_tool.py to ensure the raw HTML
body appears in the collapsible detail (response.error) as implemented in
run_mcp_tool.py (where the exception message is truncated via str(e)[:300]);
update the test near the existing checks (after asserting response.error is not
None and contains "404") to assert that "<!doctype" (or another expected HTML
snippet) is present in response.error and/or that response.error contains the
truncated exception string pattern emitted by the run_mcp_tool.py error
handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py`:
- Around line 616-622: Add an assertion in test_run_mcp_tool.py to ensure the
raw HTML body appears in the collapsible detail (response.error) as implemented
in run_mcp_tool.py (where the exception message is truncated via str(e)[:300]);
update the test near the existing checks (after asserting response.error is not
None and contains "404") to assert that "<!doctype" (or another expected HTML
snippet) is present in response.error and/or that response.error contains the
truncated exception string pattern emitted by the run_mcp_tool.py error
handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5dfe847c-feb1-4ee7-82f8-67cf03233f52

📥 Commits

Reviewing files that changed from the base of the PR and between c88ac7d and ccefed1.

📒 Files selected for processing (3)
  • autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md
  • autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py
  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • autogpt_platform/backend/backend/copilot/tools/run_mcp_tool.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: types
  • GitHub Check: Seer Code Review
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.12)
  • GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (5)
autogpt_platform/backend/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development

Files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
autogpt_platform/backend/**/*.{py,txt}

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use poetry run prefix for all Python commands, including testing, linting, formatting, and migrations

Files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
autogpt_platform/backend/backend/**/*.py

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use Prisma ORM for database operations in PostgreSQL with pgvector for embeddings

Files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
autogpt_platform/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
autogpt_platform/backend/**/*test*.py

📄 CodeRabbit inference engine (AGENTS.md)

Run poetry run test for backend testing (runs pytest with docker based postgres + prisma)

Files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
🧠 Learnings (8)
📓 Common learnings
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:9983-9995
Timestamp: 2026-02-27T15:59:00.370Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — Backend/frontend OpenAPI codegen
Learning: For MCP schema models, required OpenAPI fields must have no defaults in Pydantic. Specifically, MCPToolInfo.input_schema must be required (no Field(default_factory=dict)) so openapi.json emits it in "required", ensuring generated TS types treat input_schema as non-optional.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:9983-9995
Timestamp: 2026-02-27T15:59:00.370Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — OpenAPI/codegen
Learning: Ensuring a field is required in generated TS types needs two sides: (1) no default value on the Pydantic field, and (2) the OpenAPI model's "required" array must list it. For MCPToolInfo, making input_schema required in OpenAPI and removing Field(default_factory=dict) in the backend prevents optional typing drift.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:10030-10037
Timestamp: 2026-03-01T07:59:02.311Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — For MCP manual token storage, backend model autogpt_platform/backend/backend/api/features/mcp/routes.py defines MCPStoreTokenRequest.token as Pydantic SecretStr with a min length constraint, which generates OpenAPI schema metadata (format: "password", writeOnly: true, minLength: 1) in autogpt_platform/frontend/src/app/api/openapi.json. Prefer SecretStr (with length constraints) for sensitive request fields so generated TS clients and docs treat them as secrets.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12356
File: autogpt_platform/backend/backend/copilot/constants.py:9-12
Timestamp: 2026-03-10T08:39:13.707Z
Learning: In Significant-Gravitas/AutoGPT PR `#12356`, the `COPILOT_SYNTHETIC_ID_PREFIX = "copilot-"` check in `create_auto_approval_record` (human_review.py) is intentional and safe. The `graph_exec_id` passed to this function comes from server-side `PendingHumanReview` DB records (not from user input); the API only accepts `node_exec_id` from users. Synthetic `copilot-*` IDs are only ever created server-side in `run_block.py`. The prefix skip avoids a DB lookup for a `AgentGraphExecution` record that legitimately does not exist for CoPilot sessions, while `user_id` scoping is enforced at the auth layer and on the resulting auto-approval record.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunMCPTool/helpers.tsx:23-24
Timestamp: 2026-02-27T10:45:55.700Z
Learning: As of PR `#12213`, MCP tool response types (MCPToolsDiscoveredResponse, MCPToolOutputResponse) are defined in openapi.json and frontend code in autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunMCPTool/helpers.tsx uses the generated types from `@/app/api/__generated__/`. Other tools like RunBlock still use inline TypeScript interfaces (e.g., BlockDetailsResponse) for SSE stream payloads that are not included in openapi.json schemas. The pattern is tool-specific: use generated types when available in openapi.json, use inline types only when the payload schema is truly SSE-stream-only and not exposed via OpenAPI.
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12284
File: autogpt_platform/frontend/src/app/api/openapi.json:11897-11900
Timestamp: 2026-03-04T23:58:18.476Z
Learning: Repo: Significant-Gravitas/AutoGPT — PR `#12284`
Backend/frontend OpenAPI codegen convention: In backend/api/features/store/model.py, the StoreSubmission and StoreSubmissionAdminView models define submitted_at: datetime | None, changes_summary: str | None, and instructions: str | None with no default. This is intentional to produce “required but nullable” fields in OpenAPI (properties appear in required[] and use anyOf [type, null]). This matches Prisma’s submittedAt DateTime? and changesSummary String?. Do not flag this as a required/nullable mismatch.
📚 Learning: 2026-02-27T10:45:55.700Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunMCPTool/helpers.tsx:23-24
Timestamp: 2026-02-27T10:45:55.700Z
Learning: As of PR `#12213`, MCP tool response types (MCPToolsDiscoveredResponse, MCPToolOutputResponse) are defined in openapi.json and frontend code in autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunMCPTool/helpers.tsx uses the generated types from `@/app/api/__generated__/`. Other tools like RunBlock still use inline TypeScript interfaces (e.g., BlockDetailsResponse) for SSE stream payloads that are not included in openapi.json schemas. The pattern is tool-specific: use generated types when available in openapi.json, use inline types only when the payload schema is truly SSE-stream-only and not exposed via OpenAPI.

Applied to files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
  • autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md
📚 Learning: 2026-02-27T15:59:00.370Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:9983-9995
Timestamp: 2026-02-27T15:59:00.370Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — Backend/frontend OpenAPI codegen
Learning: For MCP schema models, required OpenAPI fields must have no defaults in Pydantic. Specifically, MCPToolInfo.input_schema must be required (no Field(default_factory=dict)) so openapi.json emits it in "required", ensuring generated TS types treat input_schema as non-optional.

Applied to files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
  • autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.

Applied to files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
  • autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md
📚 Learning: 2026-03-04T08:04:35.881Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12273
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:216-220
Timestamp: 2026-03-04T08:04:35.881Z
Learning: In the AutoGPT Copilot backend, ensure that SVG images are not treated as vision image types by excluding 'image/svg+xml' from INLINEABLE_MIME_TYPES and MULTIMODAL_TYPES in tool_adapter.py; the Claude API supports PNG, JPEG, GIF, and WebP for vision. SVGs (XML text) should be handled via the text path instead, not the vision path.

Applied to files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
📚 Learning: 2026-03-04T12:19:39.243Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12279
File: autogpt_platform/backend/backend/copilot/tools/base.py:184-188
Timestamp: 2026-03-04T12:19:39.243Z
Learning: In autogpt_platform/backend/backend/copilot/tools/, ensure that anonymous users always pass user_id=None to tool execution methods. The anon_ prefix (e.g., anon_123) is used only for PostHog/analytics distinct_id and must not be used as an actual user_id. Use a simple truthiness check on user_id (e.g., if user_id: ... else: ... or a dedicated is_authenticated flag) to distinguish anonymous from authenticated users, and review all tool execution call sites within this directory to prevent accidentally forwarding an anon_ user_id to tools.

Applied to files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
📚 Learning: 2026-03-05T15:42:08.207Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12297
File: .claude/skills/backend-check/SKILL.md:14-16
Timestamp: 2026-03-05T15:42:08.207Z
Learning: In Python files under autogpt_platform/backend (recursively), rely on poetry run format to perform formatting (Black + isort) and linting (ruff). Do not run poetry run lint as a separate step after poetry run format, since format already includes linting checks.

Applied to files:

  • autogpt_platform/backend/backend/copilot/tools/test_run_mcp_tool.py
📚 Learning: 2026-02-27T15:59:00.370Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:9983-9995
Timestamp: 2026-02-27T15:59:00.370Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — OpenAPI/codegen
Learning: Ensuring a field is required in generated TS types needs two sides: (1) no default value on the Pydantic field, and (2) the OpenAPI model's "required" array must list it. For MCPToolInfo, making input_schema required in OpenAPI and removing Field(default_factory=dict) in the backend prevents optional typing drift.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md
🔇 Additional comments (1)
autogpt_platform/backend/backend/copilot/sdk/mcp_tool_guide.md (1)

23-40: LGTM!

The documentation additions clearly address the issues outlined in the PR objectives:

  • Registry API search replaces guesswork with an authoritative lookup
  • "Check blocks first" section enforces the correct workflow
  • Explicit prohibition against URL guessing prevents the original bug (e.g., guessing https://sheets.googleapis.com/mcp)

This guidance should effectively prevent CoPilot from bypassing block discovery and constructing invalid MCP URLs.

@majdyz
majdyz requested a review from ntindle March 13, 2026 06:00
@majdyz
majdyz enabled auto-merge March 13, 2026 06:01
@majdyz
majdyz self-requested a review March 13, 2026 11:48

@autogpt-reviewer autogpt-reviewer left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #12394 — fix(copilot): prioritize block discovery over MCP and sanitize HTML errors
Author: Otto-AGPT | Requested by: @majdyz | Files: mcp_tool_guide.md (+18/-1), run_mcp_tool.py (+4/-2), test_run_mcp_tool.py (+43/+0)

🎯 Verdict: APPROVE

What This PR Does

When users ask CoPilot for Google Sheets integration, the agent was skipping 55+ available blocks, fabricating a fake MCP URL (sheets.googleapis.com/mcp), and dumping raw HTML 404 error pages into the conversation. This PR fixes the agent guide to enforce "check blocks first" behavior and sanitizes HTTP error responses so users see clean messages instead of raw HTML.

Specialist Findings

🛡️ Security ✅ — No vulnerabilities. The error field (truncated to 300 chars) could expose remote server internals, but this is low-risk since it's in a separate debug field, not the user-facing message. server_host() correctly strips credentials via urlparse().hostname. No new SSRF surface. Logger no longer dumps raw exception bodies — a security improvement.

🏗️ Architecture ✅ — Error restructuring (message vs error field) is consistent with existing ErrorResponse patterns across the codebase (search_docs.py, workspace_files.py, feature_requests.py). Guide-based enforcement is pragmatic given the LLM-driven tool dispatch architecture. No new coupling introduced. Suggestion: consider extracting _MAX_ERROR_DETAIL_LENGTH = 300 as a constant (nit).

Performance ✅ — No concerns. All changes are on the error path only. str(e)[:300] is O(1) in CPython, slices by code points (not bytes) so no Unicode issues. The server_host() call is now cached in a local variable — minor improvement. Guide changes are static prompt content with zero runtime cost.

🧪 Testing ⚠️ — The new test is well-written and covers the core scenario (HTML body doesn't leak into message, raw detail preserved in error). Missing edge cases (non-blocking): (1) no test for the 300-char truncation with long bodies, (2) no parametrized coverage of non-HTML/empty error bodies, (3) existing test_auth_error_with_existing_creds_returns_error should be updated to verify the new error field. These are should-fix for a follow-up.

📖 Quality ✅ — Guide is clear and actionable. Error messages are user-friendly. Minor nits: (1) unnecessary parentheses in message=(f"...") on line 190, (2) "MCP request" in user-facing message could be simplified to "Request" since users may not know what MCP means, (3) inline import in test could be moved to top-level for consistency.

📦 Product ✅ — Directly addresses the critical UX failure. Users now see "MCP request to sheets.googleapis.com failed with HTTP 404." instead of raw HTML dumps. The guide's "Check blocks first" section is well-positioned. Important caveat: the main CoPilot system prompt (managed externally) also needs updating to reinforce block-first behavior — the PR acknowledges this. This PR is still worth merging standalone as it makes things strictly better.

📬 Discussion ✅ — All CodeRabbit review threads resolved. majdyz addressed the actionable item (HTML test coverage) and simplified the approach. Remaining CodeRabbit nitpicks are cosmetic. No unresolved concerns. Zero independent human approvals on record — PR needs formal approval to merge. Low overlap risk with PR #12206 (different sections of same test file).

🔎 QA ⚠️ — Code review only (environment setup failed due to Docker conflicts). Frontend already renders ErrorResponse.error as de-emphasized <pre> with text-xs opacity-80 in RunMCPTool.tsx. The error field is always visible (not behind a <details> collapsible despite test name), but this is acceptable for 300-char truncated content. No UI/UX blockers found.

Blockers

None.

Should Fix (Follow-up OK)

  1. System prompt update — The mcp_tool_guide.md changes only take effect when the agent calls get_mcp_guide, which happens after it decides to pursue MCP. The block-first instruction needs to be in the system prompt's capability-check section to prevent entering the MCP workflow prematurely. (PR acknowledges this — track as required follow-up.)
  2. test_run_mcp_tool.py — Add edge case tests: truncation at 300 chars with long bodies, non-HTML error bodies, empty bodies. Update existing 403-with-creds test to verify new error field.
  3. run_mcp_tool.py:190 — Remove unnecessary parentheses: message=(f"...")message=f"...".

Nice to Have

  • Extract _MAX_ERROR_DETAIL_LENGTH = 300 as a named constant.
  • Consider simplifying user-facing message from "MCP request to {host}" to "Request to {host}" since users may not know what MCP is.
  • Consider making the error detail collapsible (<details>) in the frontend for cleaner UX.
  • Move "Check blocks first" section above "Known hosted MCP servers" in the guide to match natural priority order.

Risk Assessment

Merge risk: LOW | Rollback: EASY

Small, well-scoped change (65 lines across 3 files). Error path only — no happy-path behavior changes. All CI green. No schema/migration changes.

@ntindle Clean fix for a critical UX bug — CoPilot no longer dumps raw HTML into conversations. No blockers; should-fix items are all follow-up quality. Recommend merge.

@majdyz
majdyz added this pull request to the merge queue Mar 14, 2026
@github-project-automation github-project-automation Bot moved this from 🆕 Needs initial review to 👍🏼 Mergeable in AutoGPT development kanban Mar 14, 2026
Merged via the queue into dev with commit d9c16de Mar 14, 2026
20 checks passed
@majdyz
majdyz deleted the otto/secrt-2116-copilot-mcp-block-discovery branch March 14, 2026 13:07
@github-project-automation github-project-automation Bot moved this from 👍🏼 Mergeable to ✅ Done in AutoGPT development kanban Mar 14, 2026
Bentlybro pushed a commit that referenced this pull request Apr 4, 2026
…rrors (#12394)

Requested by @majdyz

When a user asks for Google Sheets integration, the CoPilot agent skips
block discovery entirely (despite 55+ Google Sheets blocks being
available), jumps straight to MCP, guesses a fake URL
(`https://sheets.googleapis.com/mcp`), and gets a raw HTML 404 error
page dumped into the conversation.

**Changes:**

1. **MCP guide** (`mcp_tool_guide.md`): Added "Check blocks first"
section directing the agent to use `find_block` before attempting MCP
for any service not in the known servers list. Explicitly prohibits
guessing/constructing MCP server URLs.

2. **Error handling** (`run_mcp_tool.py`): Detects HTML error pages in
HTTP responses (e.g. raw 404 pages from non-MCP endpoints) and returns a
clean one-liner like "This URL does not appear to host an MCP server"
instead of dumping the full HTML body.

**Note:** The main CoPilot system prompt (managed externally, not in
repo) should also be updated to reinforce block-first behavior in the
Capability Check section. This PR covers the in-repo changes.

Session reference: `9216df83-5f4a-48eb-9457-3ba2057638ae` (turn 3)
Ticket: [SECRT-2116](https://linear.app/autogpt/issue/SECRT-2116)

---
Co-authored-by: Zamil Majdy (@majdyz) <majdyz@gmail.com>

---------

Co-authored-by: Zamil Majdy (@majdyz) <majdyz@gmail.com>
Co-authored-by: Zamil Majdy <zamil.majdy@agpt.co>
Bentlybro pushed a commit to Bentlybro/AutoGPT that referenced this pull request Apr 4, 2026
…rrors (Significant-Gravitas#12394)

Requested by @majdyz

When a user asks for Google Sheets integration, the CoPilot agent skips
block discovery entirely (despite 55+ Google Sheets blocks being
available), jumps straight to MCP, guesses a fake URL
(`https://sheets.googleapis.com/mcp`), and gets a raw HTML 404 error
page dumped into the conversation.

**Changes:**

1. **MCP guide** (`mcp_tool_guide.md`): Added "Check blocks first"
section directing the agent to use `find_block` before attempting MCP
for any service not in the known servers list. Explicitly prohibits
guessing/constructing MCP server URLs.

2. **Error handling** (`run_mcp_tool.py`): Detects HTML error pages in
HTTP responses (e.g. raw 404 pages from non-MCP endpoints) and returns a
clean one-liner like "This URL does not appear to host an MCP server"
instead of dumping the full HTML body.

**Note:** The main CoPilot system prompt (managed externally, not in
repo) should also be updated to reinforce block-first behavior in the
Capability Check section. This PR covers the in-repo changes.

Session reference: `9216df83-5f4a-48eb-9457-3ba2057638ae` (turn 3)
Ticket: [SECRT-2116](https://linear.app/autogpt/issue/SECRT-2116)

---
Co-authored-by: Zamil Majdy (@majdyz) <majdyz@gmail.com>

---------

Co-authored-by: Zamil Majdy (@majdyz) <majdyz@gmail.com>
Co-authored-by: Zamil Majdy <zamil.majdy@agpt.co>
ntindle pushed a commit to goingforstudying-ctrl/AutoGPT that referenced this pull request Jun 5, 2026
…block exists (Significant-Gravitas#13117)

### Why / What / How

**Why:** [SECRT-2350](https://linear.app/agpt/issue/SECRT-2350) — Today
a user has to *know* that MCP exists for a given service (e.g.
MailerLite) and explicitly ask AutoPilot to connect via MCP.
Non-technical users won't know this is even an option, so requests for
services without a native block get rejected as "no integration exists"
even when an MCP integration is available. And when MCP *is* used, the
raw "MCP server / OAuth" jargon is intimidating to users who don't know
the term.

**What:** Prompt-only changes to the CoPilot tool-discovery surface (no
code/tool/schema changes). Note that the auto-discovery groundwork and
most jargon-avoidance already landed on `dev` via parallel PRs (Significant-Gravitas#12394,
Significant-Gravitas#13207); against current `dev` this PR contributes three things:

1. **Discover MCP servers via web search instead of the MCP registry
API.** The previous guidance queried
`https://registry.modelcontextprotocol.io/v0/servers?q=...`. That's a
hardcoded `/v0/` endpoint (would break on a `v1` bump) and its `q=`
search didn't reliably surface real servers — e.g. searching "sentry"
returns no Sentry result. We drop the registry lookup entirely: the
fallback chain is now **native block → known hosted-server list → web
search for the service's official MCP URL → give up**.
2. **Reframe MCP as "an integration (MCP)" in user-facing text.** The
model leads with the word "integration" and discloses `(MCP)` once, in
parentheses, the first time it's mentioned in a turn — then drops it.
This keeps non-technical users from being scared by the term while
staying transparent for users who *do* know what MCP is. Raw jargon
("MCP server", "MCP tool", "OAuth", "credentials") stays banned in
user-facing text.
3. **Harden the MCP guide** so MCP discovery is explicitly mandatory
(not optional) before declaring a service unsupported, with the new
web-search step in the required checklist.

**How:**

- `backend/backend/copilot/prompting.py` — step 2 of the Tool Discovery
Priority ladder now web-searches for the official MCP URL (instead of
querying the registry); the hostname-verification guard applies to
search-returned URLs and notes they're unvetted; adds the "(MCP)"
user-facing framing note. The anti-pattern section and "Correct flow"
pseudocode are updated from "registry lookup" to "web search".
- `backend/backend/copilot/sdk/mcp_tool_guide.md` — replaces the
registry-API section with web-search guidance; the "Check blocks first"
section becomes "Check blocks first, then MCP is MANDATORY" with a
known-list → web-search checklist; the "Communication style" section
adopts the "the <Service> integration (MCP)" convention.

**Safety note:** Web-search results are unvetted, so the prompt requires
the model to verify the server hostname is vendor-owned (e.g.
`mcp.sentry.dev` for Sentry) before any sign-in, and to ask the user
which candidate to use when the match is ambiguous — because the user is
about to hand that URL their sign-in.

No new tools, no schema changes, no migration. The cacheable system
prompt in `service.py` is untouched, so the prompt cache is preserved
(edits land only in the per-turn shareable tool notes and the on-demand
MCP guide).

### Changes 🏗️

- `prompting.py`: registry-API lookup replaced with web search for the
MCP server URL; hostname-verification note broadened to unvetted search
results; new "(MCP)" user-facing framing line; anti-pattern + "Correct
flow" updated to reference web search.
- `mcp_tool_guide.md`: registry-API section replaced with web-search
guidance; "Check blocks first" hardened to "...then MCP is MANDATORY"
with a 2-step (known list → web search) checklist; "Communication style"
section adopts the "<Service> integration (MCP)" convention.

### Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [ ] I have tested my changes according to the test plan:
- [ ] Ask AutoPilot to use a service with no native block but a known
MCP server (e.g. Linear, Notion, Stripe) — confirm it connects without
the user mentioning MCP.
- [ ] Ask AutoPilot to use a service with no native block and not in the
known list (e.g. MailerLite, Sentry) — confirm it web-searches for the
official MCP URL before saying it's unsupported.
- [ ] Confirm the model verifies the hostname is vendor-owned and asks
the user when multiple candidate URLs are ambiguous.
- [ ] Ask AutoPilot to use a service with a native block (e.g. Google
Sheets) — confirm it still uses `find_block` first and doesn't
unnecessarily reach for MCP.
- [ ] Confirm user-facing chat reads "the X integration (MCP)" on first
mention and "the X integration" / "X" thereafter; no "MCP server",
"OAuth", or "credentials" leaks.
- [ ] Ask for a service with neither a block nor an MCP server — confirm
the model only declines *after* both `find_block` and a web search
return nothing.

#### For configuration changes:

- [x] `.env.default` is updated or already compatible with my changes
- [x] `docker-compose.yml` is updated or already compatible with my
changes
- [x] I have included a list of my configuration changes in the PR
description (under **Changes**)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform/backend AutoGPT Platform - Back end size/m

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

3 participants