@kieksme/mcp-hashnode 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,183 @@
1
+ import { z } from "zod";
2
+ import { gql } from "../services/hashnode.js";
3
+ var ResponseFormat;
4
+ (function (ResponseFormat) {
5
+ ResponseFormat["MARKDOWN"] = "markdown";
6
+ ResponseFormat["JSON"] = "json";
7
+ })(ResponseFormat || (ResponseFormat = {}));
8
+ const ResponseFormatSchema = z
9
+ .nativeEnum(ResponseFormat)
10
+ .default(ResponseFormat.MARKDOWN)
11
+ .describe("Output format: 'markdown' (default) or 'json'");
12
+ // ─── Queries ──────────────────────────────────────────────────────────────────
13
+ const ME_QUERY = `
14
+ query {
15
+ me {
16
+ id
17
+ name
18
+ username
19
+ profilePicture
20
+ publications(first: 20) {
21
+ edges {
22
+ node {
23
+ id
24
+ title
25
+ displayTitle
26
+ url
27
+ }
28
+ }
29
+ }
30
+ }
31
+ }
32
+ `;
33
+ const PUBLICATION_QUERY = `
34
+ query ($host: String!) {
35
+ publication(host: $host) {
36
+ id
37
+ title
38
+ displayTitle
39
+ url
40
+ about { text }
41
+ author { name username }
42
+ }
43
+ }
44
+ `;
45
+ // ─── Register ─────────────────────────────────────────────────────────────────
46
+ export function registerPublicationTools(server) {
47
+ // hashnode_get_me ──────────────────────────────────────────────────────────
48
+ server.registerTool("hashnode_get_me", {
49
+ title: "Get current Hashnode user and their publications",
50
+ description: `Returns the authenticated user's profile and a list of their publications.
51
+ Use this first to discover your publicationId(s) before creating or listing posts.
52
+
53
+ Returns:
54
+ - id, name, username, profilePicture
55
+ - publications[]: { id, title, url }
56
+
57
+ Examples:
58
+ - "What is my publication ID?" → call hashnode_get_me
59
+ - "List my Hashnode blogs" → call hashnode_get_me`,
60
+ inputSchema: {
61
+ response_format: ResponseFormatSchema,
62
+ },
63
+ annotations: {
64
+ readOnlyHint: true,
65
+ destructiveHint: false,
66
+ idempotentHint: true,
67
+ openWorldHint: true,
68
+ },
69
+ }, async ({ response_format }) => {
70
+ try {
71
+ const data = await gql(ME_QUERY);
72
+ const me = data.me;
73
+ const publications = (me.publications?.edges ?? []).map((e) => e.node);
74
+ if (response_format === ResponseFormat.JSON) {
75
+ return {
76
+ content: [
77
+ {
78
+ type: "text",
79
+ text: JSON.stringify({ ...me, publications }, null, 2),
80
+ },
81
+ ],
82
+ structuredContent: { ...me, publications },
83
+ };
84
+ }
85
+ const lines = [
86
+ `# Hashnode User: ${me.name} (@${me.username})`,
87
+ `**ID**: ${me.id}`,
88
+ "",
89
+ "## Publications",
90
+ ];
91
+ if (publications.length === 0) {
92
+ lines.push("_No publications found._");
93
+ }
94
+ else {
95
+ for (const p of publications) {
96
+ lines.push(`- **${p.title}** — ID: \`${p.id}\` — ${p.url}`);
97
+ }
98
+ }
99
+ return { content: [{ type: "text", text: lines.join("\n") }] };
100
+ }
101
+ catch (error) {
102
+ return {
103
+ content: [
104
+ {
105
+ type: "text",
106
+ text: error instanceof Error ? error.message : String(error),
107
+ },
108
+ ],
109
+ };
110
+ }
111
+ });
112
+ // hashnode_get_publication ─────────────────────────────────────────────────
113
+ server.registerTool("hashnode_get_publication", {
114
+ title: "Get Hashnode publication by host",
115
+ description: `Fetch metadata for a Hashnode publication by its host/domain.
116
+
117
+ Args:
118
+ - host (string): Publication host, e.g. "yourblog.hashnode.dev" or a custom domain
119
+ - response_format: 'markdown' or 'json'
120
+
121
+ Returns: id, title, url, about, author info
122
+
123
+ Examples:
124
+ - host: "thinkport.hashnode.dev"`,
125
+ inputSchema: {
126
+ host: z
127
+ .string()
128
+ .min(1)
129
+ .describe("Publication host, e.g. 'yourblog.hashnode.dev' or a custom domain"),
130
+ response_format: ResponseFormatSchema,
131
+ },
132
+ annotations: {
133
+ readOnlyHint: true,
134
+ destructiveHint: false,
135
+ idempotentHint: true,
136
+ openWorldHint: true,
137
+ },
138
+ }, async ({ host, response_format }) => {
139
+ try {
140
+ const data = await gql(PUBLICATION_QUERY, { host });
141
+ const pub = data.publication;
142
+ if (!pub) {
143
+ return {
144
+ content: [
145
+ {
146
+ type: "text",
147
+ text: `Error: No publication found for host '${host}'.`,
148
+ },
149
+ ],
150
+ };
151
+ }
152
+ if (response_format === ResponseFormat.JSON) {
153
+ return {
154
+ content: [{ type: "text", text: JSON.stringify(pub, null, 2) }],
155
+ structuredContent: pub,
156
+ };
157
+ }
158
+ const lines = [
159
+ `# ${pub.displayTitle ?? pub.title}`,
160
+ `**ID**: \`${pub.id}\``,
161
+ `**URL**: ${pub.url}`,
162
+ ];
163
+ if (pub.author) {
164
+ lines.push(`**Author**: ${pub.author.name} (@${pub.author.username})`);
165
+ }
166
+ if (pub.about?.text) {
167
+ lines.push("", String(pub.about.text));
168
+ }
169
+ return { content: [{ type: "text", text: lines.join("\n") }] };
170
+ }
171
+ catch (error) {
172
+ return {
173
+ content: [
174
+ {
175
+ type: "text",
176
+ text: error instanceof Error ? error.message : String(error),
177
+ },
178
+ ],
179
+ };
180
+ }
181
+ });
182
+ }
183
+ //# sourceMappingURL=publications.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publications.js","sourceRoot":"","sources":["../../src/tools/publications.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ export interface HashnodeTag {
2
+ [key: string]: unknown;
3
+ name: string;
4
+ slug: string;
5
+ id?: string;
6
+ }
7
+ export interface HashnodePost {
8
+ [key: string]: unknown;
9
+ id: string;
10
+ title: string;
11
+ subtitle?: string;
12
+ slug: string;
13
+ url: string;
14
+ brief?: string;
15
+ publishedAt?: string;
16
+ updatedAt?: string;
17
+ readTimeInMinutes?: number;
18
+ views?: number;
19
+ tags?: HashnodeTag[];
20
+ coverImage?: {
21
+ url: string;
22
+ [key: string]: unknown;
23
+ };
24
+ author?: {
25
+ name: string;
26
+ username: string;
27
+ [key: string]: unknown;
28
+ };
29
+ publication?: {
30
+ id: string;
31
+ title: string;
32
+ url: string;
33
+ [key: string]: unknown;
34
+ };
35
+ }
36
+ export interface HashnodeDraft {
37
+ [key: string]: unknown;
38
+ id: string;
39
+ title?: string;
40
+ subtitle?: string;
41
+ slug?: string;
42
+ contentMarkdown?: string;
43
+ tags?: HashnodeTag[];
44
+ coverImage?: {
45
+ url: string;
46
+ [key: string]: unknown;
47
+ };
48
+ updatedAt?: string;
49
+ author?: {
50
+ name: string;
51
+ username: string;
52
+ [key: string]: unknown;
53
+ };
54
+ }
55
+ export interface HashnodePublication {
56
+ [key: string]: unknown;
57
+ id: string;
58
+ title: string;
59
+ displayTitle?: string;
60
+ url: string;
61
+ about?: {
62
+ html?: string;
63
+ text?: string;
64
+ [key: string]: unknown;
65
+ };
66
+ author?: {
67
+ name: string;
68
+ username: string;
69
+ [key: string]: unknown;
70
+ };
71
+ favicon?: string;
72
+ }
73
+ export interface HashnodeMe {
74
+ [key: string]: unknown;
75
+ id: string;
76
+ name: string;
77
+ username: string;
78
+ profilePicture?: string;
79
+ publications?: {
80
+ edges: Array<{
81
+ node: HashnodePublication;
82
+ [key: string]: unknown;
83
+ }>;
84
+ [key: string]: unknown;
85
+ };
86
+ }
87
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@kieksme/mcp-hashnode",
3
+ "version": "1.1.0",
4
+ "description": "MCP server for the Hashnode GraphQL API — create drafts, publish posts, manage your blog via Claude",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "mcp-hashnode": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist/",
12
+ "README.md"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/kieksme/mcp-hashnode.git"
17
+ },
18
+ "homepage": "https://github.com/kieksme/mcp-hashnode",
19
+ "bugs": {
20
+ "url": "https://github.com/kieksme/mcp-hashnode/issues"
21
+ },
22
+ "keywords": [
23
+ "mcp",
24
+ "hashnode",
25
+ "blog",
26
+ "graphql",
27
+ "claude",
28
+ "ai",
29
+ "model-context-protocol"
30
+ ],
31
+ "license": "MIT",
32
+ "engines": {
33
+ "node": ">=18"
34
+ },
35
+ "dependencies": {
36
+ "@modelcontextprotocol/sdk": "^1.12.0",
37
+ "axios": "^1.7.9",
38
+ "zod": "^3.23.8"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^22.10.0",
42
+ "tsx": "^4.19.2",
43
+ "typescript": "^5.7.2"
44
+ },
45
+ "scripts": {
46
+ "start": "node dist/index.js",
47
+ "dev": "tsx watch src/index.ts",
48
+ "build": "tsc",
49
+ "clean": "rm -rf dist"
50
+ }
51
+ }