slima-mcp 0.1.5 → 0.1.7

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/README.md CHANGED
@@ -9,6 +9,8 @@ Connect your Slima books to **any MCP-compatible AI tool** - one server, all pla
9
9
 
10
10
  ## Supported Platforms
11
11
 
12
+ ### Desktop Applications (Local MCP - stdio)
13
+
12
14
  | Platform | Status | Notes |
13
15
  |----------|--------|-------|
14
16
  | Claude Desktop | ✅ | Native MCP support |
@@ -17,17 +19,26 @@ Connect your Slima books to **any MCP-compatible AI tool** - one server, all pla
17
19
  | Cursor | ✅ | Native MCP support |
18
20
  | VS Code | ✅ | Via MCP extensions |
19
21
 
22
+ ### Web Applications (Remote MCP - HTTP)
23
+
24
+ | Platform | Status | Notes |
25
+ |----------|--------|-------|
26
+ | Claude.ai | ✅ | OAuth login, no token needed |
27
+ | ChatGPT Web | ✅ | OAuth login, no token needed |
28
+
20
29
  > MCP became the industry standard in December 2025 when Anthropic, OpenAI, and Block co-founded the Agentic AI Foundation under the Linux Foundation.
21
30
 
22
31
  ## Features
23
32
 
24
33
  ### Book Management
34
+ - **Create** new books
25
35
  - List and view your books
26
36
  - Get file/folder structure
27
37
  - Track writing statistics
28
38
 
29
39
  ### File Operations
30
40
  - **Read** any file by path
41
+ - **Edit** specific text (search & replace)
31
42
  - **Write** (replace) file content
32
43
  - **Create** new files
33
44
  - **Delete** files
@@ -134,12 +145,55 @@ Get your token from [Slima Settings](https://app.slima.ai/settings/api-tokens).
134
145
 
135
146
  After saving the configuration, restart the application to load Slima MCP.
136
147
 
148
+ ---
149
+
150
+ ## Web Applications (Remote MCP)
151
+
152
+ For web-based AI tools like Claude.ai and ChatGPT, use our hosted Remote MCP Server.
153
+
154
+ ### One-Click Connection
155
+
156
+ 1. In your AI tool, add a new MCP server
157
+ 2. Enter the URL: `https://slima-mcp.workers.dev/mcp`
158
+ 3. Click "Connect" - you'll be redirected to Slima
159
+ 4. Log in (or sign up) and click "Allow"
160
+ 5. Done! No token copying needed
161
+
162
+ ### How It Works
163
+
164
+ ```
165
+ ┌─────────────────────────────────────────────────────────┐
166
+ │ Claude.ai / ChatGPT Web │
167
+ │ │
168
+ │ 1. Click "Connect MCP Server" │
169
+ │ 2. Enter: https://slima-mcp.workers.dev/mcp │
170
+ │ ↓ │
171
+ │ 3. Redirect to Slima login │
172
+ │ ↓ │
173
+ │ 4. Approve authorization │
174
+ │ ↓ │
175
+ │ 5. Automatically connected! │
176
+ │ │
177
+ │ No API tokens. No configuration. Just works. │
178
+ └─────────────────────────────────────────────────────────┘
179
+ ```
180
+
181
+ ### Security
182
+
183
+ - **OAuth 2.0 + PKCE**: Industry-standard secure authentication
184
+ - **No Secrets Stored**: The server never stores your credentials
185
+ - **Session-Based**: Your token is stored securely in Cloudflare KV
186
+ - **Revocable**: Revoke access anytime from [Slima Settings](https://app.slima.ai/settings/api-tokens)
187
+
188
+ ---
189
+
137
190
  ## Available Tools
138
191
 
139
192
  ### Book Management
140
193
 
141
194
  | Tool | Description |
142
195
  |------|-------------|
196
+ | `create_book` | Create a new book in your library |
143
197
  | `list_books` | List all books in your Slima library |
144
198
  | `get_book` | Get details of a specific book |
145
199
  | `get_book_structure` | Get the file/folder structure of a book |
@@ -150,6 +204,7 @@ After saving the configuration, restart the application to load Slima MCP.
150
204
  | Tool | Description |
151
205
  |------|-------------|
152
206
  | `read_file` | Read content of any file by path |
207
+ | `edit_file` | Edit specific text using search & replace |
153
208
  | `write_file` | Replace entire content of a file |
154
209
  | `create_file` | Create a new file in a book |
155
210
  | `delete_file` | Delete a file from a book |
@@ -168,6 +223,7 @@ After saving the configuration, restart the application to load Slima MCP.
168
223
  Once configured, you can ask your AI:
169
224
 
170
225
  **Book & Content:**
226
+ - "Create a new book called 'My Novel'"
171
227
  - "List my books in Slima"
172
228
  - "Show me the structure of my novel"
173
229
  - "Read chapter 3 of my book"
@@ -175,6 +231,7 @@ Once configured, you can ask your AI:
175
231
 
176
232
  **File Operations:**
177
233
  - "Read the character profile for my protagonist"
234
+ - "Edit chapter 1 and change 'John' to 'James'"
178
235
  - "Create a new file called worldbuilding.md with notes about the magic system"
179
236
  - "Search for all mentions of 'blue eyes' in my book"
180
237
  - "Append this new paragraph to chapter 5"
@@ -207,6 +264,8 @@ Token is stored in `~/.slima/credentials.json` with secure permissions.
207
264
 
208
265
  ## Development
209
266
 
267
+ ### Local CLI Development
268
+
210
269
  ```bash
211
270
  # Clone the repository
212
271
  git clone https://github.com/slima-ai/slima-mcp.git
@@ -215,7 +274,7 @@ cd slima-mcp
215
274
  # Install dependencies
216
275
  npm install
217
276
 
218
- # Build
277
+ # Build CLI
219
278
  npm run build
220
279
 
221
280
  # Run tests
@@ -225,6 +284,43 @@ npm test
225
284
  npm run dev
226
285
  ```
227
286
 
287
+ ### Cloudflare Worker Development
288
+
289
+ ```bash
290
+ # Build the Worker
291
+ npm run build:worker
292
+
293
+ # Run Worker locally
294
+ npm run dev:worker
295
+
296
+ # Deploy to Cloudflare
297
+ npm run deploy:worker
298
+
299
+ # Deploy to preview environment
300
+ npm run deploy:worker:preview
301
+ ```
302
+
303
+ ### Project Structure
304
+
305
+ ```
306
+ slima-mcp/
307
+ ├── src/
308
+ │ ├── core/ # Shared core modules
309
+ │ │ ├── api/ # Slima API Client
310
+ │ │ ├── tools/ # MCP Tool implementations
311
+ │ │ └── utils/ # Utilities and errors
312
+ │ ├── cli/ # CLI entry point (stdio transport)
313
+ │ │ ├── index.ts # CLI main
314
+ │ │ ├── auth.ts # Authentication commands
315
+ │ │ └── server.ts # MCP Server for CLI
316
+ │ └── worker/ # Cloudflare Worker (HTTP transport)
317
+ │ ├── index.ts # Worker entry point
318
+ │ └── oauth.ts # OAuth 2.0 + PKCE client
319
+ ├── wrangler.toml # Cloudflare Worker config
320
+ ├── tsup.config.ts # CLI build config
321
+ └── tsup.worker.config.ts # Worker build config
322
+ ```
323
+
228
324
  ## Security
229
325
 
230
326
  - API tokens are stored locally and never shared
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // src/index.ts
3
+ // src/cli/index.ts
4
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
5
 
6
- // src/server.ts
6
+ // src/cli/server.ts
7
7
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
8
 
9
- // src/utils/errors.ts
9
+ // src/core/utils/errors.ts
10
10
  var SlimaApiError = class extends Error {
11
11
  constructor(status, code, message) {
12
12
  super(message || `API Error: ${status}`);
@@ -56,52 +56,46 @@ function formatErrorForMcp(error) {
56
56
  return "An unexpected error occurred.";
57
57
  }
58
58
 
59
- // src/utils/logger.ts
60
- var isDebug = process.env.DEBUG === "true" || process.env.DEBUG === "1";
61
- var logger = {
62
- info: (message, ...args) => {
63
- console.error(`[INFO] ${message}`, ...args);
59
+ // src/core/api/client.ts
60
+ var defaultLogger = {
61
+ debug: () => {
64
62
  },
65
- warn: (message, ...args) => {
66
- console.error(`[WARN] ${message}`, ...args);
63
+ info: () => {
67
64
  },
68
- error: (message, error) => {
69
- if (error instanceof Error) {
70
- console.error(`[ERROR] ${message}:`, error.message);
71
- if (isDebug && error.stack) {
72
- console.error(error.stack);
73
- }
74
- } else if (error !== void 0) {
75
- console.error(`[ERROR] ${message}:`, error);
76
- } else {
77
- console.error(`[ERROR] ${message}`);
78
- }
65
+ warn: () => {
79
66
  },
80
- debug: (message, ...args) => {
81
- if (isDebug) {
82
- console.error(`[DEBUG] ${message}`, ...args);
83
- }
67
+ error: () => {
84
68
  }
85
69
  };
86
-
87
- // src/api/client.ts
70
+ function isLegacyConfig(config) {
71
+ return "token" in config && typeof config.token === "string";
72
+ }
88
73
  var SlimaApiClient = class {
89
74
  baseUrl;
90
- token;
75
+ getToken;
76
+ logger;
91
77
  constructor(config) {
92
78
  this.baseUrl = config.baseUrl.replace(/\/$/, "");
93
- this.token = config.token;
79
+ if (isLegacyConfig(config)) {
80
+ const staticToken = config.token;
81
+ this.getToken = async () => staticToken;
82
+ this.logger = defaultLogger;
83
+ } else {
84
+ this.getToken = config.getToken;
85
+ this.logger = config.logger || defaultLogger;
86
+ }
94
87
  }
95
88
  /**
96
- * 發送 API 請求
89
+ * Send API request
97
90
  */
98
91
  async request(method, path, body) {
99
92
  const url = `${this.baseUrl}${path}`;
100
- logger.debug(`${method} ${path}`);
93
+ const token = await this.getToken();
94
+ this.logger.debug(`${method} ${path}`);
101
95
  const response = await fetch(url, {
102
96
  method,
103
97
  headers: {
104
- Authorization: `Bearer ${this.token}`,
98
+ Authorization: `Bearer ${token}`,
105
99
  "Content-Type": "application/json",
106
100
  "User-Agent": "slima-mcp-server/0.1.0"
107
101
  },
@@ -114,7 +108,7 @@ var SlimaApiClient = class {
114
108
  return json.data;
115
109
  }
116
110
  /**
117
- * 處理錯誤回應
111
+ * Handle error response
118
112
  */
119
113
  async handleErrorResponse(response) {
120
114
  let errorData;
@@ -135,21 +129,21 @@ var SlimaApiClient = class {
135
129
  throw new SlimaApiError(response.status, code, message);
136
130
  }
137
131
  }
138
- // === 書籍相關 ===
132
+ // === Book Operations ===
139
133
  /**
140
- * 列出所有書籍
134
+ * List all books
141
135
  */
142
136
  async listBooks() {
143
137
  return this.request("GET", "/api/v1/books");
144
138
  }
145
139
  /**
146
- * 取得書籍詳情
140
+ * Get book details
147
141
  */
148
142
  async getBook(token) {
149
143
  return this.request("GET", `/api/v1/books/${token}`);
150
144
  }
151
145
  /**
152
- * 建立新書籍
146
+ * Create a new book
153
147
  */
154
148
  async createBook(params) {
155
149
  return this.request("POST", "/api/v1/books", {
@@ -160,10 +154,9 @@ var SlimaApiClient = class {
160
154
  }
161
155
  });
162
156
  }
163
- // === 版本控制相關 ===
157
+ // === Version Control ===
164
158
  /**
165
- * 列出 Commits
166
- * 注意:API 回傳 { commits: [...] },需要解構
159
+ * List commits
167
160
  */
168
161
  async listCommits(bookToken, limit = 10) {
169
162
  const response = await this.request(
@@ -173,7 +166,7 @@ var SlimaApiClient = class {
173
166
  return response.commits;
174
167
  }
175
168
  /**
176
- * 取得 Commit 詳情
169
+ * Get commit details
177
170
  */
178
171
  async getCommit(bookToken, commitToken) {
179
172
  return this.request(
@@ -182,14 +175,13 @@ var SlimaApiClient = class {
182
175
  );
183
176
  }
184
177
  /**
185
- * 列出分支
178
+ * List branches
186
179
  */
187
180
  async listBranches(bookToken) {
188
181
  return this.request("GET", `/api/v1/books/${bookToken}/branches`);
189
182
  }
190
183
  /**
191
- * 下載 Blobs
192
- * 注意:API 回傳 { blobs: [...], notFound: [...], truncated: ... }
184
+ * Download blobs
193
185
  */
194
186
  async downloadBlobs(bookToken, hashes) {
195
187
  const response = await this.request(
@@ -199,23 +191,23 @@ var SlimaApiClient = class {
199
191
  );
200
192
  return response.blobs;
201
193
  }
202
- // === Persona 相關 ===
194
+ // === Persona ===
203
195
  /**
204
- * 列出所有 Personas
196
+ * List personas
205
197
  */
206
198
  async listPersonas(genre) {
207
199
  const params = genre ? `?genre=${encodeURIComponent(genre)}` : "";
208
200
  return this.request("GET", `/api/v1/personas${params}`);
209
201
  }
210
202
  /**
211
- * 取得 Persona 詳情
203
+ * Get persona details
212
204
  */
213
205
  async getPersona(token) {
214
206
  return this.request("GET", `/api/v1/personas/${token}`);
215
207
  }
216
- // === Reader Test 相關 ===
208
+ // === Reader Test ===
217
209
  /**
218
- * 建立 Reader Test
210
+ * Create reader test
219
211
  */
220
212
  async createReaderTest(bookToken, params) {
221
213
  return this.request(
@@ -234,7 +226,7 @@ var SlimaApiClient = class {
234
226
  );
235
227
  }
236
228
  /**
237
- * 取得 Reader Test 進度
229
+ * Get reader test progress
238
230
  */
239
231
  async getReaderTestProgress(bookToken, testToken) {
240
232
  return this.request(
@@ -243,7 +235,7 @@ var SlimaApiClient = class {
243
235
  );
244
236
  }
245
237
  /**
246
- * 取得 Reader Test 結果
238
+ * Get reader test result
247
239
  */
248
240
  async getReaderTest(bookToken, testToken) {
249
241
  return this.request(
@@ -253,7 +245,7 @@ var SlimaApiClient = class {
253
245
  }
254
246
  // === MCP File Operations ===
255
247
  /**
256
- * 讀取檔案內容
248
+ * Read file content
257
249
  */
258
250
  async readFile(bookToken, path) {
259
251
  return this.request(
@@ -263,7 +255,7 @@ var SlimaApiClient = class {
263
255
  );
264
256
  }
265
257
  /**
266
- * 建立新檔案
258
+ * Create file
267
259
  */
268
260
  async createFile(bookToken, params) {
269
261
  return this.request(
@@ -278,7 +270,7 @@ var SlimaApiClient = class {
278
270
  );
279
271
  }
280
272
  /**
281
- * 更新(覆寫)檔案內容
273
+ * Update (overwrite) file content
282
274
  */
283
275
  async updateFile(bookToken, params) {
284
276
  return this.request(
@@ -292,7 +284,7 @@ var SlimaApiClient = class {
292
284
  );
293
285
  }
294
286
  /**
295
- * 刪除檔案
287
+ * Delete file
296
288
  */
297
289
  async deleteFile(bookToken, params) {
298
290
  return this.request(
@@ -305,7 +297,7 @@ var SlimaApiClient = class {
305
297
  );
306
298
  }
307
299
  /**
308
- * 附加內容到檔案
300
+ * Append content to file
309
301
  */
310
302
  async appendToFile(bookToken, params) {
311
303
  return this.request(
@@ -319,7 +311,7 @@ var SlimaApiClient = class {
319
311
  );
320
312
  }
321
313
  /**
322
- * 搜尋檔案內容
314
+ * Search file content
323
315
  */
324
316
  async searchFiles(bookToken, params) {
325
317
  return this.request(
@@ -334,7 +326,7 @@ var SlimaApiClient = class {
334
326
  }
335
327
  };
336
328
 
337
- // src/tools/books.ts
329
+ // src/core/tools/books.ts
338
330
  import { z } from "zod";
339
331
  function registerBookTools(server, client) {
340
332
  server.tool(
@@ -536,7 +528,7 @@ function formatFileTree(files, indent = 0) {
536
528
  }).join("\n");
537
529
  }
538
530
 
539
- // src/tools/content.ts
531
+ // src/core/tools/content.ts
540
532
  import { z as z2 } from "zod";
541
533
  function registerContentTools(server, client) {
542
534
  server.tool(
@@ -691,9 +683,19 @@ function listAllFiles(files, prefix = "") {
691
683
  return result;
692
684
  }
693
685
 
694
- // src/tools/beta-reader.ts
686
+ // src/core/tools/beta-reader.ts
695
687
  import { z as z3 } from "zod";
696
- function registerBetaReaderTools(server, client) {
688
+ var defaultLogger2 = {
689
+ debug: () => {
690
+ },
691
+ info: () => {
692
+ },
693
+ warn: () => {
694
+ },
695
+ error: () => {
696
+ }
697
+ };
698
+ function registerBetaReaderTools(server, client, logger2 = defaultLogger2) {
697
699
  server.tool(
698
700
  "list_personas",
699
701
  "List available virtual reader personas for beta reading feedback",
@@ -802,7 +804,7 @@ ${formatted}
802
804
  };
803
805
  }
804
806
  const content = blobs[0].content;
805
- logger.info(`Creating reader test for ${file_path} with persona ${persona_token}`);
807
+ logger2.info(`Creating reader test for ${file_path} with persona ${persona_token}`);
806
808
  const test = await client.createReaderTest(book_token, {
807
809
  reportType: "chapter",
808
810
  personaTokens: [persona_token],
@@ -812,7 +814,8 @@ ${formatted}
812
814
  });
813
815
  const result = await pollForCompletion(
814
816
  () => client.getReaderTestProgress(book_token, test.token),
815
- 9e4
817
+ 9e4,
818
+ logger2
816
819
  );
817
820
  if (result.status === "failed") {
818
821
  return {
@@ -851,12 +854,12 @@ Test ID: ${test.token}`
851
854
  }
852
855
  );
853
856
  }
854
- async function pollForCompletion(checkFn, timeoutMs) {
857
+ async function pollForCompletion(checkFn, timeoutMs, logger2) {
855
858
  const startTime = Date.now();
856
859
  const pollInterval = 3e3;
857
860
  while (Date.now() - startTime < timeoutMs) {
858
861
  const result = await checkFn();
859
- logger.debug(`Poll status: ${result.status}, progress: ${result.progress || "N/A"}`);
862
+ logger2.debug(`Poll status: ${result.status}, progress: ${result.progress || "N/A"}`);
860
863
  if (result.status === "completed" || result.status === "failed") {
861
864
  return result;
862
865
  }
@@ -1095,7 +1098,7 @@ function searchByName2(files, name) {
1095
1098
  return null;
1096
1099
  }
1097
1100
 
1098
- // src/tools/files.ts
1101
+ // src/core/tools/files.ts
1099
1102
  import { z as z4 } from "zod";
1100
1103
  function registerFileTools(server, client) {
1101
1104
  server.tool(
@@ -1165,6 +1168,80 @@ Message: ${result.commit.name || result.commit.message}`
1165
1168
  }
1166
1169
  }
1167
1170
  );
1171
+ server.tool(
1172
+ "edit_file",
1173
+ "Edit a file by replacing a specific text segment (search and replace). Use this for precise edits instead of rewriting the entire file.",
1174
+ {
1175
+ book_token: z4.string().describe("Book token (e.g., bk_abc123)"),
1176
+ path: z4.string().describe('File path (e.g., "characters/protagonist.md")'),
1177
+ old_string: z4.string().describe("The exact text to find and replace (must match exactly, including whitespace)"),
1178
+ new_string: z4.string().describe("The new text to replace it with"),
1179
+ replace_all: z4.boolean().optional().describe("Replace all occurrences (default: false, replaces only first match)"),
1180
+ commit_message: z4.string().optional().describe("Commit message (optional, auto-generated if not provided)")
1181
+ },
1182
+ async ({ book_token, path, old_string, new_string, replace_all, commit_message }) => {
1183
+ try {
1184
+ const fileData = await client.readFile(book_token, path);
1185
+ const currentContent = fileData.content || "";
1186
+ if (!currentContent.includes(old_string)) {
1187
+ return {
1188
+ content: [
1189
+ {
1190
+ type: "text",
1191
+ text: `\u274C Edit failed: Could not find the specified text in the file.
1192
+
1193
+ **Looking for:**
1194
+ \`\`\`
1195
+ ${old_string.slice(0, 200)}${old_string.length > 200 ? "..." : ""}
1196
+ \`\`\`
1197
+
1198
+ **Tip:** Make sure the text matches exactly, including whitespace and line breaks.`
1199
+ }
1200
+ ],
1201
+ isError: true
1202
+ };
1203
+ }
1204
+ if (!replace_all) {
1205
+ const occurrences = currentContent.split(old_string).length - 1;
1206
+ if (occurrences > 1) {
1207
+ return {
1208
+ content: [
1209
+ {
1210
+ type: "text",
1211
+ text: `\u274C Edit failed: Found ${occurrences} occurrences of the text. Please provide a more specific/longer text segment to ensure a unique match, or set replace_all: true to replace all occurrences.`
1212
+ }
1213
+ ],
1214
+ isError: true
1215
+ };
1216
+ }
1217
+ }
1218
+ const newContent = replace_all ? currentContent.split(old_string).join(new_string) : currentContent.replace(old_string, new_string);
1219
+ const result = await client.updateFile(book_token, {
1220
+ path,
1221
+ content: newContent,
1222
+ commitMessage: commit_message || `Edit ${path}`
1223
+ });
1224
+ const replacementCount = replace_all ? currentContent.split(old_string).length - 1 : 1;
1225
+ return {
1226
+ content: [
1227
+ {
1228
+ type: "text",
1229
+ text: `\u2705 File edited successfully.
1230
+
1231
+ **Replacements:** ${replacementCount}
1232
+ **Commit:** ${result.commit.token}
1233
+ **Message:** ${result.commit.name || result.commit.message}`
1234
+ }
1235
+ ]
1236
+ };
1237
+ } catch (error) {
1238
+ return {
1239
+ content: [{ type: "text", text: formatErrorForMcp(error) }],
1240
+ isError: true
1241
+ };
1242
+ }
1243
+ }
1244
+ );
1168
1245
  server.tool(
1169
1246
  "create_file",
1170
1247
  "Create a new file in a book (creates a new commit)",
@@ -1323,26 +1400,55 @@ Message: ${result.commit.name || result.commit.message}`
1323
1400
  );
1324
1401
  }
1325
1402
 
1326
- // src/server.ts
1403
+ // src/core/utils/logger.ts
1404
+ var isDebug = process.env.DEBUG === "true" || process.env.DEBUG === "1";
1405
+ var logger = {
1406
+ info: (message, ...args) => {
1407
+ console.error(`[INFO] ${message}`, ...args);
1408
+ },
1409
+ warn: (message, ...args) => {
1410
+ console.error(`[WARN] ${message}`, ...args);
1411
+ },
1412
+ error: (message, error) => {
1413
+ if (error instanceof Error) {
1414
+ console.error(`[ERROR] ${message}:`, error.message);
1415
+ if (isDebug && error.stack) {
1416
+ console.error(error.stack);
1417
+ }
1418
+ } else if (error !== void 0) {
1419
+ console.error(`[ERROR] ${message}:`, error);
1420
+ } else {
1421
+ console.error(`[ERROR] ${message}`);
1422
+ }
1423
+ },
1424
+ debug: (message, ...args) => {
1425
+ if (isDebug) {
1426
+ console.error(`[DEBUG] ${message}`, ...args);
1427
+ }
1428
+ }
1429
+ };
1430
+
1431
+ // src/cli/server.ts
1327
1432
  function createSlimaServer(config) {
1328
1433
  const client = new SlimaApiClient({
1329
- token: config.apiToken,
1330
- baseUrl: config.baseUrl || "https://api.slima.ai"
1434
+ baseUrl: config.baseUrl || "https://api.slima.ai",
1435
+ getToken: async () => config.apiToken,
1436
+ logger
1331
1437
  });
1332
1438
  const server = new McpServer({
1333
1439
  name: "slima",
1334
- version: "0.1.5"
1440
+ version: "0.1.7"
1335
1441
  });
1336
1442
  registerBookTools(server, client);
1337
1443
  registerContentTools(server, client);
1338
- registerBetaReaderTools(server, client);
1444
+ registerBetaReaderTools(server, client, logger);
1339
1445
  registerFileTools(server, client);
1340
1446
  logger.info("Slima MCP Server initialized");
1341
1447
  logger.info(`API endpoint: ${config.baseUrl || "https://api.slima.ai"}`);
1342
1448
  return server;
1343
1449
  }
1344
1450
 
1345
- // src/utils/token-storage.ts
1451
+ // src/cli/token-storage.ts
1346
1452
  import { promises as fs } from "fs";
1347
1453
  import { homedir } from "os";
1348
1454
  import { join } from "path";
@@ -1776,8 +1882,8 @@ async function runStatus() {
1776
1882
  `);
1777
1883
  }
1778
1884
 
1779
- // src/index.ts
1780
- var VERSION = "0.1.5";
1885
+ // src/cli/index.ts
1886
+ var VERSION = "0.1.7";
1781
1887
  function showHelp() {
1782
1888
  console.log(`
1783
1889
  Slima MCP v${VERSION}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/server.ts","../src/utils/errors.ts","../src/utils/logger.ts","../src/api/client.ts","../src/tools/books.ts","../src/tools/content.ts","../src/tools/beta-reader.ts","../src/tools/files.ts","../src/utils/token-storage.ts","../src/cli/auth.ts"],"sourcesContent":["/**\n * Slima MCP Server - CLI Entry Point\n *\n * Usage:\n * slima-mcp - Start MCP server (requires SLIMA_API_TOKEN or prior auth)\n * slima-mcp auth - Authenticate with Slima (opens browser)\n * slima-mcp logout - Remove saved credentials\n * slima-mcp status - Show authentication status\n * slima-mcp --help - Show help\n */\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { createSlimaServer } from './server.js';\nimport { logger } from './utils/logger.js';\nimport { loadToken, getCredentialsFilePath } from './utils/token-storage.js';\nimport { runAuth, runLogout, runStatus } from './cli/auth.js';\n\ndeclare const __VERSION__: string;\nconst VERSION = __VERSION__;\n\nfunction showHelp(): void {\n console.log(`\nSlima MCP v${VERSION}\nMCP Server for Slima - AI Writing IDE for Novel Authors\n\nUSAGE:\n slima-mcp [command]\n\nCOMMANDS:\n (none) Start the MCP server\n auth Authenticate with Slima (opens browser)\n logout Remove saved credentials\n status Show authentication status\n --help Show this help message\n --version Show version\n\nAUTHENTICATION:\n 1. Run: slima-mcp auth\n 2. Complete authentication in browser\n 3. Token is automatically saved\n\n Or set SLIMA_API_TOKEN environment variable.\n\nEXAMPLES:\n slima-mcp auth # Authenticate with browser\n slima-mcp status # Check auth status\n SLIMA_API_TOKEN=xxx slima-mcp # Use env var\n\nCONFIGURATION (for AI tools):\n Claude Desktop, Cursor, etc.:\n {\n \"mcpServers\": {\n \"slima\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"slima-mcp\"]\n }\n }\n }\n\nMORE INFO:\n https://github.com/slima-ai/slima-mcp\n`);\n}\n\nfunction showVersion(): void {\n console.log(`slima-mcp v${VERSION}`);\n}\n\nasync function startServer(): Promise<void> {\n // 優先使用環境變數,否則使用儲存的 token\n let apiToken = process.env.SLIMA_API_TOKEN;\n let baseUrl = process.env.SLIMA_API_URL;\n\n if (!apiToken) {\n const credentials = await loadToken();\n if (credentials) {\n apiToken = credentials.apiToken;\n baseUrl = baseUrl || credentials.baseUrl;\n }\n }\n\n if (!apiToken) {\n logger.error('Not authenticated and SLIMA_API_TOKEN not set');\n logger.error('');\n logger.error('To authenticate, run:');\n logger.error(' slima-mcp auth');\n logger.error('');\n logger.error('Or set environment variable:');\n logger.error(' SLIMA_API_TOKEN=your_token slima-mcp');\n logger.error('');\n logger.error('Get your API token from: https://app.slima.ai/settings/api-tokens');\n process.exit(1);\n }\n\n try {\n const server = createSlimaServer({\n apiToken,\n baseUrl,\n });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n logger.info('Slima MCP Server running on stdio');\n\n // 優雅關閉\n process.on('SIGINT', async () => {\n logger.info('Shutting down...');\n await server.close();\n process.exit(0);\n });\n\n process.on('SIGTERM', async () => {\n logger.info('Shutting down...');\n await server.close();\n process.exit(0);\n });\n } catch (error) {\n logger.error('Failed to start server', error);\n process.exit(1);\n }\n}\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2);\n const command = args[0];\n\n switch (command) {\n case 'auth':\n await runAuth();\n break;\n\n case 'logout':\n await runLogout();\n break;\n\n case 'status':\n await runStatus();\n break;\n\n case '--help':\n case '-h':\n case 'help':\n showHelp();\n break;\n\n case '--version':\n case '-v':\n showVersion();\n break;\n\n case undefined:\n // 預設:啟動 MCP server\n await startServer();\n break;\n\n default:\n console.error(`Unknown command: ${command}`);\n console.error('Run `slima-mcp --help` for usage.');\n process.exit(1);\n }\n}\n\nmain();\n","/**\n * Slima MCP Server\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { SlimaApiClient } from './api/client.js';\nimport { registerBookTools } from './tools/books.js';\nimport { registerContentTools } from './tools/content.js';\nimport { registerBetaReaderTools } from './tools/beta-reader.js';\nimport { registerFileTools } from './tools/files.js';\nimport { logger } from './utils/logger.js';\n\ndeclare const __VERSION__: string;\n\nexport interface ServerConfig {\n apiToken: string;\n baseUrl?: string;\n}\n\n/**\n * 建立 Slima MCP Server\n */\nexport function createSlimaServer(config: ServerConfig): McpServer {\n const client = new SlimaApiClient({\n token: config.apiToken,\n baseUrl: config.baseUrl || 'https://api.slima.ai',\n });\n\n const server = new McpServer({\n name: 'slima',\n version: __VERSION__,\n });\n\n // 註冊工具\n registerBookTools(server, client);\n registerContentTools(server, client);\n registerBetaReaderTools(server, client);\n registerFileTools(server, client);\n\n logger.info('Slima MCP Server initialized');\n logger.info(`API endpoint: ${config.baseUrl || 'https://api.slima.ai'}`);\n\n return server;\n}\n","/**\n * 錯誤處理工具\n */\n\nexport class SlimaApiError extends Error {\n constructor(\n public status: number,\n public code?: string,\n message?: string\n ) {\n super(message || `API Error: ${status}`);\n this.name = 'SlimaApiError';\n }\n}\n\nexport class AuthenticationError extends SlimaApiError {\n constructor(message = 'Invalid or expired API token') {\n super(401, 'UNAUTHORIZED', message);\n this.name = 'AuthenticationError';\n }\n}\n\nexport class NotFoundError extends SlimaApiError {\n constructor(resource = 'Resource') {\n super(404, 'NOT_FOUND', `${resource} not found`);\n this.name = 'NotFoundError';\n }\n}\n\nexport class InsufficientCreditsError extends SlimaApiError {\n constructor() {\n super(402, 'INSUFFICIENT_CREDITS', 'Insufficient credits');\n this.name = 'InsufficientCreditsError';\n }\n}\n\n/**\n * 格式化錯誤訊息供 MCP 回傳\n */\nexport function formatErrorForMcp(error: unknown): string {\n if (error instanceof SlimaApiError) {\n switch (error.code) {\n case 'NOT_FOUND':\n return 'Resource not found. Please check the token.';\n case 'UNAUTHORIZED':\n return 'Invalid API token. Please check your configuration.';\n case 'INSUFFICIENT_CREDITS':\n return 'Insufficient credits. Please top up your account.';\n case 'TOKEN_EXPIRED':\n return 'API token has expired. Please generate a new one.';\n case 'TOKEN_REVOKED':\n return 'API token has been revoked. Please generate a new one.';\n default:\n return error.message;\n }\n }\n\n if (error instanceof Error) {\n return error.message;\n }\n\n return 'An unexpected error occurred.';\n}\n","/**\n * Logger 工具\n *\n * 重要:MCP STDIO 模式下,stdout 是協議通道,\n * 所有日誌必須輸出到 stderr\n */\n\nconst isDebug = process.env.DEBUG === 'true' || process.env.DEBUG === '1';\n\nexport const logger = {\n info: (message: string, ...args: unknown[]) => {\n console.error(`[INFO] ${message}`, ...args);\n },\n\n warn: (message: string, ...args: unknown[]) => {\n console.error(`[WARN] ${message}`, ...args);\n },\n\n error: (message: string, error?: unknown) => {\n if (error instanceof Error) {\n console.error(`[ERROR] ${message}:`, error.message);\n if (isDebug && error.stack) {\n console.error(error.stack);\n }\n } else if (error !== undefined) {\n console.error(`[ERROR] ${message}:`, error);\n } else {\n console.error(`[ERROR] ${message}`);\n }\n },\n\n debug: (message: string, ...args: unknown[]) => {\n if (isDebug) {\n console.error(`[DEBUG] ${message}`, ...args);\n }\n },\n};\n","/**\n * Slima API Client\n */\n\nimport {\n type Book,\n type Commit,\n type Branch,\n type Blob,\n type Persona,\n type ReaderTest,\n type ReaderTestProgress,\n type CreateReaderTestParams,\n type ApiResponse,\n type ApiError,\n type CommitsListResponse,\n type BlobsDownloadResponse,\n type McpFileReadResponse,\n type McpFileCreateResponse,\n type McpFileUpdateResponse,\n type McpFileDeleteResponse,\n type McpFileAppendResponse,\n type McpSearchResponse,\n} from './types.js';\nimport {\n SlimaApiError,\n AuthenticationError,\n NotFoundError,\n InsufficientCreditsError,\n} from '../utils/errors.js';\nimport { logger } from '../utils/logger.js';\n\nexport interface SlimaConfig {\n baseUrl: string;\n token: string;\n}\n\nexport class SlimaApiClient {\n private readonly baseUrl: string;\n private readonly token: string;\n\n constructor(config: SlimaConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, ''); // 移除尾部斜線\n this.token = config.token;\n }\n\n /**\n * 發送 API 請求\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n logger.debug(`${method} ${path}`);\n\n const response = await fetch(url, {\n method,\n headers: {\n Authorization: `Bearer ${this.token}`,\n 'Content-Type': 'application/json',\n 'User-Agent': 'slima-mcp-server/0.1.0',\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n await this.handleErrorResponse(response);\n }\n\n const json = (await response.json()) as ApiResponse<T>;\n return json.data;\n }\n\n /**\n * 處理錯誤回應\n */\n private async handleErrorResponse(response: Response): Promise<never> {\n let errorData: ApiError | undefined;\n\n try {\n errorData = (await response.json()) as ApiError;\n } catch {\n // 無法解析 JSON\n }\n\n const code = errorData?.error?.code;\n const message = errorData?.error?.message;\n\n switch (response.status) {\n case 401:\n throw new AuthenticationError(message);\n case 404:\n throw new NotFoundError(message);\n case 402:\n throw new InsufficientCreditsError();\n default:\n throw new SlimaApiError(response.status, code, message);\n }\n }\n\n // === 書籍相關 ===\n\n /**\n * 列出所有書籍\n */\n async listBooks(): Promise<Book[]> {\n return this.request<Book[]>('GET', '/api/v1/books');\n }\n\n /**\n * 取得書籍詳情\n */\n async getBook(token: string): Promise<Book> {\n return this.request<Book>('GET', `/api/v1/books/${token}`);\n }\n\n /**\n * 建立新書籍\n */\n async createBook(params: {\n title: string;\n authorName?: string;\n description?: string;\n }): Promise<Book> {\n return this.request<Book>('POST', '/api/v1/books', {\n book: {\n title: params.title,\n author_name: params.authorName,\n description: params.description,\n },\n });\n }\n\n // === 版本控制相關 ===\n\n /**\n * 列出 Commits\n * 注意:API 回傳 { commits: [...] },需要解構\n */\n async listCommits(bookToken: string, limit = 10): Promise<Commit[]> {\n const response = await this.request<CommitsListResponse>(\n 'GET',\n `/api/v1/books/${bookToken}/commits?limit=${limit}`\n );\n return response.commits;\n }\n\n /**\n * 取得 Commit 詳情\n */\n async getCommit(bookToken: string, commitToken: string): Promise<Commit> {\n return this.request<Commit>(\n 'GET',\n `/api/v1/books/${bookToken}/commits/${commitToken}`\n );\n }\n\n /**\n * 列出分支\n */\n async listBranches(bookToken: string): Promise<Branch[]> {\n return this.request<Branch[]>('GET', `/api/v1/books/${bookToken}/branches`);\n }\n\n /**\n * 下載 Blobs\n * 注意:API 回傳 { blobs: [...], notFound: [...], truncated: ... }\n */\n async downloadBlobs(bookToken: string, hashes: string[]): Promise<Blob[]> {\n const response = await this.request<BlobsDownloadResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/blobs/download`,\n { hashes }\n );\n return response.blobs;\n }\n\n // === Persona 相關 ===\n\n /**\n * 列出所有 Personas\n */\n async listPersonas(genre?: string): Promise<Persona[]> {\n const params = genre ? `?genre=${encodeURIComponent(genre)}` : '';\n return this.request<Persona[]>('GET', `/api/v1/personas${params}`);\n }\n\n /**\n * 取得 Persona 詳情\n */\n async getPersona(token: string): Promise<Persona> {\n return this.request<Persona>('GET', `/api/v1/personas/${token}`);\n }\n\n // === Reader Test 相關 ===\n\n /**\n * 建立 Reader Test\n */\n async createReaderTest(\n bookToken: string,\n params: CreateReaderTestParams\n ): Promise<ReaderTest> {\n return this.request<ReaderTest>(\n 'POST',\n `/api/v1/books/${bookToken}/reader_tests`,\n {\n reader_test: {\n report_type: params.reportType || 'chapter',\n persona_tokens: params.personaTokens,\n commit_token: params.commitToken,\n model: params.model,\n scope_config: params.scopeConfig,\n },\n content: params.content,\n }\n );\n }\n\n /**\n * 取得 Reader Test 進度\n */\n async getReaderTestProgress(\n bookToken: string,\n testToken: string\n ): Promise<ReaderTestProgress> {\n return this.request<ReaderTestProgress>(\n 'GET',\n `/api/v1/books/${bookToken}/reader_tests/${testToken}/progress`\n );\n }\n\n /**\n * 取得 Reader Test 結果\n */\n async getReaderTest(bookToken: string, testToken: string): Promise<ReaderTest> {\n return this.request<ReaderTest>(\n 'GET',\n `/api/v1/books/${bookToken}/reader_tests/${testToken}`\n );\n }\n\n // === MCP File Operations ===\n\n /**\n * 讀取檔案內容\n */\n async readFile(bookToken: string, path: string): Promise<McpFileReadResponse> {\n return this.request<McpFileReadResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/read`,\n { path }\n );\n }\n\n /**\n * 建立新檔案\n */\n async createFile(\n bookToken: string,\n params: { path: string; content?: string; parentPath?: string; commitMessage?: string }\n ): Promise<McpFileCreateResponse> {\n return this.request<McpFileCreateResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/create`,\n {\n path: params.path,\n content: params.content || '',\n parent_path: params.parentPath,\n commit_message: params.commitMessage,\n }\n );\n }\n\n /**\n * 更新(覆寫)檔案內容\n */\n async updateFile(\n bookToken: string,\n params: { path: string; content: string; commitMessage?: string }\n ): Promise<McpFileUpdateResponse> {\n return this.request<McpFileUpdateResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/update`,\n {\n path: params.path,\n content: params.content,\n commit_message: params.commitMessage,\n }\n );\n }\n\n /**\n * 刪除檔案\n */\n async deleteFile(\n bookToken: string,\n params: { path: string; commitMessage?: string }\n ): Promise<McpFileDeleteResponse> {\n return this.request<McpFileDeleteResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/delete`,\n {\n path: params.path,\n commit_message: params.commitMessage,\n }\n );\n }\n\n /**\n * 附加內容到檔案\n */\n async appendToFile(\n bookToken: string,\n params: { path: string; content: string; commitMessage?: string }\n ): Promise<McpFileAppendResponse> {\n return this.request<McpFileAppendResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/append`,\n {\n path: params.path,\n content: params.content,\n commit_message: params.commitMessage,\n }\n );\n }\n\n /**\n * 搜尋檔案內容\n */\n async searchFiles(\n bookToken: string,\n params: { query: string; fileTypes?: string[]; limit?: number }\n ): Promise<McpSearchResponse> {\n return this.request<McpSearchResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/search`,\n {\n query: params.query,\n file_types: params.fileTypes,\n limit: params.limit,\n }\n );\n }\n}\n","/**\n * 書籍相關 Tools\n */\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { SlimaApiClient } from '../api/client.js';\nimport { formatErrorForMcp } from '../utils/errors.js';\nimport type { FileSnapshot } from '../api/types.js';\n\n/**\n * 註冊書籍相關工具\n */\nexport function registerBookTools(\n server: McpServer,\n client: SlimaApiClient\n): void {\n // === create_book ===\n server.tool(\n 'create_book',\n 'Create a new book in your Slima library',\n {\n title: z.string().describe('Book title (required)'),\n author_name: z.string().optional().describe('Author name (optional)'),\n description: z.string().optional().describe('Book description (optional)'),\n },\n async ({ title, author_name, description }) => {\n try {\n const book = await client.createBook({\n title,\n authorName: author_name,\n description,\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `✅ Book created successfully!\\n\\n- **Title**: ${book.title}\\n- **Token**: ${book.token}\\n- **Author**: ${book.authorName || 'N/A'}\\n\\nYou can now add files using create_file with book_token: ${book.token}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === list_books ===\n server.tool(\n 'list_books',\n 'List all books in your Slima library',\n {},\n async () => {\n try {\n const books = await client.listBooks();\n\n if (books.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No books found in your library.',\n },\n ],\n };\n }\n\n const formatted = books\n .map((book) => {\n const words = book.totalWordCount?.toLocaleString() || '0';\n return `- **${book.title}** (${book.token})\\n Author: ${book.authorName || 'N/A'} | Words: ${words}`;\n })\n .join('\\n\\n');\n\n return {\n content: [\n {\n type: 'text',\n text: `Found ${books.length} book(s):\\n\\n${formatted}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === get_book ===\n server.tool(\n 'get_book',\n 'Get details of a specific book',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n },\n async ({ book_token }) => {\n try {\n const book = await client.getBook(book_token);\n\n const text = `# ${book.title}\n\n- **Token**: ${book.token}\n- **Author**: ${book.authorName || 'N/A'}\n- **Description**: ${book.description || 'N/A'}\n- **Language**: ${book.language || 'N/A'}\n- **Total Words**: ${book.totalWordCount?.toLocaleString() || '0'}\n- **Manuscript Words**: ${book.manuscriptWordCount?.toLocaleString() || '0'}\n- **Created**: ${book.createdAt}\n- **Updated**: ${book.updatedAt}`;\n\n return {\n content: [{ type: 'text', text }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === get_book_structure ===\n server.tool(\n 'get_book_structure',\n 'Get the file/folder structure of a book',\n {\n book_token: z.string().describe('Book token'),\n },\n async ({ book_token }) => {\n try {\n const commits = await client.listCommits(book_token, 1);\n\n if (commits.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No commits found. The book may be empty.',\n },\n ],\n };\n }\n\n const snapshot = commits[0].filesSnapshot;\n const tree = formatFileTree(snapshot);\n\n return {\n content: [\n {\n type: 'text',\n text: `File structure for book ${book_token}:\\n\\n${tree}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === get_writing_stats ===\n server.tool(\n 'get_writing_stats',\n 'Get writing statistics for a book',\n {\n book_token: z.string().describe('Book token'),\n },\n async ({ book_token }) => {\n try {\n const [book, commits] = await Promise.all([\n client.getBook(book_token),\n client.listCommits(book_token, 10),\n ]);\n\n // 計算最近的寫作進度\n let wordChange = 0;\n if (commits.length > 1) {\n const latest = commits[0].totalWordCount || 0;\n const oldest = commits[commits.length - 1].totalWordCount || 0;\n wordChange = latest - oldest;\n }\n\n const sign = wordChange >= 0 ? '+' : '';\n const progressText =\n commits.length > 1\n ? `${sign}${wordChange.toLocaleString()} words (last ${commits.length} commits)`\n : 'N/A (not enough history)';\n\n const text = `# Writing Stats: ${book.title}\n\n- **Total Words**: ${book.totalWordCount?.toLocaleString() || '0'}\n- **Manuscript Words**: ${book.manuscriptWordCount?.toLocaleString() || '0'}\n- **Recent Progress**: ${progressText}\n- **Commits Analyzed**: ${commits.length}\n- **Language**: ${book.language || 'N/A'}`;\n\n return {\n content: [{ type: 'text', text }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n}\n\n/**\n * 格式化檔案樹\n */\nfunction formatFileTree(files: FileSnapshot[], indent = 0): string {\n return files\n .sort((a, b) => a.position - b.position)\n .map((f) => {\n const prefix = ' '.repeat(indent);\n const icon = f.kind === 'folder' ? '📁' : '📄';\n const words = f.kind === 'file' && f.wordCount ? ` (${f.wordCount} words)` : '';\n const manuscript = f.isManuscript ? ' [M]' : '';\n let line = `${prefix}${icon} ${f.name}${words}${manuscript}`;\n\n if (f.children && f.children.length > 0) {\n line += '\\n' + formatFileTree(f.children, indent + 1);\n }\n\n return line;\n })\n .join('\\n');\n}\n","/**\n * 內容存取 Tools\n */\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { SlimaApiClient } from '../api/client.js';\nimport { formatErrorForMcp } from '../utils/errors.js';\nimport type { FileSnapshot } from '../api/types.js';\n\n/**\n * 註冊內容存取工具\n */\nexport function registerContentTools(\n server: McpServer,\n client: SlimaApiClient\n): void {\n // === get_chapter ===\n server.tool(\n 'get_chapter',\n 'Read the content of a chapter or file from a book',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n file_path: z.string().describe('File path (e.g., /chapters/01.md or chapter-01)'),\n },\n async ({ book_token, file_path }) => {\n try {\n // 1. 取得最新 commit\n const commits = await client.listCommits(book_token, 1);\n\n if (commits.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No commits found. The book may be empty.',\n },\n ],\n };\n }\n\n // 2. 在 snapshot 中找到檔案\n const file = findFileInSnapshot(commits[0].filesSnapshot, file_path);\n\n if (!file) {\n // 列出可用的檔案\n const availableFiles = listAllFiles(commits[0].filesSnapshot);\n const fileList = availableFiles.slice(0, 10).join('\\n- ');\n const moreText =\n availableFiles.length > 10\n ? `\\n... and ${availableFiles.length - 10} more files`\n : '';\n\n return {\n content: [\n {\n type: 'text',\n text: `File not found: ${file_path}\\n\\nAvailable files:\\n- ${fileList}${moreText}\\n\\nTip: Use get_book_structure to see the full file tree.`,\n },\n ],\n isError: true,\n };\n }\n\n if (file.kind === 'folder') {\n return {\n content: [\n {\n type: 'text',\n text: `\"${file_path}\" is a folder, not a file. Please specify a file path.`,\n },\n ],\n isError: true,\n };\n }\n\n if (!file.blobHash) {\n return {\n content: [\n {\n type: 'text',\n text: `File \"${file_path}\" has no content (empty file).`,\n },\n ],\n };\n }\n\n // 3. 下載 blob 內容\n const blobs = await client.downloadBlobs(book_token, [file.blobHash]);\n\n if (blobs.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'Could not retrieve file content. The blob may be missing.',\n },\n ],\n isError: true,\n };\n }\n\n const content = blobs[0].content;\n const wordInfo = file.wordCount ? ` (${file.wordCount} words)` : '';\n\n return {\n content: [\n {\n type: 'text',\n text: `# ${file.name}${wordInfo}\\n\\n${content}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n}\n\n/**\n * 在 snapshot 中尋找檔案\n * 支援多種路徑格式:\n * - 完整路徑:/chapters/01.md\n * - 相對路徑:chapters/01.md\n * - 檔名:01.md\n * - Token:chapter-01\n */\nfunction findFileInSnapshot(\n files: FileSnapshot[],\n path: string\n): FileSnapshot | null {\n // 正規化路徑\n const normalizedPath = path.replace(/^\\//, '').toLowerCase();\n const pathParts = normalizedPath.split('/');\n\n // 遞迴搜尋\n function search(\n items: FileSnapshot[],\n remainingParts: string[]\n ): FileSnapshot | null {\n for (const item of items) {\n const itemNameLower = item.name.toLowerCase();\n const itemTokenLower = item.token.toLowerCase();\n\n // 完整路徑匹配\n if (remainingParts.length === 1) {\n const target = remainingParts[0];\n if (\n itemNameLower === target ||\n itemTokenLower === target ||\n itemNameLower.replace(/\\.[^.]+$/, '') === target.replace(/\\.[^.]+$/, '')\n ) {\n return item;\n }\n }\n\n // 資料夾匹配,繼續往下搜尋\n if (item.kind === 'folder' && item.children) {\n if (\n itemNameLower === remainingParts[0] ||\n itemTokenLower === remainingParts[0]\n ) {\n const result = search(item.children, remainingParts.slice(1));\n if (result) return result;\n }\n\n // 也嘗試在子資料夾中直接搜尋\n const result = search(item.children, remainingParts);\n if (result) return result;\n }\n }\n\n return null;\n }\n\n // 先嘗試完整路徑\n let result = search(files, pathParts);\n if (result) return result;\n\n // 再嘗試只用檔名搜尋\n result = searchByName(files, pathParts[pathParts.length - 1]);\n return result;\n}\n\n/**\n * 只用檔名搜尋\n */\nfunction searchByName(\n files: FileSnapshot[],\n name: string\n): FileSnapshot | null {\n const nameLower = name.toLowerCase();\n\n for (const item of files) {\n const itemNameLower = item.name.toLowerCase();\n const itemTokenLower = item.token.toLowerCase();\n\n if (\n itemNameLower === nameLower ||\n itemTokenLower === nameLower ||\n itemNameLower.replace(/\\.[^.]+$/, '') === nameLower.replace(/\\.[^.]+$/, '')\n ) {\n return item;\n }\n\n if (item.kind === 'folder' && item.children) {\n const result = searchByName(item.children, name);\n if (result) return result;\n }\n }\n\n return null;\n}\n\n/**\n * 列出所有檔案路徑\n */\nfunction listAllFiles(files: FileSnapshot[], prefix = ''): string[] {\n const result: string[] = [];\n\n for (const item of files) {\n const path = prefix ? `${prefix}/${item.name}` : item.name;\n\n if (item.kind === 'file') {\n result.push(path);\n } else if (item.children) {\n result.push(...listAllFiles(item.children, path));\n }\n }\n\n return result;\n}\n","/**\n * Beta Reader Tools\n */\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { SlimaApiClient } from '../api/client.js';\nimport { formatErrorForMcp } from '../utils/errors.js';\nimport type { FileSnapshot, ReaderTest, IndividualFeedback, AggregatedMetrics } from '../api/types.js';\nimport { logger } from '../utils/logger.js';\n\n/**\n * 註冊 Beta Reader 工具\n */\nexport function registerBetaReaderTools(\n server: McpServer,\n client: SlimaApiClient\n): void {\n // === list_personas ===\n server.tool(\n 'list_personas',\n 'List available virtual reader personas for beta reading feedback',\n {\n genre: z\n .string()\n .optional()\n .describe('Filter by genre (e.g., fantasy, romance, scifi)'),\n },\n async ({ genre }) => {\n try {\n const personas = await client.listPersonas(genre);\n\n if (personas.length === 0) {\n const genreText = genre ? ` for genre \"${genre}\"` : '';\n return {\n content: [\n {\n type: 'text',\n text: `No personas found${genreText}.`,\n },\n ],\n };\n }\n\n const formatted = personas\n .map((p) => {\n const name = p.displayLabels?.zh || p.displayLabels?.en || p.slug;\n const tags = p.genreTags?.join(', ') || 'General';\n const demo = p.demographics || {};\n const gender = demo.gender || 'N/A';\n const age = demo.ageRange || 'N/A';\n\n return `- **${name}** (\\`${p.token}\\`)\n Gender: ${gender} | Age: ${age}\n Genres: ${tags}`;\n })\n .join('\\n\\n');\n\n return {\n content: [\n {\n type: 'text',\n text: `Available Personas (${personas.length}):\\n\\n${formatted}\\n\\n**Tip**: Use the persona token with \\`analyze_chapter\\` to get feedback from a specific reader.`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === analyze_chapter ===\n server.tool(\n 'analyze_chapter',\n 'Get AI Beta Reader feedback on a chapter from a specific reader persona. This may take a moment to complete.',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n file_path: z.string().describe('Chapter file path (e.g., /chapters/01.md)'),\n persona_token: z\n .string()\n .describe('Persona token (e.g., psn_xxx). Use list_personas to find available personas.'),\n },\n async ({ book_token, file_path, persona_token }) => {\n try {\n // 1. 取得最新 commit 和章節內容\n const commits = await client.listCommits(book_token, 1);\n\n if (commits.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No commits found. The book may be empty.',\n },\n ],\n isError: true,\n };\n }\n\n // 2. 找到檔案\n const file = findFileInSnapshot(commits[0].filesSnapshot, file_path);\n\n if (!file) {\n return {\n content: [\n {\n type: 'text',\n text: `File not found: ${file_path}. Use get_book_structure to see available files.`,\n },\n ],\n isError: true,\n };\n }\n\n if (!file.blobHash) {\n return {\n content: [\n {\n type: 'text',\n text: `File \"${file_path}\" has no content (empty file).`,\n },\n ],\n isError: true,\n };\n }\n\n // 3. 下載內容\n const blobs = await client.downloadBlobs(book_token, [file.blobHash]);\n\n if (blobs.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'Could not retrieve file content.',\n },\n ],\n isError: true,\n };\n }\n\n const content = blobs[0].content;\n\n // 4. 建立 Reader Test\n logger.info(`Creating reader test for ${file_path} with persona ${persona_token}`);\n\n const test = await client.createReaderTest(book_token, {\n reportType: 'chapter',\n personaTokens: [persona_token],\n commitToken: commits[0].token,\n scopeConfig: { chapters: [file_path] },\n content: content,\n });\n\n // 5. 輪詢等待完成(最多 90 秒)\n const result = await pollForCompletion(\n () => client.getReaderTestProgress(book_token, test.token),\n 90000\n );\n\n if (result.status === 'failed') {\n return {\n content: [\n {\n type: 'text',\n text: 'Analysis failed. Please try again later.',\n },\n ],\n isError: true,\n };\n }\n\n if (result.status !== 'completed') {\n return {\n content: [\n {\n type: 'text',\n text: `Analysis is still in progress. You can check the status later.\\n\\nTest ID: ${test.token}`,\n },\n ],\n };\n }\n\n // 6. 取得完整結果\n const fullResult = await client.getReaderTest(book_token, test.token);\n\n // 7. 格式化反饋\n const feedback = formatBetaReaderFeedback(fullResult, file.name);\n\n return {\n content: [{ type: 'text', text: feedback }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n}\n\n/**\n * 輪詢等待完成\n */\nasync function pollForCompletion(\n checkFn: () => Promise<{ status: string; progress?: number }>,\n timeoutMs: number\n): Promise<{ status: string }> {\n const startTime = Date.now();\n const pollInterval = 3000; // 3 秒\n\n while (Date.now() - startTime < timeoutMs) {\n const result = await checkFn();\n logger.debug(`Poll status: ${result.status}, progress: ${result.progress || 'N/A'}`);\n\n if (result.status === 'completed' || result.status === 'failed') {\n return result;\n }\n\n await sleep(pollInterval);\n }\n\n return { status: 'timeout' };\n}\n\n/**\n * 睡眠\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * 格式化 Beta Reader 反饋 (English headers for better AI processing)\n */\nfunction formatBetaReaderFeedback(test: ReaderTest, fileName: string): string {\n const feedbacks = test.individualFeedbacks || [];\n const metrics = test.aggregatedMetrics;\n\n if (feedbacks.length === 0 && !metrics) {\n return 'No feedback available.';\n }\n\n let output = `# Beta Reader Feedback: ${fileName}\\n\\n`;\n\n // Aggregated metrics summary\n if (metrics) {\n output += formatAggregatedMetrics(metrics);\n }\n\n // Individual persona feedback\n if (feedbacks.length > 0) {\n output += `\\n---\\n\\n## Individual Reader Feedback\\n\\n`;\n output += feedbacks.map(formatIndividualFeedback).join('\\n---\\n\\n');\n }\n\n return output;\n}\n\n/**\n * 格式化聚合指標 (English headers for better AI processing)\n */\nfunction formatAggregatedMetrics(metrics: AggregatedMetrics): string {\n let output = `## Summary Metrics\\n\\n`;\n\n // Overall\n if (metrics.overall) {\n const o = metrics.overall;\n output += `### Overall Scores\\n`;\n if (o.avgContinueReading) output += `- Continue Reading Intent: ${o.avgContinueReading.toFixed(1)}/10\\n`;\n if (o.avgRecommendation) output += `- Recommendation Score: ${o.avgRecommendation.toFixed(1)}/10\\n`;\n if (o.avgWantMore) output += `- Want More: ${o.avgWantMore.toFixed(1)}/10\\n`;\n if (o.dnfRisk) output += `- DNF Risk: ${o.dnfRisk}\\n`;\n output += '\\n';\n }\n\n // Kindle Rating\n if (metrics.kindleRating) {\n output += `### Predicted Kindle Rating\\n`;\n output += `- Average Score: ⭐ ${metrics.kindleRating.avgScore.toFixed(1)}/5\\n`;\n if (metrics.kindleRating.individualScores) {\n const scores = metrics.kindleRating.individualScores\n .map(s => `${s.personaName || 'Reader'}: ${s.score}/5 (${s.confidence})`)\n .join(', ');\n output += `- Individual Scores: ${scores}\\n`;\n }\n output += '\\n';\n }\n\n // Characters\n if (metrics.characters) {\n const c = metrics.characters;\n output += `### Character Evaluation\\n`;\n if (c.avgProtagonistLikability) output += `- Protagonist Likability: ${c.avgProtagonistLikability.toFixed(1)}/10\\n`;\n if (c.avgMotivationClarity) output += `- Motivation Clarity: ${c.avgMotivationClarity.toFixed(1)}/10\\n`;\n if (c.avgDialogueNaturalness) output += `- Dialogue Naturalness: ${c.avgDialogueNaturalness.toFixed(1)}/10\\n`;\n output += '\\n';\n }\n\n // Pacing\n if (metrics.pacing) {\n const p = metrics.pacing;\n output += `### Pacing Evaluation\\n`;\n if (p.avgOverall) output += `- Overall Pacing: ${p.avgOverall.toFixed(1)}/10\\n`;\n if (p.consensus) output += `- Consensus: ${p.consensus}\\n`;\n if (p.commonDnfTriggers && p.commonDnfTriggers.length > 0) {\n output += `- Common DNF Triggers:\\n`;\n p.commonDnfTriggers.slice(0, 3).forEach(t => {\n output += ` - ${t.trigger} (${t.count}x)\\n`;\n });\n }\n output += '\\n';\n }\n\n // Market\n if (metrics.market) {\n const m = metrics.market;\n output += `### Market Positioning\\n`;\n if (m.avgWordOfMouth) output += `- Word of Mouth Potential: ${m.avgWordOfMouth.toFixed(1)}/10\\n`;\n if (m.topComparableBooks && m.topComparableBooks.length > 0) {\n const books = m.topComparableBooks.slice(0, 3).map(b => b.title).join(', ');\n output += `- Comparable Books: ${books}\\n`;\n }\n output += '\\n';\n }\n\n return output;\n}\n\n/**\n * 格式化單一讀者反饋 (English headers for better AI processing)\n */\nfunction formatIndividualFeedback(feedback: IndividualFeedback): string {\n const name = feedback.personaName || feedback.personaToken;\n let output = `### ${name}\\n\\n`;\n\n // Overall scores\n if (feedback.overall) {\n const o = feedback.overall;\n if (o.continueReading) output += `**Continue Reading Intent**: ${o.continueReading}/10\\n`;\n if (o.recommendation) output += `**Recommendation**: ${o.recommendation}/10\\n`;\n if (o.dnfRisk) output += `**DNF Risk**: ${o.dnfRisk}\\n`;\n output += '\\n';\n }\n\n // Kindle Rating\n if (feedback.kindleRating) {\n output += `**Kindle Rating**: ⭐ ${feedback.kindleRating.score}/5`;\n if (feedback.kindleRating.rationale) {\n output += ` - ${feedback.kindleRating.rationale}`;\n }\n output += '\\n\\n';\n }\n\n // Detailed Feedback\n if (feedback.detailedFeedback) {\n const d = feedback.detailedFeedback;\n\n if (d.whatWorked) {\n output += `**What Worked**:\\n${d.whatWorked}\\n\\n`;\n }\n\n if (d.whatDidntWork) {\n output += `**What Didn't Work**:\\n${d.whatDidntWork}\\n\\n`;\n }\n\n if (d.strongestElement) {\n output += `**Strongest Element**: ${d.strongestElement}\\n`;\n }\n\n if (d.weakestElement) {\n output += `**Weakest Element**: ${d.weakestElement}\\n`;\n }\n\n if (d.wouldYouBuy) {\n output += `**Would You Buy**: ${d.wouldYouBuy}\\n`;\n }\n\n if (d.specificSuggestions && d.specificSuggestions.length > 0) {\n output += `\\n**Specific Suggestions**:\\n`;\n d.specificSuggestions.forEach((s, i) => {\n output += `${i + 1}. ${s}\\n`;\n });\n }\n\n if (d.finalThoughts) {\n output += `\\n**Final Thoughts**:\\n${d.finalThoughts}\\n`;\n }\n }\n\n // Emotional Journey\n if (feedback.emotionalJourney) {\n const e = feedback.emotionalJourney;\n if (e.emotionalArc) {\n output += `\\n**Emotional Arc**: ${e.emotionalArc}\\n`;\n }\n if (e.peakMoments && e.peakMoments.length > 0) {\n const moments = e.peakMoments.map(m =>\n typeof m === 'string' ? m : (m as { moment?: string; description?: string }).moment || (m as { moment?: string; description?: string }).description || JSON.stringify(m)\n );\n output += `**Peak Moments**: ${moments.join(', ')}\\n`;\n }\n if (e.overallMood) {\n output += `**Overall Mood**: ${e.overallMood}\\n`;\n }\n }\n\n return output;\n}\n\n/**\n * 在 snapshot 中尋找檔案(複製自 content.ts)\n */\nfunction findFileInSnapshot(\n files: FileSnapshot[],\n path: string\n): FileSnapshot | null {\n const normalizedPath = path.replace(/^\\//, '').toLowerCase();\n const pathParts = normalizedPath.split('/');\n\n function search(\n items: FileSnapshot[],\n remainingParts: string[]\n ): FileSnapshot | null {\n for (const item of items) {\n const itemNameLower = item.name.toLowerCase();\n const itemTokenLower = item.token.toLowerCase();\n\n if (remainingParts.length === 1) {\n const target = remainingParts[0];\n if (\n itemNameLower === target ||\n itemTokenLower === target ||\n itemNameLower.replace(/\\.[^.]+$/, '') === target.replace(/\\.[^.]+$/, '')\n ) {\n return item;\n }\n }\n\n if (item.kind === 'folder' && item.children) {\n if (\n itemNameLower === remainingParts[0] ||\n itemTokenLower === remainingParts[0]\n ) {\n const result = search(item.children, remainingParts.slice(1));\n if (result) return result;\n }\n\n const result = search(item.children, remainingParts);\n if (result) return result;\n }\n }\n\n return null;\n }\n\n let result = search(files, pathParts);\n if (result) return result;\n\n result = searchByName(files, pathParts[pathParts.length - 1]);\n return result;\n}\n\nfunction searchByName(\n files: FileSnapshot[],\n name: string\n): FileSnapshot | null {\n const nameLower = name.toLowerCase();\n\n for (const item of files) {\n const itemNameLower = item.name.toLowerCase();\n const itemTokenLower = item.token.toLowerCase();\n\n if (\n itemNameLower === nameLower ||\n itemTokenLower === nameLower ||\n itemNameLower.replace(/\\.[^.]+$/, '') === nameLower.replace(/\\.[^.]+$/, '')\n ) {\n return item;\n }\n\n if (item.kind === 'folder' && item.children) {\n const result = searchByName(item.children, name);\n if (result) return result;\n }\n }\n\n return null;\n}\n","/**\n * MCP File Operations Tools\n *\n * Provides tools for AI to read, write, create, delete, and search files in books.\n */\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { SlimaApiClient } from '../api/client.js';\nimport { formatErrorForMcp } from '../utils/errors.js';\n\n/**\n * Register file operation tools\n */\nexport function registerFileTools(\n server: McpServer,\n client: SlimaApiClient\n): void {\n // === read_file ===\n server.tool(\n 'read_file',\n 'Read the content of any file in a book by its path',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path (e.g., \"characters/protagonist.md\" or \"chapter-01.md\")'),\n },\n async ({ book_token, path }) => {\n try {\n const result = await client.readFile(book_token, path);\n\n const fileInfo = [\n `# ${result.file.name}`,\n '',\n `**Path:** ${result.file.path}`,\n `**Words:** ${result.file.wordCount}`,\n `**Type:** ${result.file.fileType || 'unknown'}`,\n '',\n '---',\n '',\n result.content || '(empty file)',\n ].join('\\n');\n\n return {\n content: [{ type: 'text', text: fileInfo }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === write_file ===\n server.tool(\n 'write_file',\n 'Replace the entire content of an existing file (creates a new commit)',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path (e.g., \"characters/protagonist.md\")'),\n content: z.string().describe('New content to write'),\n commit_message: z.string().optional().describe('Commit message (optional, auto-generated if not provided)'),\n },\n async ({ book_token, path, content, commit_message }) => {\n try {\n const result = await client.updateFile(book_token, {\n path,\n content,\n commitMessage: commit_message,\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `File updated successfully.\\n\\nCommit: ${result.commit.token}\\nMessage: ${result.commit.name || result.commit.message}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === create_file ===\n server.tool(\n 'create_file',\n 'Create a new file in a book (creates a new commit)',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path including parent folders (e.g., \"characters/new-character.md\")'),\n content: z.string().optional().describe('Initial content (optional, defaults to empty)'),\n commit_message: z.string().optional().describe('Commit message (optional, auto-generated if not provided)'),\n },\n async ({ book_token, path, content, commit_message }) => {\n try {\n const result = await client.createFile(book_token, {\n path,\n content: content || '',\n commitMessage: commit_message,\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `File created successfully.\\n\\nFile Token: ${result.fileToken}\\nCommit: ${result.commit.token}\\nMessage: ${result.commit.name || result.commit.message}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === delete_file ===\n server.tool(\n 'delete_file',\n 'Delete a file from a book (creates a new commit)',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path to delete (e.g., \"old-notes.md\")'),\n commit_message: z.string().optional().describe('Commit message (optional, auto-generated if not provided)'),\n },\n async ({ book_token, path, commit_message }) => {\n try {\n const result = await client.deleteFile(book_token, {\n path,\n commitMessage: commit_message,\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `File deleted successfully.\\n\\nCommit: ${result.commit.token}\\nMessage: ${result.commit.name || result.commit.message}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === append_to_file ===\n server.tool(\n 'append_to_file',\n 'Append content to the end of an existing file (creates a new commit)',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path (e.g., \"notes.md\")'),\n content: z.string().describe('Content to append'),\n commit_message: z.string().optional().describe('Commit message (optional, auto-generated if not provided)'),\n },\n async ({ book_token, path, content, commit_message }) => {\n try {\n const result = await client.appendToFile(book_token, {\n path,\n content,\n commitMessage: commit_message,\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `Content appended successfully.\\n\\nCommit: ${result.commit.token}\\nMessage: ${result.commit.name || result.commit.message}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === search_content ===\n server.tool(\n 'search_content',\n 'Search for content across all files in a book',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n query: z.string().describe('Search query (e.g., \"blue eyes\" or \"protagonist name\")'),\n file_types: z.array(z.string()).optional().describe('Filter by file types (e.g., [\"markdown\", \"txt\"])'),\n limit: z.number().optional().describe('Maximum number of matches to return (default: 20)'),\n },\n async ({ book_token, query, file_types, limit }) => {\n try {\n const result = await client.searchFiles(book_token, {\n query,\n fileTypes: file_types,\n limit,\n });\n\n if (result.matches.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: `No matches found for \"${query}\".`,\n },\n ],\n };\n }\n\n const output: string[] = [\n `# Search Results for \"${query}\"`,\n '',\n `Found ${result.matches.length} file(s) with matches:`,\n '',\n ];\n\n for (const match of result.matches) {\n output.push(`## ${match.file.path}`);\n output.push(`Words: ${match.file.wordCount} | Matches: ${match.matchCount}`);\n output.push('');\n\n for (const snippet of match.snippets) {\n output.push(`> ${snippet.text}`);\n output.push('');\n }\n }\n\n return {\n content: [{ type: 'text', text: output.join('\\n') }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n}\n","/**\n * Token Storage Utility\n *\n * 將 API Token 儲存在使用者的 home 目錄下\n * ~/.slima/credentials.json\n */\n\nimport { promises as fs } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nconst SLIMA_DIR = '.slima';\nconst CREDENTIALS_FILE = 'credentials.json';\n\ninterface Credentials {\n apiToken: string;\n createdAt: string;\n baseUrl?: string;\n}\n\nfunction getCredentialsPath(): string {\n return join(homedir(), SLIMA_DIR, CREDENTIALS_FILE);\n}\n\nfunction getSlimaDir(): string {\n return join(homedir(), SLIMA_DIR);\n}\n\n/**\n * 儲存 API Token\n */\nexport async function saveToken(apiToken: string, baseUrl?: string): Promise<void> {\n const slimaDir = getSlimaDir();\n\n // 確保目錄存在\n await fs.mkdir(slimaDir, { recursive: true });\n\n const credentials: Credentials = {\n apiToken,\n createdAt: new Date().toISOString(),\n ...(baseUrl && { baseUrl }),\n };\n\n const credentialsPath = getCredentialsPath();\n await fs.writeFile(credentialsPath, JSON.stringify(credentials, null, 2), 'utf-8');\n\n // 設定檔案權限為 600(僅擁有者可讀寫)\n await fs.chmod(credentialsPath, 0o600);\n}\n\n/**\n * 讀取 API Token\n */\nexport async function loadToken(): Promise<Credentials | null> {\n try {\n const credentialsPath = getCredentialsPath();\n const content = await fs.readFile(credentialsPath, 'utf-8');\n return JSON.parse(content) as Credentials;\n } catch {\n return null;\n }\n}\n\n/**\n * 刪除 API Token\n */\nexport async function deleteToken(): Promise<void> {\n try {\n const credentialsPath = getCredentialsPath();\n await fs.unlink(credentialsPath);\n } catch {\n // 檔案不存在,忽略\n }\n}\n\n/**\n * 檢查是否已認證\n */\nexport async function isAuthenticated(): Promise<boolean> {\n const credentials = await loadToken();\n return credentials !== null && !!credentials.apiToken;\n}\n\n/**\n * 取得 credentials 檔案路徑(供顯示用)\n */\nexport function getCredentialsFilePath(): string {\n return getCredentialsPath();\n}\n","/**\n * CLI Auth Command\n *\n * 實作類似 Claude Code / GitHub CLI 的認證流程:\n * 1. 使用者執行 `slima-mcp auth`\n * 2. 開啟瀏覽器到 Slima 認證頁面\n * 3. 使用者登入並授權\n * 4. 本地伺服器接收 callback 並儲存 token\n */\n\nimport { createServer, type IncomingMessage, type ServerResponse } from 'node:http';\nimport { URL } from 'node:url';\nimport { randomBytes } from 'node:crypto';\nimport { saveToken, loadToken, deleteToken, getCredentialsFilePath } from '../utils/token-storage.js';\n\n// 認證使用 API 伺服器(處理 OAuth 和 session)\nconst DEFAULT_API_URL = 'https://api.slima.ai';\nconst CALLBACK_PORT_START = 8765;\nconst CALLBACK_PORT_END = 8775;\n\n/**\n * 開啟瀏覽器\n */\nasync function openBrowser(url: string): Promise<void> {\n const { exec } = await import('node:child_process');\n const { promisify } = await import('node:util');\n const execAsync = promisify(exec);\n\n const platform = process.platform;\n\n try {\n if (platform === 'darwin') {\n await execAsync(`open \"${url}\"`);\n } else if (platform === 'win32') {\n await execAsync(`start \"\" \"${url}\"`);\n } else {\n // Linux / WSL\n // 嘗試多個可能的命令\n const commands = ['xdg-open', 'sensible-browser', 'wslview'];\n for (const cmd of commands) {\n try {\n await execAsync(`${cmd} \"${url}\"`);\n return;\n } catch {\n // 繼續嘗試下一個\n }\n }\n // 如果都失敗,顯示 URL 讓使用者手動開啟\n console.log(`\\nPlease open this URL in your browser:\\n${url}\\n`);\n }\n } catch {\n console.log(`\\nPlease open this URL in your browser:\\n${url}\\n`);\n }\n}\n\n/**\n * 尋找可用的 port\n */\nasync function findAvailablePort(): Promise<number> {\n for (let port = CALLBACK_PORT_START; port <= CALLBACK_PORT_END; port++) {\n const available = await isPortAvailable(port);\n if (available) {\n return port;\n }\n }\n throw new Error(`No available ports between ${CALLBACK_PORT_START} and ${CALLBACK_PORT_END}`);\n}\n\nfunction isPortAvailable(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const server = createServer();\n server.once('error', () => resolve(false));\n server.once('listening', () => {\n server.close(() => resolve(true));\n });\n server.listen(port, '127.0.0.1');\n });\n}\n\n/**\n * 產生隨機 state(防止 CSRF)\n */\nfunction generateState(): string {\n return randomBytes(16).toString('hex');\n}\n\n/**\n * 成功頁面 HTML\n * Design follows UIUX_SPEC.md: minimalist, grayscale, Inter font\n */\nfunction getSuccessHtml(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>Welcome to Slima</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap\" rel=\"stylesheet\">\n <style>\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n body {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n background: #FBFBFA;\n color: #1D1D1F;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n .container {\n text-align: center;\n padding: 48px;\n max-width: 420px;\n }\n .logo {\n width: 48px;\n height: 48px;\n margin-bottom: 32px;\n opacity: 0.9;\n }\n .success-icon {\n width: 64px;\n height: 64px;\n margin-bottom: 24px;\n border-radius: 50%;\n background: #1D1D1F;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-left: auto;\n margin-right: auto;\n }\n .success-icon svg {\n width: 32px;\n height: 32px;\n stroke: #FBFBFA;\n }\n h1 {\n font-size: 24px;\n font-weight: 600;\n letter-spacing: -0.02em;\n margin-bottom: 12px;\n color: #1D1D1F;\n }\n .subtitle {\n font-size: 15px;\n color: #6E6E73;\n line-height: 1.5;\n margin-bottom: 8px;\n }\n .hint {\n font-size: 13px;\n color: #86868B;\n margin-top: 24px;\n }\n .divider {\n width: 40px;\n height: 1px;\n background: #E5E5E5;\n margin: 32px auto;\n }\n .footer {\n font-size: 12px;\n color: #86868B;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <img src=\"https://app.slima.ai/icons/slima-black.svg\" alt=\"Slima\" class=\"logo\">\n <div class=\"success-icon\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2.5\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M4.5 12.75l6 6 9-13.5\" />\n </svg>\n </div>\n <h1>Welcome to Slima</h1>\n <p class=\"subtitle\">Authentication successful. Your CLI is now connected.</p>\n <p class=\"hint\">You can close this window and return to your terminal.</p>\n <div class=\"divider\"></div>\n <p class=\"footer\">Slima MCP is ready to use</p>\n </div>\n</body>\n</html>`;\n}\n\n/**\n * 錯誤頁面 HTML\n */\n/**\n * HTML escape to prevent XSS\n */\nfunction escapeHtml(text: string): string {\n const map: Record<string, string> = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#039;',\n };\n return text.replace(/[&<>\"']/g, (char) => map[char]);\n}\n\n/**\n * 錯誤頁面 HTML\n * Design follows UIUX_SPEC.md: minimalist, grayscale, Inter font\n */\nfunction getErrorHtml(message: string): string {\n const safeMessage = escapeHtml(message);\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>Slima - Authentication Error</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap\" rel=\"stylesheet\">\n <style>\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n body {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n background: #FBFBFA;\n color: #1D1D1F;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n .container {\n text-align: center;\n padding: 48px;\n max-width: 420px;\n }\n .logo {\n width: 48px;\n height: 48px;\n margin-bottom: 32px;\n opacity: 0.9;\n }\n .error-icon {\n width: 64px;\n height: 64px;\n margin-bottom: 24px;\n border-radius: 50%;\n background: #1D1D1F;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-left: auto;\n margin-right: auto;\n }\n .error-icon svg {\n width: 32px;\n height: 32px;\n stroke: #FBFBFA;\n }\n h1 {\n font-size: 24px;\n font-weight: 600;\n letter-spacing: -0.02em;\n margin-bottom: 12px;\n color: #1D1D1F;\n }\n .message {\n font-size: 15px;\n color: #6E6E73;\n line-height: 1.5;\n margin-bottom: 8px;\n }\n .hint {\n font-size: 13px;\n color: #86868B;\n margin-top: 24px;\n }\n .divider {\n width: 40px;\n height: 1px;\n background: #E5E5E5;\n margin: 32px auto;\n }\n .retry-link {\n display: inline-block;\n font-size: 14px;\n font-weight: 500;\n color: #1D1D1F;\n text-decoration: none;\n padding: 10px 20px;\n border: 1px solid #E5E5E5;\n border-radius: 8px;\n transition: all 0.2s ease;\n }\n .retry-link:hover {\n background: #F5F5F5;\n border-color: #D5D5D5;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <img src=\"https://app.slima.ai/icons/slima-black.svg\" alt=\"Slima\" class=\"logo\">\n <div class=\"error-icon\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2.5\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </div>\n <h1>Something went wrong</h1>\n <p class=\"message\">${safeMessage}</p>\n <p class=\"hint\">Please close this window and try again from your terminal.</p>\n <div class=\"divider\"></div>\n <a href=\"javascript:window.close()\" class=\"retry-link\">Close Window</a>\n </div>\n</body>\n</html>`;\n}\n\n/**\n * 執行認證流程\n */\nexport async function runAuth(options: { baseUrl?: string } = {}): Promise<void> {\n const baseUrl = options.baseUrl || process.env.SLIMA_API_URL || DEFAULT_API_URL;\n\n console.log('\\n🔐 Slima MCP Authentication\\n');\n\n // 檢查是否已經認證\n const existingCredentials = await loadToken();\n if (existingCredentials) {\n console.log('You are already authenticated.');\n console.log(`Token stored at: ${getCredentialsFilePath()}`);\n console.log('\\nTo re-authenticate, first run: slima-mcp logout\\n');\n return;\n }\n\n const state = generateState();\n const port = await findAvailablePort();\n\n console.log('Starting local server for authentication callback...');\n\n // 建立本地伺服器接收 callback\n const tokenPromise = new Promise<string>((resolve, reject) => {\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n const url = new URL(req.url || '/', `http://localhost:${port}`);\n\n if (url.pathname === '/callback') {\n const token = url.searchParams.get('token');\n const returnedState = url.searchParams.get('state');\n const error = url.searchParams.get('error');\n\n if (error) {\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getErrorHtml(error));\n server.close();\n reject(new Error(error));\n return;\n }\n\n if (returnedState !== state) {\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getErrorHtml('Invalid state parameter. Please try again.'));\n server.close();\n reject(new Error('State mismatch'));\n return;\n }\n\n if (!token) {\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getErrorHtml('No token received. Please try again.'));\n server.close();\n reject(new Error('No token received'));\n return;\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getSuccessHtml());\n server.close();\n resolve(token);\n } else {\n res.writeHead(404);\n res.end('Not found');\n }\n });\n\n server.listen(port, '127.0.0.1', () => {\n console.log(`Listening on http://127.0.0.1:${port}`);\n });\n\n // 30 秒超時\n setTimeout(() => {\n server.close();\n reject(new Error('Authentication timed out. Please try again.'));\n }, 30000);\n });\n\n // 建立認證 URL(使用 API endpoint)\n const authUrl = `${baseUrl}/api/v1/auth/cli?state=${state}&callback_port=${port}`;\n\n console.log('\\nOpening browser for authentication...');\n await openBrowser(authUrl);\n\n console.log('\\nWaiting for authentication...');\n console.log('(Press Ctrl+C to cancel)\\n');\n\n try {\n const token = await tokenPromise;\n await saveToken(token, baseUrl !== DEFAULT_API_URL ? baseUrl : undefined);\n\n console.log('\\n✅ Authentication successful!');\n console.log(`Token saved to: ${getCredentialsFilePath()}`);\n console.log('\\nYou can now use Slima MCP with any MCP-compatible AI tool.\\n');\n } catch (error) {\n console.error('\\n❌ Authentication failed:', error instanceof Error ? error.message : error);\n process.exit(1);\n }\n}\n\n/**\n * 登出(刪除 token)\n */\nexport async function runLogout(): Promise<void> {\n console.log('\\n🔓 Slima MCP Logout\\n');\n\n const credentials = await loadToken();\n if (!credentials) {\n console.log('You are not authenticated.\\n');\n return;\n }\n\n await deleteToken();\n console.log('✅ Successfully logged out.');\n console.log(`Removed credentials from: ${getCredentialsFilePath()}\\n`);\n}\n\n/**\n * 顯示目前狀態\n */\nexport async function runStatus(): Promise<void> {\n console.log('\\n📊 Slima MCP Status\\n');\n\n const credentials = await loadToken();\n if (!credentials) {\n console.log('Status: Not authenticated');\n console.log('\\nRun `slima-mcp auth` to authenticate.\\n');\n return;\n }\n\n console.log('Status: Authenticated');\n console.log(`Token: ${credentials.apiToken.substring(0, 15)}...`);\n console.log(`Authenticated at: ${credentials.createdAt}`);\n if (credentials.baseUrl) {\n console.log(`API URL: ${credentials.baseUrl}`);\n }\n console.log(`Credentials file: ${getCredentialsFilePath()}\\n`);\n}\n"],"mappings":"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","names":["z","z","result","z","z","findFileInSnapshot","result","searchByName","z","z"]}
1
+ {"version":3,"sources":["../src/cli/index.ts","../src/cli/server.ts","../src/core/utils/errors.ts","../src/core/api/client.ts","../src/core/tools/books.ts","../src/core/tools/content.ts","../src/core/tools/beta-reader.ts","../src/core/tools/files.ts","../src/core/utils/logger.ts","../src/cli/token-storage.ts","../src/cli/auth.ts"],"sourcesContent":["/**\n * Slima MCP Server - CLI Entry Point\n *\n * Usage:\n * slima-mcp - Start MCP server (requires SLIMA_API_TOKEN or prior auth)\n * slima-mcp auth - Authenticate with Slima (opens browser)\n * slima-mcp logout - Remove saved credentials\n * slima-mcp status - Show authentication status\n * slima-mcp --help - Show help\n */\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { createSlimaServer } from './server.js';\nimport { logger } from '../core/utils/logger.js';\nimport { loadToken, getCredentialsFilePath } from './token-storage.js';\nimport { runAuth, runLogout, runStatus } from './auth.js';\n\ndeclare const __VERSION__: string;\nconst VERSION = __VERSION__;\n\nfunction showHelp(): void {\n console.log(`\nSlima MCP v${VERSION}\nMCP Server for Slima - AI Writing IDE for Novel Authors\n\nUSAGE:\n slima-mcp [command]\n\nCOMMANDS:\n (none) Start the MCP server\n auth Authenticate with Slima (opens browser)\n logout Remove saved credentials\n status Show authentication status\n --help Show this help message\n --version Show version\n\nAUTHENTICATION:\n 1. Run: slima-mcp auth\n 2. Complete authentication in browser\n 3. Token is automatically saved\n\n Or set SLIMA_API_TOKEN environment variable.\n\nEXAMPLES:\n slima-mcp auth # Authenticate with browser\n slima-mcp status # Check auth status\n SLIMA_API_TOKEN=xxx slima-mcp # Use env var\n\nCONFIGURATION (for AI tools):\n Claude Desktop, Cursor, etc.:\n {\n \"mcpServers\": {\n \"slima\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"slima-mcp\"]\n }\n }\n }\n\nMORE INFO:\n https://github.com/slima-ai/slima-mcp\n`);\n}\n\nfunction showVersion(): void {\n console.log(`slima-mcp v${VERSION}`);\n}\n\nasync function startServer(): Promise<void> {\n // Use env var first, then stored token\n let apiToken = process.env.SLIMA_API_TOKEN;\n let baseUrl = process.env.SLIMA_API_URL;\n\n if (!apiToken) {\n const credentials = await loadToken();\n if (credentials) {\n apiToken = credentials.apiToken;\n baseUrl = baseUrl || credentials.baseUrl;\n }\n }\n\n if (!apiToken) {\n logger.error('Not authenticated and SLIMA_API_TOKEN not set');\n logger.error('');\n logger.error('To authenticate, run:');\n logger.error(' slima-mcp auth');\n logger.error('');\n logger.error('Or set environment variable:');\n logger.error(' SLIMA_API_TOKEN=your_token slima-mcp');\n logger.error('');\n logger.error('Get your API token from: https://app.slima.ai/settings/api-tokens');\n process.exit(1);\n }\n\n try {\n const server = createSlimaServer({\n apiToken,\n baseUrl,\n });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n logger.info('Slima MCP Server running on stdio');\n\n // Graceful shutdown\n process.on('SIGINT', async () => {\n logger.info('Shutting down...');\n await server.close();\n process.exit(0);\n });\n\n process.on('SIGTERM', async () => {\n logger.info('Shutting down...');\n await server.close();\n process.exit(0);\n });\n } catch (error) {\n logger.error('Failed to start server', error);\n process.exit(1);\n }\n}\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2);\n const command = args[0];\n\n switch (command) {\n case 'auth':\n await runAuth();\n break;\n\n case 'logout':\n await runLogout();\n break;\n\n case 'status':\n await runStatus();\n break;\n\n case '--help':\n case '-h':\n case 'help':\n showHelp();\n break;\n\n case '--version':\n case '-v':\n showVersion();\n break;\n\n case undefined:\n // Default: start MCP server\n await startServer();\n break;\n\n default:\n console.error(`Unknown command: ${command}`);\n console.error('Run `slima-mcp --help` for usage.');\n process.exit(1);\n }\n}\n\nmain();\n","/**\n * Slima MCP Server (CLI/stdio)\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { SlimaApiClient } from '../core/api/client.js';\nimport {\n registerBookTools,\n registerContentTools,\n registerBetaReaderTools,\n registerFileTools,\n} from '../core/tools/index.js';\nimport { logger } from '../core/utils/logger.js';\n\ndeclare const __VERSION__: string;\n\nexport interface ServerConfig {\n apiToken: string;\n baseUrl?: string;\n}\n\n/**\n * Create Slima MCP Server\n */\nexport function createSlimaServer(config: ServerConfig): McpServer {\n // Create API Client with static token getter (for CLI)\n const client = new SlimaApiClient({\n baseUrl: config.baseUrl || 'https://api.slima.ai',\n getToken: async () => config.apiToken,\n logger,\n });\n\n const server = new McpServer({\n name: 'slima',\n version: __VERSION__,\n });\n\n // Register tools\n registerBookTools(server, client);\n registerContentTools(server, client);\n registerBetaReaderTools(server, client, logger);\n registerFileTools(server, client);\n\n logger.info('Slima MCP Server initialized');\n logger.info(`API endpoint: ${config.baseUrl || 'https://api.slima.ai'}`);\n\n return server;\n}\n","/**\n * 錯誤處理工具\n */\n\nexport class SlimaApiError extends Error {\n constructor(\n public status: number,\n public code?: string,\n message?: string\n ) {\n super(message || `API Error: ${status}`);\n this.name = 'SlimaApiError';\n }\n}\n\nexport class AuthenticationError extends SlimaApiError {\n constructor(message = 'Invalid or expired API token') {\n super(401, 'UNAUTHORIZED', message);\n this.name = 'AuthenticationError';\n }\n}\n\nexport class NotFoundError extends SlimaApiError {\n constructor(resource = 'Resource') {\n super(404, 'NOT_FOUND', `${resource} not found`);\n this.name = 'NotFoundError';\n }\n}\n\nexport class InsufficientCreditsError extends SlimaApiError {\n constructor() {\n super(402, 'INSUFFICIENT_CREDITS', 'Insufficient credits');\n this.name = 'InsufficientCreditsError';\n }\n}\n\n/**\n * 格式化錯誤訊息供 MCP 回傳\n */\nexport function formatErrorForMcp(error: unknown): string {\n if (error instanceof SlimaApiError) {\n switch (error.code) {\n case 'NOT_FOUND':\n return 'Resource not found. Please check the token.';\n case 'UNAUTHORIZED':\n return 'Invalid API token. Please check your configuration.';\n case 'INSUFFICIENT_CREDITS':\n return 'Insufficient credits. Please top up your account.';\n case 'TOKEN_EXPIRED':\n return 'API token has expired. Please generate a new one.';\n case 'TOKEN_REVOKED':\n return 'API token has been revoked. Please generate a new one.';\n default:\n return error.message;\n }\n }\n\n if (error instanceof Error) {\n return error.message;\n }\n\n return 'An unexpected error occurred.';\n}\n","/**\n * Slima API Client\n *\n * Supports dependency injection for token retrieval,\n * allowing different strategies for CLI (file-based) vs Worker (KV-based).\n */\n\nimport {\n type Book,\n type Commit,\n type Branch,\n type Blob,\n type Persona,\n type ReaderTest,\n type ReaderTestProgress,\n type CreateReaderTestParams,\n type ApiResponse,\n type ApiError,\n type CommitsListResponse,\n type BlobsDownloadResponse,\n type McpFileReadResponse,\n type McpFileCreateResponse,\n type McpFileUpdateResponse,\n type McpFileDeleteResponse,\n type McpFileAppendResponse,\n type McpSearchResponse,\n} from './types.js';\nimport {\n SlimaApiError,\n AuthenticationError,\n NotFoundError,\n InsufficientCreditsError,\n} from '../utils/errors.js';\n\n// Export for logger injection (optional)\nexport interface Logger {\n debug: (message: string, ...args: unknown[]) => void;\n info: (message: string, ...args: unknown[]) => void;\n warn: (message: string, ...args: unknown[]) => void;\n error: (message: string, error?: unknown) => void;\n}\n\n// Default no-op logger\nconst defaultLogger: Logger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\n/**\n * API Client Configuration (new style with token getter)\n */\nexport interface ApiClientConfig {\n baseUrl: string;\n getToken: () => Promise<string>; // Async token getter (injected)\n logger?: Logger;\n}\n\n/**\n * Legacy config format (backwards compatibility)\n * @deprecated Use ApiClientConfig with getToken instead\n */\nexport interface SlimaConfig {\n baseUrl: string;\n token: string;\n}\n\n// Type guard to check if config is legacy format\nfunction isLegacyConfig(config: ApiClientConfig | SlimaConfig): config is SlimaConfig {\n return 'token' in config && typeof (config as SlimaConfig).token === 'string';\n}\n\nexport class SlimaApiClient {\n private readonly baseUrl: string;\n private readonly getToken: () => Promise<string>;\n private readonly logger: Logger;\n\n constructor(config: ApiClientConfig | SlimaConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, ''); // Remove trailing slash\n\n // Support both legacy and new config formats\n if (isLegacyConfig(config)) {\n const staticToken = config.token;\n this.getToken = async () => staticToken;\n this.logger = defaultLogger;\n } else {\n this.getToken = config.getToken;\n this.logger = config.logger || defaultLogger;\n }\n }\n\n /**\n * Send API request\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const token = await this.getToken();\n\n this.logger.debug(`${method} ${path}`);\n\n const response = await fetch(url, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n 'User-Agent': 'slima-mcp-server/0.1.0',\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n await this.handleErrorResponse(response);\n }\n\n const json = (await response.json()) as ApiResponse<T>;\n return json.data;\n }\n\n /**\n * Handle error response\n */\n private async handleErrorResponse(response: Response): Promise<never> {\n let errorData: ApiError | undefined;\n\n try {\n errorData = (await response.json()) as ApiError;\n } catch {\n // Cannot parse JSON\n }\n\n const code = errorData?.error?.code;\n const message = errorData?.error?.message;\n\n switch (response.status) {\n case 401:\n throw new AuthenticationError(message);\n case 404:\n throw new NotFoundError(message);\n case 402:\n throw new InsufficientCreditsError();\n default:\n throw new SlimaApiError(response.status, code, message);\n }\n }\n\n // === Book Operations ===\n\n /**\n * List all books\n */\n async listBooks(): Promise<Book[]> {\n return this.request<Book[]>('GET', '/api/v1/books');\n }\n\n /**\n * Get book details\n */\n async getBook(token: string): Promise<Book> {\n return this.request<Book>('GET', `/api/v1/books/${token}`);\n }\n\n /**\n * Create a new book\n */\n async createBook(params: {\n title: string;\n authorName?: string;\n description?: string;\n }): Promise<Book> {\n return this.request<Book>('POST', '/api/v1/books', {\n book: {\n title: params.title,\n author_name: params.authorName,\n description: params.description,\n },\n });\n }\n\n // === Version Control ===\n\n /**\n * List commits\n */\n async listCommits(bookToken: string, limit = 10): Promise<Commit[]> {\n const response = await this.request<CommitsListResponse>(\n 'GET',\n `/api/v1/books/${bookToken}/commits?limit=${limit}`\n );\n return response.commits;\n }\n\n /**\n * Get commit details\n */\n async getCommit(bookToken: string, commitToken: string): Promise<Commit> {\n return this.request<Commit>(\n 'GET',\n `/api/v1/books/${bookToken}/commits/${commitToken}`\n );\n }\n\n /**\n * List branches\n */\n async listBranches(bookToken: string): Promise<Branch[]> {\n return this.request<Branch[]>('GET', `/api/v1/books/${bookToken}/branches`);\n }\n\n /**\n * Download blobs\n */\n async downloadBlobs(bookToken: string, hashes: string[]): Promise<Blob[]> {\n const response = await this.request<BlobsDownloadResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/blobs/download`,\n { hashes }\n );\n return response.blobs;\n }\n\n // === Persona ===\n\n /**\n * List personas\n */\n async listPersonas(genre?: string): Promise<Persona[]> {\n const params = genre ? `?genre=${encodeURIComponent(genre)}` : '';\n return this.request<Persona[]>('GET', `/api/v1/personas${params}`);\n }\n\n /**\n * Get persona details\n */\n async getPersona(token: string): Promise<Persona> {\n return this.request<Persona>('GET', `/api/v1/personas/${token}`);\n }\n\n // === Reader Test ===\n\n /**\n * Create reader test\n */\n async createReaderTest(\n bookToken: string,\n params: CreateReaderTestParams\n ): Promise<ReaderTest> {\n return this.request<ReaderTest>(\n 'POST',\n `/api/v1/books/${bookToken}/reader_tests`,\n {\n reader_test: {\n report_type: params.reportType || 'chapter',\n persona_tokens: params.personaTokens,\n commit_token: params.commitToken,\n model: params.model,\n scope_config: params.scopeConfig,\n },\n content: params.content,\n }\n );\n }\n\n /**\n * Get reader test progress\n */\n async getReaderTestProgress(\n bookToken: string,\n testToken: string\n ): Promise<ReaderTestProgress> {\n return this.request<ReaderTestProgress>(\n 'GET',\n `/api/v1/books/${bookToken}/reader_tests/${testToken}/progress`\n );\n }\n\n /**\n * Get reader test result\n */\n async getReaderTest(bookToken: string, testToken: string): Promise<ReaderTest> {\n return this.request<ReaderTest>(\n 'GET',\n `/api/v1/books/${bookToken}/reader_tests/${testToken}`\n );\n }\n\n // === MCP File Operations ===\n\n /**\n * Read file content\n */\n async readFile(bookToken: string, path: string): Promise<McpFileReadResponse> {\n return this.request<McpFileReadResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/read`,\n { path }\n );\n }\n\n /**\n * Create file\n */\n async createFile(\n bookToken: string,\n params: { path: string; content?: string; parentPath?: string; commitMessage?: string }\n ): Promise<McpFileCreateResponse> {\n return this.request<McpFileCreateResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/create`,\n {\n path: params.path,\n content: params.content || '',\n parent_path: params.parentPath,\n commit_message: params.commitMessage,\n }\n );\n }\n\n /**\n * Update (overwrite) file content\n */\n async updateFile(\n bookToken: string,\n params: { path: string; content: string; commitMessage?: string }\n ): Promise<McpFileUpdateResponse> {\n return this.request<McpFileUpdateResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/update`,\n {\n path: params.path,\n content: params.content,\n commit_message: params.commitMessage,\n }\n );\n }\n\n /**\n * Delete file\n */\n async deleteFile(\n bookToken: string,\n params: { path: string; commitMessage?: string }\n ): Promise<McpFileDeleteResponse> {\n return this.request<McpFileDeleteResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/delete`,\n {\n path: params.path,\n commit_message: params.commitMessage,\n }\n );\n }\n\n /**\n * Append content to file\n */\n async appendToFile(\n bookToken: string,\n params: { path: string; content: string; commitMessage?: string }\n ): Promise<McpFileAppendResponse> {\n return this.request<McpFileAppendResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/append`,\n {\n path: params.path,\n content: params.content,\n commit_message: params.commitMessage,\n }\n );\n }\n\n /**\n * Search file content\n */\n async searchFiles(\n bookToken: string,\n params: { query: string; fileTypes?: string[]; limit?: number }\n ): Promise<McpSearchResponse> {\n return this.request<McpSearchResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/search`,\n {\n query: params.query,\n file_types: params.fileTypes,\n limit: params.limit,\n }\n );\n }\n}\n","/**\n * Book Tools\n */\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { SlimaApiClient } from '../api/client.js';\nimport { formatErrorForMcp } from '../utils/errors.js';\nimport type { FileSnapshot } from '../api/types.js';\n\n/**\n * Register book-related tools\n */\nexport function registerBookTools(\n server: McpServer,\n client: SlimaApiClient\n): void {\n // === create_book ===\n server.tool(\n 'create_book',\n 'Create a new book in your Slima library',\n {\n title: z.string().describe('Book title (required)'),\n author_name: z.string().optional().describe('Author name (optional)'),\n description: z.string().optional().describe('Book description (optional)'),\n },\n async ({ title, author_name, description }) => {\n try {\n const book = await client.createBook({\n title,\n authorName: author_name,\n description,\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `✅ Book created successfully!\\n\\n- **Title**: ${book.title}\\n- **Token**: ${book.token}\\n- **Author**: ${book.authorName || 'N/A'}\\n\\nYou can now add files using create_file with book_token: ${book.token}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === list_books ===\n server.tool(\n 'list_books',\n 'List all books in your Slima library',\n {},\n async () => {\n try {\n const books = await client.listBooks();\n\n if (books.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No books found in your library.',\n },\n ],\n };\n }\n\n const formatted = books\n .map((book) => {\n const words = book.totalWordCount?.toLocaleString() || '0';\n return `- **${book.title}** (${book.token})\\n Author: ${book.authorName || 'N/A'} | Words: ${words}`;\n })\n .join('\\n\\n');\n\n return {\n content: [\n {\n type: 'text',\n text: `Found ${books.length} book(s):\\n\\n${formatted}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === get_book ===\n server.tool(\n 'get_book',\n 'Get details of a specific book',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n },\n async ({ book_token }) => {\n try {\n const book = await client.getBook(book_token);\n\n const text = `# ${book.title}\n\n- **Token**: ${book.token}\n- **Author**: ${book.authorName || 'N/A'}\n- **Description**: ${book.description || 'N/A'}\n- **Language**: ${book.language || 'N/A'}\n- **Total Words**: ${book.totalWordCount?.toLocaleString() || '0'}\n- **Manuscript Words**: ${book.manuscriptWordCount?.toLocaleString() || '0'}\n- **Created**: ${book.createdAt}\n- **Updated**: ${book.updatedAt}`;\n\n return {\n content: [{ type: 'text', text }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === get_book_structure ===\n server.tool(\n 'get_book_structure',\n 'Get the file/folder structure of a book',\n {\n book_token: z.string().describe('Book token'),\n },\n async ({ book_token }) => {\n try {\n const commits = await client.listCommits(book_token, 1);\n\n if (commits.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No commits found. The book may be empty.',\n },\n ],\n };\n }\n\n const snapshot = commits[0].filesSnapshot;\n const tree = formatFileTree(snapshot);\n\n return {\n content: [\n {\n type: 'text',\n text: `File structure for book ${book_token}:\\n\\n${tree}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === get_writing_stats ===\n server.tool(\n 'get_writing_stats',\n 'Get writing statistics for a book',\n {\n book_token: z.string().describe('Book token'),\n },\n async ({ book_token }) => {\n try {\n const [book, commits] = await Promise.all([\n client.getBook(book_token),\n client.listCommits(book_token, 10),\n ]);\n\n // Calculate recent progress\n let wordChange = 0;\n if (commits.length > 1) {\n const latest = commits[0].totalWordCount || 0;\n const oldest = commits[commits.length - 1].totalWordCount || 0;\n wordChange = latest - oldest;\n }\n\n const sign = wordChange >= 0 ? '+' : '';\n const progressText =\n commits.length > 1\n ? `${sign}${wordChange.toLocaleString()} words (last ${commits.length} commits)`\n : 'N/A (not enough history)';\n\n const text = `# Writing Stats: ${book.title}\n\n- **Total Words**: ${book.totalWordCount?.toLocaleString() || '0'}\n- **Manuscript Words**: ${book.manuscriptWordCount?.toLocaleString() || '0'}\n- **Recent Progress**: ${progressText}\n- **Commits Analyzed**: ${commits.length}\n- **Language**: ${book.language || 'N/A'}`;\n\n return {\n content: [{ type: 'text', text }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n}\n\n/**\n * Format file tree\n */\nfunction formatFileTree(files: FileSnapshot[], indent = 0): string {\n return files\n .sort((a, b) => a.position - b.position)\n .map((f) => {\n const prefix = ' '.repeat(indent);\n const icon = f.kind === 'folder' ? '📁' : '📄';\n const words = f.kind === 'file' && f.wordCount ? ` (${f.wordCount} words)` : '';\n const manuscript = f.isManuscript ? ' [M]' : '';\n let line = `${prefix}${icon} ${f.name}${words}${manuscript}`;\n\n if (f.children && f.children.length > 0) {\n line += '\\n' + formatFileTree(f.children, indent + 1);\n }\n\n return line;\n })\n .join('\\n');\n}\n","/**\n * Content Access Tools\n */\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { SlimaApiClient } from '../api/client.js';\nimport { formatErrorForMcp } from '../utils/errors.js';\nimport type { FileSnapshot } from '../api/types.js';\n\n/**\n * Register content access tools\n */\nexport function registerContentTools(\n server: McpServer,\n client: SlimaApiClient\n): void {\n // === get_chapter ===\n server.tool(\n 'get_chapter',\n 'Read the content of a chapter or file from a book',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n file_path: z.string().describe('File path (e.g., /chapters/01.md or chapter-01)'),\n },\n async ({ book_token, file_path }) => {\n try {\n // 1. Get latest commit\n const commits = await client.listCommits(book_token, 1);\n\n if (commits.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No commits found. The book may be empty.',\n },\n ],\n };\n }\n\n // 2. Find file in snapshot\n const file = findFileInSnapshot(commits[0].filesSnapshot, file_path);\n\n if (!file) {\n // List available files\n const availableFiles = listAllFiles(commits[0].filesSnapshot);\n const fileList = availableFiles.slice(0, 10).join('\\n- ');\n const moreText =\n availableFiles.length > 10\n ? `\\n... and ${availableFiles.length - 10} more files`\n : '';\n\n return {\n content: [\n {\n type: 'text',\n text: `File not found: ${file_path}\\n\\nAvailable files:\\n- ${fileList}${moreText}\\n\\nTip: Use get_book_structure to see the full file tree.`,\n },\n ],\n isError: true,\n };\n }\n\n if (file.kind === 'folder') {\n return {\n content: [\n {\n type: 'text',\n text: `\"${file_path}\" is a folder, not a file. Please specify a file path.`,\n },\n ],\n isError: true,\n };\n }\n\n if (!file.blobHash) {\n return {\n content: [\n {\n type: 'text',\n text: `File \"${file_path}\" has no content (empty file).`,\n },\n ],\n };\n }\n\n // 3. Download blob content\n const blobs = await client.downloadBlobs(book_token, [file.blobHash]);\n\n if (blobs.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'Could not retrieve file content. The blob may be missing.',\n },\n ],\n isError: true,\n };\n }\n\n const content = blobs[0].content;\n const wordInfo = file.wordCount ? ` (${file.wordCount} words)` : '';\n\n return {\n content: [\n {\n type: 'text',\n text: `# ${file.name}${wordInfo}\\n\\n${content}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n}\n\n/**\n * Find file in snapshot\n * Supports multiple path formats:\n * - Full path: /chapters/01.md\n * - Relative path: chapters/01.md\n * - Filename: 01.md\n * - Token: chapter-01\n */\nfunction findFileInSnapshot(\n files: FileSnapshot[],\n path: string\n): FileSnapshot | null {\n // Normalize path\n const normalizedPath = path.replace(/^\\//, '').toLowerCase();\n const pathParts = normalizedPath.split('/');\n\n // Recursive search\n function search(\n items: FileSnapshot[],\n remainingParts: string[]\n ): FileSnapshot | null {\n for (const item of items) {\n const itemNameLower = item.name.toLowerCase();\n const itemTokenLower = item.token.toLowerCase();\n\n // Full path match\n if (remainingParts.length === 1) {\n const target = remainingParts[0];\n if (\n itemNameLower === target ||\n itemTokenLower === target ||\n itemNameLower.replace(/\\.[^.]+$/, '') === target.replace(/\\.[^.]+$/, '')\n ) {\n return item;\n }\n }\n\n // Folder match, continue searching\n if (item.kind === 'folder' && item.children) {\n if (\n itemNameLower === remainingParts[0] ||\n itemTokenLower === remainingParts[0]\n ) {\n const result = search(item.children, remainingParts.slice(1));\n if (result) return result;\n }\n\n // Also try searching in subfolders directly\n const result = search(item.children, remainingParts);\n if (result) return result;\n }\n }\n\n return null;\n }\n\n // Try full path first\n let result = search(files, pathParts);\n if (result) return result;\n\n // Then try searching by filename only\n result = searchByName(files, pathParts[pathParts.length - 1]);\n return result;\n}\n\n/**\n * Search by filename only\n */\nfunction searchByName(\n files: FileSnapshot[],\n name: string\n): FileSnapshot | null {\n const nameLower = name.toLowerCase();\n\n for (const item of files) {\n const itemNameLower = item.name.toLowerCase();\n const itemTokenLower = item.token.toLowerCase();\n\n if (\n itemNameLower === nameLower ||\n itemTokenLower === nameLower ||\n itemNameLower.replace(/\\.[^.]+$/, '') === nameLower.replace(/\\.[^.]+$/, '')\n ) {\n return item;\n }\n\n if (item.kind === 'folder' && item.children) {\n const result = searchByName(item.children, name);\n if (result) return result;\n }\n }\n\n return null;\n}\n\n/**\n * List all file paths\n */\nfunction listAllFiles(files: FileSnapshot[], prefix = ''): string[] {\n const result: string[] = [];\n\n for (const item of files) {\n const path = prefix ? `${prefix}/${item.name}` : item.name;\n\n if (item.kind === 'file') {\n result.push(path);\n } else if (item.children) {\n result.push(...listAllFiles(item.children, path));\n }\n }\n\n return result;\n}\n","/**\n * Beta Reader Tools\n */\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { SlimaApiClient, Logger } from '../api/client.js';\nimport { formatErrorForMcp } from '../utils/errors.js';\nimport type { FileSnapshot, ReaderTest, IndividualFeedback, AggregatedMetrics } from '../api/types.js';\n\n// Default no-op logger\nconst defaultLogger: Logger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\n/**\n * Register Beta Reader tools\n */\nexport function registerBetaReaderTools(\n server: McpServer,\n client: SlimaApiClient,\n logger: Logger = defaultLogger\n): void {\n // === list_personas ===\n server.tool(\n 'list_personas',\n 'List available virtual reader personas for beta reading feedback',\n {\n genre: z\n .string()\n .optional()\n .describe('Filter by genre (e.g., fantasy, romance, scifi)'),\n },\n async ({ genre }) => {\n try {\n const personas = await client.listPersonas(genre);\n\n if (personas.length === 0) {\n const genreText = genre ? ` for genre \"${genre}\"` : '';\n return {\n content: [\n {\n type: 'text',\n text: `No personas found${genreText}.`,\n },\n ],\n };\n }\n\n const formatted = personas\n .map((p) => {\n const name = p.displayLabels?.zh || p.displayLabels?.en || p.slug;\n const tags = p.genreTags?.join(', ') || 'General';\n const demo = p.demographics || {};\n const gender = demo.gender || 'N/A';\n const age = demo.ageRange || 'N/A';\n\n return `- **${name}** (\\`${p.token}\\`)\n Gender: ${gender} | Age: ${age}\n Genres: ${tags}`;\n })\n .join('\\n\\n');\n\n return {\n content: [\n {\n type: 'text',\n text: `Available Personas (${personas.length}):\\n\\n${formatted}\\n\\n**Tip**: Use the persona token with \\`analyze_chapter\\` to get feedback from a specific reader.`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === analyze_chapter ===\n server.tool(\n 'analyze_chapter',\n 'Get AI Beta Reader feedback on a chapter from a specific reader persona. This may take a moment to complete.',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n file_path: z.string().describe('Chapter file path (e.g., /chapters/01.md)'),\n persona_token: z\n .string()\n .describe('Persona token (e.g., psn_xxx). Use list_personas to find available personas.'),\n },\n async ({ book_token, file_path, persona_token }) => {\n try {\n // 1. Get latest commit and chapter content\n const commits = await client.listCommits(book_token, 1);\n\n if (commits.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No commits found. The book may be empty.',\n },\n ],\n isError: true,\n };\n }\n\n // 2. Find file\n const file = findFileInSnapshot(commits[0].filesSnapshot, file_path);\n\n if (!file) {\n return {\n content: [\n {\n type: 'text',\n text: `File not found: ${file_path}. Use get_book_structure to see available files.`,\n },\n ],\n isError: true,\n };\n }\n\n if (!file.blobHash) {\n return {\n content: [\n {\n type: 'text',\n text: `File \"${file_path}\" has no content (empty file).`,\n },\n ],\n isError: true,\n };\n }\n\n // 3. Download content\n const blobs = await client.downloadBlobs(book_token, [file.blobHash]);\n\n if (blobs.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'Could not retrieve file content.',\n },\n ],\n isError: true,\n };\n }\n\n const content = blobs[0].content;\n\n // 4. Create Reader Test\n logger.info(`Creating reader test for ${file_path} with persona ${persona_token}`);\n\n const test = await client.createReaderTest(book_token, {\n reportType: 'chapter',\n personaTokens: [persona_token],\n commitToken: commits[0].token,\n scopeConfig: { chapters: [file_path] },\n content: content,\n });\n\n // 5. Poll for completion (max 90 seconds)\n const result = await pollForCompletion(\n () => client.getReaderTestProgress(book_token, test.token),\n 90000,\n logger\n );\n\n if (result.status === 'failed') {\n return {\n content: [\n {\n type: 'text',\n text: 'Analysis failed. Please try again later.',\n },\n ],\n isError: true,\n };\n }\n\n if (result.status !== 'completed') {\n return {\n content: [\n {\n type: 'text',\n text: `Analysis is still in progress. You can check the status later.\\n\\nTest ID: ${test.token}`,\n },\n ],\n };\n }\n\n // 6. Get full result\n const fullResult = await client.getReaderTest(book_token, test.token);\n\n // 7. Format feedback\n const feedback = formatBetaReaderFeedback(fullResult, file.name);\n\n return {\n content: [{ type: 'text', text: feedback }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n}\n\n/**\n * Poll for completion\n */\nasync function pollForCompletion(\n checkFn: () => Promise<{ status: string; progress?: number }>,\n timeoutMs: number,\n logger: Logger\n): Promise<{ status: string }> {\n const startTime = Date.now();\n const pollInterval = 3000; // 3 seconds\n\n while (Date.now() - startTime < timeoutMs) {\n const result = await checkFn();\n logger.debug(`Poll status: ${result.status}, progress: ${result.progress || 'N/A'}`);\n\n if (result.status === 'completed' || result.status === 'failed') {\n return result;\n }\n\n await sleep(pollInterval);\n }\n\n return { status: 'timeout' };\n}\n\n/**\n * Sleep\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Format Beta Reader feedback\n */\nfunction formatBetaReaderFeedback(test: ReaderTest, fileName: string): string {\n const feedbacks = test.individualFeedbacks || [];\n const metrics = test.aggregatedMetrics;\n\n if (feedbacks.length === 0 && !metrics) {\n return 'No feedback available.';\n }\n\n let output = `# Beta Reader Feedback: ${fileName}\\n\\n`;\n\n // Aggregated metrics summary\n if (metrics) {\n output += formatAggregatedMetrics(metrics);\n }\n\n // Individual persona feedback\n if (feedbacks.length > 0) {\n output += `\\n---\\n\\n## Individual Reader Feedback\\n\\n`;\n output += feedbacks.map(formatIndividualFeedback).join('\\n---\\n\\n');\n }\n\n return output;\n}\n\n/**\n * Format aggregated metrics\n */\nfunction formatAggregatedMetrics(metrics: AggregatedMetrics): string {\n let output = `## Summary Metrics\\n\\n`;\n\n // Overall\n if (metrics.overall) {\n const o = metrics.overall;\n output += `### Overall Scores\\n`;\n if (o.avgContinueReading) output += `- Continue Reading Intent: ${o.avgContinueReading.toFixed(1)}/10\\n`;\n if (o.avgRecommendation) output += `- Recommendation Score: ${o.avgRecommendation.toFixed(1)}/10\\n`;\n if (o.avgWantMore) output += `- Want More: ${o.avgWantMore.toFixed(1)}/10\\n`;\n if (o.dnfRisk) output += `- DNF Risk: ${o.dnfRisk}\\n`;\n output += '\\n';\n }\n\n // Kindle Rating\n if (metrics.kindleRating) {\n output += `### Predicted Kindle Rating\\n`;\n output += `- Average Score: ⭐ ${metrics.kindleRating.avgScore.toFixed(1)}/5\\n`;\n if (metrics.kindleRating.individualScores) {\n const scores = metrics.kindleRating.individualScores\n .map(s => `${s.personaName || 'Reader'}: ${s.score}/5 (${s.confidence})`)\n .join(', ');\n output += `- Individual Scores: ${scores}\\n`;\n }\n output += '\\n';\n }\n\n // Characters\n if (metrics.characters) {\n const c = metrics.characters;\n output += `### Character Evaluation\\n`;\n if (c.avgProtagonistLikability) output += `- Protagonist Likability: ${c.avgProtagonistLikability.toFixed(1)}/10\\n`;\n if (c.avgMotivationClarity) output += `- Motivation Clarity: ${c.avgMotivationClarity.toFixed(1)}/10\\n`;\n if (c.avgDialogueNaturalness) output += `- Dialogue Naturalness: ${c.avgDialogueNaturalness.toFixed(1)}/10\\n`;\n output += '\\n';\n }\n\n // Pacing\n if (metrics.pacing) {\n const p = metrics.pacing;\n output += `### Pacing Evaluation\\n`;\n if (p.avgOverall) output += `- Overall Pacing: ${p.avgOverall.toFixed(1)}/10\\n`;\n if (p.consensus) output += `- Consensus: ${p.consensus}\\n`;\n if (p.commonDnfTriggers && p.commonDnfTriggers.length > 0) {\n output += `- Common DNF Triggers:\\n`;\n p.commonDnfTriggers.slice(0, 3).forEach(t => {\n output += ` - ${t.trigger} (${t.count}x)\\n`;\n });\n }\n output += '\\n';\n }\n\n // Market\n if (metrics.market) {\n const m = metrics.market;\n output += `### Market Positioning\\n`;\n if (m.avgWordOfMouth) output += `- Word of Mouth Potential: ${m.avgWordOfMouth.toFixed(1)}/10\\n`;\n if (m.topComparableBooks && m.topComparableBooks.length > 0) {\n const books = m.topComparableBooks.slice(0, 3).map(b => b.title).join(', ');\n output += `- Comparable Books: ${books}\\n`;\n }\n output += '\\n';\n }\n\n return output;\n}\n\n/**\n * Format individual feedback\n */\nfunction formatIndividualFeedback(feedback: IndividualFeedback): string {\n const name = feedback.personaName || feedback.personaToken;\n let output = `### ${name}\\n\\n`;\n\n // Overall scores\n if (feedback.overall) {\n const o = feedback.overall;\n if (o.continueReading) output += `**Continue Reading Intent**: ${o.continueReading}/10\\n`;\n if (o.recommendation) output += `**Recommendation**: ${o.recommendation}/10\\n`;\n if (o.dnfRisk) output += `**DNF Risk**: ${o.dnfRisk}\\n`;\n output += '\\n';\n }\n\n // Kindle Rating\n if (feedback.kindleRating) {\n output += `**Kindle Rating**: ⭐ ${feedback.kindleRating.score}/5`;\n if (feedback.kindleRating.rationale) {\n output += ` - ${feedback.kindleRating.rationale}`;\n }\n output += '\\n\\n';\n }\n\n // Detailed Feedback\n if (feedback.detailedFeedback) {\n const d = feedback.detailedFeedback;\n\n if (d.whatWorked) {\n output += `**What Worked**:\\n${d.whatWorked}\\n\\n`;\n }\n\n if (d.whatDidntWork) {\n output += `**What Didn't Work**:\\n${d.whatDidntWork}\\n\\n`;\n }\n\n if (d.strongestElement) {\n output += `**Strongest Element**: ${d.strongestElement}\\n`;\n }\n\n if (d.weakestElement) {\n output += `**Weakest Element**: ${d.weakestElement}\\n`;\n }\n\n if (d.wouldYouBuy) {\n output += `**Would You Buy**: ${d.wouldYouBuy}\\n`;\n }\n\n if (d.specificSuggestions && d.specificSuggestions.length > 0) {\n output += `\\n**Specific Suggestions**:\\n`;\n d.specificSuggestions.forEach((s, i) => {\n output += `${i + 1}. ${s}\\n`;\n });\n }\n\n if (d.finalThoughts) {\n output += `\\n**Final Thoughts**:\\n${d.finalThoughts}\\n`;\n }\n }\n\n // Emotional Journey\n if (feedback.emotionalJourney) {\n const e = feedback.emotionalJourney;\n if (e.emotionalArc) {\n output += `\\n**Emotional Arc**: ${e.emotionalArc}\\n`;\n }\n if (e.peakMoments && e.peakMoments.length > 0) {\n const moments = e.peakMoments.map(m =>\n typeof m === 'string' ? m : (m as { moment?: string; description?: string }).moment || (m as { moment?: string; description?: string }).description || JSON.stringify(m)\n );\n output += `**Peak Moments**: ${moments.join(', ')}\\n`;\n }\n if (e.overallMood) {\n output += `**Overall Mood**: ${e.overallMood}\\n`;\n }\n }\n\n return output;\n}\n\n/**\n * Find file in snapshot\n */\nfunction findFileInSnapshot(\n files: FileSnapshot[],\n path: string\n): FileSnapshot | null {\n const normalizedPath = path.replace(/^\\//, '').toLowerCase();\n const pathParts = normalizedPath.split('/');\n\n function search(\n items: FileSnapshot[],\n remainingParts: string[]\n ): FileSnapshot | null {\n for (const item of items) {\n const itemNameLower = item.name.toLowerCase();\n const itemTokenLower = item.token.toLowerCase();\n\n if (remainingParts.length === 1) {\n const target = remainingParts[0];\n if (\n itemNameLower === target ||\n itemTokenLower === target ||\n itemNameLower.replace(/\\.[^.]+$/, '') === target.replace(/\\.[^.]+$/, '')\n ) {\n return item;\n }\n }\n\n if (item.kind === 'folder' && item.children) {\n if (\n itemNameLower === remainingParts[0] ||\n itemTokenLower === remainingParts[0]\n ) {\n const result = search(item.children, remainingParts.slice(1));\n if (result) return result;\n }\n\n const result = search(item.children, remainingParts);\n if (result) return result;\n }\n }\n\n return null;\n }\n\n let result = search(files, pathParts);\n if (result) return result;\n\n result = searchByName(files, pathParts[pathParts.length - 1]);\n return result;\n}\n\nfunction searchByName(\n files: FileSnapshot[],\n name: string\n): FileSnapshot | null {\n const nameLower = name.toLowerCase();\n\n for (const item of files) {\n const itemNameLower = item.name.toLowerCase();\n const itemTokenLower = item.token.toLowerCase();\n\n if (\n itemNameLower === nameLower ||\n itemTokenLower === nameLower ||\n itemNameLower.replace(/\\.[^.]+$/, '') === nameLower.replace(/\\.[^.]+$/, '')\n ) {\n return item;\n }\n\n if (item.kind === 'folder' && item.children) {\n const result = searchByName(item.children, name);\n if (result) return result;\n }\n }\n\n return null;\n}\n","/**\n * MCP File Operations Tools\n *\n * Provides tools for AI to read, write, create, delete, and search files in books.\n */\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { SlimaApiClient } from '../api/client.js';\nimport { formatErrorForMcp } from '../utils/errors.js';\n\n/**\n * Register file operation tools\n */\nexport function registerFileTools(\n server: McpServer,\n client: SlimaApiClient\n): void {\n // === read_file ===\n server.tool(\n 'read_file',\n 'Read the content of any file in a book by its path',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path (e.g., \"characters/protagonist.md\" or \"chapter-01.md\")'),\n },\n async ({ book_token, path }) => {\n try {\n const result = await client.readFile(book_token, path);\n\n const fileInfo = [\n `# ${result.file.name}`,\n '',\n `**Path:** ${result.file.path}`,\n `**Words:** ${result.file.wordCount}`,\n `**Type:** ${result.file.fileType || 'unknown'}`,\n '',\n '---',\n '',\n result.content || '(empty file)',\n ].join('\\n');\n\n return {\n content: [{ type: 'text', text: fileInfo }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === write_file ===\n server.tool(\n 'write_file',\n 'Replace the entire content of an existing file (creates a new commit)',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path (e.g., \"characters/protagonist.md\")'),\n content: z.string().describe('New content to write'),\n commit_message: z.string().optional().describe('Commit message (optional, auto-generated if not provided)'),\n },\n async ({ book_token, path, content, commit_message }) => {\n try {\n const result = await client.updateFile(book_token, {\n path,\n content,\n commitMessage: commit_message,\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `File updated successfully.\\n\\nCommit: ${result.commit.token}\\nMessage: ${result.commit.name || result.commit.message}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === edit_file ===\n server.tool(\n 'edit_file',\n 'Edit a file by replacing a specific text segment (search and replace). Use this for precise edits instead of rewriting the entire file.',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path (e.g., \"characters/protagonist.md\")'),\n old_string: z.string().describe('The exact text to find and replace (must match exactly, including whitespace)'),\n new_string: z.string().describe('The new text to replace it with'),\n replace_all: z.boolean().optional().describe('Replace all occurrences (default: false, replaces only first match)'),\n commit_message: z.string().optional().describe('Commit message (optional, auto-generated if not provided)'),\n },\n async ({ book_token, path, old_string, new_string, replace_all, commit_message }) => {\n try {\n // 1. Read current content\n const fileData = await client.readFile(book_token, path);\n const currentContent = fileData.content || '';\n\n // 2. Check if old_string exists\n if (!currentContent.includes(old_string)) {\n return {\n content: [\n {\n type: 'text',\n text: `❌ Edit failed: Could not find the specified text in the file.\\n\\n**Looking for:**\\n\\`\\`\\`\\n${old_string.slice(0, 200)}${old_string.length > 200 ? '...' : ''}\\n\\`\\`\\`\\n\\n**Tip:** Make sure the text matches exactly, including whitespace and line breaks.`,\n },\n ],\n isError: true,\n };\n }\n\n // 3. Check for uniqueness (if not replace_all)\n if (!replace_all) {\n const occurrences = currentContent.split(old_string).length - 1;\n if (occurrences > 1) {\n return {\n content: [\n {\n type: 'text',\n text: `❌ Edit failed: Found ${occurrences} occurrences of the text. Please provide a more specific/longer text segment to ensure a unique match, or set replace_all: true to replace all occurrences.`,\n },\n ],\n isError: true,\n };\n }\n }\n\n // 4. Perform replacement\n const newContent = replace_all\n ? currentContent.split(old_string).join(new_string)\n : currentContent.replace(old_string, new_string);\n\n // 5. Write back\n const result = await client.updateFile(book_token, {\n path,\n content: newContent,\n commitMessage: commit_message || `Edit ${path}`,\n });\n\n const replacementCount = replace_all\n ? currentContent.split(old_string).length - 1\n : 1;\n\n return {\n content: [\n {\n type: 'text',\n text: `✅ File edited successfully.\\n\\n**Replacements:** ${replacementCount}\\n**Commit:** ${result.commit.token}\\n**Message:** ${result.commit.name || result.commit.message}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === create_file ===\n server.tool(\n 'create_file',\n 'Create a new file in a book (creates a new commit)',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path including parent folders (e.g., \"characters/new-character.md\")'),\n content: z.string().optional().describe('Initial content (optional, defaults to empty)'),\n commit_message: z.string().optional().describe('Commit message (optional, auto-generated if not provided)'),\n },\n async ({ book_token, path, content, commit_message }) => {\n try {\n const result = await client.createFile(book_token, {\n path,\n content: content || '',\n commitMessage: commit_message,\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `File created successfully.\\n\\nFile Token: ${result.fileToken}\\nCommit: ${result.commit.token}\\nMessage: ${result.commit.name || result.commit.message}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === delete_file ===\n server.tool(\n 'delete_file',\n 'Delete a file from a book (creates a new commit)',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path to delete (e.g., \"old-notes.md\")'),\n commit_message: z.string().optional().describe('Commit message (optional, auto-generated if not provided)'),\n },\n async ({ book_token, path, commit_message }) => {\n try {\n const result = await client.deleteFile(book_token, {\n path,\n commitMessage: commit_message,\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `File deleted successfully.\\n\\nCommit: ${result.commit.token}\\nMessage: ${result.commit.name || result.commit.message}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === append_to_file ===\n server.tool(\n 'append_to_file',\n 'Append content to the end of an existing file (creates a new commit)',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path (e.g., \"notes.md\")'),\n content: z.string().describe('Content to append'),\n commit_message: z.string().optional().describe('Commit message (optional, auto-generated if not provided)'),\n },\n async ({ book_token, path, content, commit_message }) => {\n try {\n const result = await client.appendToFile(book_token, {\n path,\n content,\n commitMessage: commit_message,\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `Content appended successfully.\\n\\nCommit: ${result.commit.token}\\nMessage: ${result.commit.name || result.commit.message}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === search_content ===\n server.tool(\n 'search_content',\n 'Search for content across all files in a book',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n query: z.string().describe('Search query (e.g., \"blue eyes\" or \"protagonist name\")'),\n file_types: z.array(z.string()).optional().describe('Filter by file types (e.g., [\"markdown\", \"txt\"])'),\n limit: z.number().optional().describe('Maximum number of matches to return (default: 20)'),\n },\n async ({ book_token, query, file_types, limit }) => {\n try {\n const result = await client.searchFiles(book_token, {\n query,\n fileTypes: file_types,\n limit,\n });\n\n if (result.matches.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: `No matches found for \"${query}\".`,\n },\n ],\n };\n }\n\n const output: string[] = [\n `# Search Results for \"${query}\"`,\n '',\n `Found ${result.matches.length} file(s) with matches:`,\n '',\n ];\n\n for (const match of result.matches) {\n output.push(`## ${match.file.path}`);\n output.push(`Words: ${match.file.wordCount} | Matches: ${match.matchCount}`);\n output.push('');\n\n for (const snippet of match.snippets) {\n output.push(`> ${snippet.text}`);\n output.push('');\n }\n }\n\n return {\n content: [{ type: 'text', text: output.join('\\n') }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n}\n","/**\n * Logger 工具\n *\n * 重要:MCP STDIO 模式下,stdout 是協議通道,\n * 所有日誌必須輸出到 stderr\n */\n\nconst isDebug = process.env.DEBUG === 'true' || process.env.DEBUG === '1';\n\nexport const logger = {\n info: (message: string, ...args: unknown[]) => {\n console.error(`[INFO] ${message}`, ...args);\n },\n\n warn: (message: string, ...args: unknown[]) => {\n console.error(`[WARN] ${message}`, ...args);\n },\n\n error: (message: string, error?: unknown) => {\n if (error instanceof Error) {\n console.error(`[ERROR] ${message}:`, error.message);\n if (isDebug && error.stack) {\n console.error(error.stack);\n }\n } else if (error !== undefined) {\n console.error(`[ERROR] ${message}:`, error);\n } else {\n console.error(`[ERROR] ${message}`);\n }\n },\n\n debug: (message: string, ...args: unknown[]) => {\n if (isDebug) {\n console.error(`[DEBUG] ${message}`, ...args);\n }\n },\n};\n","/**\n * Token Storage Utility (CLI)\n *\n * Stores API Token in user's home directory\n * ~/.slima/credentials.json\n */\n\nimport { promises as fs } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nconst SLIMA_DIR = '.slima';\nconst CREDENTIALS_FILE = 'credentials.json';\n\ninterface Credentials {\n apiToken: string;\n createdAt: string;\n baseUrl?: string;\n}\n\nfunction getCredentialsPath(): string {\n return join(homedir(), SLIMA_DIR, CREDENTIALS_FILE);\n}\n\nfunction getSlimaDir(): string {\n return join(homedir(), SLIMA_DIR);\n}\n\n/**\n * Save API Token\n */\nexport async function saveToken(apiToken: string, baseUrl?: string): Promise<void> {\n const slimaDir = getSlimaDir();\n\n // Ensure directory exists\n await fs.mkdir(slimaDir, { recursive: true });\n\n const credentials: Credentials = {\n apiToken,\n createdAt: new Date().toISOString(),\n ...(baseUrl && { baseUrl }),\n };\n\n const credentialsPath = getCredentialsPath();\n await fs.writeFile(credentialsPath, JSON.stringify(credentials, null, 2), 'utf-8');\n\n // Set file permission to 600 (owner only)\n await fs.chmod(credentialsPath, 0o600);\n}\n\n/**\n * Load API Token\n */\nexport async function loadToken(): Promise<Credentials | null> {\n try {\n const credentialsPath = getCredentialsPath();\n const content = await fs.readFile(credentialsPath, 'utf-8');\n return JSON.parse(content) as Credentials;\n } catch {\n return null;\n }\n}\n\n/**\n * Delete API Token\n */\nexport async function deleteToken(): Promise<void> {\n try {\n const credentialsPath = getCredentialsPath();\n await fs.unlink(credentialsPath);\n } catch {\n // File doesn't exist, ignore\n }\n}\n\n/**\n * Check if authenticated\n */\nexport async function isAuthenticated(): Promise<boolean> {\n const credentials = await loadToken();\n return credentials !== null && !!credentials.apiToken;\n}\n\n/**\n * Get credentials file path (for display)\n */\nexport function getCredentialsFilePath(): string {\n return getCredentialsPath();\n}\n","/**\n * CLI Auth Command\n *\n * 實作類似 Claude Code / GitHub CLI 的認證流程:\n * 1. 使用者執行 `slima-mcp auth`\n * 2. 開啟瀏覽器到 Slima 認證頁面\n * 3. 使用者登入並授權\n * 4. 本地伺服器接收 callback 並儲存 token\n */\n\nimport { createServer, type IncomingMessage, type ServerResponse } from 'node:http';\nimport { URL } from 'node:url';\nimport { randomBytes } from 'node:crypto';\nimport { saveToken, loadToken, deleteToken, getCredentialsFilePath } from './token-storage.js';\n\n// 認證使用 API 伺服器(處理 OAuth 和 session)\nconst DEFAULT_API_URL = 'https://api.slima.ai';\nconst CALLBACK_PORT_START = 8765;\nconst CALLBACK_PORT_END = 8775;\n\n/**\n * 開啟瀏覽器\n */\nasync function openBrowser(url: string): Promise<void> {\n const { exec } = await import('node:child_process');\n const { promisify } = await import('node:util');\n const execAsync = promisify(exec);\n\n const platform = process.platform;\n\n try {\n if (platform === 'darwin') {\n await execAsync(`open \"${url}\"`);\n } else if (platform === 'win32') {\n await execAsync(`start \"\" \"${url}\"`);\n } else {\n // Linux / WSL\n // 嘗試多個可能的命令\n const commands = ['xdg-open', 'sensible-browser', 'wslview'];\n for (const cmd of commands) {\n try {\n await execAsync(`${cmd} \"${url}\"`);\n return;\n } catch {\n // 繼續嘗試下一個\n }\n }\n // 如果都失敗,顯示 URL 讓使用者手動開啟\n console.log(`\\nPlease open this URL in your browser:\\n${url}\\n`);\n }\n } catch {\n console.log(`\\nPlease open this URL in your browser:\\n${url}\\n`);\n }\n}\n\n/**\n * 尋找可用的 port\n */\nasync function findAvailablePort(): Promise<number> {\n for (let port = CALLBACK_PORT_START; port <= CALLBACK_PORT_END; port++) {\n const available = await isPortAvailable(port);\n if (available) {\n return port;\n }\n }\n throw new Error(`No available ports between ${CALLBACK_PORT_START} and ${CALLBACK_PORT_END}`);\n}\n\nfunction isPortAvailable(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const server = createServer();\n server.once('error', () => resolve(false));\n server.once('listening', () => {\n server.close(() => resolve(true));\n });\n server.listen(port, '127.0.0.1');\n });\n}\n\n/**\n * 產生隨機 state(防止 CSRF)\n */\nfunction generateState(): string {\n return randomBytes(16).toString('hex');\n}\n\n/**\n * 成功頁面 HTML\n * Design follows UIUX_SPEC.md: minimalist, grayscale, Inter font\n */\nfunction getSuccessHtml(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>Welcome to Slima</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap\" rel=\"stylesheet\">\n <style>\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n body {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n background: #FBFBFA;\n color: #1D1D1F;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n .container {\n text-align: center;\n padding: 48px;\n max-width: 420px;\n }\n .logo {\n width: 48px;\n height: 48px;\n margin-bottom: 32px;\n opacity: 0.9;\n }\n .success-icon {\n width: 64px;\n height: 64px;\n margin-bottom: 24px;\n border-radius: 50%;\n background: #1D1D1F;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-left: auto;\n margin-right: auto;\n }\n .success-icon svg {\n width: 32px;\n height: 32px;\n stroke: #FBFBFA;\n }\n h1 {\n font-size: 24px;\n font-weight: 600;\n letter-spacing: -0.02em;\n margin-bottom: 12px;\n color: #1D1D1F;\n }\n .subtitle {\n font-size: 15px;\n color: #6E6E73;\n line-height: 1.5;\n margin-bottom: 8px;\n }\n .hint {\n font-size: 13px;\n color: #86868B;\n margin-top: 24px;\n }\n .divider {\n width: 40px;\n height: 1px;\n background: #E5E5E5;\n margin: 32px auto;\n }\n .footer {\n font-size: 12px;\n color: #86868B;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <img src=\"https://app.slima.ai/icons/slima-black.svg\" alt=\"Slima\" class=\"logo\">\n <div class=\"success-icon\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2.5\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M4.5 12.75l6 6 9-13.5\" />\n </svg>\n </div>\n <h1>Welcome to Slima</h1>\n <p class=\"subtitle\">Authentication successful. Your CLI is now connected.</p>\n <p class=\"hint\">You can close this window and return to your terminal.</p>\n <div class=\"divider\"></div>\n <p class=\"footer\">Slima MCP is ready to use</p>\n </div>\n</body>\n</html>`;\n}\n\n/**\n * 錯誤頁面 HTML\n */\n/**\n * HTML escape to prevent XSS\n */\nfunction escapeHtml(text: string): string {\n const map: Record<string, string> = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#039;',\n };\n return text.replace(/[&<>\"']/g, (char) => map[char]);\n}\n\n/**\n * 錯誤頁面 HTML\n * Design follows UIUX_SPEC.md: minimalist, grayscale, Inter font\n */\nfunction getErrorHtml(message: string): string {\n const safeMessage = escapeHtml(message);\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>Slima - Authentication Error</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap\" rel=\"stylesheet\">\n <style>\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n body {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n background: #FBFBFA;\n color: #1D1D1F;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n .container {\n text-align: center;\n padding: 48px;\n max-width: 420px;\n }\n .logo {\n width: 48px;\n height: 48px;\n margin-bottom: 32px;\n opacity: 0.9;\n }\n .error-icon {\n width: 64px;\n height: 64px;\n margin-bottom: 24px;\n border-radius: 50%;\n background: #1D1D1F;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-left: auto;\n margin-right: auto;\n }\n .error-icon svg {\n width: 32px;\n height: 32px;\n stroke: #FBFBFA;\n }\n h1 {\n font-size: 24px;\n font-weight: 600;\n letter-spacing: -0.02em;\n margin-bottom: 12px;\n color: #1D1D1F;\n }\n .message {\n font-size: 15px;\n color: #6E6E73;\n line-height: 1.5;\n margin-bottom: 8px;\n }\n .hint {\n font-size: 13px;\n color: #86868B;\n margin-top: 24px;\n }\n .divider {\n width: 40px;\n height: 1px;\n background: #E5E5E5;\n margin: 32px auto;\n }\n .retry-link {\n display: inline-block;\n font-size: 14px;\n font-weight: 500;\n color: #1D1D1F;\n text-decoration: none;\n padding: 10px 20px;\n border: 1px solid #E5E5E5;\n border-radius: 8px;\n transition: all 0.2s ease;\n }\n .retry-link:hover {\n background: #F5F5F5;\n border-color: #D5D5D5;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <img src=\"https://app.slima.ai/icons/slima-black.svg\" alt=\"Slima\" class=\"logo\">\n <div class=\"error-icon\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2.5\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </div>\n <h1>Something went wrong</h1>\n <p class=\"message\">${safeMessage}</p>\n <p class=\"hint\">Please close this window and try again from your terminal.</p>\n <div class=\"divider\"></div>\n <a href=\"javascript:window.close()\" class=\"retry-link\">Close Window</a>\n </div>\n</body>\n</html>`;\n}\n\n/**\n * 執行認證流程\n */\nexport async function runAuth(options: { baseUrl?: string } = {}): Promise<void> {\n const baseUrl = options.baseUrl || process.env.SLIMA_API_URL || DEFAULT_API_URL;\n\n console.log('\\n🔐 Slima MCP Authentication\\n');\n\n // 檢查是否已經認證\n const existingCredentials = await loadToken();\n if (existingCredentials) {\n console.log('You are already authenticated.');\n console.log(`Token stored at: ${getCredentialsFilePath()}`);\n console.log('\\nTo re-authenticate, first run: slima-mcp logout\\n');\n return;\n }\n\n const state = generateState();\n const port = await findAvailablePort();\n\n console.log('Starting local server for authentication callback...');\n\n // 建立本地伺服器接收 callback\n const tokenPromise = new Promise<string>((resolve, reject) => {\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n const url = new URL(req.url || '/', `http://localhost:${port}`);\n\n if (url.pathname === '/callback') {\n const token = url.searchParams.get('token');\n const returnedState = url.searchParams.get('state');\n const error = url.searchParams.get('error');\n\n if (error) {\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getErrorHtml(error));\n server.close();\n reject(new Error(error));\n return;\n }\n\n if (returnedState !== state) {\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getErrorHtml('Invalid state parameter. Please try again.'));\n server.close();\n reject(new Error('State mismatch'));\n return;\n }\n\n if (!token) {\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getErrorHtml('No token received. Please try again.'));\n server.close();\n reject(new Error('No token received'));\n return;\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getSuccessHtml());\n server.close();\n resolve(token);\n } else {\n res.writeHead(404);\n res.end('Not found');\n }\n });\n\n server.listen(port, '127.0.0.1', () => {\n console.log(`Listening on http://127.0.0.1:${port}`);\n });\n\n // 30 秒超時\n setTimeout(() => {\n server.close();\n reject(new Error('Authentication timed out. Please try again.'));\n }, 30000);\n });\n\n // 建立認證 URL(使用 API endpoint)\n const authUrl = `${baseUrl}/api/v1/auth/cli?state=${state}&callback_port=${port}`;\n\n console.log('\\nOpening browser for authentication...');\n await openBrowser(authUrl);\n\n console.log('\\nWaiting for authentication...');\n console.log('(Press Ctrl+C to cancel)\\n');\n\n try {\n const token = await tokenPromise;\n await saveToken(token, baseUrl !== DEFAULT_API_URL ? baseUrl : undefined);\n\n console.log('\\n✅ Authentication successful!');\n console.log(`Token saved to: ${getCredentialsFilePath()}`);\n console.log('\\nYou can now use Slima MCP with any MCP-compatible AI tool.\\n');\n } catch (error) {\n console.error('\\n❌ Authentication failed:', error instanceof Error ? error.message : error);\n process.exit(1);\n }\n}\n\n/**\n * 登出(刪除 token)\n */\nexport async function runLogout(): Promise<void> {\n console.log('\\n🔓 Slima MCP Logout\\n');\n\n const credentials = await loadToken();\n if (!credentials) {\n console.log('You are not authenticated.\\n');\n return;\n }\n\n await deleteToken();\n console.log('✅ Successfully logged out.');\n console.log(`Removed credentials from: ${getCredentialsFilePath()}\\n`);\n}\n\n/**\n * 顯示目前狀態\n */\nexport async function runStatus(): Promise<void> {\n console.log('\\n📊 Slima MCP Status\\n');\n\n const credentials = await loadToken();\n if (!credentials) {\n console.log('Status: Not authenticated');\n console.log('\\nRun `slima-mcp auth` to authenticate.\\n');\n return;\n }\n\n console.log('Status: Authenticated');\n console.log(`Token: ${credentials.apiToken.substring(0, 15)}...`);\n console.log(`Authenticated at: ${credentials.createdAt}`);\n if (credentials.baseUrl) {\n console.log(`API URL: ${credentials.baseUrl}`);\n }\n console.log(`Credentials file: ${getCredentialsFilePath()}\\n`);\n}\n"],"mappings":"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","names":["z","z","result","z","defaultLogger","logger","z","findFileInSnapshot","result","searchByName","z","z"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slima-mcp",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "MCP Server for Slima - AI Writing IDE for Novel Authors. Works with Claude, ChatGPT, Gemini, Cursor, and any MCP-compatible AI tool.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -15,7 +15,13 @@
15
15
  ],
16
16
  "scripts": {
17
17
  "build": "tsup",
18
+ "build:cli": "tsup",
19
+ "build:worker": "tsup --config tsup.worker.config.ts",
20
+ "build:all": "npm run build:cli && npm run build:worker",
18
21
  "dev": "tsup --watch",
22
+ "dev:worker": "wrangler dev",
23
+ "deploy:worker": "npm run build:worker && wrangler deploy",
24
+ "deploy:worker:preview": "npm run build:worker && wrangler deploy --env preview",
19
25
  "test": "vitest",
20
26
  "test:run": "vitest run",
21
27
  "test:coverage": "vitest run --coverage",
@@ -51,14 +57,18 @@
51
57
  "node": ">=20.0.0"
52
58
  },
53
59
  "dependencies": {
60
+ "@cloudflare/workers-oauth-provider": "^0.2.2",
54
61
  "@modelcontextprotocol/sdk": "^1.0.0",
62
+ "hono": "^4.0.0",
55
63
  "zod": "^3.23.0"
56
64
  },
57
65
  "devDependencies": {
66
+ "@cloudflare/workers-types": "^4.0.0",
58
67
  "@types/node": "^20.0.0",
59
68
  "@vitest/coverage-v8": "^2.1.9",
60
69
  "tsup": "^8.0.0",
61
70
  "typescript": "^5.4.0",
62
- "vitest": "^2.0.0"
71
+ "vitest": "^2.0.0",
72
+ "wrangler": "^3.0.0"
63
73
  }
64
74
  }