mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-09 15:19:10 +02:00
fix: auto-detect URLs in ATProto posts for clickable links
The ATProto MCP server now properly handles URLs in posts by:
- Auto-detecting plain URLs in text and creating facets to make them clickable
- Supporting rich text links with custom anchor text
- Preventing overlap between auto-detected and explicitly provided links
This ensures all URLs are clickable on Bluesky, whether they're plain URLs
in text or rich text links with custom anchors.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
d3f120a059
commit
04efab4257
1 changed files with 42 additions and 7 deletions
|
|
@ -44,9 +44,8 @@ def create_post(
|
|||
embed = None
|
||||
reply_ref = None
|
||||
|
||||
# Handle rich text facets (links and mentions)
|
||||
if links or mentions:
|
||||
facets = _build_facets(text, links, mentions, client)
|
||||
# Always build facets to handle auto-detected URLs and explicit links/mentions
|
||||
facets = _build_facets(text, links, mentions, client)
|
||||
|
||||
# Handle replies
|
||||
if reply_to:
|
||||
|
|
@ -63,7 +62,7 @@ def create_post(
|
|||
# Images only - use send_images for proper handling
|
||||
return _send_images(text, images, image_alts, facets, reply_ref, client)
|
||||
|
||||
# Send the post
|
||||
# Send the post (always include facets if any were created)
|
||||
post = client.send_post(
|
||||
text=text,
|
||||
facets=facets if facets else None,
|
||||
|
|
@ -96,10 +95,18 @@ def _build_facets(
|
|||
mentions: list[RichTextMention] | None,
|
||||
client,
|
||||
):
|
||||
"""Build facets for rich text formatting."""
|
||||
facets = []
|
||||
"""Build facets for rich text formatting, including auto-detected URLs."""
|
||||
import re
|
||||
|
||||
# Process links
|
||||
facets = []
|
||||
covered_ranges = []
|
||||
|
||||
# URL regex pattern for auto-detection
|
||||
url_pattern = re.compile(
|
||||
r"https?://(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&/=]*)"
|
||||
)
|
||||
|
||||
# Process explicit links first
|
||||
if links:
|
||||
for link in links:
|
||||
start = text.find(link["text"])
|
||||
|
|
@ -107,6 +114,9 @@ def _build_facets(
|
|||
continue
|
||||
end = start + len(link["text"])
|
||||
|
||||
# Track this range as covered
|
||||
covered_ranges.append((start, end))
|
||||
|
||||
facets.append(
|
||||
models.AppBskyRichtextFacet.Main(
|
||||
features=[models.AppBskyRichtextFacet.Link(uri=link["url"])],
|
||||
|
|
@ -117,6 +127,30 @@ def _build_facets(
|
|||
)
|
||||
)
|
||||
|
||||
# Auto-detect URLs that aren't already covered by explicit links
|
||||
for match in url_pattern.finditer(text):
|
||||
url = match.group()
|
||||
start = match.start()
|
||||
end = match.end()
|
||||
|
||||
# Check if this URL overlaps with any explicit link
|
||||
overlaps = False
|
||||
for covered_start, covered_end in covered_ranges:
|
||||
if not (end <= covered_start or start >= covered_end):
|
||||
overlaps = True
|
||||
break
|
||||
|
||||
if not overlaps:
|
||||
facets.append(
|
||||
models.AppBskyRichtextFacet.Main(
|
||||
features=[models.AppBskyRichtextFacet.Link(uri=url)],
|
||||
index=models.AppBskyRichtextFacet.ByteSlice(
|
||||
byte_start=len(text[:start].encode("UTF-8")),
|
||||
byte_end=len(text[:end].encode("UTF-8")),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
# Process mentions
|
||||
if mentions:
|
||||
for mention in mentions:
|
||||
|
|
@ -253,7 +287,8 @@ def _send_images(
|
|||
|
||||
# Send post with images
|
||||
# Note: send_images doesn't support facets or reply_to directly
|
||||
# So we need to use send_post with manual image upload if we have those
|
||||
# So we need to use send_post with manual image upload if we have facets or replies
|
||||
# Since we always create facets now (for URL auto-detection), we'll always use this path
|
||||
if facets or reply_ref:
|
||||
# Manual image upload
|
||||
images = []
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue