Skip to main content
Glama
utils.py28.7 kB
""" Utility functions for the Hashnode MCP server. """ import json def format_posts(posts_data: dict) -> str: """ Format posts data for display Args: posts_data: The data returned from the Hashnode API Returns: A formatted string representation of the posts data """ if not posts_data or "data" not in posts_data or not posts_data["data"]: return "No data found." if "publication" in posts_data["data"] and posts_data["data"]["publication"]: publication = posts_data["data"]["publication"] result = f"# Publication: {publication.get('title', 'Untitled')}\n\n" result += f"Team Publication: {'Yes' if publication.get('isTeam', False) else 'No'}\n\n" if "posts" in publication and "edges" in publication["posts"]: result += "## Posts\n\n" for edge in publication["posts"]["edges"]: if "node" in edge: node = edge["node"] result += f"### {node.get('title', 'Untitled')}\n" if "url" in node and node["url"]: result += f"URL: {node['url']}\n" result += f"Slug: {node.get('slug', '')}\n" if "publishedAt" in node and node["publishedAt"]: result += f"Date: {node['publishedAt']}\n" if "author" in node and node["author"]: author = node["author"] result += f"Author: {author.get('name', 'Unknown')}\n" if "username" in author: result += f"Author Username: {author['username']}\n" if "profilePicture" in author and author["profilePicture"]: result += f"Author Profile Picture: {author['profilePicture']}\n" if "coverImage" in node and node["coverImage"] and "url" in node["coverImage"]: result += f"Cover Image: {node['coverImage']['url']}\n" result += f"Brief: {node.get('brief', 'No description available.')}\n\n" return result return "No publication data found." def format_search_results(search_data: dict) -> str: """ Format search results data for display Args: search_data: The data returned from the Hashnode API search Returns: A formatted string representation of the search results """ if not search_data or "data" not in search_data or not search_data["data"]: return "No search results found." if "searchPostsOfPublication" in search_data["data"]: search_results = search_data["data"]["searchPostsOfPublication"] result = "# Search Results\n\n" if "edges" in search_results and search_results["edges"]: for edge in search_results["edges"]: if "node" in edge: node = edge["node"] result += f"## {node.get('title', 'Untitled')}\n" if "url" in node and node["url"]: result += f"URL: {node['url']}\n" result += f"Slug: {node.get('slug', '')}\n" if "publishedAt" in node and node["publishedAt"]: result += f"Date: {node['publishedAt']}\n" if "author" in node and node["author"]: author = node["author"] result += f"Author: {author.get('name', 'Unknown')}\n" if "username" in author: result += f"Author Username: {author['username']}\n" if "profilePicture" in author and author["profilePicture"]: result += f"Author Profile Picture: {author['profilePicture']}\n" if "coverImage" in node and node["coverImage"] and "url" in node["coverImage"]: result += f"Cover Image: {node['coverImage']['url']}\n" result += f"Brief: {node.get('brief', 'No description available.')}\n\n" if "pageInfo" in search_results: page_info = search_results["pageInfo"] result += "## Pagination\n" result += f"Has Next Page: {page_info.get('hasNextPage', False)}\n" if page_info.get('hasNextPage', False) and "endCursor" in page_info: result += f"End Cursor: {page_info['endCursor']}\n" result += "\n" return result else: return "No matching posts found." return "No search results found." # GraphQL query constants TEST_QUERY = """ query { __schema { queryType { name } } } """ TOGGLE_FOLLOW_MUTATION = """ mutation ToggleFollowUser($username: String!) { toggleFollowUser(username: $username) { user { following } } } """ CREATE_ARTICLE_MUTATION = """ mutation PublishPost($input: PublishPostInput!) { publishPost(input: $input) { post { id slug title url brief publishedAt publication { id title } } } } """ PUBLISH_DRAFT_MUTATION = """ mutation PublishDraft($draftId: ObjectId!) { publishDraft(draftId: $draftId) { post { id slug title url brief publishedAt } } } """ CREATE_WEBHOOK_MUTATION = """ mutation CreateWebhook($publicationId: ObjectId!, $url: String!, $events: [WebhookEvent!]!, $secret: String!) { createWebhook( input: { publicationId: $publicationId url: $url events: $events secret: $secret } ) { webhook { id url events createdAt updatedAt } } } """ UPDATE_ARTICLE_MUTATION = """ mutation UpdatePost($input: UpdatePostInput!) { updatePost(input: $input) { post { id slug title url brief publishedAt } } } """ GET_POST_BY_ID_QUERY = """ query Post($id: ID!) { post(id: $id) { id slug previousSlugs title subtitle author { id username name profilePicture } url canonicalUrl publication { id title displayTitle url } cuid coverImage { url isPortrait attribution photographer isAttributionHidden } brief readTimeInMinutes views content { markdown html text } publishedAt updatedAt } } """ GET_PUBLICATION_ID_QUERY = """ query GetPublicationByHost($host: String!) { publication(host: $host) { id title } } """ GET_PUBLICATION_POSTS_QUERY = """ query GetPublicationByHost($host: String!, $first: Int!) { publication(host: $host) { isTeam title posts(first: $first) { edges { node { title brief url } } } } } """ SEARCH_POSTS_OF_PUBLICATION_QUERY = """ query SearchPostsOfPublication( $first: Int!, $after: String, $filter: SearchPostsOfPublicationFilter! ) { searchPostsOfPublication( first: $first, after: $after, sortBy: DATE_PUBLISHED_DESC, filter: $filter ) { edges { node { id title brief publishedAt author { name } } cursor } pageInfo { hasNextPage endCursor } } } """ GET_TOP_ARTICLES_QUERY = """ query GetTopArticles($first: Int!) { feed(first: $first) { edges { node { ... on Post { id title brief url publishedAt author { name username } } } } } } """ GET_ARTICLES_BY_TAG_QUERY = """ query GetArticlesByTag($tag: String!, $first: Int!) { tag(slug: $tag) { name slug posts(first: $first) { edges { node { id title brief url publishedAt author { name username } } } } } } """ GET_USER_INFO_QUERY = """ query GetUserInfo($username: String!) { user(username: $username) { id name username profilePicture bio { text } socialMediaLinks { twitter github linkedin website } publications(first: 5) { edges { node { id title url } } } followersCount followingsCount } } """ GET_ARTICLES_BY_USERNAME_QUERY = """ query GetArticlesByUsername($username: String!, $first: Int!) { user(username: $username) { id name username posts(first: $first) { edges { node { id title brief url publishedAt } } } } } """ def format_article_creation(creation_data: dict) -> str: """ Format article creation response data for display Args: creation_data: The data returned from the Hashnode API after creating an article Returns: A formatted string representation of the created article """ if not creation_data or "data" not in creation_data or not creation_data["data"]: return "No data returned from article creation." if "publishPost" in creation_data["data"] and creation_data["data"]["publishPost"]: result = creation_data["data"]["publishPost"] if "post" in result and result["post"]: post = result["post"] response = "# Article Created Successfully\n\n" response += f"Title: {post.get('title', 'Untitled')}\n" response += f"ID: {post.get('id', 'Unknown')}\n" if "slug" in post: response += f"Slug: {post['slug']}\n" if "url" in post: response += f"URL: {post['url']}\n" if "brief" in post and post["brief"]: response += f"Brief: {post['brief']}\n" if "publishedAt" in post and post["publishedAt"]: response += f"Published At: {post['publishedAt']}\n" else: response += "Status: Draft (Not published)\n" return response return "Article created but no post data returned." return "Failed to create article." def format_article_update(update_data: dict) -> str: """ Format article update response data for display Args: update_data: The data returned from the Hashnode API after updating an article Returns: A formatted string representation of the updated article """ if not update_data or "data" not in update_data or not update_data["data"]: return "No data returned from article update." if "updatePost" in update_data["data"] and update_data["data"]["updatePost"]: result = update_data["data"]["updatePost"] if "post" in result and result["post"]: post = result["post"] response = "# Article Updated Successfully\n\n" response += f"Title: {post.get('title', 'Untitled')}\n" response += f"ID: {post.get('id', 'Unknown')}\n" if "slug" in post: response += f"Slug: {post['slug']}\n" if "url" in post: response += f"URL: {post['url']}\n" if "brief" in post and post["brief"]: response += f"Brief: {post['brief']}\n" if "publishedAt" in post and post["publishedAt"]: response += f"Published At: {post['publishedAt']}\n" else: response += "Status: Draft (Not published)\n" return response return "Article updated but no post data returned." return "Failed to update article." def format_toggle_follow_result(toggle_data: dict) -> str: """ Format toggle follow response data for display Args: toggle_data: The data returned from the Hashnode API after toggling follow status Returns: A formatted string representation of the toggle follow result """ if not toggle_data or "data" not in toggle_data or not toggle_data["data"]: return "No data returned from toggle follow operation." if "toggleFollowUser" in toggle_data["data"] and toggle_data["data"]["toggleFollowUser"]: result = toggle_data["data"]["toggleFollowUser"] if "user" in result and "following" in result["user"]: status = result["user"]["following"] if status: return "Successfully followed the user." else: return "Successfully unfollowed the user." return "Failed to toggle follow status." def format_publish_draft_result(publish_data: dict) -> str: """ Format publish draft response data for display Args: publish_data: The data returned from the Hashnode API after publishing a draft Returns: A formatted string representation of the published draft """ if not publish_data or "data" not in publish_data or not publish_data["data"]: return "No data returned from draft publication." if "publishDraft" in publish_data["data"] and publish_data["data"]["publishDraft"]: result = publish_data["data"]["publishDraft"] if "post" in result and result["post"]: post = result["post"] response = "# Draft Published Successfully\n\n" response += f"Title: {post.get('title', 'Untitled')}\n" response += f"ID: {post.get('id', 'Unknown')}\n" if "slug" in post: response += f"Slug: {post['slug']}\n" if "url" in post: response += f"URL: {post['url']}\n" if "brief" in post and post["brief"]: response += f"Brief: {post['brief']}\n" if "publishedAt" in post and post["publishedAt"]: response += f"Published At: {post['publishedAt']}\n" return response return "Draft published but no post data returned." return "Failed to publish draft." def format_create_webhook_result(webhook_data: dict) -> str: """ Format create webhook response data for display Args: webhook_data: The data returned from the Hashnode API after creating a webhook Returns: A formatted string representation of the created webhook """ if not webhook_data or "data" not in webhook_data or not webhook_data["data"]: return "No data returned from webhook creation." if "createWebhook" in webhook_data["data"] and webhook_data["data"]["createWebhook"]: result = webhook_data["data"]["createWebhook"] if "webhook" in result and result["webhook"]: webhook = result["webhook"] response = "# Webhook Created Successfully\n\n" response += f"ID: {webhook.get('id', 'Unknown')}\n" if "url" in webhook: response += f"URL: {webhook['url']}\n" if "events" in webhook and webhook["events"]: response += f"Events: {', '.join(webhook['events'])}\n" if "createdAt" in webhook: response += f"Created At: {webhook['createdAt']}\n" if "updatedAt" in webhook: response += f"Updated At: {webhook['updatedAt']}\n" return response return "Webhook created but no webhook data returned." return "Failed to create webhook." def format_post_details(post_data: dict) -> str: """ Format post details data for display Args: post_data: The data returned from the Hashnode API for a specific post Returns: A formatted string representation of the post details """ if not post_data or "data" not in post_data or not post_data["data"]: return "No post data found." if "post" in post_data["data"] and post_data["data"]["post"]: post = post_data["data"]["post"] result = f"# {post.get('title', 'Untitled')}\n\n" if "subtitle" in post and post["subtitle"]: result += f"## {post['subtitle']}\n\n" # Basic post information result += "## Post Information\n\n" result += f"ID: {post.get('id', 'Unknown')}\n" result += f"Slug: {post.get('slug', 'Unknown')}\n" if "url" in post and post["url"]: result += f"URL: {post['url']}\n" if "canonicalUrl" in post and post["canonicalUrl"]: result += f"Canonical URL: {post['canonicalUrl']}\n" if "publishedAt" in post and post["publishedAt"]: result += f"Published: {post['publishedAt']}\n" if "updatedAt" in post and post["updatedAt"]: result += f"Last Updated: {post['updatedAt']}\n" if "readTimeInMinutes" in post: result += f"Read Time: {post['readTimeInMinutes']} minutes\n" if "views" in post: result += f"Views: {post['views']}\n" # Author information if "author" in post and post["author"]: author = post["author"] result += "\n## Author\n\n" result += f"Name: {author.get('name', 'Unknown')}\n" if "username" in author: result += f"Username: {author['username']}\n" if "id" in author: result += f"ID: {author['id']}\n" if "profilePicture" in author and author["profilePicture"]: result += f"Profile Picture: {author['profilePicture']}\n" # Publication information if "publication" in post and post["publication"]: publication = post["publication"] result += "\n## Publication\n\n" result += f"Title: {publication.get('title', 'Unknown')}\n" if "displayTitle" in publication: result += f"Display Title: {publication['displayTitle']}\n" if "id" in publication: result += f"ID: {publication['id']}\n" if "url" in publication: result += f"URL: {publication['url']}\n" # Cover image if "coverImage" in post and post["coverImage"]: cover_image = post["coverImage"] result += "\n## Cover Image\n\n" if "url" in cover_image: result += f"URL: {cover_image['url']}\n" if "isPortrait" in cover_image: result += f"Is Portrait: {cover_image['isPortrait']}\n" if "photographer" in cover_image and cover_image["photographer"]: result += f"Photographer: {cover_image['photographer']}\n" if "attribution" in cover_image and cover_image["attribution"]: result += f"Attribution: {cover_image['attribution']}\n" # Brief if "brief" in post and post["brief"]: result += f"\n## Brief\n\n{post['brief']}\n" # Content if "content" in post and post["content"]: content = post["content"] result += "\n## Content\n\n" if "text" in content and content["text"]: # Limit the text content to a reasonable length for display text = content["text"] max_length = 1000 if len(text) > max_length: text = text[:max_length] + "...\n\n(Content truncated for display. Full content available in the markdown or html fields.)" result += text # Note: We're not including the full markdown or HTML content in the display # as they could be very large, but we note their availability if "markdown" in content and content["markdown"]: result += "\n\n(Full markdown content available but not displayed due to length)" if "html" in content and content["html"]: result += "\n\n(Full HTML content available but not displayed due to length)" return result return "No post data found." def format_top_articles(top_articles_data: dict) -> str: """ Format top articles data for display Args: top_articles_data: The data returned from the Hashnode API for top articles Returns: A formatted string representation of the top articles """ if not top_articles_data or "data" not in top_articles_data or not top_articles_data["data"]: return "No top articles data found." if "feed" in top_articles_data["data"] and "edges" in top_articles_data["data"]["feed"]: edges = top_articles_data["data"]["feed"]["edges"] result = "# Top Articles on Hashnode\n\n" if not edges: return "No top articles found." for edge in edges: if "node" in edge: node = edge["node"] title = node.get("title", "Untitled") result += f"## {title}\n" if "id" in node: result += f"ID: {node['id']}\n" if "url" in node: result += f"URL: {node['url']}\n" if "author" in node and node["author"]: author = node["author"] result += f"Author: {author.get('name', 'Unknown')}" if "username" in author: result += f" (@{author['username']})" result += "\n" if "publishedAt" in node and node["publishedAt"]: from datetime import datetime try: published_date = datetime.fromisoformat(node["publishedAt"].replace("Z", "+00:00")) result += f"Published: {published_date.strftime('%b %d, %Y')}\n" except: result += f"Published: {node['publishedAt']}\n" if "brief" in node and node["brief"]: brief = node["brief"] max_length = 200 if len(brief) > max_length: brief = brief[:max_length] + "..." result += f"Description: {brief}\n" result += "\n" return result return "No top articles data found." def format_articles_by_tag(tag_data: dict) -> str: """ Format articles by tag data for display Args: tag_data: The data returned from the Hashnode API for articles by tag Returns: A formatted string representation of the articles by tag """ if not tag_data or "data" not in tag_data or not tag_data["data"]: return "No tag data found." if "tag" in tag_data["data"] and tag_data["data"]["tag"]: tag = tag_data["data"]["tag"] tag_name = tag.get("name", tag.get("slug", "Unknown Tag")) result = f"# Articles with Tag: {tag_name}\n\n" if "posts" not in tag or "edges" not in tag["posts"] or not tag["posts"]["edges"]: return f"No articles found with tag '{tag_name}'." for edge in tag["posts"]["edges"]: if "node" in edge: node = edge["node"] title = node.get("title", "Untitled") result += f"## {title}\n" if "id" in node: result += f"ID: {node['id']}\n" if "url" in node: result += f"URL: {node['url']}\n" if "author" in node and node["author"]: author = node["author"] result += f"Author: {author.get('name', 'Unknown')}" if "username" in author: result += f" (@{author['username']})" result += "\n" if "publishedAt" in node and node["publishedAt"]: from datetime import datetime try: published_date = datetime.fromisoformat(node["publishedAt"].replace("Z", "+00:00")) result += f"Published: {published_date.strftime('%b %d, %Y')}\n" except: result += f"Published: {node['publishedAt']}\n" if "brief" in node and node["brief"]: brief = node["brief"] max_length = 200 if len(brief) > max_length: brief = brief[:max_length] + "..." result += f"Description: {brief}\n" result += "\n" return result return "No tag data found." def format_user_info(user_data: dict) -> str: """ Format user information data for display Args: user_data: The data returned from the Hashnode API for a specific user Returns: A formatted string representation of the user information """ if not user_data or "data" not in user_data or not user_data["data"]: return "No user data found." if "user" in user_data["data"] and user_data["data"]["user"]: user = user_data["data"]["user"] result = f"# User: {user.get('name', 'Unknown')}\n\n" # Basic user information result += "## User Information\n\n" result += f"ID: {user.get('id', 'Unknown')}\n" result += f"Username: {user.get('username', 'Unknown')}\n" if "profilePicture" in user and user["profilePicture"]: result += f"Profile Picture: {user['profilePicture']}\n" if "followersCount" in user: result += f"Followers: {user['followersCount']}\n" if "followingsCount" in user: result += f"Following: {user['followingsCount']}\n" # Bio if "bio" in user and user["bio"] and "text" in user["bio"] and user["bio"]["text"]: result += f"\n## Bio\n\n{user['bio']['text']}\n" # Social media links if "socialMediaLinks" in user and user["socialMediaLinks"]: social_media = user["socialMediaLinks"] result += "\n## Social Media\n\n" if "twitter" in social_media and social_media["twitter"]: result += f"Twitter: {social_media['twitter']}\n" if "github" in social_media and social_media["github"]: result += f"GitHub: {social_media['github']}\n" if "linkedin" in social_media and social_media["linkedin"]: result += f"LinkedIn: {social_media['linkedin']}\n" if "website" in social_media and social_media["website"]: result += f"Website: {social_media['website']}\n" # Publications if "publications" in user and "edges" in user["publications"] and user["publications"]["edges"]: result += "\n## Publications\n\n" for edge in user["publications"]["edges"]: if "node" in edge: node = edge["node"] result += f"- {node.get('title', 'Untitled')}" if "url" in node and node["url"]: result += f" ({node['url']})" result += "\n" return result return "No user data found."

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sbmagar13/hashnode-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server