Skip to main content
Glama

Dev.to MCP Server

server.py8.12 kB
import httpx from mcp.server.fastmcp import FastMCP, Context import os # Create a Dev.to MCP server mcp = FastMCP("Dev.to MCP Server") # Constants BASE_URL = "https://dev.to/api" # Helper functions async def fetch_from_api(path: str, params: dict = None) -> dict: """Helper function to fetch data from Dev.to API""" async with httpx.AsyncClient() as client: url = f"{BASE_URL}{path}" response = await client.get(url, params=params, timeout=10.0) response.raise_for_status() return response.json() # Resources @mcp.tool() async def get_latest_articles() -> str: """Get the latest articles from Dev.to""" articles = await fetch_from_api("/articles/latest") return format_articles(articles[:10]) # Limiting to 10 for readability @mcp.tool() async def get_top_articles() -> str: """Get the top articles from Dev.to""" articles = await fetch_from_api("/articles") return format_articles(articles[:10]) # Limiting to 10 for readability @mcp.tool() async def get_articles_by_tag(tag: str) -> str: """Get articles by tag from Dev.to""" articles = await fetch_from_api("/articles", params={"tag": tag}) return format_articles(articles[:10]) # Limiting to 10 for readability @mcp.tool() async def get_article_by_id(id: str) -> str: """Get a specific article by ID from Dev.to""" article = await fetch_from_api(f"/articles/{id}") return format_article_details(article) # Tools @mcp.tool() async def search_articles(query: str, page: int = 1) -> str: """ Search for articles on Dev.to Args: query: Search term to find articles page: Page number for pagination (default: 1) """ articles = await fetch_from_api("/articles", params={"page": page}) filtered_articles = [ article for article in articles if query.lower() in article.get("title", "").lower() or query.lower() in article.get("description", "").lower() ] return format_articles(filtered_articles[:10]) @mcp.tool() async def get_article_details(article_id: int) -> str: """ Get detailed information about a specific article Args: article_id: The ID of the article to retrieve """ article = await fetch_from_api(f"/articles/{article_id}") return format_article_details(article) @mcp.tool() async def get_articles_by_username(username: str) -> str: """ Get articles written by a specific user Args: username: The username of the author """ articles = await fetch_from_api("/articles", params={"username": username}) return format_articles(articles[:10]) @mcp.tool() async def get_user_info(username: str) -> str: """ Get information about a Dev.to user Args: username: The username of the user """ try: user = await fetch_from_api(f"/users/{username}") return format_user_profile(user) except httpx.HTTPStatusError as e: if e.response.status_code == 404: return f"User {username} not found." raise e @mcp.tool() async def create_article(title: str, body_markdown: str, tags: str = "", published: bool = False) -> str: """ Create and publish a new article on Dev.to Args: title: The title of the article body_markdown: The content of the article in markdown format tags: Comma-separated list of tags (e.g., "python,tutorial,webdev") published: Whether to publish immediately (True) or save as draft (False) """ article_data = { "article": { "title": title, "body_markdown": body_markdown, "published": published, "tags": tags } } async with httpx.AsyncClient() as client: response = await client.post(f"{BASE_URL}/articles", json=article_data, headers={"Content-Type": "application/json", "api-key": os.getenv("DEV_TO_API_KEY")}, timeout=10.0) response.raise_for_status() article = response.json() return f"Article created successfully with ID: {article.get('id')}\nURL: {article.get('url')}" @mcp.tool() async def update_article(article_id: int, title: str = None, body_markdown: str = None, tags: str = None, published: bool = None) -> str: """ Update an existing article on Dev.to Args: article_id: The ID of the article to update title: New title for the article (optional) body_markdown: New content in markdown format (optional) tags: New comma-separated list of tags (optional) published: Change publish status (optional) """ # First get the current article data article = await fetch_from_api(f"/articles/{article_id}") # Prepare update data with only the fields that are provided update_data = {"article": {}} if title is not None: update_data["article"]["title"] = title if body_markdown is not None: update_data["article"]["body_markdown"] = body_markdown if tags is not None: update_data["article"]["tags"] = tags if published is not None: update_data["article"]["published"] = published async with httpx.AsyncClient() as client: response = await client.put(f"{BASE_URL}/articles/{article_id}", json=update_data, timeout=10.0) response.raise_for_status() updated_article = response.json() return f"Article updated successfully\nURL: {updated_article.get('url')}" # Helper formatting functions def format_articles(articles: list) -> str: """Format a list of articles for display""" if not articles: return "No articles found." result = "# Dev.to Articles\n\n" for article in articles: title = article.get("title", "Untitled") author = article.get("user", {}).get("name", "Unknown Author") published_date = article.get("readable_publish_date", "Unknown date") article_id = article.get("id", "") tags = article.get("tags", "") result += f"## {title}\n" result += f"ID: {article_id}\n" result += f"Author: {author}\n" result += f"Published: {published_date}\n" result += f"Tags: {tags}\n" result += f"Description: {article.get('description', 'No description available.')}\n\n" return result def format_article_details(article: dict) -> str: """Format a single article with full details""" if not article: return "Article not found." title = article.get("title", "Untitled") author = article.get("user", {}).get("name", "Unknown Author") published_date = article.get("readable_publish_date", "Unknown date") body = article.get("body_markdown", "No content available.") tags = article.get("tags", "") result = f"# {title}\n\n" result += f"Author: {author}\n" result += f"Published: {published_date}\n" result += f"Tags: {tags}\n\n" result += "## Content\n\n" result += body return result def format_user_profile(user: dict) -> str: """Format a user profile for display""" if not user: return "User not found." username = user.get("username", "Unknown") name = user.get("name", "Unknown") bio = user.get("summary", "No bio available.") twitter = user.get("twitter_username", "") github = user.get("github_username", "") website = user.get("website_url", "") location = user.get("location", "") joined = user.get("joined_at", "") result = f"# {name} (@{username})\n\n" result += f"Bio: {bio}\n\n" result += "## Details\n" if location: result += f"Location: {location}\n" if joined: result += f"Member since: {joined}\n" result += "\n## Links\n" if twitter: result += f"Twitter: @{twitter}\n" if github: result += f"GitHub: {github}\n" if website: result += f"Website: {website}\n" return result if __name__ == "__main__": print("Starting Dev.to MCP server...") mcp.run()

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/Arindam200/devto-mcp'

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