get_topic_posts
Fetch paginated posts from USCardForum topics to read discussions in batches, starting at a specified position with optional markdown source.
Instructions
Fetch a batch of posts from a topic starting at a specific position.
Args:
topic_id: The numeric topic ID
post_number: Which post number to start from (default: 1 = first post)
include_raw: Include raw markdown source (default: False, returns HTML)
This fetches ~20 posts per call starting from post_number.
Use for paginated reading of topics.
Returns a list of Post objects with:
- post_number: Position in topic (1, 2, 3...)
- username: Author's username
- cooked: HTML content of the post
- raw: Markdown source (if include_raw=True)
- created_at: When posted
- updated_at: Last edit time
- like_count: Number of likes
- reply_count: Number of direct replies
- reply_to_post_number: Which post this replies to (if any)
Pagination example:
1. Call with post_number=1, get posts 1-20
2. Call with post_number=21, get posts 21-40
3. Continue until no posts returned
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| topic_id | Yes | The numeric topic ID | |
| post_number | No | Which post number to start from (default: 1 = first post) | |
| include_raw | No | Include raw markdown source (default: False, returns HTML) |
Implementation Reference
- The primary handler function decorated with @mcp.tool(). It defines the input schema via Annotated Fields and executes the tool logic by delegating to the DiscourseClient.get_topic_posts method.@mcp.tool() def get_topic_posts( topic_id: Annotated[ int, Field(description="The numeric topic ID"), ], post_number: Annotated[ int, Field(default=1, description="Which post number to start from (default: 1 = first post)"), ] = 1, include_raw: Annotated[ bool, Field(default=False, description="Include raw markdown source (default: False, returns HTML)"), ] = False, ) -> list[Post]: """ Fetch a batch of posts from a topic starting at a specific position. Args: topic_id: The numeric topic ID post_number: Which post number to start from (default: 1 = first post) include_raw: Include raw markdown source (default: False, returns HTML) This fetches ~20 posts per call starting from post_number. Use for paginated reading of topics. Returns a list of Post objects with: - post_number: Position in topic (1, 2, 3...) - username: Author's username - cooked: HTML content of the post - raw: Markdown source (if include_raw=True) - created_at: When posted - updated_at: Last edit time - like_count: Number of likes - reply_count: Number of direct replies - reply_to_post_number: Which post this replies to (if any) Pagination example: 1. Call with post_number=1, get posts 1-20 2. Call with post_number=21, get posts 21-40 3. Continue until no posts returned """ return get_client().get_topic_posts( topic_id, post_number=post_number, include_raw=include_raw )
- src/uscardforum/server.py:15-45 (registration)Imports all server tools including get_topic_posts from server_tools package. Importing the decorated function registers it with the global FastMCP instance.from uscardforum.server_tools import ( analyze_user, bookmark_post, compare_cards, find_data_points, get_all_topic_posts, get_categories, get_current_session, get_hot_topics, get_new_topics, get_notifications, get_top_topics, get_topic_info, get_topic_posts, get_user_actions, get_user_badges, get_user_followers, get_user_following, get_user_reactions, get_user_replies, get_user_summary, get_user_topics, list_users_with_badge, login, research_topic, resource_categories, resource_hot_topics, resource_new_topics, search_forum, subscribe_topic, )
- src/uscardforum/server_tools/__init__.py:32-32 (registration)Re-exports the get_topic_posts tool from the topics module, enabling its import and registration via the server_tools package.from .topics import get_topic_info, get_topic_posts, get_all_topic_posts
- Shared helper function get_client() that provides the DiscourseClient instance used by all tools, including automatic login if credentials are provided.def get_client() -> DiscourseClient: """Get or create the Discourse client instance.""" global _client, _login_attempted if _client is None: base_url = os.environ.get("USCARDFORUM_URL", "https://www.uscardforum.com") timeout = float(os.environ.get("USCARDFORUM_TIMEOUT", "15.0")) _client = DiscourseClient(base_url=base_url, timeout_seconds=timeout) # Auto-login if credentials are provided if not _login_attempted: _login_attempted = True username = os.environ.get("NITAN_USERNAME") password = os.environ.get("NITAN_PASSWORD") if username and password: try: result = _client.login(username, password) if result.success: print(f"[uscardforum] Auto-login successful as '{result.username}'") elif result.requires_2fa: print( "[uscardforum] Auto-login failed: 2FA required. Use login() tool with second_factor_token." ) else: print( f"[uscardforum] Auto-login failed: {result.error or 'Unknown error'}" ) except Exception as e: # pragma: no cover - logging side effect print(f"[uscardforum] Auto-login error: {e}") return _client
- Imports the Post model used as return type for get_topic_posts, which defines the output schema.from uscardforum.models.topics import Post, TopicInfo, TopicSummary from uscardforum.server_core import get_client, mcp @mcp.tool() def get_hot_topics( page: Annotated[ int | None, Field(default=None, description="Page number for pagination (0-indexed, default: 0)"), ] = None, ) -> list[TopicSummary]: """ Fetch currently trending/hot topics from USCardForum. This returns the most actively discussed topics right now, ranked by engagement metrics like recent replies, views, and likes. Use this to: - See what the community is currently discussing - Find breaking news or time-sensitive opportunities - Discover popular ongoing discussions Args: page: Page number for pagination (0-indexed). Use page=1 to get more topics. Returns a list of TopicSummary objects with fields: - id: Topic ID (use with get_topic_posts) - title: Topic title - posts_count: Total replies - views: View count - like_count: Total likes - created_at: Creation timestamp - last_posted_at: Last activity timestamp Example response interpretation: A topic with high views but low posts may be informational. A topic with many recent posts is actively being discussed. """ return get_client().get_hot_topics(page=page) @mcp.tool() def get_new_topics( page: Annotated[ int | None, Field(default=None, description="Page number for pagination (0-indexed, default: 0)"), ] = None, ) -> list[TopicSummary]: """ Fetch the latest/newest topics from USCardForum. Returns recently created topics sorted by creation time (newest first). These may have fewer replies but contain fresh information. Use this to: - Find newly posted deals or offers - See fresh questions from the community - Discover emerging discussions before they get popular Args: page: Page number for pagination (0-indexed). Use page=1 to get more topics. Returns a list of TopicSummary objects with: - id: Topic ID - title: Topic title - posts_count: Number of posts - created_at: When the topic was created - category_id: Which forum section it's in Tip: New topics with high view counts may indicate important news. """ return get_client().get_new_topics(page=page) @mcp.tool() def get_top_topics( period: Annotated[ str, Field( default="monthly", description="Time window for ranking: 'daily', 'weekly', 'monthly' (default), 'quarterly', or 'yearly'", ), ] = "monthly", page: Annotated[ int | None, Field(default=None, description="Page number for pagination (0-indexed, default: 0)"), ] = None, ) -> list[TopicSummary]: """ Fetch top-performing topics for a specific time period. Args: period: Time window for ranking. Must be one of: - "daily": Top topics from today - "weekly": Top topics this week - "monthly": Top topics this month (default) - "quarterly": Top topics this quarter - "yearly": Top topics this year page: Page number for pagination (0-indexed). Use page=1 to get more topics. Use this to: - Find the most valuable discussions in a time range - Research historically important threads - Identify evergreen popular content Returns TopicSummary objects sorted by engagement score. Example: Use "yearly" to find the most impactful discussions, or "daily" to see what's trending today. """ return get_client().get_top_topics(period=period, page=page) @mcp.tool() def get_topic_info( topic_id: Annotated[ int, Field(description="The numeric topic ID (from URLs like /t/slug/12345)"), ], ) -> TopicInfo: """ Get metadata about a specific topic without fetching all posts. Args: topic_id: The numeric topic ID (from URLs like /t/slug/12345) Use this FIRST before reading a topic to: - Check how many posts it contains (for pagination planning) - Get the topic title and timestamps - Decide whether to fetch all posts or paginate Returns a TopicInfo object with: - topic_id: The topic ID - title: Full topic title - post_count: Total number of posts - highest_post_number: Last post number (may differ from count if posts deleted) - last_posted_at: When the last reply was made Strategy for large topics: - <50 posts: Safe to fetch all at once - 50-200 posts: Consider using max_posts parameter - >200 posts: Fetch in batches or summarize key posts """ return get_client().get_topic_info(topic_id) @mcp.tool() def get_topic_posts( topic_id: Annotated[ int, Field(description="The numeric topic ID"), ], post_number: Annotated[ int, Field(default=1, description="Which post number to start from (default: 1 = first post)"), ] = 1, include_raw: Annotated[ bool, Field(default=False, description="Include raw markdown source (default: False, returns HTML)"), ] = False, ) -> list[Post]: