codeblog-mcp 2.8.2 → 2.9.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.
- package/dist/lib/auth-guard.js +6 -6
- package/dist/lib/config.d.ts +9 -6
- package/dist/lib/config.js +19 -6
- package/dist/tools/agents.js +2 -2
- package/dist/tools/daily-report.js +3 -4
- package/dist/tools/setup.js +10 -8
- package/package.json +1 -1
- package/dist/lib/client-config.d.ts +0 -3
- package/dist/lib/client-config.js +0 -44
package/dist/lib/auth-guard.js
CHANGED
|
@@ -42,7 +42,7 @@ function meIsInAgentList(meAgentId, agents) {
|
|
|
42
42
|
return agents.some((agent) => agent.id === meAgentId);
|
|
43
43
|
}
|
|
44
44
|
function clearPollutedConfigAndBlock() {
|
|
45
|
-
saveConfig({ apiKey: undefined, userId: undefined, activeAgent: undefined });
|
|
45
|
+
saveConfig({ auth: { apiKey: undefined, userId: undefined, activeAgent: undefined } });
|
|
46
46
|
return {
|
|
47
47
|
content: [text(`Security alert: Your CodeBlog API key appears polluted and is resolving to an agent ` +
|
|
48
48
|
`that does not belong to your account. We cleared local config to protect your identity.\n\n` +
|
|
@@ -60,7 +60,7 @@ async function verifyIdentity(apiKey, serverUrl) {
|
|
|
60
60
|
return null;
|
|
61
61
|
identityVerified = true;
|
|
62
62
|
const config = loadConfig();
|
|
63
|
-
if (!config.userId) {
|
|
63
|
+
if (!config.auth?.userId) {
|
|
64
64
|
// Legacy config without userId — backfill it on first run
|
|
65
65
|
try {
|
|
66
66
|
const res = await fetch(`${serverUrl}/api/v1/agents/me`, {
|
|
@@ -77,7 +77,7 @@ async function verifyIdentity(apiKey, serverUrl) {
|
|
|
77
77
|
return clearPollutedConfigAndBlock();
|
|
78
78
|
}
|
|
79
79
|
if (remoteUserId) {
|
|
80
|
-
saveConfig({ userId: remoteUserId });
|
|
80
|
+
saveConfig({ auth: { userId: remoteUserId } });
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
}
|
|
@@ -94,7 +94,7 @@ async function verifyIdentity(apiKey, serverUrl) {
|
|
|
94
94
|
});
|
|
95
95
|
if (!res.ok) {
|
|
96
96
|
// API key invalid — clear config
|
|
97
|
-
saveConfig({ apiKey: undefined, userId: undefined, activeAgent: undefined });
|
|
97
|
+
saveConfig({ auth: { apiKey: undefined, userId: undefined, activeAgent: undefined } });
|
|
98
98
|
return {
|
|
99
99
|
content: [text(`Your API key is invalid or expired. Please run codeblog_setup again.\n\n` +
|
|
100
100
|
SETUP_GUIDE)],
|
|
@@ -109,9 +109,9 @@ async function verifyIdentity(apiKey, serverUrl) {
|
|
|
109
109
|
if (meInList === false) {
|
|
110
110
|
return clearPollutedConfigAndBlock();
|
|
111
111
|
}
|
|
112
|
-
if (remoteUserId && remoteUserId !== config.userId) {
|
|
112
|
+
if (remoteUserId && remoteUserId !== config.auth?.userId) {
|
|
113
113
|
// IDENTITY MISMATCH — config was polluted by another user's API key
|
|
114
|
-
saveConfig({ apiKey: undefined, userId: undefined, activeAgent: undefined });
|
|
114
|
+
saveConfig({ auth: { apiKey: undefined, userId: undefined, activeAgent: undefined } });
|
|
115
115
|
return {
|
|
116
116
|
content: [text(`Security alert: Your CodeBlog config was using a different user's API key. ` +
|
|
117
117
|
`The config has been cleared for your protection.\n\n` +
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
export declare const CONFIG_DIR: string;
|
|
2
2
|
export declare const CONFIG_FILE: string;
|
|
3
|
-
export interface
|
|
3
|
+
export interface AuthConfig {
|
|
4
4
|
apiKey?: string;
|
|
5
|
-
userId?: string;
|
|
6
|
-
url?: string;
|
|
7
|
-
defaultLanguage?: string;
|
|
8
5
|
activeAgent?: string;
|
|
6
|
+
userId?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface CodeblogConfig {
|
|
9
|
+
serverUrl?: string;
|
|
10
|
+
dailyReportHour?: number;
|
|
11
|
+
auth?: AuthConfig;
|
|
12
|
+
cli?: Record<string, unknown>;
|
|
9
13
|
}
|
|
10
14
|
export declare function loadConfig(): CodeblogConfig;
|
|
11
|
-
export declare function saveConfig(config: CodeblogConfig): void;
|
|
15
|
+
export declare function saveConfig(config: Partial<CodeblogConfig>): void;
|
|
12
16
|
export declare function getApiKey(): string;
|
|
13
17
|
export declare function getUrl(): string;
|
|
14
|
-
export declare function getLanguage(): string | undefined;
|
|
15
18
|
export declare const SETUP_GUIDE: string;
|
|
16
19
|
export declare const text: (t: string) => {
|
|
17
20
|
type: "text";
|
package/dist/lib/config.js
CHANGED
|
@@ -13,22 +13,35 @@ export function loadConfig() {
|
|
|
13
13
|
catch { }
|
|
14
14
|
return {};
|
|
15
15
|
}
|
|
16
|
+
function deepMerge(target, source) {
|
|
17
|
+
const result = { ...target };
|
|
18
|
+
for (const key of Object.keys(source)) {
|
|
19
|
+
const val = source[key];
|
|
20
|
+
if (val === undefined) {
|
|
21
|
+
delete result[key];
|
|
22
|
+
}
|
|
23
|
+
else if (typeof val === "object" && !Array.isArray(val) && val !== null) {
|
|
24
|
+
result[key] = deepMerge(result[key] || {}, val);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
result[key] = val;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
16
32
|
export function saveConfig(config) {
|
|
17
33
|
if (!fs.existsSync(CONFIG_DIR)) {
|
|
18
34
|
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
19
35
|
}
|
|
20
36
|
const existing = loadConfig();
|
|
21
|
-
const merged =
|
|
37
|
+
const merged = deepMerge(existing, config);
|
|
22
38
|
fs.writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2));
|
|
23
39
|
}
|
|
24
40
|
export function getApiKey() {
|
|
25
|
-
return process.env.CODEBLOG_API_KEY || loadConfig().apiKey || "";
|
|
41
|
+
return process.env.CODEBLOG_API_KEY || loadConfig().auth?.apiKey || "";
|
|
26
42
|
}
|
|
27
43
|
export function getUrl() {
|
|
28
|
-
return process.env.CODEBLOG_URL || loadConfig().
|
|
29
|
-
}
|
|
30
|
-
export function getLanguage() {
|
|
31
|
-
return process.env.CODEBLOG_LANGUAGE || loadConfig().defaultLanguage;
|
|
44
|
+
return process.env.CODEBLOG_URL || loadConfig().serverUrl || "https://codeblog.ai";
|
|
32
45
|
}
|
|
33
46
|
export const SETUP_GUIDE = `CodeBlog is not set up yet. To get started, run the codeblog_setup tool.\n\n` +
|
|
34
47
|
`• New user: provide email + username + password → codeblog_setup(email, username, password)\n` +
|
package/dist/tools/agents.js
CHANGED
|
@@ -55,7 +55,7 @@ export function registerAgentTools(server) {
|
|
|
55
55
|
let output = `## Your Agents (${agents.length})\n\n`;
|
|
56
56
|
const config = loadConfig();
|
|
57
57
|
for (const a of agents) {
|
|
58
|
-
const isCurrent = a.is_current || a.name === config.activeAgent;
|
|
58
|
+
const isCurrent = a.is_current || a.name === config.auth?.activeAgent;
|
|
59
59
|
const marker = isCurrent ? " ← current" : "";
|
|
60
60
|
output += `- **${a.name}** (${a.source_type})${marker}\n`;
|
|
61
61
|
output += ` ID: \`${a.id}\` | Posts: ${a.posts_count} | Created: ${a.created_at}\n`;
|
|
@@ -174,7 +174,7 @@ export function registerAgentTools(server) {
|
|
|
174
174
|
const data = await res.json();
|
|
175
175
|
const target = data.agent;
|
|
176
176
|
// Save the target agent's API key and name to config
|
|
177
|
-
saveConfig({ apiKey: target.api_key, activeAgent: target.name });
|
|
177
|
+
saveConfig({ auth: { apiKey: target.api_key, activeAgent: target.name } });
|
|
178
178
|
return {
|
|
179
179
|
content: [
|
|
180
180
|
text(`✅ Switched to agent **${target.name}** (${target.source_type})!\n\n` +
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { text } from "../lib/config.js";
|
|
3
|
-
import { loadClientConfig, saveClientConfig } from "../lib/client-config.js";
|
|
2
|
+
import { text, loadConfig, saveConfig } from "../lib/config.js";
|
|
4
3
|
import { withAuth } from "../lib/auth-guard.js";
|
|
5
4
|
import { collectDailyUsage, formatTokens, formatCost, } from "../lib/usage-collector.js";
|
|
6
5
|
// ─── Tool registration ───────────────────────────────────────────────
|
|
@@ -271,7 +270,7 @@ export function registerDailyReportTools(server) {
|
|
|
271
270
|
},
|
|
272
271
|
}, async ({ auto_hour, get }) => {
|
|
273
272
|
if (get) {
|
|
274
|
-
const cfg =
|
|
273
|
+
const cfg = loadConfig();
|
|
275
274
|
const hour = normalizeDailyReportHour(cfg.dailyReportHour);
|
|
276
275
|
const enabled = hour >= 0;
|
|
277
276
|
return {
|
|
@@ -293,7 +292,7 @@ export function registerDailyReportTools(server) {
|
|
|
293
292
|
],
|
|
294
293
|
};
|
|
295
294
|
}
|
|
296
|
-
|
|
295
|
+
saveConfig({ dailyReportHour: auto_hour });
|
|
297
296
|
const enabled = auto_hour >= 0;
|
|
298
297
|
return {
|
|
299
298
|
content: [
|
package/dist/tools/setup.js
CHANGED
|
@@ -35,9 +35,11 @@ export function registerSetupTools(server, PKG_VERSION) {
|
|
|
35
35
|
}
|
|
36
36
|
const data = await res.json();
|
|
37
37
|
const resolvedUserId = data.agent?.userId || data.userId;
|
|
38
|
-
const config = {
|
|
38
|
+
const config = {
|
|
39
|
+
auth: { apiKey: result.api_key, activeAgent: data.agent.name, userId: resolvedUserId },
|
|
40
|
+
};
|
|
39
41
|
if (url)
|
|
40
|
-
config.
|
|
42
|
+
config.serverUrl = url;
|
|
41
43
|
saveConfig(config);
|
|
42
44
|
// Check if user has multiple agents
|
|
43
45
|
let multiAgentNote = "";
|
|
@@ -105,12 +107,10 @@ export function registerSetupTools(server, PKG_VERSION) {
|
|
|
105
107
|
// Use the first activated agent
|
|
106
108
|
const agent = data.agents[0];
|
|
107
109
|
const config = {
|
|
108
|
-
apiKey: agent.api_key,
|
|
109
|
-
activeAgent: agent.name,
|
|
110
|
-
userId: data.user.id,
|
|
110
|
+
auth: { apiKey: agent.api_key, activeAgent: agent.name, userId: data.user.id },
|
|
111
111
|
};
|
|
112
112
|
if (url)
|
|
113
|
-
config.
|
|
113
|
+
config.serverUrl = url;
|
|
114
114
|
saveConfig(config);
|
|
115
115
|
const agentList = data.agents.map((a) => ` • ${a.name} (${a.posts_count} posts)`).join("\n");
|
|
116
116
|
const multiAgentPrompt = data.agents.length > 1
|
|
@@ -154,9 +154,11 @@ export function registerSetupTools(server, PKG_VERSION) {
|
|
|
154
154
|
if (!res.ok) {
|
|
155
155
|
return { content: [text(`Setup failed: ${data.error || "Unknown error"}`)], isError: true };
|
|
156
156
|
}
|
|
157
|
-
const config = {
|
|
157
|
+
const config = {
|
|
158
|
+
auth: { apiKey: data.agent.api_key, activeAgent: data.agent.name, userId: data.user.id },
|
|
159
|
+
};
|
|
158
160
|
if (url)
|
|
159
|
-
config.
|
|
161
|
+
config.serverUrl = url;
|
|
160
162
|
saveConfig(config);
|
|
161
163
|
return {
|
|
162
164
|
content: [text(`✅ CodeBlog setup complete!\n\n` +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeblog-mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0",
|
|
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": {
|
|
@@ -1,44 +0,0 @@
|
|
|
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
|
-
}
|