hashnode-mcp
v1.0.1
Published
A comprehensive MCP server for the Hashnode GraphQL API with full TDD coverage
Maintainers
Readme
📝 Hashnode MCP Server
Supercharge your AI assistant with complete Hashnode integration
A comprehensive Model Context Protocol server that exposes the complete Hashnode GraphQL API as MCP tools, enabling seamless integration between your Hashnode blog and MCP-compatible applications like Claude Desktop and Cline.
Features • Installation • Available Tools • Configuration • Development
✨ Features
- 📝 21 Comprehensive Tools - Complete Hashnode API coverage
- 🔄 Hot-Swap Architecture - Automatic tool discovery with zero-config
- 🎯 Type-Safe - Built with TypeScript strict mode and Zod validation
- 📊 High Test Coverage - 95%+ coverage with TDD methodology
- 🚀 Zero Dependencies - Minimal footprint, maximum performance
- 🔌 Drop-in Integration - Works with Claude Desktop, Cline, and other MCP clients
- ✍️ Full CRUD Operations - Read, write, update, and delete posts and drafts
- 📅 Scheduling Support - Schedule and manage post publication times
🛠️ Available Tools
This MCP server provides 21 tools covering the full spectrum of Hashnode API operations:
📚 Publication Management
| Tool | Description |
|------|-------------|
| getPublicationByHost | Retrieve publication information by domain |
| getPublicationPosts | Fetch posts from a publication with pagination |
📄 Post Operations
| Tool | Description |
|------|-------------|
| getPostBySlug | Get a specific post by its slug |
| searchPosts | Search for posts within a publication |
👤 User Operations
| Tool | Description |
|------|-------------|
| getUserByUsername | Retrieve user profile information |
| getMe | Get current authenticated user information |
| getMyDrafts | Retrieve drafts for the authenticated user |
| getMyPosts | Retrieve published posts for the authenticated user |
✍️ Draft & Publishing Operations (Requires Auth)
| Tool | Description |
|------|-------------|
| createDraft | Create a new draft for a publication |
| updateDraft | Update an existing draft |
| publishDraft | Publish a draft to make it a live post |
| createPost | Create and publish a post directly |
| updatePost | Update an existing published post |
| removePost | Remove/delete a published post |
| restorePost | Restore a previously removed post |
📅 Scheduling Operations (Requires Auth)
| Tool | Description |
|------|-------------|
| scheduleDraft | Schedule a draft for future publication |
| rescheduleDraft | Reschedule a previously scheduled draft |
| cancelScheduledDraft | Cancel a scheduled draft |
📖 Series Operations
| Tool | Description |
|------|-------------|
| getSeriesBySlug | Get series information by slug |
| getSeriesPosts | Retrieve posts from a series with pagination |
| addPostToSeries | Add a post to an existing series |
📑 Static Page Operations
| Tool | Description |
|------|-------------|
| getStaticPageBySlug | Get a static page by its slug |
📦 Installation
Quick Start with Claude Desktop
Add to your Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"hashnode": {
"command": "npx",
"args": ["-y", "hashnode-mcp"]
}
}
}Global Installation
npm install -g hashnode-mcpFor Cline (VS Code)
Add to your Cline MCP settings (cline_mcp_settings.json):
{
"mcpServers": {
"hashnode": {
"command": "npx",
"args": ["-y", "hashnode-mcp"]
}
}
}⚙️ Configuration
Authentication
Some operations require a Hashnode Personal Access Token (PAT). Get your PAT from Hashnode Settings.
With Environment Variable
export HASHNODE_API_TOKEN="your-token-here"With Claude Desktop/Cline
Add the token to your MCP configuration:
{
"mcpServers": {
"hashnode": {
"command": "npx",
"args": ["-y", "hashnode-mcp"],
"env": {
"HASHNODE_API_TOKEN": "your-token-here"
}
}
}
}Operations requiring authentication:
- ✅ Getting authenticated user info (
getMe,getMyDrafts,getMyPosts) - ✅ Creating/updating drafts and posts
- ✅ Publishing and scheduling
- ✅ Series management
- ✅ Post removal and restoration
🎯 Usage Examples
Get Publication Information
// MCP clients can invoke:
{
"tool": "getPublicationByHost",
"arguments": {
"host": "blog.hashnode.com"
}
}Search Posts
{
"tool": "searchPosts",
"arguments": {
"publicationId": "your-publication-id",
"query": "GraphQL tutorial",
"first": 10
}
}Create and Publish a Draft
{
"tool": "createDraft",
"arguments": {
"publicationId": "your-publication-id",
"title": "My New Post",
"contentMarkdown": "# Hello World\n\nThis is my post content."
}
}Schedule a Post
{
"tool": "scheduleDraft",
"arguments": {
"draftId": "your-draft-id",
"authorId": "your-author-id",
"publishAt": "2024-12-31T00:00:00.000Z"
}
}🏗️ Architecture
This server uses a hot-swap autoloader pattern:
src/
├── index.ts # Entry point with MCP server setup
├── server.ts # Server creation and tool registration
├── autoloader.ts # Dynamic tool discovery
├── utils/
│ └── graphql-client.ts # GraphQL client with error handling
└── tools/ # Tool implementations
├── publication.ts # Each exports toolDefinitions
├── post.ts
├── user.ts
├── me.ts
├── draft.ts
├── series.ts
└── static-page.tsKey Benefits:
- Add new tools by dropping files in
src/tools/ - Zero server code changes required
- Each tool is independently testable
- 95%+ coverage on all tools
📚 API Reference
getPublicationByHost
Retrieves publication information by host domain.
host(string, required) - The host domain of the publication- Returns: Publication object with id, title, description, and metadata
getPublicationPosts
Fetches posts from a publication with pagination support.
publicationId(string, required) - The ID of the publicationfirst(number, optional, default: 10) - Number of posts to fetchafter(string, optional) - Cursor for pagination- Returns: PostConnection with edges, pageInfo for pagination
getPostBySlug
Gets a specific post by its slug from a publication.
publicationHost(string, required) - The host domain of the publicationslug(string, required) - The slug of the post- Returns: Post object with id, title, slug, brief, url, and content
searchPosts
Searches for posts within a publication.
publicationId(string, required) - The ID of the publicationquery(string, required) - Search query stringfirst(number, optional, default: 10) - Number of results to fetch- Returns: SearchPostConnection with matching posts
getUserByUsername
Retrieves user information by username.
username(string, required) - The username of the user- Returns: User object with id, username, name, tagline, and bio
getMe
Gets information about the currently authenticated user. Requires auth.
- Returns: User object with id, username, name, tagline, and bio
getMyDrafts
Retrieves drafts for the authenticated user. Requires auth.
first(number, optional, default: 10) - Number of drafts to fetch- Returns: DraftConnection with edges and pageInfo for pagination
getMyPosts
Retrieves published posts for the authenticated user. Requires auth.
first(number, optional, default: 10) - Number of posts to fetch- Returns: PostConnection with edges and pageInfo for pagination
All tools in this section require authentication via HASHNODE_API_TOKEN.
createDraft
Creates a new draft for a publication.
publicationId(string, required) - The ID of the publicationtitle(string, required) - The title of the draftcontentMarkdown(string, required) - The content in Markdown format- Returns: Draft object with id, title, and slug
updateDraft
Updates an existing draft.
draftId(string, required) - The ID of the draft to updatetitle(string, required) - The updated titlecontentMarkdown(string, required) - The updated content in Markdown- Returns: Updated Draft object
publishDraft
Publishes a draft to make it a live post.
draftId(string, required) - The ID of the draft to publish- Returns: Published Post object with id, title, slug, and url
createPost
Creates and publishes a post directly without creating a draft first.
publicationId(string, required) - The ID of the publicationtitle(string, required) - The title of the postcontentMarkdown(string, required) - The content in Markdown format- Returns: Published Post object with id, title, slug, and url
updatePost
Updates an existing published post.
postId(string, required) - The ID of the post to updatetitle(string, required) - The updated titlecontentMarkdown(string, required) - The updated content in Markdown- Returns: Updated Post object with id, title, slug, and url
removePost
Removes/deletes a published post.
postId(string, required) - The ID of the post to remove- Returns: Removed Post object with id, title, slug, and url
restorePost
Restores a previously removed post.
postId(string, required) - The ID of the post to restore- Returns: Restored Post object with id, title, slug, and url
All tools in this section require authentication via HASHNODE_API_TOKEN.
scheduleDraft
Schedules a draft for future publication.
draftId(string, required) - The ID of the draft to scheduleauthorId(string, required) - The ID of the author scheduling the draftpublishAt(string, required) - ISO 8601 date-time string (e.g., "2024-12-31T00:00:00.000Z")- Returns: ScheduledPost object with id and scheduledDate
rescheduleDraft
Reschedules a previously scheduled draft to a new date/time.
draftId(string, required) - The ID of the scheduled draftpublishAt(string, required) - New ISO 8601 date-time string for publication- Returns: ScheduledPost object with id and updated scheduledDate
cancelScheduledDraft
Cancels a scheduled draft and returns it to normal draft status.
draftId(string, required) - The ID of the scheduled draft to cancel- Returns: ScheduledPost object with id and scheduledDate
getSeriesBySlug
Gets series information by slug from a publication.
publicationHost(string, required) - The host domain of the publicationslug(string, required) - The slug of the series- Returns: Series object with id, name, slug, and description
getSeriesPosts
Retrieves posts from a series with pagination support.
publicationHost(string, required) - The host domain of the publicationseriesSlug(string, required) - The slug of the seriesfirst(number, optional, default: 10) - Number of posts to fetch- Returns: SeriesPostConnection with edges and pageInfo
addPostToSeries
Adds a post to an existing series. Requires auth.
seriesId(string, required) - The ID of the seriespostId(string, required) - The ID of the post to add- Returns: Series object with id, name, and slug
getStaticPageBySlug
Gets a static page by its slug from a publication.
publicationHost(string, required) - The host domain of the publicationslug(string, required) - The slug of the static page- Returns: StaticPage object with id, title, slug, and content
🧪 Development
Prerequisites
- Node.js v16+
- npm or pnpm
Setup
# Clone repository
git clone https://github.com/rawveg/hashnode-mcp.git
cd hashnode-mcp
# Install dependencies
npm install
# Build project
npm run build
# Run tests
npm test
# Run tests with coverage
npm run test:coverageTest Coverage
This project follows a strict TDD methodology:
Statements : 95%+
Branches : 95%+
Functions : 100%
Lines : 95%+Testing Philosophy:
- Red-Green-Refactor cycle for all features
- Critical paths always tested
- Edge cases selectively tested
- Error paths prioritized by impact
Adding a New Tool
- Create
src/tools/your-tool.ts:
import { ToolDefinition } from '../autoloader.js';
import { GraphQLClient } from '../utils/graphql-client.js';
export const toolDefinitions: ToolDefinition[] = [
{
name: 'yourTool',
description: 'Your tool description',
inputSchema: {
type: 'object',
properties: {
param: { type: 'string' }
},
required: ['param']
},
handler: async (client, args) => {
// Implementation
return 'result';
}
}
];- Create tests in
__tests__/tools/your-tool.test.ts - Done! The autoloader discovers it automatically.
🤝 Contributing
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Write tests - We maintain 95%+ coverage
- Commit with clear messages (
git commit -m 'Add amazing feature') - Push to your branch (
git push origin feature/amazing-feature) - Open a Pull Request
Code Quality Standards
- All new tools must export
toolDefinitions - Maintain ≥80% test coverage
- Follow existing TypeScript patterns
- TypeScript strict mode is non-negotiable
- Follow the Red-Green-Refactor TDD cycle
📄 License
This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
See LICENSE for details.
🔗 Related Projects
- Hashnode API Documentation - Official Hashnode GraphQL API docs
- Model Context Protocol - Open standard for AI assistant integration
- Claude Desktop - Anthropic's desktop application
- Cline - VS Code AI assistant
🙏 Acknowledgments
Built with:
- MCP SDK - Model Context Protocol SDK
- Hashnode GraphQL API - Hashnode's official API
Made with ❤️ by Tim Green
