note-mcp 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +105 -12
  2. package/dist/index.js +384 -130
  3. package/package.json +5 -2
package/README.md CHANGED
@@ -7,6 +7,12 @@ Unofficial stdio MCP server for note.com. It uses cookie-based access to note.co
7
7
 
8
8
  ## Install / run
9
9
 
10
+ ```bash
11
+ npx note-mcp
12
+ ```
13
+
14
+ For advanced/server setups, you can still provide a Cookie header through the environment:
15
+
10
16
  ```bash
11
17
  NOTE_COOKIE='your note.com cookie string' npx note-mcp
12
18
  ```
@@ -16,12 +22,25 @@ For local development:
16
22
  ```bash
17
23
  npm install
18
24
  npm run build
19
- NOTE_COOKIE='your note.com cookie string' node dist/index.js
25
+ node dist/index.js
20
26
  ```
21
27
 
22
28
  ## MCP client configuration
23
29
 
24
- Example:
30
+ Desktop/local browser-login friendly setup:
31
+
32
+ ```json
33
+ {
34
+ "mcpServers": {
35
+ "note": {
36
+ "command": "npx",
37
+ "args": ["-y", "note-mcp"]
38
+ }
39
+ }
40
+ }
41
+ ```
42
+
43
+ Advanced env-based setup:
25
44
 
26
45
  ```json
27
46
  {
@@ -37,15 +56,98 @@ Example:
37
56
  }
38
57
  ```
39
58
 
59
+ ## Authentication
60
+
61
+ `note-mcp` supports two authentication paths.
62
+
63
+ ### 1. Local/desktop: browser login
64
+
65
+ For local desktop agents, ask the agent to call:
66
+
67
+ - `note_auth_login`
68
+
69
+ Or run it directly:
70
+
71
+ ```bash
72
+ npx note-mcp auth
73
+ ```
74
+
75
+ This opens a browser, lets you log in to note.com normally, then stores note.com cookies in:
76
+
77
+ ```text
78
+ ~/.config/note-mcp/config.json
79
+ ```
80
+
81
+ If the browser executable is not installed yet, install Playwright's Chromium once on the same machine/user account, then retry:
82
+
83
+ ```bash
84
+ npx playwright install chromium
85
+ ```
86
+
87
+ When using `note-mcp` only through `npx` and Playwright is not otherwise installed globally/in the project, this form is often more reliable:
88
+
89
+ ```bash
90
+ npx -p playwright playwright install chromium
91
+ ```
92
+
93
+ For remote servers, containers, or CI, prefer the secret/env/config-file path below instead of browser login.
94
+
95
+ The config file is written with `0600` permissions where supported.
96
+
97
+ Useful CLI commands:
98
+
99
+ ```bash
100
+ npx note-mcp auth --status
101
+ npx note-mcp auth --clear
102
+ npx note-mcp auth --headless
103
+ npx note-mcp auth --headed
104
+ ```
105
+
106
+ ### 2. Advanced/server/CI: secret, env, or config file
107
+
108
+ For remote agents, servers, CI, and secret managers, provide a Cookie header via:
109
+
110
+ - `NOTE_COOKIE`
111
+ - `NOTE_SESSION_COOKIE`
112
+ - `NOTE_MCP_CONFIG` pointing to a config JSON file
113
+ - MCP tool `note_set_cookie`
114
+
115
+ Example config file:
116
+
117
+ ```json
118
+ {
119
+ "cookie": "your note.com Cookie header",
120
+ "updatedAt": "2026-06-21T00:00:00.000Z"
121
+ }
122
+ ```
123
+
124
+ Cookie lookup priority:
125
+
126
+ 1. `NOTE_COOKIE`
127
+ 2. `NOTE_SESSION_COOKIE`
128
+ 3. config file cookie
129
+
40
130
  ## Tools
41
131
 
42
- - `note_auth_check` — verify cookie-based access to note.com internal APIs
132
+ Authentication/setup tools:
133
+
134
+ - `note_auth_status` — inspect whether auth is configured
135
+ - `note_auth_login` — open a browser login flow and save cookies locally
136
+ - `note_set_cookie` — save a Cookie header to the local config file, optionally verifying it first
137
+ - `note_clear_cookie` — delete the stored config-file cookie
138
+ - `note_login_help` — explain supported setup paths
139
+
140
+ note.com tools:
141
+
142
+ - `note_auth_check` — verify configured cookie-based access to note.com internal APIs
43
143
  - `note_list_my_notes` — list notes for the authenticated account
44
144
  - `note_list_drafts` — list drafts for the authenticated account
45
145
  - `note_get_note` — fetch a note by note key, e.g. `n1a0b26f944f4`
46
146
  - `note_create_draft` — create a draft
47
147
  - `note_update_draft` — update a draft by draft id
48
148
 
149
+ If authentication is missing, note tools return an `auth_required` error suggesting `note_auth_login` or `note_set_cookie`.
150
+
49
151
  ## API basis
50
152
 
51
153
  The initial endpoints are based on public, unofficial note API references, including:
@@ -60,15 +162,6 @@ Known endpoint basis:
60
162
  - Draft save: `POST /v1/text_notes/draft_save?id={draftId}`
61
163
  - Auth smoke test: `GET /v3/notice_counts`
62
164
 
63
- ## Authentication
64
-
65
- Set one of these environment variables before launching the server:
66
-
67
- - `NOTE_COOKIE`
68
- - `NOTE_SESSION_COOKIE`
69
-
70
- Use the full Cookie header value from an authenticated browser session.
71
-
72
165
  ## Release
73
166
 
74
167
  Releases are handled by GitHub Actions + semantic-release.
package/dist/index.js CHANGED
@@ -6,15 +6,98 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
6
6
  import { z } from "zod";
7
7
 
8
8
  // src/note/auth.ts
9
+ import { mkdir, readFile, rm, writeFile } from "fs/promises";
10
+ import { dirname, join } from "path";
11
+ import { homedir } from "os";
9
12
  var COOKIE_ENV_KEYS = ["NOTE_COOKIE", "NOTE_SESSION_COOKIE"];
10
- function readCookieFromEnv(env = process.env) {
13
+ var CONFIG_ENV_KEY = "NOTE_MCP_CONFIG";
14
+ var AuthRequiredError = class extends Error {
15
+ constructor(message = "note.com authentication is not configured.") {
16
+ super(message);
17
+ this.name = "AuthRequiredError";
18
+ }
19
+ };
20
+ async function readCookie(options = {}) {
21
+ const envCookie = readCookieFromEnvValue(options.env ?? process.env);
22
+ if (envCookie) return envCookie;
23
+ const storedCookie = await readCookieFromConfig(options);
24
+ if (storedCookie) return storedCookie;
25
+ throw new AuthRequiredError();
26
+ }
27
+ async function authStatus(options = {}) {
28
+ const env = options.env ?? process.env;
29
+ const envCookie = readCookieFromEnvValue(env);
30
+ const configPath = resolveConfigPath(options);
31
+ if (envCookie) {
32
+ return {
33
+ configured: true,
34
+ source: "env",
35
+ configPath,
36
+ cookiePreview: previewCookie(envCookie),
37
+ message: "note.com cookie is configured from environment variables."
38
+ };
39
+ }
40
+ const configCookie = await readCookieFromConfig(options);
41
+ if (configCookie) {
42
+ return {
43
+ configured: true,
44
+ source: "config",
45
+ configPath,
46
+ cookiePreview: previewCookie(configCookie),
47
+ message: "note.com cookie is configured from note-mcp config file."
48
+ };
49
+ }
50
+ return {
51
+ configured: false,
52
+ source: "none",
53
+ configPath,
54
+ message: "note.com cookie is not configured. Use note_auth_login for browser login or note_set_cookie / NOTE_COOKIE for advanced setups.",
55
+ suggestedTools: ["note_auth_login", "note_set_cookie"]
56
+ };
57
+ }
58
+ async function saveCookie(cookie, options = {}) {
59
+ const trimmed = cookie.trim();
60
+ if (!trimmed) throw new Error("Cookie must not be empty.");
61
+ const configPath = resolveConfigPath(options);
62
+ await mkdir(dirname(configPath), { recursive: true });
63
+ const config = {
64
+ cookie: trimmed,
65
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
66
+ };
67
+ await writeFile(configPath, `${JSON.stringify(config, null, 2)}
68
+ `, { mode: 384 });
69
+ return authStatus({ ...options, env: {} });
70
+ }
71
+ async function clearStoredCookie(options = {}) {
72
+ await rm(resolveConfigPath(options), { force: true });
73
+ return authStatus({ ...options, env: {} });
74
+ }
75
+ function resolveConfigPath(options = {}) {
76
+ const env = options.env ?? process.env;
77
+ return options.configPath ?? env[CONFIG_ENV_KEY] ?? join(homedir(), ".config", "note-mcp", "config.json");
78
+ }
79
+ function readCookieFromEnvValue(env) {
11
80
  for (const key of COOKIE_ENV_KEYS) {
12
81
  const value = env[key];
13
82
  if (value?.trim()) return value.trim();
14
83
  }
15
- throw new Error(
16
- `Missing note.com cookie. Set ${COOKIE_ENV_KEYS.join(" or ")} before starting note-mcp.`
17
- );
84
+ return null;
85
+ }
86
+ async function readCookieFromConfig(options) {
87
+ try {
88
+ const raw = await readFile(resolveConfigPath(options), "utf8");
89
+ const parsed = JSON.parse(raw);
90
+ return parsed.cookie?.trim() || null;
91
+ } catch (error) {
92
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
93
+ return null;
94
+ }
95
+ throw error;
96
+ }
97
+ }
98
+ function previewCookie(cookie) {
99
+ if (cookie.length <= 8) return "********";
100
+ return `${cookie.slice(0, 4)}\u2026${cookie.slice(-4)}`;
18
101
  }
19
102
 
20
103
  // src/note/errors.ts
@@ -108,13 +191,287 @@ async function parseBody(response) {
108
191
  }
109
192
  }
110
193
 
194
+ // src/note/browser-login.ts
195
+ function cookiesToHeader(cookies) {
196
+ const noteCookies = cookies.filter((cookie) => isNoteDomain(cookie.domain));
197
+ if (noteCookies.length === 0) {
198
+ throw new Error("No note.com cookies were found in the browser session.");
199
+ }
200
+ return noteCookies.map((cookie) => `${cookie.name}=${cookie.value}`).join("; ");
201
+ }
202
+ async function runBrowserLogin(options = {}) {
203
+ const timeoutMs = options.timeoutMs ?? 18e4;
204
+ const headless = options.headless ?? process.env.NOTE_MCP_HEADLESS === "true";
205
+ const { chromium } = await importPlaywright();
206
+ let browser;
207
+ try {
208
+ browser = await chromium.launch({ headless });
209
+ } catch (error) {
210
+ throw toBrowserLoginError(error);
211
+ }
212
+ try {
213
+ const context = await browser.newContext();
214
+ const page = await context.newPage();
215
+ await page.goto("https://note.com/login", { waitUntil: "domcontentloaded" });
216
+ const deadline = Date.now() + timeoutMs;
217
+ let lastCookie = "";
218
+ while (Date.now() < deadline) {
219
+ await page.waitForTimeout(2e3);
220
+ const cookie = cookiesToHeader(await context.cookies("https://note.com"));
221
+ lastCookie = cookie;
222
+ const client = new NoteClient({ cookie });
223
+ try {
224
+ await client.authCheck();
225
+ if (options.save ?? true) {
226
+ await saveCookie(cookie);
227
+ }
228
+ return {
229
+ authenticated: true,
230
+ saved: options.save ?? true,
231
+ cookiePreview: previewCookie2(cookie),
232
+ message: "note.com authentication configured from browser login."
233
+ };
234
+ } catch {
235
+ }
236
+ }
237
+ throw new Error(
238
+ lastCookie ? "Timed out waiting for note.com authentication to become valid." : "Timed out waiting for note.com login cookies."
239
+ );
240
+ } finally {
241
+ await browser.close();
242
+ }
243
+ }
244
+ async function importPlaywright() {
245
+ try {
246
+ return await import("playwright");
247
+ } catch (error) {
248
+ throw new Error(
249
+ `Browser login requires the optional "playwright" package and a usable desktop browser environment. Original error: ${error instanceof Error ? error.message : String(error)}`
250
+ );
251
+ }
252
+ }
253
+ function toBrowserLoginError(error) {
254
+ const message = error instanceof Error ? error.message : String(error);
255
+ if (message.includes("Executable doesn't exist") || message.includes("Please run the following command to download new browsers") || message.includes("playwright install")) {
256
+ return new Error(
257
+ [
258
+ "Playwright browser is not installed, so note-mcp cannot open the note.com browser login flow.",
259
+ "Run this once on the same machine/user account, then retry:",
260
+ " npx playwright install chromium",
261
+ "If you are running note-mcp through npx and Playwright is not otherwise installed, use:",
262
+ " npx -p playwright playwright install chromium",
263
+ "For remote servers, containers, or CI, prefer NOTE_COOKIE / NOTE_SESSION_COOKIE or NOTE_MCP_CONFIG instead of browser login."
264
+ ].join("\n")
265
+ );
266
+ }
267
+ return error instanceof Error ? error : new Error(message);
268
+ }
269
+ function isNoteDomain(domain) {
270
+ const normalized = domain.replace(/^\./, "").toLowerCase();
271
+ return normalized === "note.com" || normalized.endsWith(".note.com");
272
+ }
273
+ function previewCookie2(cookie) {
274
+ if (cookie.length <= 8) return "********";
275
+ return `${cookie.slice(0, 4)}\u2026${cookie.slice(-4)}`;
276
+ }
277
+
111
278
  // src/index.ts
112
- var server = new McpServer({
113
- name: "note-mcp",
114
- version: "0.0.0-development"
115
- });
116
- function createClient() {
117
- return new NoteClient({ cookie: readCookieFromEnv() });
279
+ if (process.argv[2] === "auth") {
280
+ await runAuthCli(process.argv.slice(3));
281
+ } else {
282
+ await runMcpServer();
283
+ }
284
+ async function runAuthCli(args) {
285
+ try {
286
+ if (args.includes("--status")) {
287
+ console.log(jsonText(await authStatus()));
288
+ return;
289
+ }
290
+ if (args.includes("--clear")) {
291
+ console.log(jsonText(await clearStoredCookie()));
292
+ return;
293
+ }
294
+ const headless = args.includes("--headless") ? true : args.includes("--headed") ? false : void 0;
295
+ console.error("Opening note.com login in a browser. Complete login there; note-mcp will save cookies locally.");
296
+ console.log(jsonText(await runBrowserLogin(headless === void 0 ? {} : { headless })));
297
+ } catch (error) {
298
+ console.error(jsonText(errorDetail(error)));
299
+ process.exitCode = 1;
300
+ }
301
+ }
302
+ async function runMcpServer() {
303
+ const server = new McpServer({
304
+ name: "note-mcp",
305
+ version: "1.1.0-development"
306
+ });
307
+ server.registerTool(
308
+ "note_auth_status",
309
+ {
310
+ title: "Get note.com authentication status",
311
+ description: "Checks whether note-mcp has a note.com cookie from env or config file.",
312
+ inputSchema: {}
313
+ },
314
+ async () => result(await authStatus())
315
+ );
316
+ server.registerTool(
317
+ "note_auth_login",
318
+ {
319
+ title: "Log in to note.com with a browser",
320
+ description: "Opens a local Playwright browser login flow and saves note.com cookies to the note-mcp config file. Intended for desktop/local agents; remote/headless servers should use env or note_set_cookie.",
321
+ inputSchema: {
322
+ headless: z.boolean().optional()
323
+ }
324
+ },
325
+ async ({ headless }) => {
326
+ try {
327
+ return result(await runBrowserLogin({ ...headless === void 0 ? {} : { headless } }));
328
+ } catch (error) {
329
+ return errorResult(error);
330
+ }
331
+ }
332
+ );
333
+ server.registerTool(
334
+ "note_set_cookie",
335
+ {
336
+ title: "Set note.com cookie",
337
+ description: "Stores a note.com Cookie header in the local note-mcp config file. By default, verifies the cookie before saving.",
338
+ inputSchema: {
339
+ cookie: z.string().min(1),
340
+ verify: z.boolean().default(true)
341
+ }
342
+ },
343
+ async ({ cookie, verify }) => {
344
+ try {
345
+ if (verify) {
346
+ await new NoteClient({ cookie }).authCheck();
347
+ }
348
+ return result(await saveCookie(cookie));
349
+ } catch (error) {
350
+ return errorResult(error);
351
+ }
352
+ }
353
+ );
354
+ server.registerTool(
355
+ "note_clear_cookie",
356
+ {
357
+ title: "Clear stored note.com cookie",
358
+ description: "Deletes the note-mcp config file cookie. Environment cookies are not modified.",
359
+ inputSchema: {}
360
+ },
361
+ async () => {
362
+ try {
363
+ return result(await clearStoredCookie());
364
+ } catch (error) {
365
+ return errorResult(error);
366
+ }
367
+ }
368
+ );
369
+ server.registerTool(
370
+ "note_login_help",
371
+ {
372
+ title: "Get note-mcp login help",
373
+ description: "Explains the supported note-mcp authentication setup paths.",
374
+ inputSchema: {}
375
+ },
376
+ async () => result({
377
+ recommended: "For local/desktop agents, call note_auth_login to open a browser login flow.",
378
+ advanced: "For servers/CI, provide NOTE_COOKIE / NOTE_SESSION_COOKIE or call note_set_cookie with a Cookie header obtained by a trusted operator.",
379
+ configFile: (await authStatus()).configPath,
380
+ cli: ["npx note-mcp auth", "npx note-mcp auth --status", "npx note-mcp auth --clear"]
381
+ })
382
+ );
383
+ server.registerTool(
384
+ "note_auth_check",
385
+ {
386
+ title: "Check note.com authentication",
387
+ description: "Checks whether configured note.com cookies can access note.com internal APIs.",
388
+ inputSchema: {}
389
+ },
390
+ async () => withClient((client) => client.authCheck())
391
+ );
392
+ server.registerTool(
393
+ "note_list_my_notes",
394
+ {
395
+ title: "List my note.com notes",
396
+ description: "Lists notes for the authenticated note.com account.",
397
+ inputSchema: {
398
+ page: z.number().int().positive().default(1)
399
+ }
400
+ },
401
+ async ({ page }) => withClient((client) => client.listMyNotes(page))
402
+ );
403
+ server.registerTool(
404
+ "note_list_drafts",
405
+ {
406
+ title: "List note.com drafts",
407
+ description: "Lists drafts for the authenticated note.com account. This uses an unofficial internal API and may need adjustment if note.com changes endpoints.",
408
+ inputSchema: {
409
+ page: z.number().int().positive().default(1)
410
+ }
411
+ },
412
+ async ({ page }) => withClient((client) => client.listDrafts(page))
413
+ );
414
+ server.registerTool(
415
+ "note_get_note",
416
+ {
417
+ title: "Get note.com note",
418
+ description: "Fetches a note by note key, e.g. n1a0b26f944f4.",
419
+ inputSchema: {
420
+ noteKey: z.string().min(1)
421
+ }
422
+ },
423
+ async ({ noteKey }) => withClient((client) => client.getNote(noteKey))
424
+ );
425
+ server.registerTool(
426
+ "note_create_draft",
427
+ {
428
+ title: "Create note.com draft",
429
+ description: "Creates a note.com draft with title/body/hashtags using an unofficial internal API.",
430
+ inputSchema: {
431
+ title: z.string().min(1),
432
+ body: z.string().min(1),
433
+ hashtags: z.array(z.string().min(1)).optional()
434
+ }
435
+ },
436
+ async ({ title, body, hashtags }) => withClient(
437
+ (client) => client.createDraft({
438
+ title,
439
+ body,
440
+ ...hashtags ? { hashtags } : {}
441
+ })
442
+ )
443
+ );
444
+ server.registerTool(
445
+ "note_update_draft",
446
+ {
447
+ title: "Update note.com draft",
448
+ description: "Updates a note.com draft by draft id using an unofficial internal API.",
449
+ inputSchema: {
450
+ draftId: z.string().min(1),
451
+ title: z.string().min(1),
452
+ body: z.string().min(1),
453
+ hashtags: z.array(z.string().min(1)).optional()
454
+ }
455
+ },
456
+ async ({ draftId, title, body, hashtags }) => withClient(
457
+ (client) => client.updateDraft({
458
+ draftId,
459
+ title,
460
+ body,
461
+ ...hashtags ? { hashtags } : {}
462
+ })
463
+ )
464
+ );
465
+ const transport = new StdioServerTransport();
466
+ await server.connect(transport);
467
+ }
468
+ async function withClient(fn) {
469
+ try {
470
+ const client = new NoteClient({ cookie: await readCookie() });
471
+ return result(await fn(client));
472
+ } catch (error) {
473
+ return errorResult(error);
474
+ }
118
475
  }
119
476
  function jsonText(value) {
120
477
  return JSON.stringify(value, null, 2);
@@ -130,134 +487,31 @@ function result(value) {
130
487
  };
131
488
  }
132
489
  function errorResult(error) {
133
- const detail = error instanceof NoteApiError ? { message: error.message, status: error.status, body: error.body } : { message: toErrorMessage(error) };
134
490
  return {
135
491
  isError: true,
136
492
  content: [
137
493
  {
138
494
  type: "text",
139
- text: jsonText(detail)
495
+ text: jsonText(errorDetail(error))
140
496
  }
141
497
  ]
142
498
  };
143
499
  }
144
- server.registerTool(
145
- "note_auth_check",
146
- {
147
- title: "Check note.com authentication",
148
- description: "Checks whether NOTE_COOKIE / NOTE_SESSION_COOKIE can access note.com internal APIs.",
149
- inputSchema: {}
150
- },
151
- async () => {
152
- try {
153
- return result(await createClient().authCheck());
154
- } catch (error) {
155
- return errorResult(error);
156
- }
157
- }
158
- );
159
- server.registerTool(
160
- "note_list_my_notes",
161
- {
162
- title: "List my note.com notes",
163
- description: "Lists notes for the authenticated note.com account.",
164
- inputSchema: {
165
- page: z.number().int().positive().default(1)
166
- }
167
- },
168
- async ({ page }) => {
169
- try {
170
- return result(await createClient().listMyNotes(page));
171
- } catch (error) {
172
- return errorResult(error);
173
- }
174
- }
175
- );
176
- server.registerTool(
177
- "note_list_drafts",
178
- {
179
- title: "List note.com drafts",
180
- description: "Lists drafts for the authenticated note.com account. This uses an unofficial internal API and may need adjustment if note.com changes endpoints.",
181
- inputSchema: {
182
- page: z.number().int().positive().default(1)
183
- }
184
- },
185
- async ({ page }) => {
186
- try {
187
- return result(await createClient().listDrafts(page));
188
- } catch (error) {
189
- return errorResult(error);
190
- }
500
+ function errorDetail(error) {
501
+ if (error instanceof AuthRequiredError) {
502
+ return {
503
+ error: "auth_required",
504
+ message: error.message,
505
+ suggestedTools: ["note_auth_login", "note_set_cookie"]
506
+ };
191
507
  }
192
- );
193
- server.registerTool(
194
- "note_get_note",
195
- {
196
- title: "Get note.com note",
197
- description: "Fetches a note by note key, e.g. n1a0b26f944f4.",
198
- inputSchema: {
199
- noteKey: z.string().min(1)
200
- }
201
- },
202
- async ({ noteKey }) => {
203
- try {
204
- return result(await createClient().getNote(noteKey));
205
- } catch (error) {
206
- return errorResult(error);
207
- }
208
- }
209
- );
210
- server.registerTool(
211
- "note_create_draft",
212
- {
213
- title: "Create note.com draft",
214
- description: "Creates a note.com draft with title/body/hashtags using an unofficial internal API.",
215
- inputSchema: {
216
- title: z.string().min(1),
217
- body: z.string().min(1),
218
- hashtags: z.array(z.string().min(1)).optional()
219
- }
220
- },
221
- async ({ title, body, hashtags }) => {
222
- try {
223
- return result(
224
- await createClient().createDraft({
225
- title,
226
- body,
227
- ...hashtags ? { hashtags } : {}
228
- })
229
- );
230
- } catch (error) {
231
- return errorResult(error);
232
- }
508
+ if (error instanceof NoteApiError) {
509
+ return {
510
+ error: "note_api_error",
511
+ message: error.message,
512
+ status: error.status,
513
+ body: error.body
514
+ };
233
515
  }
234
- );
235
- server.registerTool(
236
- "note_update_draft",
237
- {
238
- title: "Update note.com draft",
239
- description: "Updates a note.com draft by draft id using an unofficial internal API.",
240
- inputSchema: {
241
- draftId: z.string().min(1),
242
- title: z.string().min(1),
243
- body: z.string().min(1),
244
- hashtags: z.array(z.string().min(1)).optional()
245
- }
246
- },
247
- async ({ draftId, title, body, hashtags }) => {
248
- try {
249
- return result(
250
- await createClient().updateDraft({
251
- draftId,
252
- title,
253
- body,
254
- ...hashtags ? { hashtags } : {}
255
- })
256
- );
257
- } catch (error) {
258
- return errorResult(error);
259
- }
260
- }
261
- );
262
- var transport = new StdioServerTransport();
263
- await server.connect(transport);
516
+ return { error: "error", message: toErrorMessage(error) };
517
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "note-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "Unofficial stdio MCP server for note.com using cookie-based internal APIs.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,7 +12,7 @@
12
12
  "LICENSE"
13
13
  ],
14
14
  "scripts": {
15
- "build": "tsup src/index.ts --format esm --dts --clean",
15
+ "build": "tsup src/index.ts --format esm --dts --clean --external playwright",
16
16
  "dev": "tsx src/index.ts",
17
17
  "lint": "eslint .",
18
18
  "format": "prettier --check .",
@@ -61,5 +61,8 @@
61
61
  "typescript": "^6.0.3",
62
62
  "typescript-eslint": "^8.61.1",
63
63
  "vitest": "^4.1.9"
64
+ },
65
+ "optionalDependencies": {
66
+ "playwright": "^1.61.0"
64
67
  }
65
68
  }