Skip to content

fix: MCP/REST generations incorrectly triggering autoplay#637

Open
depopry wants to merge 5 commits into
jamiepine:mainfrom
depopry:fix/mcp-autoplay-source-635
Open

fix: MCP/REST generations incorrectly triggering autoplay#637
depopry wants to merge 5 commits into
jamiepine:mainfrom
depopry:fix/mcp-autoplay-source-635

Conversation

@depopry
Copy link
Copy Markdown

@depopry depopry commented May 11, 2026

Summary

Fixes #635.

  • Agent-initiated generations (MCP tool / POST /speak) were stored with source="manual" in the DB because generate_speech() ignored the caller's origin
  • The frontend SSE handler checks gen.source to skip autoplay for agent sources ("mcp", "rest"), but always received "manual" — so autoplay fired even when disabled
  • Now passes source="mcp" / source="rest" through GenerationRequest so the DB row carries the correct origin and the frontend's AGENT_SOURCES guard works reliably

Test plan

  • Generate via MCP tool (voicebox.speak) with autoplay disabled — should NOT auto-play in main window
  • Generate via POST /speak with autoplay disabled — should NOT auto-play in main window
  • Generate manually in the UI — should still auto-play when setting is enabled
  • Personality-rewrite via MCP — source should remain "mcp", not downgrade to "personality_speak"

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Generation requests are now tagged with their origin (e.g., REST, manual, personality, MCP) for clearer tracking.
    • The REST speak endpoint marks requests as coming from REST.
    • Caller-supplied source values are preserved; personality-based tagging is only applied when no explicit source is provided.
    • Generation and history responses may include version information for returned items.

Review Change Stack

Agent-initiated generations (via MCP tool or POST /speak) were stored in
the database with source="manual" because generate_speech() ignored the
caller's origin. The frontend SSE handler checks gen.source to skip
autoplay for agent sources, but it received "manual" and played anyway.

Pass source="mcp" / source="rest" through GenerationRequest so the DB
row carries the correct origin and the frontend's AGENT_SOURCES guard
works reliably.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: d14e8e59-4dc7-4f64-995c-55e5f9bfbb21

📥 Commits

Reviewing files that changed from the base of the PR and between b856f75 and cb2b913.

📒 Files selected for processing (4)
  • backend/mcp_server/tools.py
  • backend/models.py
  • backend/routes/generations.py
  • backend/routes/speak.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • backend/routes/speak.py
  • backend/routes/generations.py
  • backend/models.py

📝 Walkthrough

Walkthrough

Adds a GenerationSource Literal and optional GenerationRequest.source; converts many Pydantic List[...] annotations to built-in list[...]. MCP and REST entry points tag requests with "mcp" or "rest"; generate_speech preserves caller-supplied source and conditionally applies "personality_speak"; history.create_generation accepts GenerationSource.

Changes

Request Source Attribution

Layer / File(s) Summary
Data Model
backend/models.py
GenerationSource Literal added; GenerationRequest gains source: Optional[GenerationSource]; GenerationResponse and HistoryResponse gain versions: Optional[list[GenerationVersionResponse]]; many List[...]list[...] typing updates.
History Service
backend/services/history.py
Imports GenerationSource; create_generation(..., source) annotated as GenerationSource (default "manual").
Service Logic
backend/routes/generations.py
generate_speech accepts optional source, derives resolved_source (default "manual"), passes source=resolved_source to history.create_generation; personality rewrite sets "personality_speak" only when no explicit source provided.
Entry Points
backend/mcp_server/tools.py, backend/routes/speak.py
MCP _speak calls generate_speech with source="mcp"; REST /speak calls generate_speech with source="rest".
sequenceDiagram
  participant MCP
  participant REST
  participant Generator as generate_speech
  participant History as history.create_generation
  participant Models as backend.models

  MCP->>Generator: request(source="mcp")
  REST->>Generator: request(source="rest")
  Generator->>Generator: resolve source (data.source? -> resolved_source)
  Generator->>History: create_generation(..., source=resolved_source)
  History->>Models: store GenerationResponse with source/versions
Loading

🎯 3 (Moderate) | ⏱️ ~20 minutes

🐰 I hopped through code with a tiny tap,
Tagging each voice on its routing map,
From burrowed MCP to RESTy door,
Every generation marked and stored. 🎙️

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the core issue being fixed: preventing MCP and REST-initiated generations from incorrectly triggering autoplay.
Linked Issues check ✅ Passed The PR fulfills issue #635 by propagating source information through GenerationRequest and GenerationResponse, enabling the frontend to correctly skip autoplay for agent-originated generations.
Out of Scope Changes check ✅ Passed All changes directly support fixing the autoplay issue: adding GenerationSource typing, propagating source through request/response layers, and updating type annotations to modern Python syntax.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
backend/models.py (1)

103-105: ⚡ Quick win

Consider adding a validation pattern for the source field.

For consistency with other fields in this model (e.g., engine, language, model_size), consider restricting source to known values to prevent typos and tighten the API contract.

✨ Suggested validation pattern
 source: Optional[str] = Field(
-    None, description="Origin of the request (e.g. 'mcp', 'rest'). Internal use — not exposed to public API docs."
+    None,
+    pattern="^(manual|mcp|rest|personality_speak|import)$",
+    description="Origin of the request (e.g. 'mcp', 'rest'). Internal use — not exposed to public API docs."
 )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/models.py` around lines 103 - 105, The source Field (source:
Optional[str] = Field(...)) should be constrained to known values to prevent
typos; replace the free-form string with a typed constraint (e.g., a Source enum
or typing.Literal of allowed values like 'mcp' and 'rest') and update the Field
declaration on the model to use that type (or add a regex/pattern argument to
Field) so validation occurs automatically when constructing instances of the
model.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@backend/models.py`:
- Around line 103-105: The source Field (source: Optional[str] = Field(...))
should be constrained to known values to prevent typos; replace the free-form
string with a typed constraint (e.g., a Source enum or typing.Literal of allowed
values like 'mcp' and 'rest') and update the Field declaration on the model to
use that type (or add a regex/pattern argument to Field) so validation occurs
automatically when constructing instances of the model.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f1844f41-e371-46d6-aff2-957fbca9ea33

📥 Commits

Reviewing files that changed from the base of the PR and between b35b909 and 743e60a.

📒 Files selected for processing (4)
  • backend/mcp_server/tools.py
  • backend/models.py
  • backend/routes/generations.py
  • backend/routes/speak.py

Replaces free-form Optional[str] with a Literal type so Pydantic
rejects unknown source values at validation time.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@depopry depopry changed the title Fix MCP/REST generations incorrectly triggering autoplay fix: MCP/REST generations incorrectly triggering autoplay May 11, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
backend/models.py (1)

6-6: ⚡ Quick win

Prefer modern list over typing.List for Python 3.9+.

The typing.List import is deprecated in favor of the built-in list type for type hints (PEP 585). Since this codebase likely targets Python 3.9 or later, you can use list[...] directly.

♻️ Proposed fix
-from typing import Literal, Optional, List
+from typing import Literal, Optional

Then replace List[...] with list[...] throughout the file (lines 41, 102, etc.).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/models.py` at line 6, Replace the deprecated typing.List import with
the built-in generic list by removing List from the import line and updating all
uses of List[...] in this module (e.g., type annotations referenced around the
symbols on lines noted such as the annotations near the class/variable usages at
lines ~41 and ~102) to use list[...]; ensure the top import becomes "from typing
import Literal, Optional" and change every List[...] occurrence to list[...] to
adopt PEP 585 style.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@backend/models.py`:
- Line 6: Replace the deprecated typing.List import with the built-in generic
list by removing List from the import line and updating all uses of List[...] in
this module (e.g., type annotations referenced around the symbols on lines noted
such as the annotations near the class/variable usages at lines ~41 and ~102) to
use list[...]; ensure the top import becomes "from typing import Literal,
Optional" and change every List[...] occurrence to list[...] to adopt PEP 585
style.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 743b9c63-ba9e-45da-bc60-2c818f1aa898

📥 Commits

Reviewing files that changed from the base of the PR and between 743e60a and 8f41317.

📒 Files selected for processing (2)
  • backend/models.py
  • backend/services/history.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
backend/models.py (1)

126-126: ⚡ Quick win

Use GenerationSource type for consistency and validation.

The source field in GenerationResponse uses plain str, while GenerationRequest.source (line 105) uses the GenerationSource Literal type. For consistency, type safety, and to enable Pydantic validation of source values, consider using GenerationSource here as well.

♻️ Proposed fix for type consistency
-    source: str = "manual"
+    source: GenerationSource = "manual"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/models.py` at line 126, Change the type of the source field on the
GenerationResponse model from plain str to the existing GenerationSource Literal
so Pydantic will validate and ensure consistency with GenerationRequest; update
the source declaration in the GenerationResponse class (symbol:
GenerationResponse.source) to use GenerationSource and run tests/type checks to
confirm no other code expects arbitrary strings.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@backend/models.py`:
- Line 126: Change the type of the source field on the GenerationResponse model
from plain str to the existing GenerationSource Literal so Pydantic will
validate and ensure consistency with GenerationRequest; update the source
declaration in the GenerationResponse class (symbol: GenerationResponse.source)
to use GenerationSource and run tests/type checks to confirm no other code
expects arbitrary strings.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fe24e52d-c083-425e-b668-eb9fc7dfb834

📥 Commits

Reviewing files that changed from the base of the PR and between 8f41317 and bec0cc2.

📒 Files selected for processing (1)
  • backend/models.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/models.py`:
- Around line 105-107: The public model GenerationRequest exposes the internal
provenance field source (of type GenerationSource) allowing clients to spoof
origin; remove the `source: Optional[GenerationSource]` Field from the
GenerationRequest model, stop accepting it on public endpoints (/generate,
/generate/stream), and instead propagate source server-side (e.g., set a handler
parameter or use a separate internal model like InternalGenerationRequest or a
request context value) so only server code assigns GenerationRequest.source
internally when creating downstream objects that rely on source.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c327c629-6b61-44fc-873b-bd47d5de8eb7

📥 Commits

Reviewing files that changed from the base of the PR and between bec0cc2 and b856f75.

📒 Files selected for processing (1)
  • backend/models.py

Comment thread backend/models.py Outdated
The source field was exposed on the public API model, allowing clients
to spoof request provenance. Now source is only set server-side via a
function parameter on generate_speech().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCP Generation occasionally auto-plays

1 participant