feat(network): add get_pending_invitations tool#447
Conversation
Greptile SummaryThis PR adds a read-only tool for viewing pending LinkedIn network invitations. The main changes are:
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (4): Last reviewed commit: "Merge branch 'main' into feature/446-get..." | Re-trigger Greptile |
Three fixes for the Greptile review on stickerdaniel#447: 1. Trim returned references to the per-call `limit`. Previously `limit` only governed how many scroll passes ran; the build_references pipeline emitted every profile anchor it saw. References are now sliced to `[:limit]` after build_references runs, so a `limit=5` call cannot return more than five inviter references. The docstring now explicitly states that `sections` text may still include extra cards because LinkedIn pages invitations a screenful at a time. 2. Add an `invitations` entry to `_REFERENCE_CAPS` matching the tool's `Field(le=100)` ceiling. The previous fallback to the default cap of 12 silently truncated profile links for invitation lists larger than twelve, leaving most returned invitations without usable references. 3. Strengthen the expand-toggle selector so it skips `aria-expanded="true"` nodes in addition to the existing `data-mcp-clicked` marker. LinkedIn re-uses the same testid for the post-expand collapse control, so the previous selector could click an already-expanded note and silently re-truncate it. The collapsed state ships without an `aria-expanded` attribute at all on this surface, so `:not([aria-expanded="true"])` is the conservative shape: it matches the unset and explicit `"false"` states while skipping the expanded ones.
|
Thanks for the careful review — all three P1 items addressed in 3920cb1. 1. Limit not enforced. References are now sliced to 2. References truncated at the default cap of 12. Added an 3. Expanded notes could collapse. Strengthened the expand-toggle selector to CI green on the new commit; full suite passes locally aside from one pre-existing umask-sensitive case in |
Read-only tool that lists pending LinkedIn network invitations from
/mynetwork/invitation-manager/{received|sent}/, mirroring the existing
get_inbox extractor pattern. Accept/ignore/withdraw actions are
intentionally out of scope for this PR.
Closes stickerdaniel#446
Click `data-testid="expandable-text-button"` toggles before extracting text so the returned `sections["invitations"]` carries full invite bodies instead of LinkedIn's "... see more" preview. The testid attribute is the locale-independent signal — the visible verb varies by locale and is unsafe to depend on per repo guidelines. LinkedIn renders these buttons with inline `pointer-events: none`, which blocks Playwright's standard click from reaching the React handler. To work around that the implementation injects a scoped one-shot stylesheet re-enabling pointer events on these specific testid'd nodes and dispatches a synthetic bubbling MouseEvent so the handler fires. Buttons are tagged with `data-mcp-clicked` after the first dispatch so a second pass picks up newly lazy-loaded cards without re-toggling already-expanded notes (the post-click "collapse" button shares the same testid).
Three fixes for the Greptile review on stickerdaniel#447: 1. Trim returned references to the per-call `limit`. Previously `limit` only governed how many scroll passes ran; the build_references pipeline emitted every profile anchor it saw. References are now sliced to `[:limit]` after build_references runs, so a `limit=5` call cannot return more than five inviter references. The docstring now explicitly states that `sections` text may still include extra cards because LinkedIn pages invitations a screenful at a time. 2. Add an `invitations` entry to `_REFERENCE_CAPS` matching the tool's `Field(le=100)` ceiling. The previous fallback to the default cap of 12 silently truncated profile links for invitation lists larger than twelve, leaving most returned invitations without usable references. 3. Strengthen the expand-toggle selector so it skips `aria-expanded="true"` nodes in addition to the existing `data-mcp-clicked` marker. LinkedIn re-uses the same testid for the post-expand collapse control, so the previous selector could click an already-expanded note and silently re-truncate it. The collapsed state ships without an `aria-expanded` attribute at all on this surface, so `:not([aria-expanded="true"])` is the conservative shape: it matches the unset and explicit `"false"` states while skipping the expanded ones.
3920cb1 to
b2b42c6
Compare
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Summary
Adds a new read-only MCP tool
get_pending_invitationsfor listing pending LinkedIn network invitations (received or sent). This fills the gap noted in #446 — recruiter "Invite + note" messages live in/mynetwork/invitation-manager/and never reachget_inboxuntil accepted, so an agent assisting with inbound triage cannot see them today.What's in scope
tools/network.py::register_network_toolswith signatureget_pending_invitations(ctx, limit=20, kind="received"|"sent").LinkedInExtractor.get_pending_invitations(limit, kind)that follows theget_inboxtemplate line-for-line:_navigate_to_page->detect_rate_limit->_wait_for_main_text->handle_modal_close->_scroll_main_scrollable_region->_expand_invitation_note_toggles->_extract_root_content(["main"])->strip_linkedin_noise->build_references->_single_section_result._expand_invitation_note_toggleshelper. Uses the locale-independentdata-testid="expandable-text-button"selector, injects a scoped one-shot stylesheet to re-enablepointer-events(LinkedIn ships these buttons with inlinepointer-events: none), and dispatches a synthetic bubbling MouseEvent so the React handler fires. Clicked buttons are tagged withdata-mcp-clickedso a second pass picks up lazy-loaded cards without re-toggling already-expanded notes.server.py::create_mcp_server().TestNetworkToolsintests/test_tools.py(4 cases: happy path,sentkind, invalid kind rejection, limit-bound rejection);_make_mock_extractorextended;get_pending_invitationsadded to both timeout-coverage tuples.docs/docker-hub.mdfeatures list, andmanifest.jsontools array all updated.Intentionally out of scope
fields.py— this is a one-page tool, not a sectioned scraper, matchingget_feed/get_inbox.Testing
uv run pytest: 510 passed locally. One pre-existing failure intest_browser_security.py::test_harden_linkedin_tree_noop_outside_linkedinreproduces onmainbefore this branch withumask=002; it's environmental and unrelated.uv run ruff check . --fix && uv run ruff format .: clean.uv run ty check: clean.uv run pre-commit run --all-files: all hooks pass.LinkedInExtractor.get_pending_invitations(limit=5, kind="received")on a real account. Result shape conforms to the standard{url, sections, references}contract; sections text contained 12 inviter blocks with 11/11 truncated invitation notes auto-expanded (textmatchedpokaż mniejx11,pokaż więcejx0); references carried 12kind: "person"entries with/in/USERNAME/URLs.Synthetic prompt
Generated with Claude Opus 4.7
Closes #446