codeblog-mcp 2.8.0 → 2.8.1
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,44 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import * as os from "os";
|
|
4
|
+
// The CLI/TUI client uses XDG-compliant paths (~/.config/codeblog/config.json)
|
|
5
|
+
// while the MCP server uses ~/.codeblog/config.json.
|
|
6
|
+
// This module reads/writes the CLIENT config so MCP tools can configure
|
|
7
|
+
// client-side behavior (e.g. daily report auto-trigger hour).
|
|
8
|
+
function getClientConfigDir() {
|
|
9
|
+
const home = os.homedir();
|
|
10
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME;
|
|
11
|
+
if (process.platform === "win32") {
|
|
12
|
+
return path.join(process.env.APPDATA || path.join(home, "AppData", "Roaming"), "codeblog");
|
|
13
|
+
}
|
|
14
|
+
return path.join(xdgConfig || path.join(home, ".config"), "codeblog");
|
|
15
|
+
}
|
|
16
|
+
export function getClientConfigPath() {
|
|
17
|
+
return path.join(getClientConfigDir(), "config.json");
|
|
18
|
+
}
|
|
19
|
+
export function loadClientConfig() {
|
|
20
|
+
try {
|
|
21
|
+
const filePath = getClientConfigPath();
|
|
22
|
+
if (fs.existsSync(filePath)) {
|
|
23
|
+
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch { }
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
export function saveClientConfig(partial) {
|
|
30
|
+
const dir = getClientConfigDir();
|
|
31
|
+
if (!fs.existsSync(dir)) {
|
|
32
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
const existing = loadClientConfig();
|
|
35
|
+
const merged = { ...existing, ...partial };
|
|
36
|
+
const filePath = getClientConfigPath();
|
|
37
|
+
fs.writeFileSync(filePath, JSON.stringify(merged, null, 2));
|
|
38
|
+
try {
|
|
39
|
+
fs.chmodSync(filePath, 0o600);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Best-effort on non-POSIX platforms.
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { text } from "../lib/config.js";
|
|
3
|
+
import { loadClientConfig, saveClientConfig } from "../lib/client-config.js";
|
|
3
4
|
import { withAuth } from "../lib/auth-guard.js";
|
|
4
5
|
import { collectDailyUsage, formatTokens, formatCost, } from "../lib/usage-collector.js";
|
|
5
6
|
// ─── Tool registration ───────────────────────────────────────────────
|
|
@@ -40,7 +41,10 @@ export function registerDailyReportTools(server) {
|
|
|
40
41
|
" Use tables for: overall stats, model usage breakdown, IDE breakdown, project breakdown.\n" +
|
|
41
42
|
" But tables should NOT be the main structure — the narrative story comes first.\n" +
|
|
42
43
|
"- If there were multiple projects, tell each project's story separately with depth.\n" +
|
|
43
|
-
"- If blog posts were published today,
|
|
44
|
+
"- If blog posts were published today (provided in todaysPosts), review them and decide which ones\n" +
|
|
45
|
+
" are relevant to the day's work. For relevant posts, reference them naturally in the narrative\n" +
|
|
46
|
+
" using markdown links: [Post Title](url). You don't have to mention every post — only those\n" +
|
|
47
|
+
" that relate to what was worked on. If none of the posts are relevant, skip them entirely.\n" +
|
|
44
48
|
"- End with a reflection: what did you learn? what's next?\n\n" +
|
|
45
49
|
"BAD example (DO NOT write like this):\n" +
|
|
46
50
|
" '## 数据一览\\n编码会话:7\\nToken:73M\\n花费:$200'\n" +
|
|
@@ -246,8 +250,64 @@ export function registerDailyReportTools(server) {
|
|
|
246
250
|
};
|
|
247
251
|
}
|
|
248
252
|
}));
|
|
253
|
+
// ─── configure_daily_report ──────────────────────────────────────
|
|
254
|
+
server.registerTool("configure_daily_report", {
|
|
255
|
+
description: "Configure daily report preferences.\n" +
|
|
256
|
+
"Supports setting the auto-trigger hour (0-23) for the TUI client.\n" +
|
|
257
|
+
"Set auto_hour to -1 to disable auto-trigger entirely.\n" +
|
|
258
|
+
"Use get=true to read current settings without changing anything.",
|
|
259
|
+
inputSchema: {
|
|
260
|
+
auto_hour: z
|
|
261
|
+
.number()
|
|
262
|
+
.int()
|
|
263
|
+
.min(-1)
|
|
264
|
+
.max(23)
|
|
265
|
+
.optional()
|
|
266
|
+
.describe("Hour (0-23) to auto-trigger daily report. Default is 22 (10 PM). Set to -1 to disable auto-trigger."),
|
|
267
|
+
get: z
|
|
268
|
+
.boolean()
|
|
269
|
+
.optional()
|
|
270
|
+
.describe("If true, return current settings without changing anything."),
|
|
271
|
+
},
|
|
272
|
+
}, async ({ auto_hour, get }) => {
|
|
273
|
+
if (get) {
|
|
274
|
+
const cfg = loadClientConfig();
|
|
275
|
+
const hour = normalizeDailyReportHour(cfg.dailyReportHour);
|
|
276
|
+
const enabled = hour >= 0;
|
|
277
|
+
return {
|
|
278
|
+
content: [
|
|
279
|
+
text(JSON.stringify({
|
|
280
|
+
auto_hour: hour,
|
|
281
|
+
enabled,
|
|
282
|
+
message: enabled
|
|
283
|
+
? `Daily report auto-triggers at ${String(hour).padStart(2, "0")}:00 local time.`
|
|
284
|
+
: "Daily report auto-trigger is disabled.",
|
|
285
|
+
})),
|
|
286
|
+
],
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
if (auto_hour === undefined) {
|
|
290
|
+
return {
|
|
291
|
+
content: [
|
|
292
|
+
text("No changes made. Provide auto_hour (0-23, or -1 to disable) to update settings."),
|
|
293
|
+
],
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
saveClientConfig({ dailyReportHour: auto_hour });
|
|
297
|
+
const enabled = auto_hour >= 0;
|
|
298
|
+
return {
|
|
299
|
+
content: [
|
|
300
|
+
text(JSON.stringify({
|
|
301
|
+
auto_hour,
|
|
302
|
+
enabled,
|
|
303
|
+
message: enabled
|
|
304
|
+
? `Daily report auto-trigger set to ${String(auto_hour).padStart(2, "0")}:00 local time. The TUI will pick up this change on next check cycle.`
|
|
305
|
+
: "Daily report auto-trigger has been disabled.",
|
|
306
|
+
})),
|
|
307
|
+
],
|
|
308
|
+
};
|
|
309
|
+
});
|
|
249
310
|
}
|
|
250
|
-
// ─── Helpers ─────────────────────────────────────────────────────────
|
|
251
311
|
function getActiveHoursRange(hourly) {
|
|
252
312
|
const hours = Object.keys(hourly)
|
|
253
313
|
.map(Number)
|
|
@@ -257,6 +317,15 @@ function getActiveHoursRange(hourly) {
|
|
|
257
317
|
const fmt = (h) => `${String(h).padStart(2, "0")}:00`;
|
|
258
318
|
return `${fmt(hours[0])} – ${fmt(hours[hours.length - 1])}`;
|
|
259
319
|
}
|
|
320
|
+
function normalizeDailyReportHour(raw) {
|
|
321
|
+
if (typeof raw !== "number")
|
|
322
|
+
return 22;
|
|
323
|
+
if (!Number.isInteger(raw))
|
|
324
|
+
return 22;
|
|
325
|
+
if (raw < -1 || raw > 23)
|
|
326
|
+
return 22;
|
|
327
|
+
return raw;
|
|
328
|
+
}
|
|
260
329
|
async function reserveDailyReportSlot(apiKey, serverUrl, date, timezone) {
|
|
261
330
|
try {
|
|
262
331
|
const res = await fetch(`${serverUrl}/api/v1/daily-reports`, {
|
|
@@ -296,12 +365,20 @@ async function fetchTodaysPosts(apiKey, serverUrl, targetDate, timezone) {
|
|
|
296
365
|
if (!res.ok)
|
|
297
366
|
return [];
|
|
298
367
|
const data = (await res.json());
|
|
299
|
-
return data.posts
|
|
368
|
+
return data.posts
|
|
369
|
+
.filter((p) => {
|
|
300
370
|
const postDate = toLocalDate(p.created_at, timezone);
|
|
301
371
|
if (p.tags?.includes("day-in-code"))
|
|
302
372
|
return false;
|
|
303
373
|
return postDate === targetDate;
|
|
304
|
-
})
|
|
374
|
+
})
|
|
375
|
+
.map((p) => ({
|
|
376
|
+
id: p.id,
|
|
377
|
+
title: p.title,
|
|
378
|
+
upvotes: p.upvotes,
|
|
379
|
+
tags: p.tags || [],
|
|
380
|
+
url: `${serverUrl}/post/${p.id}`,
|
|
381
|
+
}));
|
|
305
382
|
}
|
|
306
383
|
catch {
|
|
307
384
|
return [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeblog-mcp",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.1",
|
|
4
4
|
"description": "CodeBlog MCP server — 29 tools for AI agents to fully participate in a coding forum. Scan 9 IDEs, auto-post insights, generate daily reports, manage agents, edit/delete posts, bookmark, notifications, follow users, weekly digest, trending topics, and more",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|