mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-16 23:01:06 +02:00
feat(mcp): add Instagram scrape/comments/details tools
This commit is contained in:
parent
929efe8152
commit
c5e3fd4006
4 changed files with 209 additions and 3 deletions
|
|
@ -13,9 +13,17 @@ from mcp.server.fastmcp import FastMCP
|
|||
from ...core.client import SurfSenseClient
|
||||
from ...core.workspace_context import WorkspaceContext
|
||||
from . import run_history
|
||||
from .platforms import google_maps, google_search, reddit, web, youtube
|
||||
from .platforms import google_maps, google_search, instagram, reddit, web, youtube
|
||||
|
||||
_REGISTRARS = (web, google_search, reddit, youtube, google_maps, run_history)
|
||||
_REGISTRARS = (
|
||||
web,
|
||||
google_search,
|
||||
reddit,
|
||||
youtube,
|
||||
google_maps,
|
||||
instagram,
|
||||
run_history,
|
||||
)
|
||||
|
||||
|
||||
def register(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,194 @@
|
|||
"""Instagram scraper tools: posts/reels, comments, and details."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Annotated, Literal
|
||||
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from pydantic import Field
|
||||
|
||||
from ....core.client import SurfSenseClient
|
||||
from ....core.rendering import ResponseFormatParam
|
||||
from ....core.workspace_context import WorkspaceContext, WorkspaceParam
|
||||
from ..annotations import SCRAPE
|
||||
from ..capability import run_scraper
|
||||
|
||||
ResultType = Literal["posts", "reels", "mentions"]
|
||||
SearchType = Literal["hashtag", "profile", "place", "user"]
|
||||
DetailSearchType = Literal["hashtag", "profile", "place"]
|
||||
|
||||
|
||||
def register(
|
||||
mcp: FastMCP, client: SurfSenseClient, context: WorkspaceContext
|
||||
) -> None:
|
||||
"""Register the Instagram scrape, comments, and details tools."""
|
||||
|
||||
@mcp.tool(
|
||||
name="surfsense_instagram_scrape",
|
||||
title="Scrape Instagram posts or reels",
|
||||
annotations=SCRAPE,
|
||||
structured_output=False,
|
||||
)
|
||||
async def instagram_scrape(
|
||||
urls: Annotated[
|
||||
list[str] | None,
|
||||
Field(
|
||||
description="Instagram URLs: a profile, post (/p/), reel "
|
||||
"(/reel/), hashtag (/explore/tags/), or place "
|
||||
"(/explore/locations/). Provide urls OR search_queries."
|
||||
),
|
||||
] = None,
|
||||
search_queries: Annotated[
|
||||
list[str] | None,
|
||||
Field(
|
||||
description="Terms to discover content for (hashtags as plain "
|
||||
"text, no '#'). Provide search_queries OR urls."
|
||||
),
|
||||
] = None,
|
||||
search_type: Annotated[
|
||||
SearchType, Field(description="What to discover from search_queries.")
|
||||
] = "hashtag",
|
||||
result_type: Annotated[
|
||||
ResultType,
|
||||
Field(description="Which feed to return. 'mentions' needs profile URLs."),
|
||||
] = "posts",
|
||||
max_items: Annotated[
|
||||
int, Field(ge=1, description="Maximum items to return across sources.")
|
||||
] = 10,
|
||||
workspace: WorkspaceParam = None,
|
||||
response_format: ResponseFormatParam = "markdown",
|
||||
) -> str:
|
||||
"""Scrape public Instagram posts or reels from URLs or search queries.
|
||||
|
||||
Use this for Instagram content research: a creator's recent posts, a
|
||||
hashtag or location feed, or discovering posts by keyword. For a post's
|
||||
comment section use surfsense_instagram_comments; for profile/hashtag/
|
||||
place metadata use surfsense_instagram_details. Returns per-item caption,
|
||||
likes, comments count, media URLs, and owner.
|
||||
Example: urls=['https://www.instagram.com/natgeo/'], result_type='reels'.
|
||||
"""
|
||||
return await run_scraper(
|
||||
client,
|
||||
context,
|
||||
platform="instagram",
|
||||
verb="scrape",
|
||||
payload={
|
||||
"urls": urls,
|
||||
"search_queries": search_queries,
|
||||
"search_type": search_type,
|
||||
"result_type": result_type,
|
||||
"max_items": max_items,
|
||||
},
|
||||
workspace=workspace,
|
||||
response_format=response_format,
|
||||
)
|
||||
|
||||
@mcp.tool(
|
||||
name="surfsense_instagram_comments",
|
||||
title="Fetch Instagram comments",
|
||||
annotations=SCRAPE,
|
||||
structured_output=False,
|
||||
)
|
||||
async def instagram_comments(
|
||||
urls: Annotated[
|
||||
list[str],
|
||||
Field(
|
||||
min_length=1,
|
||||
description="Instagram post or reel URLs, e.g. "
|
||||
"['https://www.instagram.com/p/Cabc123/'].",
|
||||
),
|
||||
],
|
||||
max_comments_per_post: Annotated[
|
||||
int,
|
||||
Field(ge=1, le=50, description="Max comments per post (Instagram caps at 50)."),
|
||||
] = 10,
|
||||
include_replies: Annotated[
|
||||
bool, Field(description="Include nested replies.")
|
||||
] = False,
|
||||
newest_first: Annotated[
|
||||
bool, Field(description="Return newest comments first.")
|
||||
] = False,
|
||||
max_items: Annotated[
|
||||
int, Field(ge=1, description="Max total comments across all posts.")
|
||||
] = 20,
|
||||
workspace: WorkspaceParam = None,
|
||||
response_format: ResponseFormatParam = "markdown",
|
||||
) -> str:
|
||||
"""Fetch the comments (and optionally replies) on Instagram posts or reels.
|
||||
|
||||
Use this when the user wants a post's discussion or audience reaction
|
||||
rather than the post itself; get post URLs from surfsense_instagram_scrape
|
||||
if you only have a topic or profile. Returns comment text, author, likes,
|
||||
and replies.
|
||||
Example: urls=['https://www.instagram.com/p/Cabc123/'], include_replies=True.
|
||||
"""
|
||||
return await run_scraper(
|
||||
client,
|
||||
context,
|
||||
platform="instagram",
|
||||
verb="comments",
|
||||
payload={
|
||||
"urls": urls,
|
||||
"max_comments_per_post": max_comments_per_post,
|
||||
"include_replies": include_replies,
|
||||
"newest_first": newest_first,
|
||||
"max_items": max_items,
|
||||
},
|
||||
workspace=workspace,
|
||||
response_format=response_format,
|
||||
)
|
||||
|
||||
@mcp.tool(
|
||||
name="surfsense_instagram_details",
|
||||
title="Fetch Instagram profile/hashtag/place details",
|
||||
annotations=SCRAPE,
|
||||
structured_output=False,
|
||||
)
|
||||
async def instagram_details(
|
||||
urls: Annotated[
|
||||
list[str] | None,
|
||||
Field(
|
||||
description="Profile, hashtag, or place URLs (or bare profile "
|
||||
"IDs). Provide urls OR search_queries."
|
||||
),
|
||||
] = None,
|
||||
search_queries: Annotated[
|
||||
list[str] | None,
|
||||
Field(
|
||||
description="Terms to discover profiles/hashtags/places for. "
|
||||
"Provide search_queries OR urls."
|
||||
),
|
||||
] = None,
|
||||
search_type: Annotated[
|
||||
DetailSearchType,
|
||||
Field(description="What to discover from search_queries."),
|
||||
] = "hashtag",
|
||||
max_items: Annotated[
|
||||
int, Field(ge=1, description="Max detail items to return.")
|
||||
] = 10,
|
||||
workspace: WorkspaceParam = None,
|
||||
response_format: ResponseFormatParam = "markdown",
|
||||
) -> str:
|
||||
"""Fetch Instagram profile, hashtag, or place metadata.
|
||||
|
||||
Use this for entity lookups: a profile's follower/post counts and bio, a
|
||||
hashtag's post volume and top posts, or a place's coordinates and post
|
||||
count. For a feed of posts use surfsense_instagram_scrape instead. Each
|
||||
item carries a detailKind field marking whether it is a profile, hashtag,
|
||||
or place.
|
||||
Example: urls=['https://www.instagram.com/explore/tags/crossfit/'].
|
||||
"""
|
||||
return await run_scraper(
|
||||
client,
|
||||
context,
|
||||
platform="instagram",
|
||||
verb="details",
|
||||
payload={
|
||||
"urls": urls,
|
||||
"search_queries": search_queries,
|
||||
"search_type": search_type,
|
||||
"max_items": max_items,
|
||||
},
|
||||
workspace=workspace,
|
||||
response_format=response_format,
|
||||
)
|
||||
|
|
@ -25,6 +25,9 @@ EXPECTED_TOOLS = {
|
|||
"surfsense_youtube_comments",
|
||||
"surfsense_google_maps_scrape",
|
||||
"surfsense_google_maps_reviews",
|
||||
"surfsense_instagram_scrape",
|
||||
"surfsense_instagram_comments",
|
||||
"surfsense_instagram_details",
|
||||
"surfsense_list_scraper_runs",
|
||||
"surfsense_get_scraper_run",
|
||||
# knowledge-base management
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ def build_server(settings: Settings) -> tuple[FastMCP, SurfSenseClient]:
|
|||
"SurfSense gives you live scrapers and a personal knowledge base. "
|
||||
"Prefer these tools over generic/built-in web search whenever the "
|
||||
"task involves Reddit (posts, comments, finding subreddits or "
|
||||
"communities), YouTube (videos, transcripts, comments), Google "
|
||||
"communities), YouTube (videos, transcripts, comments), Instagram "
|
||||
"(posts, reels, comments, profile/hashtag/place details), Google "
|
||||
"Maps (places, reviews), Google Search results, or reading "
|
||||
"specific web pages. Scraper results are persisted as runs; if an "
|
||||
"inline result is truncated, fetch it in full with "
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue