@claudeink/mcp-server 0.0.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.
- package/LICENSE +21 -0
- package/README.md +102 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +98 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1232 -0
- package/package.json +61 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1232 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
|
|
7
|
+
// src/tools/auth.ts
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
|
|
10
|
+
// src/lib/config.ts
|
|
11
|
+
import { readFile, writeFile, mkdir, chmod, access } from "fs/promises";
|
|
12
|
+
import { join } from "path";
|
|
13
|
+
import { homedir } from "os";
|
|
14
|
+
var CLAUDEINK_DIR = join(homedir(), ".claudeink");
|
|
15
|
+
var PATHS = {
|
|
16
|
+
credentials: join(CLAUDEINK_DIR, "credentials.json"),
|
|
17
|
+
config: join(CLAUDEINK_DIR, "config.json"),
|
|
18
|
+
syncState: join(CLAUDEINK_DIR, "sync-state.json"),
|
|
19
|
+
tagQueue: join(CLAUDEINK_DIR, "tag-queue.json"),
|
|
20
|
+
crawlSchedules: join(CLAUDEINK_DIR, "crawl-schedules.json"),
|
|
21
|
+
logs: join(CLAUDEINK_DIR, "logs")
|
|
22
|
+
};
|
|
23
|
+
var DEFAULT_CONFIG = {
|
|
24
|
+
apiBaseUrl: "https://api.claudeink.app",
|
|
25
|
+
syncIntervalMs: 3e5,
|
|
26
|
+
// 5 minutes
|
|
27
|
+
heartbeatIntervalMs: 3e5,
|
|
28
|
+
maxTagQueueBatch: 20,
|
|
29
|
+
workflowDir: ""
|
|
30
|
+
};
|
|
31
|
+
var DEFAULT_SYNC_STATE = {
|
|
32
|
+
lastSyncAt: null,
|
|
33
|
+
configVersion: "0.0.0",
|
|
34
|
+
pendingPushCount: 0
|
|
35
|
+
};
|
|
36
|
+
var DEFAULT_TAG_QUEUE = {
|
|
37
|
+
queue: [],
|
|
38
|
+
failed: [],
|
|
39
|
+
history: {
|
|
40
|
+
lastProcessed: null,
|
|
41
|
+
totalProcessed: 0,
|
|
42
|
+
avgTagsPerItem: 0
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
var DEFAULT_CRAWL_SCHEDULES = {
|
|
46
|
+
schedules: []
|
|
47
|
+
};
|
|
48
|
+
async function ensureDir() {
|
|
49
|
+
await mkdir(CLAUDEINK_DIR, { recursive: true });
|
|
50
|
+
await mkdir(PATHS.logs, { recursive: true });
|
|
51
|
+
}
|
|
52
|
+
async function fileExists(path) {
|
|
53
|
+
try {
|
|
54
|
+
await access(path);
|
|
55
|
+
return true;
|
|
56
|
+
} catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async function readJson(path, defaultValue) {
|
|
61
|
+
try {
|
|
62
|
+
const content = await readFile(path, "utf-8");
|
|
63
|
+
return JSON.parse(content);
|
|
64
|
+
} catch {
|
|
65
|
+
return defaultValue;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function writeJson(path, data, secure = false) {
|
|
69
|
+
await ensureDir();
|
|
70
|
+
await writeFile(path, JSON.stringify(data, null, 2), "utf-8");
|
|
71
|
+
if (secure) {
|
|
72
|
+
await chmod(path, 384);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function getCredentials() {
|
|
76
|
+
if (!await fileExists(PATHS.credentials)) return null;
|
|
77
|
+
return readJson(PATHS.credentials, null);
|
|
78
|
+
}
|
|
79
|
+
async function saveCredentials(creds) {
|
|
80
|
+
await writeJson(PATHS.credentials, creds, true);
|
|
81
|
+
}
|
|
82
|
+
async function getConfig() {
|
|
83
|
+
return readJson(PATHS.config, DEFAULT_CONFIG);
|
|
84
|
+
}
|
|
85
|
+
async function saveConfig(config) {
|
|
86
|
+
const current = await getConfig();
|
|
87
|
+
await writeJson(PATHS.config, { ...current, ...config });
|
|
88
|
+
}
|
|
89
|
+
async function getSyncState() {
|
|
90
|
+
return readJson(PATHS.syncState, DEFAULT_SYNC_STATE);
|
|
91
|
+
}
|
|
92
|
+
async function saveSyncState(state) {
|
|
93
|
+
const current = await getSyncState();
|
|
94
|
+
await writeJson(PATHS.syncState, { ...current, ...state });
|
|
95
|
+
}
|
|
96
|
+
async function getTagQueue() {
|
|
97
|
+
return readJson(PATHS.tagQueue, DEFAULT_TAG_QUEUE);
|
|
98
|
+
}
|
|
99
|
+
async function saveTagQueue(queue) {
|
|
100
|
+
await writeJson(PATHS.tagQueue, queue);
|
|
101
|
+
}
|
|
102
|
+
async function getCrawlSchedules() {
|
|
103
|
+
return readJson(PATHS.crawlSchedules, DEFAULT_CRAWL_SCHEDULES);
|
|
104
|
+
}
|
|
105
|
+
async function saveCrawlSchedules(schedules) {
|
|
106
|
+
await writeJson(PATHS.crawlSchedules, schedules);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/lib/api-client.ts
|
|
110
|
+
async function request(path, options = {}) {
|
|
111
|
+
const config = await getConfig();
|
|
112
|
+
const url = `${config.apiBaseUrl}${path}`;
|
|
113
|
+
try {
|
|
114
|
+
const headers = {
|
|
115
|
+
"Content-Type": "application/json",
|
|
116
|
+
"User-Agent": "ClaudeInk-MCP/0.1.0"
|
|
117
|
+
};
|
|
118
|
+
if (options.token) {
|
|
119
|
+
headers["Authorization"] = `Bearer ${options.token}`;
|
|
120
|
+
} else {
|
|
121
|
+
const creds = await getCredentials();
|
|
122
|
+
if (creds?.token) {
|
|
123
|
+
headers["Authorization"] = `Bearer ${creds.token}`;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const res = await fetch(url, {
|
|
127
|
+
method: options.method || "GET",
|
|
128
|
+
headers,
|
|
129
|
+
body: options.body ? JSON.stringify(options.body) : void 0
|
|
130
|
+
});
|
|
131
|
+
const data = await res.json().catch(() => null);
|
|
132
|
+
return {
|
|
133
|
+
ok: res.ok,
|
|
134
|
+
status: res.status,
|
|
135
|
+
data: data ?? void 0,
|
|
136
|
+
error: res.ok ? void 0 : `HTTP ${res.status}`
|
|
137
|
+
};
|
|
138
|
+
} catch (err) {
|
|
139
|
+
return {
|
|
140
|
+
ok: false,
|
|
141
|
+
status: 0,
|
|
142
|
+
error: err instanceof Error ? err.message : "Unknown error"
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async function activateLicense(key) {
|
|
147
|
+
return request("/api/auth/activate", {
|
|
148
|
+
method: "POST",
|
|
149
|
+
body: { key }
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
async function fetchLatestConfig(localVersion) {
|
|
153
|
+
return request(
|
|
154
|
+
`/api/config/latest?version=${localVersion}`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
async function pushSyncBatch(payload) {
|
|
158
|
+
return request("/api/sync/batch", {
|
|
159
|
+
method: "POST",
|
|
160
|
+
body: payload
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
async function checkAuthStatus() {
|
|
164
|
+
return request(
|
|
165
|
+
"/api/auth/status"
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// src/tools/auth.ts
|
|
170
|
+
import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
171
|
+
import { join as join2 } from "path";
|
|
172
|
+
var activateSchema = z.object({
|
|
173
|
+
key: z.string().describe("License key, e.g. WF-XXXX-XXXX-XXXX"),
|
|
174
|
+
workflowDir: z.string().describe("\u5DE5\u4F5C\u6D41\u6839\u76EE\u5F55\u8DEF\u5F84")
|
|
175
|
+
});
|
|
176
|
+
async function authActivate(input) {
|
|
177
|
+
const res = await activateLicense(input.key);
|
|
178
|
+
if (!res.ok || !res.data) {
|
|
179
|
+
return {
|
|
180
|
+
success: false,
|
|
181
|
+
message: `License key \u9A8C\u8BC1\u5931\u8D25: ${res.error || "\u672A\u77E5\u9519\u8BEF"}`
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
await saveCredentials({
|
|
185
|
+
licenseKey: input.key,
|
|
186
|
+
token: res.data.token,
|
|
187
|
+
userId: res.data.userId,
|
|
188
|
+
plan: res.data.plan,
|
|
189
|
+
expiresAt: res.data.expiresAt
|
|
190
|
+
});
|
|
191
|
+
await saveConfig({ workflowDir: input.workflowDir });
|
|
192
|
+
const configRes = await fetchLatestConfig("0.0.0");
|
|
193
|
+
if (configRes.ok && configRes.data) {
|
|
194
|
+
for (const [filePath, content] of Object.entries(configRes.data.files)) {
|
|
195
|
+
const fullPath = join2(input.workflowDir, filePath);
|
|
196
|
+
const dir = fullPath.substring(0, fullPath.lastIndexOf("/"));
|
|
197
|
+
await mkdir2(dir, { recursive: true });
|
|
198
|
+
await writeFile2(fullPath, content, "utf-8");
|
|
199
|
+
}
|
|
200
|
+
await saveSyncState({ configVersion: configRes.data.version });
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
success: true,
|
|
204
|
+
message: `\u6FC0\u6D3B\u6210\u529F\uFF01\u5957\u9910: ${res.data.plan}\uFF0C\u5230\u671F: ${res.data.expiresAt}`,
|
|
205
|
+
data: {
|
|
206
|
+
plan: res.data.plan,
|
|
207
|
+
expiresAt: res.data.expiresAt,
|
|
208
|
+
configVersion: configRes.data?.version || "unknown"
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
async function authStatus() {
|
|
213
|
+
const creds = await getCredentials();
|
|
214
|
+
if (!creds) {
|
|
215
|
+
return {
|
|
216
|
+
success: false,
|
|
217
|
+
message: "\u672A\u6FC0\u6D3B\u3002\u8BF7\u5148\u4F7F\u7528 auth.activate \u6FC0\u6D3B license key\u3002"
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
const res = await checkAuthStatus();
|
|
221
|
+
if (res.ok && res.data) {
|
|
222
|
+
return {
|
|
223
|
+
success: true,
|
|
224
|
+
message: `\u5DF2\u6FC0\u6D3B\u3002\u5957\u9910: ${res.data.plan}\uFF0C\u5230\u671F: ${res.data.expiresAt}\uFF0C\u72B6\u6001: ${res.data.valid ? "\u6709\u6548" : "\u5DF2\u8FC7\u671F"}`,
|
|
225
|
+
data: res.data
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
const expired = new Date(creds.expiresAt) < /* @__PURE__ */ new Date();
|
|
229
|
+
return {
|
|
230
|
+
success: true,
|
|
231
|
+
message: `\u5DF2\u6FC0\u6D3B\uFF08\u79BB\u7EBF\u6A21\u5F0F\uFF09\u3002\u5957\u9910: ${creds.plan}\uFF0C\u5230\u671F: ${creds.expiresAt}${expired ? "\uFF08\u5DF2\u8FC7\u671F\uFF09" : ""}`,
|
|
232
|
+
data: {
|
|
233
|
+
plan: creds.plan,
|
|
234
|
+
expiresAt: creds.expiresAt,
|
|
235
|
+
valid: !expired,
|
|
236
|
+
offline: true
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
async function configSync() {
|
|
241
|
+
const syncState = await getSyncState();
|
|
242
|
+
const res = await fetchLatestConfig(syncState.configVersion);
|
|
243
|
+
if (!res.ok || !res.data) {
|
|
244
|
+
return {
|
|
245
|
+
success: false,
|
|
246
|
+
message: `\u914D\u7F6E\u540C\u6B65\u5931\u8D25: ${res.error || "\u65E0\u6CD5\u8FDE\u63A5\u4E91\u7AEF"}`
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
if (res.data.version === syncState.configVersion) {
|
|
250
|
+
return {
|
|
251
|
+
success: true,
|
|
252
|
+
message: `\u914D\u7F6E\u5DF2\u662F\u6700\u65B0\u7248\u672C (${syncState.configVersion})`
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
const config = await getConfig();
|
|
256
|
+
for (const [filePath, content] of Object.entries(res.data.files)) {
|
|
257
|
+
const fullPath = join2(config.workflowDir, filePath);
|
|
258
|
+
const dir = fullPath.substring(0, fullPath.lastIndexOf("/"));
|
|
259
|
+
await mkdir2(dir, { recursive: true });
|
|
260
|
+
await writeFile2(fullPath, content, "utf-8");
|
|
261
|
+
}
|
|
262
|
+
await saveSyncState({ configVersion: res.data.version });
|
|
263
|
+
return {
|
|
264
|
+
success: true,
|
|
265
|
+
message: `\u914D\u7F6E\u5DF2\u66F4\u65B0: ${syncState.configVersion} \u2192 ${res.data.version}
|
|
266
|
+
\u53D8\u66F4: ${res.data.changelog}`,
|
|
267
|
+
data: { version: res.data.version, changelog: res.data.changelog }
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
async function configVersion() {
|
|
271
|
+
const syncState = await getSyncState();
|
|
272
|
+
return {
|
|
273
|
+
success: true,
|
|
274
|
+
message: `\u5F53\u524D\u672C\u5730\u914D\u7F6E\u7248\u672C: ${syncState.configVersion}`,
|
|
275
|
+
data: { version: syncState.configVersion }
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// src/tools/source.ts
|
|
280
|
+
import { z as z2 } from "zod";
|
|
281
|
+
import { mkdir as mkdir3 } from "fs/promises";
|
|
282
|
+
import { join as join4 } from "path";
|
|
283
|
+
|
|
284
|
+
// src/lib/sources.ts
|
|
285
|
+
import { readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
|
|
286
|
+
import { join as join3, relative } from "path";
|
|
287
|
+
import matter from "gray-matter";
|
|
288
|
+
import { glob } from "glob";
|
|
289
|
+
async function readSourceFile(filePath) {
|
|
290
|
+
const raw = await readFile2(filePath, "utf-8");
|
|
291
|
+
const { data, content } = matter(raw);
|
|
292
|
+
const config = await getConfig();
|
|
293
|
+
return {
|
|
294
|
+
filePath,
|
|
295
|
+
relativePath: relative(config.workflowDir, filePath),
|
|
296
|
+
meta: data,
|
|
297
|
+
content: content.trim()
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
async function writeSourceFile(filePath, meta, content) {
|
|
301
|
+
const output = matter.stringify(content, meta);
|
|
302
|
+
await writeFile3(filePath, output, "utf-8");
|
|
303
|
+
}
|
|
304
|
+
async function updateSourceTags(filePath, tags) {
|
|
305
|
+
const source = await readSourceFile(filePath);
|
|
306
|
+
source.meta.tags = tags;
|
|
307
|
+
source.meta.taggedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
308
|
+
source.meta.taggedBy = "local-claude";
|
|
309
|
+
await writeSourceFile(filePath, source.meta, source.content);
|
|
310
|
+
}
|
|
311
|
+
async function listSources(options) {
|
|
312
|
+
const config = await getConfig();
|
|
313
|
+
const baseDir = options.folder ? join3(config.workflowDir, "sources", options.folder) : join3(config.workflowDir, "sources");
|
|
314
|
+
const files = await glob("**/*.md", { cwd: baseDir, absolute: true });
|
|
315
|
+
const sources = [];
|
|
316
|
+
for (const file of files) {
|
|
317
|
+
try {
|
|
318
|
+
const source = await readSourceFile(file);
|
|
319
|
+
if (options.tagged === true && (!source.meta.tags || source.meta.tags.length === 0)) {
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
if (options.tagged === false && source.meta.tags && source.meta.tags.length > 0) {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
sources.push(source);
|
|
326
|
+
} catch {
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
sources.sort(
|
|
331
|
+
(a, b) => new Date(b.meta.published || 0).getTime() - new Date(a.meta.published || 0).getTime()
|
|
332
|
+
);
|
|
333
|
+
if (options.limit && options.limit > 0) {
|
|
334
|
+
return sources.slice(0, options.limit);
|
|
335
|
+
}
|
|
336
|
+
return sources;
|
|
337
|
+
}
|
|
338
|
+
async function searchSources(options) {
|
|
339
|
+
const all = await listSources({ tagged: true });
|
|
340
|
+
let results = all;
|
|
341
|
+
if (options.tags && options.tags.length > 0) {
|
|
342
|
+
results = results.filter(
|
|
343
|
+
(s) => options.tags.some((tag) => s.meta.tags?.includes(tag))
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
if (options.dateFrom) {
|
|
347
|
+
const from = new Date(options.dateFrom).getTime();
|
|
348
|
+
results = results.filter(
|
|
349
|
+
(s) => new Date(s.meta.published || 0).getTime() >= from
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
if (options.dateTo) {
|
|
353
|
+
const to = new Date(options.dateTo).getTime();
|
|
354
|
+
results = results.filter(
|
|
355
|
+
(s) => new Date(s.meta.published || 0).getTime() <= to
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
if (options.query) {
|
|
359
|
+
const q = options.query.toLowerCase();
|
|
360
|
+
results = results.filter(
|
|
361
|
+
(s) => s.meta.title.toLowerCase().includes(q) || s.content.toLowerCase().includes(q)
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
if (options.limit && options.limit > 0) {
|
|
365
|
+
return results.slice(0, options.limit);
|
|
366
|
+
}
|
|
367
|
+
return results;
|
|
368
|
+
}
|
|
369
|
+
async function addToTagQueue(file, title, source) {
|
|
370
|
+
const queue = await getTagQueue();
|
|
371
|
+
if (queue.queue.some((item) => item.file === file)) return;
|
|
372
|
+
queue.queue.push({
|
|
373
|
+
file,
|
|
374
|
+
title,
|
|
375
|
+
addedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
376
|
+
source
|
|
377
|
+
});
|
|
378
|
+
queue.queue.sort((a, b) => {
|
|
379
|
+
if (a.source === "manual" && b.source === "crawl") return -1;
|
|
380
|
+
if (a.source === "crawl" && b.source === "manual") return 1;
|
|
381
|
+
return new Date(a.addedAt).getTime() - new Date(b.addedAt).getTime();
|
|
382
|
+
});
|
|
383
|
+
await saveTagQueue(queue);
|
|
384
|
+
}
|
|
385
|
+
async function getNextTagBatch() {
|
|
386
|
+
const config = await getConfig();
|
|
387
|
+
const queue = await getTagQueue();
|
|
388
|
+
return queue.queue.slice(0, config.maxTagQueueBatch);
|
|
389
|
+
}
|
|
390
|
+
async function markTagged(file, tagsCount) {
|
|
391
|
+
const queue = await getTagQueue();
|
|
392
|
+
queue.queue = queue.queue.filter((item) => item.file !== file);
|
|
393
|
+
const totalBefore = queue.history.totalProcessed;
|
|
394
|
+
const avgBefore = queue.history.avgTagsPerItem;
|
|
395
|
+
queue.history.totalProcessed = totalBefore + 1;
|
|
396
|
+
queue.history.avgTagsPerItem = (avgBefore * totalBefore + tagsCount) / (totalBefore + 1);
|
|
397
|
+
queue.history.lastProcessed = (/* @__PURE__ */ new Date()).toISOString();
|
|
398
|
+
await saveTagQueue(queue);
|
|
399
|
+
}
|
|
400
|
+
async function markTagFailed(file) {
|
|
401
|
+
const queue = await getTagQueue();
|
|
402
|
+
const idx = queue.queue.findIndex((item2) => item2.file === file);
|
|
403
|
+
if (idx === -1) return;
|
|
404
|
+
const item = queue.queue[idx];
|
|
405
|
+
item.retryCount = (item.retryCount || 0) + 1;
|
|
406
|
+
if (item.retryCount >= 3) {
|
|
407
|
+
queue.queue.splice(idx, 1);
|
|
408
|
+
queue.failed.push(item);
|
|
409
|
+
}
|
|
410
|
+
await saveTagQueue(queue);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// src/tools/source.ts
|
|
414
|
+
var sourceAddSchema = z2.object({
|
|
415
|
+
title: z2.string().describe("\u7D20\u6750\u6807\u9898"),
|
|
416
|
+
content: z2.string().describe("\u7D20\u6750\u6B63\u6587"),
|
|
417
|
+
url: z2.string().optional().describe("\u6765\u6E90 URL"),
|
|
418
|
+
tags: z2.array(z2.string()).optional().describe("\u6807\u7B7E\uFF08\u5982\u4E0D\u63D0\u4F9B\u5219\u52A0\u5165\u6807\u7B7E\u961F\u5217\u7531 Claude \u540E\u7EED\u6253\u6807\u7B7E\uFF09"),
|
|
419
|
+
folder: z2.string().optional().describe("\u5B58\u653E\u5B50\u76EE\u5F55\uFF0C\u5982 articles/techcrunch")
|
|
420
|
+
});
|
|
421
|
+
async function sourceAdd(input) {
|
|
422
|
+
const config = await getConfig();
|
|
423
|
+
const folder = input.folder || "articles";
|
|
424
|
+
const dir = join4(config.workflowDir, "sources", folder);
|
|
425
|
+
await mkdir3(dir, { recursive: true });
|
|
426
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
427
|
+
const slug = input.title.replace(/[^\u4e00-\u9fa5a-zA-Z0-9\s-]/g, "").replace(/\s+/g, "-").slice(0, 50);
|
|
428
|
+
const filename = `${date}-${slug}.md`;
|
|
429
|
+
const filePath = join4(dir, filename);
|
|
430
|
+
const meta = {
|
|
431
|
+
title: input.title,
|
|
432
|
+
source: input.url ? new URL(input.url).hostname : "manual",
|
|
433
|
+
published: date,
|
|
434
|
+
url: input.url
|
|
435
|
+
};
|
|
436
|
+
if (input.tags && input.tags.length > 0) {
|
|
437
|
+
meta.tags = input.tags;
|
|
438
|
+
meta.taggedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
439
|
+
meta.taggedBy = "local-claude";
|
|
440
|
+
await writeSourceFile(filePath, meta, input.content);
|
|
441
|
+
return {
|
|
442
|
+
success: true,
|
|
443
|
+
message: `\u7D20\u6750\u5DF2\u5165\u5E93: ${filename}\uFF08\u542B ${input.tags.length} \u4E2A\u6807\u7B7E\uFF09`,
|
|
444
|
+
data: { file: filePath, tags: input.tags }
|
|
445
|
+
};
|
|
446
|
+
} else {
|
|
447
|
+
await writeSourceFile(filePath, meta, input.content);
|
|
448
|
+
const relativePath = `sources/${folder}/${filename}`;
|
|
449
|
+
await addToTagQueue(relativePath, input.title, "manual");
|
|
450
|
+
return {
|
|
451
|
+
success: true,
|
|
452
|
+
message: `\u7D20\u6750\u5DF2\u5165\u5E93: ${filename}\uFF08\u5DF2\u52A0\u5165\u6807\u7B7E\u961F\u5217\uFF0C\u7B49\u5F85 Claude \u6253\u6807\u7B7E\uFF09`,
|
|
453
|
+
data: { file: filePath, queued: true }
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
var sourceSearchSchema = z2.object({
|
|
458
|
+
query: z2.string().optional().describe("\u5173\u952E\u8BCD\u641C\u7D22"),
|
|
459
|
+
tags: z2.array(z2.string()).optional().describe("\u6309\u6807\u7B7E\u7B5B\u9009"),
|
|
460
|
+
dateFrom: z2.string().optional().describe("\u8D77\u59CB\u65E5\u671F YYYY-MM-DD"),
|
|
461
|
+
dateTo: z2.string().optional().describe("\u622A\u6B62\u65E5\u671F YYYY-MM-DD"),
|
|
462
|
+
limit: z2.number().optional().describe("\u8FD4\u56DE\u6570\u91CF\u4E0A\u9650")
|
|
463
|
+
});
|
|
464
|
+
async function sourceSearch(input) {
|
|
465
|
+
const results = await searchSources({
|
|
466
|
+
query: input.query,
|
|
467
|
+
tags: input.tags,
|
|
468
|
+
dateFrom: input.dateFrom,
|
|
469
|
+
dateTo: input.dateTo,
|
|
470
|
+
limit: input.limit
|
|
471
|
+
});
|
|
472
|
+
return {
|
|
473
|
+
success: true,
|
|
474
|
+
message: `\u627E\u5230 ${results.length} \u7BC7\u76F8\u5173\u7D20\u6750`,
|
|
475
|
+
data: results.map((s) => ({
|
|
476
|
+
file: s.relativePath,
|
|
477
|
+
title: s.meta.title,
|
|
478
|
+
tags: s.meta.tags,
|
|
479
|
+
published: s.meta.published,
|
|
480
|
+
source: s.meta.source
|
|
481
|
+
}))
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
var sourceListSchema = z2.object({
|
|
485
|
+
folder: z2.string().optional().describe("\u5B50\u76EE\u5F55"),
|
|
486
|
+
tagged: z2.boolean().optional().describe("true=\u5DF2\u6253\u6807\u7B7E, false=\u672A\u6253\u6807\u7B7E, \u4E0D\u4F20=\u5168\u90E8"),
|
|
487
|
+
limit: z2.number().optional().describe("\u8FD4\u56DE\u6570\u91CF\u4E0A\u9650")
|
|
488
|
+
});
|
|
489
|
+
async function sourceList(input) {
|
|
490
|
+
const results = await listSources({
|
|
491
|
+
folder: input.folder,
|
|
492
|
+
tagged: input.tagged,
|
|
493
|
+
limit: input.limit
|
|
494
|
+
});
|
|
495
|
+
return {
|
|
496
|
+
success: true,
|
|
497
|
+
message: `\u5171 ${results.length} \u7BC7\u7D20\u6750`,
|
|
498
|
+
data: results.map((s) => ({
|
|
499
|
+
file: s.relativePath,
|
|
500
|
+
title: s.meta.title,
|
|
501
|
+
tags: s.meta.tags || [],
|
|
502
|
+
published: s.meta.published
|
|
503
|
+
}))
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
var sourceCrawlSchema = z2.object({
|
|
507
|
+
sourceId: z2.string().optional().describe("\u6307\u5B9A\u722C\u866B\u6E90\u540D\u79F0\uFF0C\u4E0D\u4F20\u5219\u5168\u91CF")
|
|
508
|
+
});
|
|
509
|
+
async function sourceCrawl(input) {
|
|
510
|
+
const schedules = await getCrawlSchedules();
|
|
511
|
+
if (schedules.schedules.length === 0) {
|
|
512
|
+
return {
|
|
513
|
+
success: false,
|
|
514
|
+
message: "\u6682\u65E0\u914D\u7F6E\u722C\u866B\u6E90\u3002\u8BF7\u5148\u7528 source.schedule \u6DFB\u52A0\u3002"
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
const targets = input.sourceId ? schedules.schedules.filter((s) => s.name === input.sourceId) : schedules.schedules.filter((s) => s.enabled);
|
|
518
|
+
if (targets.length === 0) {
|
|
519
|
+
return {
|
|
520
|
+
success: false,
|
|
521
|
+
message: `\u672A\u627E\u5230\u722C\u866B\u6E90: ${input.sourceId}`
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
return {
|
|
525
|
+
success: true,
|
|
526
|
+
message: `\u5DF2\u89E6\u53D1 ${targets.length} \u4E2A\u722C\u866B\u6E90: ${targets.map((t) => t.name).join(", ")}`,
|
|
527
|
+
data: { triggered: targets.map((t) => t.name) }
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
var sourceScheduleSchema = z2.object({
|
|
531
|
+
action: z2.enum(["add", "remove", "list"]).describe("\u64CD\u4F5C\u7C7B\u578B"),
|
|
532
|
+
name: z2.string().optional().describe("\u722C\u866B\u6E90\u540D\u79F0"),
|
|
533
|
+
url: z2.string().optional().describe("RSS/Sitemap URL"),
|
|
534
|
+
type: z2.enum(["rss", "sitemap", "custom"]).optional().describe("\u6E90\u7C7B\u578B"),
|
|
535
|
+
cron: z2.string().optional().describe("Cron \u8868\u8FBE\u5F0F"),
|
|
536
|
+
targetFolder: z2.string().optional().describe("\u76EE\u6807\u5B58\u653E\u76EE\u5F55")
|
|
537
|
+
});
|
|
538
|
+
async function sourceSchedule(input) {
|
|
539
|
+
const schedules = await getCrawlSchedules();
|
|
540
|
+
if (input.action === "list") {
|
|
541
|
+
return {
|
|
542
|
+
success: true,
|
|
543
|
+
message: `\u5171 ${schedules.schedules.length} \u4E2A\u722C\u866B\u6E90`,
|
|
544
|
+
data: schedules.schedules
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
if (input.action === "add") {
|
|
548
|
+
if (!input.name || !input.url || !input.cron) {
|
|
549
|
+
return {
|
|
550
|
+
success: false,
|
|
551
|
+
message: "\u6DFB\u52A0\u722C\u866B\u6E90\u9700\u8981 name, url, cron \u53C2\u6570"
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
if (schedules.schedules.some((s) => s.name === input.name)) {
|
|
555
|
+
return { success: false, message: `\u722C\u866B\u6E90 ${input.name} \u5DF2\u5B58\u5728` };
|
|
556
|
+
}
|
|
557
|
+
schedules.schedules.push({
|
|
558
|
+
name: input.name,
|
|
559
|
+
url: input.url,
|
|
560
|
+
type: input.type || "rss",
|
|
561
|
+
cron: input.cron,
|
|
562
|
+
targetFolder: input.targetFolder || `sources/articles/${input.name}/`,
|
|
563
|
+
enabled: true,
|
|
564
|
+
lastRun: null
|
|
565
|
+
});
|
|
566
|
+
await saveCrawlSchedules(schedules);
|
|
567
|
+
return {
|
|
568
|
+
success: true,
|
|
569
|
+
message: `\u722C\u866B\u6E90 ${input.name} \u5DF2\u6DFB\u52A0\uFF0CCron: ${input.cron}`
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
if (input.action === "remove") {
|
|
573
|
+
if (!input.name) {
|
|
574
|
+
return { success: false, message: "\u5220\u9664\u722C\u866B\u6E90\u9700\u8981 name \u53C2\u6570" };
|
|
575
|
+
}
|
|
576
|
+
schedules.schedules = schedules.schedules.filter(
|
|
577
|
+
(s) => s.name !== input.name
|
|
578
|
+
);
|
|
579
|
+
await saveCrawlSchedules(schedules);
|
|
580
|
+
return { success: true, message: `\u722C\u866B\u6E90 ${input.name} \u5DF2\u5220\u9664` };
|
|
581
|
+
}
|
|
582
|
+
return { success: false, message: "\u672A\u77E5\u64CD\u4F5C" };
|
|
583
|
+
}
|
|
584
|
+
var sourceTagSchema = z2.object({
|
|
585
|
+
mode: z2.enum(["queue", "single"]).describe("queue=\u5904\u7406\u6807\u7B7E\u961F\u5217, single=\u5355\u4E2A\u6587\u4EF6\u6253\u6807\u7B7E"),
|
|
586
|
+
file: z2.string().optional().describe("single \u6A21\u5F0F\u9700\u8981\u6307\u5B9A\u6587\u4EF6\u8DEF\u5F84")
|
|
587
|
+
});
|
|
588
|
+
async function sourceTag(input) {
|
|
589
|
+
if (input.mode === "single") {
|
|
590
|
+
if (!input.file) {
|
|
591
|
+
return { success: false, message: "single \u6A21\u5F0F\u9700\u8981 file \u53C2\u6570" };
|
|
592
|
+
}
|
|
593
|
+
try {
|
|
594
|
+
const source = await readSourceFile(input.file);
|
|
595
|
+
return {
|
|
596
|
+
success: true,
|
|
597
|
+
message: `\u8BF7\u4E3A\u4EE5\u4E0B\u7D20\u6750\u751F\u6210 3-8 \u4E2A\u6807\u7B7E:
|
|
598
|
+
|
|
599
|
+
\u6807\u9898: ${source.meta.title}
|
|
600
|
+
\u6765\u6E90: ${source.meta.source}
|
|
601
|
+
|
|
602
|
+
\u5185\u5BB9\u6458\u8981: ${source.content.slice(0, 500)}...`,
|
|
603
|
+
data: {
|
|
604
|
+
file: input.file,
|
|
605
|
+
title: source.meta.title,
|
|
606
|
+
contentPreview: source.content.slice(0, 1e3),
|
|
607
|
+
needsTags: true
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
} catch {
|
|
611
|
+
return { success: false, message: `\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6: ${input.file}` };
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
const batch = await getNextTagBatch();
|
|
615
|
+
if (batch.length === 0) {
|
|
616
|
+
return {
|
|
617
|
+
success: true,
|
|
618
|
+
message: "\u6807\u7B7E\u961F\u5217\u4E3A\u7A7A\uFF0C\u65E0\u9700\u5904\u7406\u3002",
|
|
619
|
+
data: { pending: 0 }
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
const items = [];
|
|
623
|
+
for (const item of batch) {
|
|
624
|
+
try {
|
|
625
|
+
const config = await getConfig();
|
|
626
|
+
const fullPath = join4(config.workflowDir, item.file);
|
|
627
|
+
const source = await readSourceFile(fullPath);
|
|
628
|
+
items.push({
|
|
629
|
+
file: item.file,
|
|
630
|
+
title: item.title,
|
|
631
|
+
contentPreview: source.content.slice(0, 500),
|
|
632
|
+
source: item.source
|
|
633
|
+
});
|
|
634
|
+
} catch {
|
|
635
|
+
items.push({
|
|
636
|
+
file: item.file,
|
|
637
|
+
title: item.title,
|
|
638
|
+
contentPreview: "[\u65E0\u6CD5\u8BFB\u53D6]",
|
|
639
|
+
source: item.source
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
const queue = await getTagQueue();
|
|
644
|
+
return {
|
|
645
|
+
success: true,
|
|
646
|
+
message: `\u6807\u7B7E\u961F\u5217\u4E2D\u6709 ${queue.queue.length} \u7BC7\u5F85\u5904\u7406\uFF0C\u672C\u6B21\u6279\u6B21 ${items.length} \u7BC7\u3002\u8BF7\u9010\u6761\u5206\u6790\u5E76\u751F\u6210\u6807\u7B7E\u3002`,
|
|
647
|
+
data: {
|
|
648
|
+
totalPending: queue.queue.length,
|
|
649
|
+
batch: items,
|
|
650
|
+
needsTags: true
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
async function sourceTagStatus() {
|
|
655
|
+
const queue = await getTagQueue();
|
|
656
|
+
return {
|
|
657
|
+
success: true,
|
|
658
|
+
message: `\u5F85\u5904\u7406: ${queue.queue.length} \u7BC7 | \u5931\u8D25: ${queue.failed.length} \u7BC7 | \u7D2F\u8BA1\u5DF2\u5904\u7406: ${queue.history.totalProcessed} \u7BC7 | \u5E73\u5747\u6807\u7B7E\u6570: ${queue.history.avgTagsPerItem.toFixed(1)}`,
|
|
659
|
+
data: {
|
|
660
|
+
pending: queue.queue.length,
|
|
661
|
+
failed: queue.failed.length,
|
|
662
|
+
totalProcessed: queue.history.totalProcessed,
|
|
663
|
+
avgTagsPerItem: queue.history.avgTagsPerItem,
|
|
664
|
+
lastProcessed: queue.history.lastProcessed
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
var sourceTagApplySchema = z2.object({
|
|
669
|
+
file: z2.string().describe("\u7D20\u6750\u6587\u4EF6\u8DEF\u5F84"),
|
|
670
|
+
tags: z2.array(z2.string()).describe("Claude \u751F\u6210\u7684\u6807\u7B7E")
|
|
671
|
+
});
|
|
672
|
+
async function sourceTagApply(input) {
|
|
673
|
+
try {
|
|
674
|
+
const config = await getConfig();
|
|
675
|
+
const fullPath = input.file.startsWith("/") ? input.file : join4(config.workflowDir, input.file);
|
|
676
|
+
await updateSourceTags(fullPath, input.tags);
|
|
677
|
+
await markTagged(input.file, input.tags.length);
|
|
678
|
+
return {
|
|
679
|
+
success: true,
|
|
680
|
+
message: `\u6807\u7B7E\u5DF2\u5199\u5165: ${input.tags.join(", ")}`,
|
|
681
|
+
data: { file: input.file, tags: input.tags }
|
|
682
|
+
};
|
|
683
|
+
} catch (err) {
|
|
684
|
+
await markTagFailed(input.file);
|
|
685
|
+
return {
|
|
686
|
+
success: false,
|
|
687
|
+
message: `\u6807\u7B7E\u5199\u5165\u5931\u8D25: ${err instanceof Error ? err.message : "\u672A\u77E5\u9519\u8BEF"}`
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// src/tools/draft.ts
|
|
693
|
+
import { z as z3 } from "zod";
|
|
694
|
+
|
|
695
|
+
// src/lib/drafts.ts
|
|
696
|
+
import { readFile as readFile3, writeFile as writeFile5, mkdir as mkdir4, unlink } from "fs/promises";
|
|
697
|
+
import { join as join5, basename } from "path";
|
|
698
|
+
import matter2 from "gray-matter";
|
|
699
|
+
import { glob as glob2 } from "glob";
|
|
700
|
+
import { randomUUID } from "crypto";
|
|
701
|
+
function getDraftDir(account, workflowDir) {
|
|
702
|
+
if (account.toLowerCase() === "auston") {
|
|
703
|
+
return join5(workflowDir, "drafts");
|
|
704
|
+
}
|
|
705
|
+
return join5(workflowDir, "accounts", account, "drafts");
|
|
706
|
+
}
|
|
707
|
+
function getPublishedDir(account, workflowDir) {
|
|
708
|
+
if (account.toLowerCase() === "auston") {
|
|
709
|
+
return join5(workflowDir, "published");
|
|
710
|
+
}
|
|
711
|
+
return join5(workflowDir, "accounts", account, "published");
|
|
712
|
+
}
|
|
713
|
+
function generateDraftFilename(title) {
|
|
714
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
715
|
+
const slug = title.replace(/[^\u4e00-\u9fa5a-zA-Z0-9\s-]/g, "").replace(/\s+/g, "-").slice(0, 50);
|
|
716
|
+
return `${date}-${slug}.md`;
|
|
717
|
+
}
|
|
718
|
+
async function saveDraft(options) {
|
|
719
|
+
const config = await getConfig();
|
|
720
|
+
const dir = getDraftDir(options.account, config.workflowDir);
|
|
721
|
+
await mkdir4(dir, { recursive: true });
|
|
722
|
+
const id = randomUUID().slice(0, 8);
|
|
723
|
+
const filename = generateDraftFilename(options.title);
|
|
724
|
+
const filePath = join5(dir, filename);
|
|
725
|
+
const meta = {
|
|
726
|
+
id,
|
|
727
|
+
account: options.account,
|
|
728
|
+
platform: options.platform,
|
|
729
|
+
title: options.title,
|
|
730
|
+
status: options.status || "draft",
|
|
731
|
+
tags: options.tags || [],
|
|
732
|
+
wordCount: options.content.length,
|
|
733
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
734
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
735
|
+
};
|
|
736
|
+
const output = matter2.stringify(options.content, meta);
|
|
737
|
+
await writeFile5(filePath, output, "utf-8");
|
|
738
|
+
return { filePath, meta, content: options.content };
|
|
739
|
+
}
|
|
740
|
+
async function listDrafts(options) {
|
|
741
|
+
const config = await getConfig();
|
|
742
|
+
const drafts = [];
|
|
743
|
+
const dirs = [];
|
|
744
|
+
if (options?.account) {
|
|
745
|
+
dirs.push(getDraftDir(options.account, config.workflowDir));
|
|
746
|
+
} else {
|
|
747
|
+
dirs.push(join5(config.workflowDir, "drafts"));
|
|
748
|
+
const accountGlob = await glob2("accounts/*/drafts", {
|
|
749
|
+
cwd: config.workflowDir,
|
|
750
|
+
absolute: true
|
|
751
|
+
});
|
|
752
|
+
dirs.push(...accountGlob);
|
|
753
|
+
}
|
|
754
|
+
for (const dir of dirs) {
|
|
755
|
+
try {
|
|
756
|
+
const files = await glob2("*.md", { cwd: dir, absolute: true });
|
|
757
|
+
for (const file of files) {
|
|
758
|
+
try {
|
|
759
|
+
const raw = await readFile3(file, "utf-8");
|
|
760
|
+
const { data, content } = matter2(raw);
|
|
761
|
+
const meta = data;
|
|
762
|
+
if (options?.status && meta.status !== options.status) continue;
|
|
763
|
+
drafts.push({ filePath: file, meta, content: content.trim() });
|
|
764
|
+
} catch {
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
} catch {
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
drafts.sort(
|
|
773
|
+
(a, b) => new Date(b.meta.updatedAt).getTime() - new Date(a.meta.updatedAt).getTime()
|
|
774
|
+
);
|
|
775
|
+
return drafts;
|
|
776
|
+
}
|
|
777
|
+
async function updateDraft(options) {
|
|
778
|
+
const raw = await readFile3(options.file, "utf-8");
|
|
779
|
+
const { data, content } = matter2(raw);
|
|
780
|
+
const meta = data;
|
|
781
|
+
if (options.title) meta.title = options.title;
|
|
782
|
+
if (options.status) meta.status = options.status;
|
|
783
|
+
if (options.tags) meta.tags = options.tags;
|
|
784
|
+
const newContent = options.content ?? content.trim();
|
|
785
|
+
meta.wordCount = newContent.length;
|
|
786
|
+
meta.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
787
|
+
const output = matter2.stringify(newContent, meta);
|
|
788
|
+
await writeFile5(options.file, output, "utf-8");
|
|
789
|
+
return { filePath: options.file, meta, content: newContent };
|
|
790
|
+
}
|
|
791
|
+
async function deleteDraft(file) {
|
|
792
|
+
await unlink(file);
|
|
793
|
+
}
|
|
794
|
+
async function publishDraft(options) {
|
|
795
|
+
const raw = await readFile3(options.file, "utf-8");
|
|
796
|
+
const { data, content } = matter2(raw);
|
|
797
|
+
const meta = data;
|
|
798
|
+
meta.status = "published";
|
|
799
|
+
meta.publishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
800
|
+
if (options.platformUrl) meta.platformUrl = options.platformUrl;
|
|
801
|
+
meta.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
802
|
+
const config = await getConfig();
|
|
803
|
+
const publishedDir = getPublishedDir(meta.account, config.workflowDir);
|
|
804
|
+
await mkdir4(publishedDir, { recursive: true });
|
|
805
|
+
const newPath = join5(publishedDir, basename(options.file));
|
|
806
|
+
const output = matter2.stringify(content.trim(), meta);
|
|
807
|
+
await writeFile5(newPath, output, "utf-8");
|
|
808
|
+
await unlink(options.file);
|
|
809
|
+
return { filePath: newPath, meta, content: content.trim() };
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// src/tools/draft.ts
|
|
813
|
+
var draftSaveSchema = z3.object({
|
|
814
|
+
account: z3.string().describe("\u8D26\u53F7\u540D\u79F0"),
|
|
815
|
+
platform: z3.string().describe("\u5E73\u53F0\u540D\u79F0\uFF0C\u5982 wechat"),
|
|
816
|
+
title: z3.string().describe("\u6587\u7AE0\u6807\u9898"),
|
|
817
|
+
content: z3.string().describe("\u6587\u7AE0\u6B63\u6587\uFF08Markdown\uFF09"),
|
|
818
|
+
tags: z3.array(z3.string()).optional().describe("\u6587\u7AE0\u6807\u7B7E"),
|
|
819
|
+
status: z3.enum(["draft", "review", "ready"]).optional().describe("\u521D\u59CB\u72B6\u6001\uFF0C\u9ED8\u8BA4 draft")
|
|
820
|
+
});
|
|
821
|
+
async function draftSave(input) {
|
|
822
|
+
const draft = await saveDraft({
|
|
823
|
+
account: input.account,
|
|
824
|
+
platform: input.platform,
|
|
825
|
+
title: input.title,
|
|
826
|
+
content: input.content,
|
|
827
|
+
tags: input.tags,
|
|
828
|
+
status: input.status
|
|
829
|
+
});
|
|
830
|
+
return {
|
|
831
|
+
success: true,
|
|
832
|
+
message: `\u8349\u7A3F\u5DF2\u4FDD\u5B58: ${draft.meta.title} (${draft.meta.wordCount} \u5B57)`,
|
|
833
|
+
data: {
|
|
834
|
+
file: draft.filePath,
|
|
835
|
+
id: draft.meta.id,
|
|
836
|
+
status: draft.meta.status
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
var draftListSchema = z3.object({
|
|
841
|
+
account: z3.string().optional().describe("\u6309\u8D26\u53F7\u7B5B\u9009"),
|
|
842
|
+
status: z3.enum(["draft", "review", "ready", "published", "archived"]).optional().describe("\u6309\u72B6\u6001\u7B5B\u9009")
|
|
843
|
+
});
|
|
844
|
+
async function draftList(input) {
|
|
845
|
+
const drafts = await listDrafts({
|
|
846
|
+
account: input.account,
|
|
847
|
+
status: input.status
|
|
848
|
+
});
|
|
849
|
+
return {
|
|
850
|
+
success: true,
|
|
851
|
+
message: `\u627E\u5230 ${drafts.length} \u7BC7\u8349\u7A3F`,
|
|
852
|
+
data: drafts.map((d) => ({
|
|
853
|
+
file: d.filePath,
|
|
854
|
+
id: d.meta.id,
|
|
855
|
+
account: d.meta.account,
|
|
856
|
+
platform: d.meta.platform,
|
|
857
|
+
title: d.meta.title,
|
|
858
|
+
status: d.meta.status,
|
|
859
|
+
wordCount: d.meta.wordCount,
|
|
860
|
+
updatedAt: d.meta.updatedAt
|
|
861
|
+
}))
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
var draftUpdateSchema = z3.object({
|
|
865
|
+
file: z3.string().describe("\u8349\u7A3F\u6587\u4EF6\u8DEF\u5F84"),
|
|
866
|
+
content: z3.string().optional().describe("\u66F4\u65B0\u7684\u6B63\u6587"),
|
|
867
|
+
title: z3.string().optional().describe("\u66F4\u65B0\u7684\u6807\u9898"),
|
|
868
|
+
status: z3.enum(["draft", "review", "ready"]).optional().describe("\u66F4\u65B0\u72B6\u6001"),
|
|
869
|
+
tags: z3.array(z3.string()).optional().describe("\u66F4\u65B0\u6807\u7B7E")
|
|
870
|
+
});
|
|
871
|
+
async function draftUpdate(input) {
|
|
872
|
+
const draft = await updateDraft({
|
|
873
|
+
file: input.file,
|
|
874
|
+
content: input.content,
|
|
875
|
+
title: input.title,
|
|
876
|
+
status: input.status,
|
|
877
|
+
tags: input.tags
|
|
878
|
+
});
|
|
879
|
+
return {
|
|
880
|
+
success: true,
|
|
881
|
+
message: `\u8349\u7A3F\u5DF2\u66F4\u65B0: ${draft.meta.title}`,
|
|
882
|
+
data: {
|
|
883
|
+
file: draft.filePath,
|
|
884
|
+
status: draft.meta.status,
|
|
885
|
+
wordCount: draft.meta.wordCount
|
|
886
|
+
}
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
var draftDeleteSchema = z3.object({
|
|
890
|
+
file: z3.string().describe("\u8349\u7A3F\u6587\u4EF6\u8DEF\u5F84")
|
|
891
|
+
});
|
|
892
|
+
async function draftDelete(input) {
|
|
893
|
+
await deleteDraft(input.file);
|
|
894
|
+
return {
|
|
895
|
+
success: true,
|
|
896
|
+
message: `\u8349\u7A3F\u5DF2\u5220\u9664: ${input.file}`
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
var draftPublishSchema = z3.object({
|
|
900
|
+
file: z3.string().describe("\u8349\u7A3F\u6587\u4EF6\u8DEF\u5F84"),
|
|
901
|
+
platformUrl: z3.string().optional().describe("\u5E73\u53F0\u53D1\u5E03\u94FE\u63A5")
|
|
902
|
+
});
|
|
903
|
+
async function draftPublish(input) {
|
|
904
|
+
const draft = await publishDraft({
|
|
905
|
+
file: input.file,
|
|
906
|
+
platformUrl: input.platformUrl
|
|
907
|
+
});
|
|
908
|
+
return {
|
|
909
|
+
success: true,
|
|
910
|
+
message: `\u5DF2\u53D1\u5E03: ${draft.meta.title} \u2192 ${draft.filePath}`,
|
|
911
|
+
data: {
|
|
912
|
+
file: draft.filePath,
|
|
913
|
+
publishedAt: draft.meta.publishedAt,
|
|
914
|
+
platformUrl: draft.meta.platformUrl
|
|
915
|
+
}
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// src/tools/analytics.ts
|
|
920
|
+
import { z as z4 } from "zod";
|
|
921
|
+
var analyticsPushSchema = z4.object({
|
|
922
|
+
articleId: z4.string().describe("\u6587\u7AE0 ID"),
|
|
923
|
+
metrics: z4.object({
|
|
924
|
+
reads: z4.number().describe("\u9605\u8BFB\u6570"),
|
|
925
|
+
likes: z4.number().describe("\u70B9\u8D5E\u6570"),
|
|
926
|
+
shares: z4.number().describe("\u5206\u4EAB\u6570"),
|
|
927
|
+
comments: z4.number().describe("\u8BC4\u8BBA\u6570"),
|
|
928
|
+
followers: z4.number().optional().describe("\u65B0\u589E\u5173\u6CE8")
|
|
929
|
+
})
|
|
930
|
+
});
|
|
931
|
+
async function analyticsPush(input) {
|
|
932
|
+
const payload = {
|
|
933
|
+
articleId: input.articleId,
|
|
934
|
+
metrics: input.metrics,
|
|
935
|
+
recordedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
936
|
+
};
|
|
937
|
+
const res = await pushSyncBatch({
|
|
938
|
+
sources: [],
|
|
939
|
+
drafts: [],
|
|
940
|
+
published: [],
|
|
941
|
+
analytics: [payload]
|
|
942
|
+
});
|
|
943
|
+
if (!res.ok) {
|
|
944
|
+
return {
|
|
945
|
+
success: false,
|
|
946
|
+
message: `\u6570\u636E\u63A8\u9001\u5931\u8D25: ${res.error}\uFF08\u6570\u636E\u5DF2\u7F13\u5B58\u5728\u672C\u5730\uFF0C\u4E0B\u6B21\u540C\u6B65\u65F6\u91CD\u8BD5\uFF09`
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
return {
|
|
950
|
+
success: true,
|
|
951
|
+
message: `\u6570\u636E\u5DF2\u63A8\u9001: \u9605\u8BFB ${input.metrics.reads} | \u70B9\u8D5E ${input.metrics.likes} | \u5206\u4EAB ${input.metrics.shares} | \u8BC4\u8BBA ${input.metrics.comments}`,
|
|
952
|
+
data: payload
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
var analyticsReportSchema = z4.object({
|
|
956
|
+
account: z4.string().optional().describe("\u6309\u8D26\u53F7\u7B5B\u9009"),
|
|
957
|
+
period: z4.enum(["7d", "30d", "90d", "all"]).optional().describe("\u65F6\u95F4\u8303\u56F4")
|
|
958
|
+
});
|
|
959
|
+
async function analyticsReport(input) {
|
|
960
|
+
return {
|
|
961
|
+
success: true,
|
|
962
|
+
message: "\u6570\u636E\u62A5\u544A\u529F\u80FD\u9700\u8981\u4E91\u7AEF Dashboard \u652F\u6301\uFF0C\u8BF7\u8BBF\u95EE https://app.claudeink.app \u67E5\u770B\u8BE6\u7EC6\u6570\u636E\u5206\u6790\u3002",
|
|
963
|
+
data: {
|
|
964
|
+
dashboardUrl: "https://app.claudeink.app/analytics",
|
|
965
|
+
account: input.account,
|
|
966
|
+
period: input.period || "30d"
|
|
967
|
+
}
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// src/tools/account.ts
|
|
972
|
+
import { z as z5 } from "zod";
|
|
973
|
+
import { readFile as readFile4, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
|
|
974
|
+
import { join as join6 } from "path";
|
|
975
|
+
import { glob as glob3 } from "glob";
|
|
976
|
+
async function accountList() {
|
|
977
|
+
const config = await getConfig();
|
|
978
|
+
const yamlFiles = await glob3("accounts/*.yaml", {
|
|
979
|
+
cwd: config.workflowDir,
|
|
980
|
+
absolute: true
|
|
981
|
+
});
|
|
982
|
+
const accounts = [];
|
|
983
|
+
for (const file of yamlFiles) {
|
|
984
|
+
if (file.endsWith("_template.yaml")) continue;
|
|
985
|
+
try {
|
|
986
|
+
const content = await readFile4(file, "utf-8");
|
|
987
|
+
const name = extractYamlField(content, "name");
|
|
988
|
+
const platform = extractYamlField(content, "platform");
|
|
989
|
+
const desc = extractYamlField(content, "description");
|
|
990
|
+
accounts.push({ name, platform, description: desc, file });
|
|
991
|
+
} catch {
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
return {
|
|
996
|
+
success: true,
|
|
997
|
+
message: `\u5171 ${accounts.length} \u4E2A\u8D26\u53F7`,
|
|
998
|
+
data: accounts
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
var accountSwitchSchema = z5.object({
|
|
1002
|
+
name: z5.string().describe("\u8981\u5207\u6362\u5230\u7684\u8D26\u53F7\u540D\u79F0")
|
|
1003
|
+
});
|
|
1004
|
+
async function accountSwitch(input) {
|
|
1005
|
+
const config = await getConfig();
|
|
1006
|
+
const yamlPath = join6(config.workflowDir, "accounts", `${input.name}.yaml`);
|
|
1007
|
+
try {
|
|
1008
|
+
const content = await readFile4(yamlPath, "utf-8");
|
|
1009
|
+
const platform = extractYamlField(content, "platform");
|
|
1010
|
+
let platformRules = "";
|
|
1011
|
+
if (platform) {
|
|
1012
|
+
try {
|
|
1013
|
+
platformRules = await readFile4(
|
|
1014
|
+
join6(config.workflowDir, "platforms", `${platform}.md`),
|
|
1015
|
+
"utf-8"
|
|
1016
|
+
);
|
|
1017
|
+
} catch {
|
|
1018
|
+
platformRules = "[\u5E73\u53F0\u89C4\u5219\u6587\u4EF6\u4E0D\u5B58\u5728]";
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
return {
|
|
1022
|
+
success: true,
|
|
1023
|
+
message: `\u5DF2\u5207\u6362\u5230\u8D26\u53F7: ${input.name} (${platform || "\u672A\u77E5\u5E73\u53F0"})\u3002\u8BF7\u6309\u987A\u5E8F\u52A0\u8F7D\uFF1A
|
|
1024
|
+
1. base-rules.md
|
|
1025
|
+
2. platforms/${platform}.md
|
|
1026
|
+
3. accounts/${input.name}.yaml`,
|
|
1027
|
+
data: {
|
|
1028
|
+
account: input.name,
|
|
1029
|
+
platform,
|
|
1030
|
+
configFile: yamlPath,
|
|
1031
|
+
platformRulesLoaded: platformRules.length > 0
|
|
1032
|
+
}
|
|
1033
|
+
};
|
|
1034
|
+
} catch {
|
|
1035
|
+
return {
|
|
1036
|
+
success: false,
|
|
1037
|
+
message: `\u8D26\u53F7 ${input.name} \u4E0D\u5B58\u5728\u3002\u8BF7\u68C0\u67E5 accounts/ \u76EE\u5F55\u3002`
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
var accountCreateSchema = z5.object({
|
|
1042
|
+
name: z5.string().describe("\u8D26\u53F7\u540D\u79F0\uFF08\u82F1\u6587\u5C0F\u5199\uFF0C\u7528\u4F5C\u6587\u4EF6\u540D\uFF09"),
|
|
1043
|
+
platform: z5.string().describe("\u5E73\u53F0\u540D\u79F0\uFF0C\u5982 wechat, xiaohongshu"),
|
|
1044
|
+
displayName: z5.string().optional().describe("\u663E\u793A\u540D\u79F0"),
|
|
1045
|
+
domains: z5.array(z5.string()).describe("\u5185\u5BB9\u9886\u57DF"),
|
|
1046
|
+
description: z5.string().optional().describe("\u8D26\u53F7\u5B9A\u4F4D\u63CF\u8FF0")
|
|
1047
|
+
});
|
|
1048
|
+
async function accountCreate(input) {
|
|
1049
|
+
const config = await getConfig();
|
|
1050
|
+
const yamlPath = join6(config.workflowDir, "accounts", `${input.name}.yaml`);
|
|
1051
|
+
try {
|
|
1052
|
+
await readFile4(yamlPath);
|
|
1053
|
+
return { success: false, message: `\u8D26\u53F7 ${input.name} \u5DF2\u5B58\u5728` };
|
|
1054
|
+
} catch {
|
|
1055
|
+
}
|
|
1056
|
+
let template = "";
|
|
1057
|
+
try {
|
|
1058
|
+
template = await readFile4(
|
|
1059
|
+
join6(config.workflowDir, "accounts", "_template.yaml"),
|
|
1060
|
+
"utf-8"
|
|
1061
|
+
);
|
|
1062
|
+
} catch {
|
|
1063
|
+
template = defaultTemplate();
|
|
1064
|
+
}
|
|
1065
|
+
const yamlContent = template.replace(/name:\s*""/, `name: "${input.displayName || input.name}"`).replace(/platform:\s*""/, `platform: "${input.platform}"`).replace(/description:\s*""/, `description: "${input.description || ""}"`).replace(
|
|
1066
|
+
/domains:\s*\[\]/,
|
|
1067
|
+
`domains:
|
|
1068
|
+
${input.domains.map((d) => ` - "${d}"`).join("\n")}`
|
|
1069
|
+
);
|
|
1070
|
+
await writeFile6(yamlPath, yamlContent, "utf-8");
|
|
1071
|
+
const accountDir = join6(config.workflowDir, "accounts", input.name);
|
|
1072
|
+
await mkdir5(join6(accountDir, "drafts"), { recursive: true });
|
|
1073
|
+
await mkdir5(join6(accountDir, "published"), { recursive: true });
|
|
1074
|
+
await mkdir5(join6(accountDir, "assets"), { recursive: true });
|
|
1075
|
+
return {
|
|
1076
|
+
success: true,
|
|
1077
|
+
message: `\u8D26\u53F7 ${input.name} \u5DF2\u521B\u5EFA
|
|
1078
|
+
\u914D\u7F6E: accounts/${input.name}.yaml
|
|
1079
|
+
\u76EE\u5F55: accounts/${input.name}/drafts/, published/, assets/`,
|
|
1080
|
+
data: {
|
|
1081
|
+
config: yamlPath,
|
|
1082
|
+
directories: [
|
|
1083
|
+
`accounts/${input.name}/drafts/`,
|
|
1084
|
+
`accounts/${input.name}/published/`,
|
|
1085
|
+
`accounts/${input.name}/assets/`
|
|
1086
|
+
]
|
|
1087
|
+
}
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
function extractYamlField(yaml, field) {
|
|
1091
|
+
const regex = new RegExp(`^${field}:\\s*["']?([^"'\\n]+)["']?`, "m");
|
|
1092
|
+
const match = yaml.match(regex);
|
|
1093
|
+
return match ? match[1].trim() : "";
|
|
1094
|
+
}
|
|
1095
|
+
function defaultTemplate() {
|
|
1096
|
+
return `# ClaudeInk \u8D26\u53F7\u914D\u7F6E
|
|
1097
|
+
name: ""
|
|
1098
|
+
platform: ""
|
|
1099
|
+
description: ""
|
|
1100
|
+
|
|
1101
|
+
domains: []
|
|
1102
|
+
|
|
1103
|
+
audience:
|
|
1104
|
+
age_range: ""
|
|
1105
|
+
interests: []
|
|
1106
|
+
|
|
1107
|
+
style:
|
|
1108
|
+
tone: "\u5E73\u5B9E"
|
|
1109
|
+
voice: "\u53E3\u8BED\u5316"
|
|
1110
|
+
formality: "medium"
|
|
1111
|
+
emotion: "\u9002\u5EA6"
|
|
1112
|
+
humor: "\u5076\u5C14"
|
|
1113
|
+
language: "zh-CN"
|
|
1114
|
+
|
|
1115
|
+
persona: ""
|
|
1116
|
+
|
|
1117
|
+
content_rules:
|
|
1118
|
+
min_words: null
|
|
1119
|
+
max_words: null
|
|
1120
|
+
paragraph_style: null
|
|
1121
|
+
|
|
1122
|
+
templates: []
|
|
1123
|
+
|
|
1124
|
+
paths:
|
|
1125
|
+
drafts: null
|
|
1126
|
+
published: null
|
|
1127
|
+
assets: null
|
|
1128
|
+
`;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
// src/index.ts
|
|
1132
|
+
var server = new McpServer({
|
|
1133
|
+
name: "ClaudeInk",
|
|
1134
|
+
version: "0.1.0"
|
|
1135
|
+
});
|
|
1136
|
+
server.tool("auth.activate", "\u9A8C\u8BC1 license key \u5E76\u6FC0\u6D3B ClaudeInk", activateSchema.shape, async (input) => {
|
|
1137
|
+
const result = await authActivate(input);
|
|
1138
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1139
|
+
});
|
|
1140
|
+
server.tool("auth.status", "\u67E5\u770B\u5F53\u524D\u6388\u6743\u72B6\u6001", {}, async () => {
|
|
1141
|
+
const result = await authStatus();
|
|
1142
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1143
|
+
});
|
|
1144
|
+
server.tool("config.sync", "\u62C9\u53D6\u4E91\u7AEF\u6700\u65B0\u5DE5\u4F5C\u6D41\u914D\u7F6E", {}, async () => {
|
|
1145
|
+
const result = await configSync();
|
|
1146
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1147
|
+
});
|
|
1148
|
+
server.tool("config.version", "\u67E5\u770B\u672C\u5730\u914D\u7F6E\u7248\u672C", {}, async () => {
|
|
1149
|
+
const result = await configVersion();
|
|
1150
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1151
|
+
});
|
|
1152
|
+
server.tool("source.add", "\u6DFB\u52A0\u7D20\u6750\u5230\u672C\u5730\u7D20\u6750\u5E93", sourceAddSchema.shape, async (input) => {
|
|
1153
|
+
const result = await sourceAdd(input);
|
|
1154
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1155
|
+
});
|
|
1156
|
+
server.tool("source.search", "\u641C\u7D22\u672C\u5730\u7D20\u6750", sourceSearchSchema.shape, async (input) => {
|
|
1157
|
+
const result = await sourceSearch(input);
|
|
1158
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1159
|
+
});
|
|
1160
|
+
server.tool("source.list", "\u5217\u51FA\u7D20\u6750\u76EE\u5F55", sourceListSchema.shape, async (input) => {
|
|
1161
|
+
const result = await sourceList(input);
|
|
1162
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1163
|
+
});
|
|
1164
|
+
server.tool("source.crawl", "\u89E6\u53D1\u722C\u866B\u6293\u53D6", sourceCrawlSchema.shape, async (input) => {
|
|
1165
|
+
const result = await sourceCrawl(input);
|
|
1166
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1167
|
+
});
|
|
1168
|
+
server.tool("source.schedule", "\u7BA1\u7406\u5B9A\u65F6\u722C\u866B\u4EFB\u52A1", sourceScheduleSchema.shape, async (input) => {
|
|
1169
|
+
const result = await sourceSchedule(input);
|
|
1170
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1171
|
+
});
|
|
1172
|
+
server.tool("source.tag", "\u5904\u7406\u7D20\u6750\u6807\u7B7E\uFF08\u961F\u5217\u6216\u5355\u4E2A\uFF09", sourceTagSchema.shape, async (input) => {
|
|
1173
|
+
const result = await sourceTag(input);
|
|
1174
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1175
|
+
});
|
|
1176
|
+
server.tool("source.tag_status", "\u67E5\u770B\u6807\u7B7E\u961F\u5217\u72B6\u6001", {}, async () => {
|
|
1177
|
+
const result = await sourceTagStatus();
|
|
1178
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1179
|
+
});
|
|
1180
|
+
server.tool("source.tag_apply", "\u5199\u5165 Claude \u751F\u6210\u7684\u6807\u7B7E\u5230\u7D20\u6750\u6587\u4EF6", sourceTagApplySchema.shape, async (input) => {
|
|
1181
|
+
const result = await sourceTagApply(input);
|
|
1182
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1183
|
+
});
|
|
1184
|
+
server.tool("draft.save", "\u4FDD\u5B58\u8349\u7A3F", draftSaveSchema.shape, async (input) => {
|
|
1185
|
+
const result = await draftSave(input);
|
|
1186
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1187
|
+
});
|
|
1188
|
+
server.tool("draft.list", "\u5217\u51FA\u8349\u7A3F", draftListSchema.shape, async (input) => {
|
|
1189
|
+
const result = await draftList(input);
|
|
1190
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1191
|
+
});
|
|
1192
|
+
server.tool("draft.update", "\u66F4\u65B0\u8349\u7A3F", draftUpdateSchema.shape, async (input) => {
|
|
1193
|
+
const result = await draftUpdate(input);
|
|
1194
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1195
|
+
});
|
|
1196
|
+
server.tool("draft.delete", "\u5220\u9664\u8349\u7A3F", draftDeleteSchema.shape, async (input) => {
|
|
1197
|
+
const result = await draftDelete(input);
|
|
1198
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1199
|
+
});
|
|
1200
|
+
server.tool("draft.publish", "\u6807\u8BB0\u8349\u7A3F\u4E3A\u5DF2\u53D1\u5E03", draftPublishSchema.shape, async (input) => {
|
|
1201
|
+
const result = await draftPublish(input);
|
|
1202
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1203
|
+
});
|
|
1204
|
+
server.tool("analytics.push", "\u63A8\u9001\u6587\u7AE0\u8868\u73B0\u6570\u636E", analyticsPushSchema.shape, async (input) => {
|
|
1205
|
+
const result = await analyticsPush(input);
|
|
1206
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1207
|
+
});
|
|
1208
|
+
server.tool("analytics.report", "\u83B7\u53D6\u6570\u636E\u5206\u6790\u62A5\u544A", analyticsReportSchema.shape, async (input) => {
|
|
1209
|
+
const result = await analyticsReport(input);
|
|
1210
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1211
|
+
});
|
|
1212
|
+
server.tool("account.list", "\u5217\u51FA\u6240\u6709\u8D26\u53F7", {}, async () => {
|
|
1213
|
+
const result = await accountList();
|
|
1214
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1215
|
+
});
|
|
1216
|
+
server.tool("account.switch", "\u5207\u6362\u5F53\u524D\u8D26\u53F7", accountSwitchSchema.shape, async (input) => {
|
|
1217
|
+
const result = await accountSwitch(input);
|
|
1218
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1219
|
+
});
|
|
1220
|
+
server.tool("account.create", "\u521B\u5EFA\u65B0\u8D26\u53F7", accountCreateSchema.shape, async (input) => {
|
|
1221
|
+
const result = await accountCreate(input);
|
|
1222
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1223
|
+
});
|
|
1224
|
+
async function main() {
|
|
1225
|
+
const transport = new StdioServerTransport();
|
|
1226
|
+
await server.connect(transport);
|
|
1227
|
+
console.error("[ClaudeInk MCP] Server started on stdio");
|
|
1228
|
+
}
|
|
1229
|
+
main().catch((err) => {
|
|
1230
|
+
console.error("[ClaudeInk MCP] Fatal error:", err);
|
|
1231
|
+
process.exit(1);
|
|
1232
|
+
});
|