@claudeink/mcp-server 0.1.0 → 0.3.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.
Files changed (2) hide show
  1. package/dist/index.js +315 -0
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1128,6 +1128,309 @@ paths:
1128
1128
  `;
1129
1129
  }
1130
1130
 
1131
+ // src/tools/workflow.ts
1132
+ import { z as z6 } from "zod";
1133
+ import { cp, mkdir as mkdir6, access as access2, readFile as readFile5 } from "fs/promises";
1134
+ import { join as join7, dirname as dirname2 } from "path";
1135
+ import { fileURLToPath } from "url";
1136
+ var __filename = fileURLToPath(import.meta.url);
1137
+ var __dirname = dirname2(__filename);
1138
+ var WORKFLOW_SRC = join7(__dirname, "..", "workflow");
1139
+ var workflowInitSchema = z6.object({
1140
+ workDir: z6.string().describe("\u5DE5\u4F5C\u6D41\u521D\u59CB\u5316\u76EE\u6807\u76EE\u5F55\uFF08\u7EDD\u5BF9\u8DEF\u5F84\uFF09"),
1141
+ licenseKey: z6.string().optional().describe("License Key\uFF08\u53EF\u9009\uFF0C\u4F20\u5165\u5219\u81EA\u52A8\u6FC0\u6D3B\uFF09")
1142
+ });
1143
+ async function workflowInit(input) {
1144
+ const cwd = input.workDir;
1145
+ const results = [];
1146
+ try {
1147
+ const items = ["CLAUDE.md", "base-rules.md", "platforms", "accounts", "tools"];
1148
+ for (const item of items) {
1149
+ const src = join7(WORKFLOW_SRC, item);
1150
+ const dest = join7(cwd, item);
1151
+ try {
1152
+ await access2(dest);
1153
+ results.push(`\u23ED\uFE0F ${item} \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7`);
1154
+ } catch {
1155
+ await cp(src, dest, { recursive: true });
1156
+ results.push(`\u2705 ${item}`);
1157
+ }
1158
+ }
1159
+ const dirs = [
1160
+ "sources/articles",
1161
+ "sources/video-transcripts",
1162
+ "sources/notes",
1163
+ "sources/data",
1164
+ "sources/daily",
1165
+ "templates",
1166
+ ".claudeink"
1167
+ ];
1168
+ for (const dir of dirs) {
1169
+ await mkdir6(join7(cwd, dir), { recursive: true });
1170
+ }
1171
+ results.push("\u2705 \u8FD0\u884C\u65F6\u76EE\u5F55\u5DF2\u521B\u5EFA\uFF08sources/, templates/, .claudeink/\uFF09");
1172
+ if (input.licenseKey) {
1173
+ try {
1174
+ const res = await fetch("https://app.claudeink.com/api/auth/activate", {
1175
+ method: "POST",
1176
+ headers: { "Content-Type": "application/json" },
1177
+ body: JSON.stringify({ key: input.licenseKey })
1178
+ });
1179
+ const data = await res.json();
1180
+ if (data.userId) {
1181
+ const { writeFile: writeFile7 } = await import("fs/promises");
1182
+ await writeFile7(
1183
+ join7(cwd, ".claudeink", "credentials.json"),
1184
+ JSON.stringify(data, null, 2),
1185
+ { mode: 384 }
1186
+ );
1187
+ results.push(`\u2705 License \u6FC0\u6D3B\u6210\u529F\uFF08\u5957\u9910: ${data.plan}\uFF0C\u5230\u671F: ${data.expiresAt}\uFF09`);
1188
+ } else {
1189
+ results.push(`\u26A0\uFE0F License \u6FC0\u6D3B\u5931\u8D25: ${JSON.stringify(data)}`);
1190
+ }
1191
+ } catch (err) {
1192
+ results.push(`\u26A0\uFE0F \u6FC0\u6D3B\u7F51\u7EDC\u9519\u8BEF: ${err instanceof Error ? err.message : err}`);
1193
+ }
1194
+ }
1195
+ return {
1196
+ success: true,
1197
+ message: [
1198
+ "\u{1F389} ClaudeInk \u5DE5\u4F5C\u6D41\u521D\u59CB\u5316\u5B8C\u6210\uFF01",
1199
+ "",
1200
+ ...results,
1201
+ "",
1202
+ "\u{1F4C2} \u5DF2\u91CA\u653E\u6587\u4EF6\uFF1A",
1203
+ "\u251C\u2500\u2500 CLAUDE.md \uFF08\u7CFB\u7EDF\u7D22\u5F15\uFF0C\u8BF7\u5148\u8BFB\u53D6\uFF09",
1204
+ "\u251C\u2500\u2500 base-rules.md \uFF08\u901A\u7528\u5199\u4F5C\u5E95\u5EA7\uFF09",
1205
+ "\u251C\u2500\u2500 platforms/ \uFF085 \u4E2A\u5E73\u53F0\u89C4\u5219\uFF09",
1206
+ "\u251C\u2500\u2500 accounts/ \uFF08\u8D26\u53F7\u914D\u7F6E\u6A21\u677F\uFF09",
1207
+ "\u251C\u2500\u2500 sources/ \uFF08\u5171\u4EAB\u7D20\u6750\u5E93\uFF09",
1208
+ "\u2514\u2500\u2500 tools/ \uFF08\u722C\u866B\u7B49\u5DE5\u5177\uFF09",
1209
+ "",
1210
+ "\u{1F3AF} \u4E0B\u4E00\u6B65\uFF1A",
1211
+ "1. \u8BFB\u53D6 CLAUDE.md \u4E86\u89E3\u4E09\u5C42\u914D\u7F6E\u67B6\u6784",
1212
+ "2. \u4F7F\u7528 /\u65B0\u5EFA\u8D26\u53F7 \u521B\u5EFA\u7B2C\u4E00\u4E2A\u81EA\u5A92\u4F53\u8D26\u53F7",
1213
+ "3. \u4F7F\u7528 /\u7F16\u5199 \u5F00\u59CB\u5185\u5BB9\u521B\u4F5C"
1214
+ ].join("\n")
1215
+ };
1216
+ } catch (err) {
1217
+ return {
1218
+ success: false,
1219
+ message: `\u521D\u59CB\u5316\u5931\u8D25: ${err instanceof Error ? err.message : err}`
1220
+ };
1221
+ }
1222
+ }
1223
+ async function workflowStatus() {
1224
+ const cwd = process.cwd();
1225
+ const checks = [];
1226
+ const requiredFiles = [
1227
+ { path: "CLAUDE.md", label: "\u7CFB\u7EDF\u7D22\u5F15" },
1228
+ { path: "base-rules.md", label: "\u901A\u7528\u5199\u4F5C\u5E95\u5EA7" },
1229
+ { path: "platforms/wechat.md", label: "\u5FAE\u4FE1\u516C\u4F17\u53F7\u89C4\u5219" },
1230
+ { path: "accounts/_template.yaml", label: "\u8D26\u53F7\u6A21\u677F" }
1231
+ ];
1232
+ let allOk = true;
1233
+ for (const file of requiredFiles) {
1234
+ try {
1235
+ await access2(join7(cwd, file.path));
1236
+ checks.push(`\u2705 ${file.label}\uFF08${file.path}\uFF09`);
1237
+ } catch {
1238
+ checks.push(`\u274C ${file.label}\uFF08${file.path}\uFF09\u2014 \u7F3A\u5931`);
1239
+ allOk = false;
1240
+ }
1241
+ }
1242
+ try {
1243
+ const creds = await readFile5(join7(cwd, ".claudeink/credentials.json"), "utf-8");
1244
+ const data = JSON.parse(creds);
1245
+ if (data.userId) {
1246
+ checks.push(`\u2705 License \u5DF2\u6FC0\u6D3B\uFF08\u5957\u9910: ${data.plan}\uFF09`);
1247
+ } else {
1248
+ checks.push("\u26A0\uFE0F \u51ED\u8BC1\u6587\u4EF6\u5B58\u5728\u4F46\u672A\u6FC0\u6D3B");
1249
+ }
1250
+ } catch {
1251
+ checks.push("\u274C \u672A\u627E\u5230\u51ED\u8BC1\u6587\u4EF6\uFF08.claudeink/credentials.json\uFF09");
1252
+ allOk = false;
1253
+ }
1254
+ try {
1255
+ const { glob: glob5 } = await import("glob");
1256
+ const yamlFiles = await glob5("accounts/*.yaml", { cwd, ignore: "accounts/_template.yaml" });
1257
+ if (yamlFiles.length > 0) {
1258
+ checks.push(`\u2705 ${yamlFiles.length} \u4E2A\u8D26\u53F7\u914D\u7F6E`);
1259
+ } else {
1260
+ checks.push("\u2139\uFE0F \u6682\u65E0\u8D26\u53F7\uFF0C\u4F7F\u7528 /\u65B0\u5EFA\u8D26\u53F7 \u521B\u5EFA");
1261
+ }
1262
+ } catch {
1263
+ checks.push("\u2139\uFE0F \u6682\u65E0\u8D26\u53F7");
1264
+ }
1265
+ return {
1266
+ success: true,
1267
+ message: [
1268
+ allOk ? "\u2705 \u5DE5\u4F5C\u6D41\u72B6\u6001\uFF1A\u5C31\u7EEA" : "\u26A0\uFE0F \u5DE5\u4F5C\u6D41\u72B6\u6001\uFF1A\u9700\u8981\u521D\u59CB\u5316",
1269
+ "",
1270
+ ...checks,
1271
+ "",
1272
+ allOk ? "" : "\u8BF7\u8C03\u7528 workflow.init \u5B8C\u6210\u521D\u59CB\u5316\u3002"
1273
+ ].join("\n")
1274
+ };
1275
+ }
1276
+
1277
+ // src/tools/sync.ts
1278
+ import { z as z7 } from "zod";
1279
+ import { readFile as readFile6, stat as stat2 } from "fs/promises";
1280
+ import { join as join8, basename as basename2, extname as extname2 } from "path";
1281
+ import matter3 from "gray-matter";
1282
+ import { glob as glob4 } from "glob";
1283
+ var syncPushSchema = z7.object({
1284
+ workDir: z7.string().optional().describe("\u5DE5\u4F5C\u76EE\u5F55\uFF08\u9ED8\u8BA4\u4F7F\u7528\u914D\u7F6E\u4E2D\u7684 workflowDir\uFF09")
1285
+ });
1286
+ async function syncPush(input) {
1287
+ const config = await getConfig();
1288
+ const creds = await getCredentials();
1289
+ if (!creds?.token) {
1290
+ return { success: false, message: "\u672A\u6FC0\u6D3B\uFF0C\u8BF7\u5148\u4F7F\u7528 auth.activate \u6FC0\u6D3B License" };
1291
+ }
1292
+ const workDir = input.workDir || config.workflowDir;
1293
+ if (!workDir) {
1294
+ return { success: false, message: "\u672A\u8BBE\u7F6E\u5DE5\u4F5C\u76EE\u5F55\uFF0C\u8BF7\u5728 config.json \u4E2D\u914D\u7F6E workflowDir" };
1295
+ }
1296
+ const sources = [];
1297
+ const drafts = [];
1298
+ const published = [];
1299
+ try {
1300
+ const sourceFiles = await glob4("sources/**/*.md", { cwd: workDir });
1301
+ for (const file of sourceFiles) {
1302
+ try {
1303
+ const raw = await readFile6(join8(workDir, file), "utf-8");
1304
+ const { data } = matter3(raw);
1305
+ const id = basename2(file, extname2(file));
1306
+ sources.push({
1307
+ id,
1308
+ title: data.title || id,
1309
+ source: data.source || "unknown",
1310
+ tags: Array.isArray(data.tags) ? data.tags : [],
1311
+ sourceUrl: data.url || null,
1312
+ createdAt: data.published || (/* @__PURE__ */ new Date()).toISOString()
1313
+ });
1314
+ } catch {
1315
+ }
1316
+ }
1317
+ } catch {
1318
+ }
1319
+ const accounts = {};
1320
+ try {
1321
+ const yamlFiles = await glob4("accounts/*.yaml", { cwd: workDir, ignore: "accounts/_template.yaml" });
1322
+ for (const file of yamlFiles) {
1323
+ try {
1324
+ const content = await readFile6(join8(workDir, file), "utf-8");
1325
+ const nameMatch = content.match(/^name:\s*"?([^"\n]+)"?/m);
1326
+ const idMatch = content.match(/^id:\s*"?([^"\n]+)"?/m);
1327
+ const platformMatch = content.match(/^platform:\s*"?([^"\n]+)"?/m);
1328
+ const draftsMatch = content.match(/drafts:\s*"?([^"\n]+)"?/m);
1329
+ const publishedMatch = content.match(/published:\s*"?([^"\n]+)"?/m);
1330
+ if (idMatch && nameMatch) {
1331
+ const id = idMatch[1].trim();
1332
+ const name = nameMatch[1].trim();
1333
+ const platform = platformMatch?.[1]?.trim() || "unknown";
1334
+ const dp = (draftsMatch?.[1] || `accounts/${id}/drafts/`).replace("{id}", id).trim();
1335
+ const pp = (publishedMatch?.[1] || `accounts/${id}/published/`).replace("{id}", id).trim();
1336
+ accounts[id] = { name, platform, draftsPath: dp, publishedPath: pp };
1337
+ }
1338
+ } catch {
1339
+ }
1340
+ }
1341
+ } catch {
1342
+ }
1343
+ for (const [accId, acc] of Object.entries(accounts)) {
1344
+ try {
1345
+ const draftFiles = await glob4("**/*.md", { cwd: join8(workDir, acc.draftsPath) });
1346
+ for (const file of draftFiles) {
1347
+ try {
1348
+ const fullPath = join8(workDir, acc.draftsPath, file);
1349
+ const raw = await readFile6(fullPath, "utf-8");
1350
+ const { data, content } = matter3(raw);
1351
+ const fileStat = await stat2(fullPath);
1352
+ const id = basename2(file, extname2(file));
1353
+ drafts.push({
1354
+ id,
1355
+ account: acc.name,
1356
+ platform: acc.platform,
1357
+ title: data.title || id,
1358
+ status: data.status || "draft",
1359
+ wordCount: content.trim().length,
1360
+ tags: Array.isArray(data.tags) ? data.tags : [],
1361
+ createdAt: data.created || fileStat.birthtime.toISOString(),
1362
+ updatedAt: fileStat.mtime.toISOString()
1363
+ });
1364
+ } catch {
1365
+ }
1366
+ }
1367
+ } catch {
1368
+ }
1369
+ }
1370
+ for (const [accId, acc] of Object.entries(accounts)) {
1371
+ try {
1372
+ const pubFiles = await glob4("**/*.md", { cwd: join8(workDir, acc.publishedPath) });
1373
+ for (const file of pubFiles) {
1374
+ try {
1375
+ const raw = await readFile6(join8(workDir, acc.publishedPath, file), "utf-8");
1376
+ const { data } = matter3(raw);
1377
+ const id = basename2(file, extname2(file));
1378
+ published.push({
1379
+ id,
1380
+ account: acc.name,
1381
+ platform: acc.platform,
1382
+ title: data.title || id,
1383
+ platformUrl: data.url || data.platform_url || null,
1384
+ publishedAt: data.published_at || data.publishedAt || (/* @__PURE__ */ new Date()).toISOString()
1385
+ });
1386
+ } catch {
1387
+ }
1388
+ }
1389
+ } catch {
1390
+ }
1391
+ }
1392
+ const payload = { sources, drafts, published, analytics: [] };
1393
+ try {
1394
+ const res = await fetch(`${config.apiBaseUrl}/api/sync/batch`, {
1395
+ method: "POST",
1396
+ headers: {
1397
+ "Content-Type": "application/json",
1398
+ "Authorization": `Bearer ${creds.token}`
1399
+ },
1400
+ body: JSON.stringify(payload)
1401
+ });
1402
+ const result = await res.json();
1403
+ if (res.ok) {
1404
+ return {
1405
+ success: true,
1406
+ message: [
1407
+ `\u2705 \u540C\u6B65\u5B8C\u6210`,
1408
+ ` \u7D20\u6750: ${sources.length} \u7BC7`,
1409
+ ` \u8349\u7A3F: ${drafts.length} \u7BC7`,
1410
+ ` \u5DF2\u53D1\u5E03: ${published.length} \u7BC7`,
1411
+ ` \u4E91\u7AEF\u63A5\u53D7: ${result.accepted || 0} \u6761`
1412
+ ].join("\n"),
1413
+ data: {
1414
+ sources: sources.length,
1415
+ drafts: drafts.length,
1416
+ published: published.length,
1417
+ accepted: result.accepted || 0
1418
+ }
1419
+ };
1420
+ } else {
1421
+ return {
1422
+ success: false,
1423
+ message: `\u540C\u6B65\u5931\u8D25: HTTP ${res.status} \u2014 ${JSON.stringify(result)}`
1424
+ };
1425
+ }
1426
+ } catch (err) {
1427
+ return {
1428
+ success: false,
1429
+ message: `\u540C\u6B65\u7F51\u7EDC\u9519\u8BEF: ${err instanceof Error ? err.message : err}`
1430
+ };
1431
+ }
1432
+ }
1433
+
1131
1434
  // src/index.ts
1132
1435
  var server = new McpServer({
1133
1436
  name: "ClaudeInk",
@@ -1221,6 +1524,18 @@ server.tool("account.create", "\u521B\u5EFA\u65B0\u8D26\u53F7", accountCreateSch
1221
1524
  const result = await accountCreate(input);
1222
1525
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1223
1526
  });
1527
+ server.tool("sync.push", "\u5C06\u672C\u5730\u7D20\u6750\u3001\u8349\u7A3F\u3001\u5DF2\u53D1\u5E03\u6587\u7AE0\u7684\u5143\u6570\u636E\u540C\u6B65\u5230 ClaudeInk \u4E91\u7AEF\u63A7\u5236\u53F0", syncPushSchema.shape, async (input) => {
1528
+ const result = await syncPush(input);
1529
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1530
+ });
1531
+ server.tool("workflow.init", "\u521D\u59CB\u5316\u5199\u4F5C\u5DE5\u4F5C\u6D41\uFF08\u91CA\u653E\u4E09\u5C42\u914D\u7F6E + \u5E73\u53F0\u89C4\u5219 + \u8D26\u53F7\u6A21\u677F + \u722C\u866B\u5DE5\u5177\u5230\u5DE5\u4F5C\u76EE\u5F55\uFF09", workflowInitSchema.shape, async (input) => {
1532
+ const result = await workflowInit(input);
1533
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1534
+ });
1535
+ server.tool("workflow.status", "\u68C0\u67E5\u5199\u4F5C\u5DE5\u4F5C\u6D41\u72B6\u6001\uFF08\u914D\u7F6E\u6587\u4EF6\u3001\u51ED\u8BC1\u3001\u8D26\u53F7\u662F\u5426\u5C31\u7EEA\uFF09", {}, async () => {
1536
+ const result = await workflowStatus();
1537
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1538
+ });
1224
1539
  async function main() {
1225
1540
  const transport = new StdioServerTransport();
1226
1541
  await server.connect(transport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claudeink/mcp-server",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "ClaudeInk MCP Server - 自媒体多平台写作系统的本地 MCP 服务,连接 Claude 与云端后台",
5
5
  "mcpName": "io.github.weekdmond/claudeink",
6
6
  "type": "module",