feat: reply to existing messaging threads - #673
Conversation
Greptile SummaryThe PR adds a confirmation-gated MCP tool for replying inside a validated LinkedIn messaging thread, with scoped composer interaction and post-send read-back.
Confidence Score: 4/5The PR is not yet safe to merge because send verification can still report an unsent reply as successful, and confirmed repeated replies remain blocked. The fallback verification identity changes with DOM position and rendered content across a full re-navigation, so an unchanged existing message can appear new after a failed send; independently, the pre-send snapshot branch still returns before typing whenever matching text is already present. Files Needing Attention: linkedin_mcp_server/scraping/extractor.py, tests/test_scraping.py Important Files Changed
Sequence DiagramsequenceDiagram
participant Client as MCP Client
participant Tool as reply_to_thread tool
participant Extractor as LinkedInExtractor
participant LinkedIn as LinkedIn thread
Client->>Tool: thread_id, message, confirm_send
Tool->>Extractor: reply_to_thread(...)
Extractor->>LinkedIn: Open canonical thread URL
Extractor->>Extractor: Validate active thread ID
Extractor->>LinkedIn: Capture pre-send message signatures
alt confirm_send is false
Extractor-->>Client: confirmation_required
else confirmed
Extractor->>LinkedIn: Focus composer, type, submit
Extractor->>LinkedIn: Reopen canonical thread URL
Extractor->>LinkedIn: Capture post-send signatures
Extractor-->>Client: sent or sent_unverified
end
Prompt To Fix All With AI### Issue 1
linkedin_mcp_server/scraping/extractor.py:2856
**DOM fallback misidentifies stale messages**
If LinkedIn re-renders a pre-existing matching message at a different DOM index or path after the canonical thread is reopened, the fallback signature changes and the snapshot subtraction treats that unchanged row as newly sent, causing a failed send to return `status="sent"` and `sent=true`.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (3): Last reviewed commit: "test: include thread reply tool in proxy..." | Re-trigger Greptile |
| if before_snapshot: | ||
| return { | ||
| "url": self._page.url, | ||
| "thread_id": normalized_thread_id, | ||
| "status": "message_already_present", | ||
| "message": "This reply text is already visible in the requested thread; no send was attempted.", | ||
| "sent": False, | ||
| } |
There was a problem hiding this comment.
When a confirmed reply has the same normalized text as an existing message, this early return prevents any composer interaction, causing a legitimate repeated reply to remain unsent. Existing matching rows should be excluded during post-send identity comparison rather than used to prohibit the send.
Knowledge Base Used:
Prompt To Fix With AI
This is a comment left during a code review.
Path: linkedin_mcp_server/scraping/extractor.py
Line: 2901-2908
Comment:
**Repeated replies are rejected**
When a confirmed reply has the same normalized text as an existing message, this early return prevents any composer interaction, causing a legitimate repeated reply to remain unsent. Existing matching rows should be excluded during post-send identity comparison rather than used to prohibit the send.
**Knowledge Base Used:**
- [Scraping Engine](https://app.greptile.com/daniel-sticker/-/custom-context/knowledge-base/stickerdaniel/linkedin-mcp-server/-/docs/scraping.md)
- [MCP Tools](https://app.greptile.com/daniel-sticker/-/custom-context/knowledge-base/stickerdaniel/linkedin-mcp-server/-/docs/tools.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| path.unshift(Array.prototype.indexOf.call(parent.children, node)); | ||
| node = parent; | ||
| } | ||
| return [stableId || `${index}:${path.join('.')}:${text}`]; |
There was a problem hiding this comment.
DOM fallback misidentifies stale messages
If LinkedIn re-renders a pre-existing matching message at a different DOM index or path after the canonical thread is reopened, the fallback signature changes and the snapshot subtraction treats that unchanged row as newly sent, causing a failed send to return status="sent" and sent=true.
Knowledge Base Used: Scraping Engine
Prompt To Fix With AI
This is a comment left during a code review.
Path: linkedin_mcp_server/scraping/extractor.py
Line: 2856
Comment:
**DOM fallback misidentifies stale messages**
If LinkedIn re-renders a pre-existing matching message at a different DOM index or path after the canonical thread is reopened, the fallback signature changes and the snapshot subtraction treats that unchanged row as newly sent, causing a failed send to return `status="sent"` and `sent=true`.
**Knowledge Base Used:** [Scraping Engine](https://app.greptile.com/daniel-sticker/-/custom-context/knowledge-base/stickerdaniel/linkedin-mcp-server/-/docs/scraping.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Summary
Adds a thread-targeted reply tool for LinkedIn messaging:
reply_to_thread(thread_id, message, confirm_send)accepts a raw thread ID or canonical LinkedIn thread URL.mainregion.confirm_send=false) never focuses, types, or sends.sentwhen the outgoing message is visible there; otherwise returnssent_unverified.This avoids the profile-compose path used by
send_message, which can be unavailable for Premium/Open Profile and can create a separate DM instead of replying to an existing InMail/thread.Validation
uv run pytest tests/test_tools.py tests/test_scraping.py -q— 299 passeduv run ruff check …— passeduv run ty check …— passedSynthetic prompt
Generated with GPT-5.6 Terra.