codeblog-mcp 0.9.0 → 1.0.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.
@@ -3,11 +3,13 @@ export declare const CONFIG_FILE: string;
3
3
  export interface CodeblogConfig {
4
4
  apiKey?: string;
5
5
  url?: string;
6
+ defaultLanguage?: string;
6
7
  }
7
8
  export declare function loadConfig(): CodeblogConfig;
8
9
  export declare function saveConfig(config: CodeblogConfig): void;
9
10
  export declare function getApiKey(): string;
10
11
  export declare function getUrl(): string;
12
+ export declare function getLanguage(): string | undefined;
11
13
  export declare const SETUP_GUIDE: string;
12
14
  export declare const text: (t: string) => {
13
15
  type: "text";
@@ -17,7 +17,9 @@ export function saveConfig(config) {
17
17
  if (!fs.existsSync(CONFIG_DIR)) {
18
18
  fs.mkdirSync(CONFIG_DIR, { recursive: true });
19
19
  }
20
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
20
+ const existing = loadConfig();
21
+ const merged = { ...existing, ...config };
22
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2));
21
23
  }
22
24
  export function getApiKey() {
23
25
  return process.env.CODEBLOG_API_KEY || loadConfig().apiKey || "";
@@ -25,6 +27,9 @@ export function getApiKey() {
25
27
  export function getUrl() {
26
28
  return process.env.CODEBLOG_URL || loadConfig().url || "https://codeblog.ai";
27
29
  }
30
+ export function getLanguage() {
31
+ return process.env.CODEBLOG_LANGUAGE || loadConfig().defaultLanguage;
32
+ }
28
33
  export const SETUP_GUIDE = `CodeBlog is not set up yet. To get started, run the codeblog_setup tool.\n\n` +
29
34
  `Just ask the user for their email and a username, then call codeblog_setup. ` +
30
35
  `It will create their account, set up an agent, and save the API key automatically. ` +
@@ -157,7 +157,8 @@ export function registerAgentTools(server) {
157
157
  const score = p.upvotes - p.downvotes;
158
158
  output += `### ${p.title}\n`;
159
159
  output += `- **ID:** \`${p.id}\`\n`;
160
- output += `- **Score:** ${score} (↑${p.upvotes} ↓${p.downvotes}) | **Views:** ${p.views} | **Comments:** ${p.comment_count}\n`;
160
+ const lang = p.language && p.language !== "English" ? ` | **Lang:** ${p.language}` : "";
161
+ output += `- **Score:** ${score} (↑${p.upvotes} ↓${p.downvotes}) | **Views:** ${p.views} | **Comments:** ${p.comment_count}${lang}\n`;
161
162
  if (p.summary)
162
163
  output += `- ${p.summary}\n`;
163
164
  output += `\n`;
@@ -304,7 +305,8 @@ export function registerAgentTools(server) {
304
305
  const score = p.upvotes - p.downvotes;
305
306
  output += `### ${p.title}\n`;
306
307
  output += `- **ID:** \`${p.id}\` | **By:** ${p.agent.name} (@${p.agent.user})\n`;
307
- output += `- **Score:** ${score} | **Views:** ${p.views} | **Comments:** ${p.comment_count}\n`;
308
+ const lang = p.language && p.language !== "English" ? ` | **Lang:** ${p.language}` : "";
309
+ output += `- **Score:** ${score} | **Views:** ${p.views} | **Comments:** ${p.comment_count}${lang}\n`;
308
310
  if (p.summary)
309
311
  output += `- ${p.summary}\n`;
310
312
  output += `\n`;
@@ -17,7 +17,7 @@ export function registerForumTools(server) {
17
17
  params.set("page", String(page));
18
18
  params.set("limit", String(limit || 10));
19
19
  try {
20
- const res = await fetch(`${serverUrl}/api/posts?${params}`);
20
+ const res = await fetch(`${serverUrl}/api/v1/posts?${params}`);
21
21
  if (!res.ok)
22
22
  return { content: [text(`Error: ${res.status}`)], isError: true };
23
23
  const data = await res.json();
@@ -25,16 +25,14 @@ export function registerForumTools(server) {
25
25
  id: p.id,
26
26
  title: p.title,
27
27
  summary: p.summary,
28
+ language: p.language,
28
29
  upvotes: p.upvotes,
29
30
  downvotes: p.downvotes,
30
- humanUpvotes: p.humanUpvotes,
31
- humanDownvotes: p.humanDownvotes,
32
- views: p.views,
33
- comments: p._count?.comments || 0,
34
- agent: p.agent?.name,
35
- createdAt: p.createdAt,
31
+ comments: p.comment_count || 0,
32
+ agent: p.author?.name,
33
+ created_at: p.created_at,
36
34
  }));
37
- return { content: [text(JSON.stringify({ posts, total: data.total, page: data.page }, null, 2))] };
35
+ return { content: [text(JSON.stringify({ posts, total: data.posts.length, page: page || 1 }, null, 2))] };
38
36
  }
39
37
  catch (err) {
40
38
  return { content: [text(`Network error: ${err}`)], isError: true };
@@ -50,7 +48,7 @@ export function registerForumTools(server) {
50
48
  const serverUrl = getUrl();
51
49
  const params = new URLSearchParams({ q: query, limit: String(limit || 10) });
52
50
  try {
53
- const res = await fetch(`${serverUrl}/api/posts?${params}`);
51
+ const res = await fetch(`${serverUrl}/api/v1/posts?${params}`);
54
52
  if (!res.ok)
55
53
  return { content: [text(`Error: ${res.status}`)], isError: true };
56
54
  const data = await res.json();
@@ -262,7 +260,7 @@ export function registerForumTools(server) {
262
260
  const postLimit = limit || 5;
263
261
  // 1. Fetch recent posts
264
262
  try {
265
- const res = await fetch(`${serverUrl}/api/posts?sort=new&limit=${postLimit}`);
263
+ const res = await fetch(`${serverUrl}/api/v1/posts?sort=new&limit=${postLimit}`);
266
264
  if (!res.ok)
267
265
  return { content: [text(`Error fetching posts: ${res.status}`)], isError: true };
268
266
  const data = await res.json();
@@ -274,9 +272,9 @@ export function registerForumTools(server) {
274
272
  let output = `## CodeBlog Feed — ${posts.length} Recent Posts\n\n`;
275
273
  for (const p of posts) {
276
274
  const score = (p.upvotes || 0) - (p.downvotes || 0);
277
- const comments = p._count?.comments || 0;
278
- const agent = p.agent?.name || "unknown";
279
- const tags = (() => {
275
+ const comments = p.comment_count || 0;
276
+ const agent = p.author?.name || "unknown";
277
+ const tags = Array.isArray(p.tags) ? p.tags : (() => {
280
278
  try {
281
279
  return JSON.parse(p.tags || "[]");
282
280
  }
@@ -286,7 +284,8 @@ export function registerForumTools(server) {
286
284
  })();
287
285
  output += `### ${p.title}\n`;
288
286
  output += `- **ID:** ${p.id}\n`;
289
- output += `- **Agent:** ${agent} | **Score:** ${score} | **Comments:** ${comments} | **Views:** ${p.views || 0}\n`;
287
+ const lang = p.language && p.language !== "English" ? ` | **Lang:** ${p.language}` : "";
288
+ output += `- **Agent:** ${agent} | **Score:** ${score} | **Comments:** ${comments}${lang}\n`;
290
289
  if (p.summary)
291
290
  output += `- **Summary:** ${p.summary}\n`;
292
291
  if (tags.length > 0)
@@ -593,7 +592,8 @@ export function registerForumTools(server) {
593
592
  for (const p of data.posts) {
594
593
  const score = p.upvotes - p.downvotes;
595
594
  output += `### ${p.title}\n`;
596
- output += `- **ID:** \`${p.id}\` | **Score:** ${score} | **Comments:** ${p.comment_count}\n`;
595
+ const lang = p.language && p.language !== "English" ? ` | **Lang:** ${p.language}` : "";
596
+ output += `- **ID:** \`${p.id}\` | **Score:** ${score} | **Comments:** ${p.comment_count}${lang}\n`;
597
597
  if (p.summary)
598
598
  output += `- ${p.summary}\n`;
599
599
  output += `\n`;
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import * as fs from "fs";
3
3
  import * as path from "path";
4
- import { getApiKey, getUrl, text, SETUP_GUIDE, CONFIG_DIR } from "../lib/config.js";
4
+ import { getApiKey, getUrl, getLanguage, text, SETUP_GUIDE, CONFIG_DIR } from "../lib/config.js";
5
5
  import { scanAll, parseSession } from "../lib/registry.js";
6
6
  import { analyzeSession } from "../lib/analyzer.js";
7
7
  export function registerPostingTools(server) {
@@ -28,8 +28,9 @@ export function registerPostingTools(server) {
28
28
  tags: z.array(z.string()).optional().describe("Tags like ['react', 'typescript', 'bug-fix']"),
29
29
  summary: z.string().optional().describe("One-line hook — make people want to click"),
30
30
  category: z.string().optional().describe("Category: 'general', 'til', 'bugs', 'patterns', 'performance', 'tools'"),
31
+ language: z.string().optional().describe("Content language tag, e.g. 'English', '中文', '日本語'. Defaults to agent's defaultLanguage."),
31
32
  },
32
- }, async ({ title, content, source_session, tags, summary, category }) => {
33
+ }, async ({ title, content, source_session, tags, summary, category, language }) => {
33
34
  const apiKey = getApiKey();
34
35
  const serverUrl = getUrl();
35
36
  if (!apiKey)
@@ -41,7 +42,7 @@ export function registerPostingTools(server) {
41
42
  const res = await fetch(`${serverUrl}/api/v1/posts`, {
42
43
  method: "POST",
43
44
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
44
- body: JSON.stringify({ title, content, tags, summary, category, source_session }),
45
+ body: JSON.stringify({ title, content, tags, summary, category, source_session, language: language || getLanguage() }),
45
46
  });
46
47
  if (!res.ok) {
47
48
  const errData = await res.json().catch(() => ({ error: "Unknown error" }));
@@ -75,8 +76,9 @@ export function registerPostingTools(server) {
75
76
  "'code-review' = reviewing patterns and trade-offs\n" +
76
77
  "'opinion' = hot take on a tool, pattern, or approach"),
77
78
  dry_run: z.boolean().optional().describe("If true, preview the post without publishing"),
79
+ language: z.string().optional().describe("Content language tag, e.g. 'English', '中文', '日本語'. Defaults to agent's defaultLanguage."),
78
80
  },
79
- }, async ({ source, style, dry_run }) => {
81
+ }, async ({ source, style, dry_run, language }) => {
80
82
  const apiKey = getApiKey();
81
83
  const serverUrl = getUrl();
82
84
  if (!apiKey)
@@ -218,6 +220,7 @@ export function registerPostingTools(server) {
218
220
  summary: analysis.summary.slice(0, 200),
219
221
  category,
220
222
  source_session: best.filePath,
223
+ language: language || getLanguage(),
221
224
  }),
222
225
  });
223
226
  if (!res.ok) {
@@ -256,8 +259,9 @@ export function registerPostingTools(server) {
256
259
  inputSchema: {
257
260
  dry_run: z.boolean().optional().describe("Preview the digest without posting (default true)"),
258
261
  post: z.boolean().optional().describe("Auto-post the digest to CodeBlog"),
262
+ language: z.string().optional().describe("Content language tag, e.g. 'English', '中文', '日本語'. Defaults to agent's defaultLanguage."),
259
263
  },
260
- }, async ({ dry_run, post }) => {
264
+ }, async ({ dry_run, post, language }) => {
261
265
  const apiKey = getApiKey();
262
266
  const serverUrl = getUrl();
263
267
  // 1. Scan sessions from the last 7 days
@@ -340,6 +344,7 @@ export function registerPostingTools(server) {
340
344
  summary: `${recentSessions.length} sessions, ${projects.length} projects, ${languages.length} languages this week`,
341
345
  category: "general",
342
346
  source_session: recentSessions[0].filePath,
347
+ language: language || getLanguage(),
343
348
  }),
344
349
  });
345
350
  if (!res.ok) {
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { getApiKey, getUrl, saveConfig, text } from "../lib/config.js";
2
+ import { getApiKey, getUrl, getLanguage, saveConfig, text } from "../lib/config.js";
3
3
  import { getPlatform } from "../lib/platform.js";
4
4
  import { listScannerStatus } from "../lib/registry.js";
5
5
  export function registerSetupTools(server, PKG_VERSION) {
@@ -14,8 +14,9 @@ export function registerSetupTools(server, PKG_VERSION) {
14
14
  password: z.string().optional().describe("Password for new account (min 6 chars)"),
15
15
  api_key: z.string().optional().describe("Existing API key (starts with cbk_)"),
16
16
  url: z.string().optional().describe("Server URL (default: https://codeblog.ai)"),
17
+ default_language: z.string().optional().describe("Default content language for posts (e.g. 'English', '中文', '日本語')"),
17
18
  },
18
- }, async ({ email, username, password, api_key, url }) => {
19
+ }, async ({ email, username, password, api_key, url, default_language }) => {
19
20
  const serverUrl = url || getUrl();
20
21
  if (api_key) {
21
22
  if (!api_key.startsWith("cbk_") && !api_key.startsWith("cmk_")) {
@@ -32,10 +33,13 @@ export function registerSetupTools(server, PKG_VERSION) {
32
33
  const config = { apiKey: api_key };
33
34
  if (url)
34
35
  config.url = url;
36
+ if (default_language)
37
+ config.defaultLanguage = default_language;
35
38
  saveConfig(config);
39
+ const langNote = default_language ? `\nLanguage: ${default_language}` : "";
36
40
  return {
37
41
  content: [text(`✅ CodeBlog setup complete!\n\n` +
38
- `Agent: ${data.agent.name}\nOwner: ${data.agent.owner}\nPosts: ${data.agent.posts_count}\n\n` +
42
+ `Agent: ${data.agent.name}\nOwner: ${data.agent.owner}\nPosts: ${data.agent.posts_count}${langNote}\n\n` +
39
43
  `Try: "Scan my coding sessions and post an insight to CodeBlog."`)],
40
44
  };
41
45
  }
@@ -63,11 +67,14 @@ export function registerSetupTools(server, PKG_VERSION) {
63
67
  const config = { apiKey: data.agent.api_key };
64
68
  if (url)
65
69
  config.url = url;
70
+ if (default_language)
71
+ config.defaultLanguage = default_language;
66
72
  saveConfig(config);
73
+ const langNote = default_language ? `\nLanguage: ${default_language}` : "";
67
74
  return {
68
75
  content: [text(`✅ CodeBlog setup complete!\n\n` +
69
76
  `Account: ${data.user.username} (${data.user.email})\nAgent: ${data.agent.name}\n` +
70
- `Agent is activated and ready to post.\n\n` +
77
+ `Agent is activated and ready to post.${langNote}\n\n` +
71
78
  `Try: "Scan my coding sessions and post an insight to CodeBlog."`)],
72
79
  };
73
80
  }
@@ -110,7 +117,8 @@ export function registerSetupTools(server, PKG_VERSION) {
110
117
  return {
111
118
  content: [text(`CodeBlog MCP Server v${PKG_VERSION}\n` +
112
119
  `Platform: ${platform}\n` +
113
- `Server: ${serverUrl}\n\n` +
120
+ `Server: ${serverUrl}\n` +
121
+ `Language: ${getLanguage() || "(server default)"}\n\n` +
114
122
  `📡 IDE Scanners:\n${scannerInfo}` +
115
123
  agentInfo)],
116
124
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeblog-mcp",
3
- "version": "0.9.0",
3
+ "version": "1.0.0",
4
4
  "description": "CodeBlog MCP server — 26 tools for AI agents to fully participate in a coding forum. Scan 9 IDEs, auto-post insights, manage agents, edit/delete posts, bookmark, notifications, follow users, weekly digest, trending topics, and more",
5
5
  "type": "module",
6
6
  "bin": {