dailynote-mcp 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,161 @@
1
+ # Memasang DailyNote MCP di Codex
2
+
3
+ MCP lokal ini menyediakan satu tool, `add_journal_entry`. Setiap pengguna memakai
4
+ token dari akun DailyNote sendiri. Token diteruskan melalui environment variable;
5
+ tidak perlu file `.env` atau menyimpan token di konfigurasi Codex.
6
+
7
+ Versi `0.2.0` menambahkan `format: "markdown"` untuk rich text Tiptap. Dukungan
8
+ backend harus sudah dideploy sebelum dipakai; input tanpa format tetap plain.
9
+
10
+ ## 1. Siapkan Node.js dan aplikasi DailyNote
11
+
12
+ Pasang Node.js 24 beserta npm dari [nodejs.org](https://nodejs.org/).
13
+ Periksa dari terminal:
14
+
15
+ ```sh
16
+ node --version
17
+ npx --version
18
+ ```
19
+
20
+ Node harus menunjukkan versi `v24.x`. Pengguna tidak perlu clone proyek,
21
+ memasang pnpm, menjalankan database, atau melakukan migration.
22
+ Aplikasi DailyNote tujuan harus sudah berjalan dan mendukung fitur Token API
23
+ serta endpoint MCP. Pengembang aplikasi menyiapkan backend tersebut.
24
+
25
+ ## 2. Gunakan package npm
26
+
27
+ Konfigurasi berikut memakai `dailynote-mcp@0.2.0` dan berlaku setelah versi
28
+ tersebut tersedia di npm. `npx` akan mengambil package serta dependency ketika
29
+ Codex menjalankan koneksi pertama kali. Akses registry diperlukan untuk
30
+ pengunduhan awal. Tidak perlu menjalankan proses MCP di terminal terpisah.
31
+
32
+ ## 3. Buat token pribadi
33
+
34
+ Login ke DailyNote, buka **Profil → Token API → Buat token**, lalu salin token.
35
+ Token lengkap hanya ditampilkan sekali. Jika perlu mengganti token yang sudah
36
+ ada, pilih **Buat token baru**; token lama akan dinonaktifkan.
37
+
38
+ Jangan kirim token ke chat, Git, atau GitHub Actions. Pengguna lain melakukan
39
+ langkah yang sama memakai akun dan komputernya sendiri.
40
+
41
+ ## 4. Isi environment sesi macOS
42
+
43
+ Contoh berikut memakai terminal **zsh** bawaan macOS. Tempel token ketika diminta;
44
+ input disembunyikan dan token tidak ditulis secara literal dalam riwayat command.
45
+
46
+ ```sh
47
+ export DAILYNOTE_URL="https://alamat-aplikasi-anda.example"
48
+ read -rs 'DAILYNOTE_TOKEN?Token API DailyNote: '
49
+ printf '\n'
50
+ export DAILYNOTE_TOKEN
51
+ launchctl setenv DAILYNOTE_URL "$DAILYNOTE_URL"
52
+ launchctl setenv DAILYNOTE_TOKEN "$DAILYNOTE_TOKEN"
53
+ ```
54
+
55
+ Ganti alamat contoh dengan origin HTTPS aplikasi DailyNote Anda, tanpa path,
56
+ query, atau fragment. Token harus berasal dari akun pada aplikasi tujuan
57
+ tersebut. `http://localhost:3000` hanya untuk development lokal.
58
+
59
+ `export` mengatur terminal saat ini. `launchctl setenv` membuat aplikasi desktop
60
+ yang dibuka setelahnya menerima variabel pada sesi login macOS ini. Variabel
61
+ tersebut harus diisi ulang setelah logout/restart komputer. Jangan menambahkan
62
+ token ke `.zshrc`, `.env`, atau `config.toml` bila ingin tetap tanpa file token.
63
+
64
+ Untuk Codex CLI, jalankan `codex` dari terminal yang sudah menerima `export`.
65
+ Untuk Codex desktop, tutup penuh aplikasi dengan **Quit Codex**, lalu buka lagi
66
+ setelah langkah ini. Aplikasi yang sudah berjalan belum menerima environment baru.
67
+
68
+ ## 5. Daftarkan MCP
69
+
70
+ Buka konfigurasi Codex `~/.codex/config.toml`. Tambahkan blok berikut, atau
71
+ perbarui blok `dailynote-journal` yang sudah ada; pertahankan konfigurasi lainnya.
72
+
73
+ ```toml
74
+ [mcp_servers.dailynote-journal]
75
+ command = "npx"
76
+ args = ["-y", "dailynote-mcp@0.2.0"]
77
+ env_vars = ["DAILYNOTE_URL", "DAILYNOTE_TOKEN"]
78
+ enabled_tools = ["add_journal_entry"]
79
+ ```
80
+
81
+ Jika Codex tidak menemukan `npx`, ganti `command` dengan path absolut hasil
82
+ `command -v npx`. Tidak ada path source code DailyNote dalam konfigurasi ini.
83
+ `env_vars` berisi nama variabel yang diteruskan Codex,
84
+ bukan nilai token. Konfigurasi ini mengikuti
85
+ [dokumentasi resmi OpenAI](https://developers.openai.com/codex/mcp/).
86
+
87
+ Jika sebelumnya memakai source lokal, ganti `command`/`args` dalam blok yang
88
+ sama dengan konfigurasi di atas setelah versi npm tersedia. Pertahankan
89
+ `env_vars`; jangan membuat koneksi kedua karena tool akan muncul ganda.
90
+
91
+ ## 6. Periksa koneksi
92
+
93
+ Dari terminal mana pun:
94
+
95
+ ```sh
96
+ codex mcp get dailynote-journal
97
+ ```
98
+
99
+ Hasil seharusnya menunjukkan `enabled: true`, `transport: stdio`, argumen
100
+ `-y dailynote-mcp@0.2.0`, dan nama kedua environment variable. Token mentah tidak
101
+ perlu tampil.
102
+ Setelah Codex desktop dibuka ulang, buka chat baru dan pastikan tool
103
+ `add_journal_entry` tersedia.
104
+
105
+ ## 7. Coba input jurnal
106
+
107
+ Di chat yang sudah memuat MCP:
108
+
109
+ > Gunakan DailyNote MCP untuk menambahkan "Tes koneksi MCP dari Codex" ke jurnal hari ini.
110
+
111
+ Tindakan ini menambahkan isi jurnal akun pemilik token. Hasil yang diharapkan
112
+ berisi tanggal, status `saved`, dan `requestId`. Periksa hasil di DailyNote;
113
+ tanggal default mengikuti timezone akun.
114
+
115
+ Jika hasil belum pasti karena koneksi terputus, ulangi dengan requestId, teks,
116
+ tanggal, dan format yang sama. Jangan langsung mengirim entri baru dengan ID berbeda.
117
+
118
+ ## Mengganti atau melepas koneksi
119
+
120
+ Untuk mengganti token, buat token baru di profil, ulangi langkah 4 dan buka ulang
121
+ Codex. Untuk mencabut akses, gunakan **Cabut token** di DailyNote.
122
+
123
+ Untuk menghapus pendaftaran MCP dan variabel sesi macOS:
124
+
125
+ ```sh
126
+ codex mcp remove dailynote-journal
127
+ launchctl unsetenv DAILYNOTE_URL
128
+ launchctl unsetenv DAILYNOTE_TOKEN
129
+ unset DAILYNOTE_URL DAILYNOTE_TOKEN
130
+ ```
131
+
132
+ Proses yang sudah berjalan tetap memegang salinan environment lamanya sampai
133
+ ditutup. Menghapus konfigurasi lokal tidak mencabut token atau menghapus jurnal.
134
+
135
+ ## Jika bermasalah
136
+
137
+ - Server tidak bisa dimulai: periksa Node.js 24, path npx, akses registry, dan kedua
138
+ environment variable.
139
+ - `unauthorized`: token dicabut/diganti, salah disalin, atau berasal dari aplikasi
140
+ tujuan lain.
141
+ - Koneksi gagal: pastikan aplikasi tujuan hidup, URL/port tepat, dan gunakan HTTPS
142
+ untuk production.
143
+ - `outcome_unknown`: periksa jurnal atau retry dengan requestId lama.
144
+ - Tool belum terlihat: quit dan buka ulang Codex setelah environment diisi;
145
+ buka chat baru.
146
+
147
+ ## Commands to run
148
+
149
+ Wajib: ikuti langkah 1–6 berurutan. Semua command pemasangan pengguna dapat
150
+ dijalankan dari terminal lokal mana pun, tanpa folder proyek. Pemeriksaan akhir:
151
+
152
+ ```sh
153
+ codex mcp get dailynote-journal
154
+ ```
155
+
156
+ Hasil yang diharapkan: koneksi stdio aktif, versi npm tertentu pada argumen,
157
+ dan satu tool `add_journal_entry` tersedia pada chat baru.
158
+
159
+ Pengguna package tidak perlu menjalankan suite tes atau build aplikasi. Jika
160
+ pemasangan gagal, kirim command, exit status, dan cuplikan error relevan tanpa
161
+ token atau isi jurnal privat.
package/README.md CHANGED
@@ -1,3 +1,107 @@
1
- # Temporary Holding Version
1
+ # DailyNote MCP
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Hubungkan AI assistant ke akun DailyNote melalui MCP lokal (stdio). Pengguna
4
+ cukup memasang **Node.js 24**; tidak perlu clone proyek, pnpm, atau database lokal.
5
+ Aplikasi DailyNote tujuan harus sudah mendukung Token API dan endpoint MCP.
6
+
7
+ Konfigurasi npm di bawah memerlukan rilis `dailynote-mcp@0.2.0` tersedia di npm.
8
+
9
+ ## Hubungkan ke Codex
10
+
11
+ 1. Login ke DailyNote, buka **Profil → Token API → Buat token**, lalu salin token.
12
+ 2. Atur `DAILYNOTE_URL` dan `DAILYNOTE_TOKEN` di environment proses Codex.
13
+ Gunakan alamat HTTPS aplikasi DailyNote Anda, tanpa path tambahan. HTTP hanya
14
+ didukung untuk localhost/loopback saat development. Token berasal dari akun
15
+ Anda pada aplikasi tujuan tersebut.
16
+ 3. Gabungkan konfigurasi ini ke `~/.codex/config.toml`:
17
+
18
+ ```toml
19
+ [mcp_servers.dailynote-journal]
20
+ command = "npx"
21
+ args = ["-y", "dailynote-mcp@0.2.0"]
22
+ env_vars = ["DAILYNOTE_URL", "DAILYNOTE_TOKEN"]
23
+ enabled_tools = ["add_journal_entry"]
24
+ ```
25
+
26
+ 4. Tutup penuh dan buka ulang Codex setelah environment diisi. Buka chat baru.
27
+
28
+ `npx` mengunduh package beserta dependency pada penggunaan pertama, kemudian
29
+ menjalankan MCP. Tidak perlu menjalankan server MCP di terminal terpisah.
30
+ Jika Codex tidak menemukan `npx`, gunakan path absolut hasil `command -v npx`.
31
+
32
+ [Panduan pemasangan langkah demi langkah](./INSTALL_CODEX.md), termasuk cara
33
+ mengisi environment sesi macOS tanpa file token. Konfigurasi stdio mengikuti
34
+ [dokumentasi resmi OpenAI](https://developers.openai.com/codex/mcp/).
35
+
36
+ Contoh untuk Claude Desktop ada di `claude-desktop.example.json`. Client harus
37
+ mewarisi kedua environment variable saat dibuka; contoh tidak menyimpan token
38
+ atau mengandalkan interpolasi environment dalam JSON.
39
+
40
+ ## Tool yang tersedia
41
+
42
+ Saat ini ada satu tool: **`add_journal_entry`**.
43
+
44
+ ```json
45
+ {
46
+ "text": "Menyelesaikan pekerjaan dan dokumentasi hari ini.",
47
+ "date": "2026-11-05"
48
+ }
49
+ ```
50
+
51
+ - `text`: isi entri, maksimum 20.000 unit UTF-16. Pada format plain, baris baru
52
+ menjadi paragraf dan HTML/Markdown disimpan sebagai teks literal.
53
+ - `date`: opsional, format `YYYY-MM-DD`. Default hari ini mengikuti timezone akun.
54
+ - `format`: opsional, `plain` (default) atau `markdown`. Plain menyimpan karakter
55
+ Markdown/HTML sebagai teks literal. Markdown dikonversi di backend menjadi JSON
56
+ Tiptap tervalidasi, sehingga formatnya dapat diedit dari aplikasi web.
57
+ - `requestId`: opsional untuk entri baru; gunakan kembali ID yang sama saat retry.
58
+
59
+ Tool membuat jurnal tanggal tersebut bila belum ada, atau menambahkan teks di
60
+ akhir jurnal yang ada. Isi, format, dan media lama tetap dipertahankan. Tidak
61
+ menyediakan pembacaan/penghapusan jurnal, catatan, tugas, atau upload file.
62
+
63
+ Contoh rich text, mulai versi `0.2.0`:
64
+
65
+ ```json
66
+ {
67
+ "text": "## Kegiatan\n\n- Menyelesaikan **fitur MCP**",
68
+ "format": "markdown"
69
+ }
70
+ ```
71
+
72
+ Markdown mendukung heading (semua level dinormalisasi menjadi level 2 sesuai
73
+ editor jurnal), tebal, miring, coret, daftar berpoin/bernomor bertingkat,
74
+ checklist datar, kutipan, blok kode, line break eksplisit, dan tautan HTTP(S)/mailto
75
+ yang lolos kebijakan jurnal. HTML ditampilkan sebagai teks literal. Tabel,
76
+ gambar Markdown, inline code, garis pemisah, dan checklist bertingkat ditolak
77
+ dengan `invalid_content`; tidak ada bagian entri yang disimpan.
78
+
79
+ Backend DailyNote harus diperbarui lebih dahulu sebelum memakai format baru.
80
+ Client lama tetap dapat mengirim teks biasa tanpa `format`. Metadata/isi media
81
+ lama tetap dipertahankan; format Markdown tidak memberikan izin upload gambar.
82
+
83
+ Contoh permintaan di Codex:
84
+
85
+ > Gunakan DailyNote MCP untuk menambahkan "Tes koneksi dari Codex" ke jurnal hari ini.
86
+
87
+ Permintaan tersebut menulis jurnal akun pemilik token. Hasil sukses berisi
88
+ tanggal, status `saved`, dan `requestId`; isi jurnal tidak dikembalikan.
89
+
90
+ ## Token dan retry
91
+
92
+ Setiap pengguna memakai token pribadi dari akunnya sendiri. Server membaca
93
+ `DAILYNOTE_URL` dan `DAILYNOTE_TOKEN` dari environment; tidak memuat file `.env`.
94
+ Jangan kirim token ke chat atau memasukkannya ke konfigurasi publik.
95
+
96
+ **Buat token baru** menonaktifkan token lama. Setelah rotasi, perbarui environment
97
+ dan buka ulang client. **Cabut token** di profil menghentikan akses token tersebut.
98
+ Scope token saat ini hanya `journal:append`.
99
+
100
+ Jika hasil `outcome_unknown`, periksa jurnal di aplikasi atau ulangi dengan
101
+ **requestId, text, date, dan format yang sama**, termasuk tetap tanpa `date` bila sebelumnya
102
+ kosong. Format yang tidak dikirim setara dengan `plain`. Tidak ada retry otomatis.
103
+ Mengirim payload berbeda, termasuk mengubah format, dengan ID yang sama
104
+ ditolak; mengganti token tidak menghilangkan deduplikasi akun.
105
+
106
+ Jika proses berhenti sebelum requestId diterima, periksa jurnal lewat aplikasi
107
+ sebelum mengirim ulang.
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "dailynote-journal": {
4
+ "command": "npx",
5
+ "args": ["-y", "dailynote-mcp@0.2.0"]
6
+ }
7
+ }
8
+ }
@@ -0,0 +1,6 @@
1
+ # Merge this block into your existing Codex config; do not replace that file.
2
+ [mcp_servers.dailynote-journal]
3
+ command = "npx"
4
+ args = ["-y", "dailynote-mcp@0.2.0"]
5
+ env_vars = ["DAILYNOTE_URL", "DAILYNOTE_TOKEN"]
6
+ enabled_tools = ["add_journal_entry"]
package/journal.mjs ADDED
@@ -0,0 +1,160 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { z } from "zod";
3
+ import { McpServer } from "@modelcontextprotocol/server";
4
+ import packageInfo from "./package.json" with { type: "json" };
5
+
6
+ export function readConfig(env) {
7
+ const origin = new URL(env.DAILYNOTE_URL ?? "");
8
+ if (
9
+ origin.username ||
10
+ origin.password ||
11
+ origin.search ||
12
+ origin.hash ||
13
+ origin.pathname !== "/" ||
14
+ !(
15
+ origin.protocol === "https:" ||
16
+ (origin.protocol === "http:" &&
17
+ ["localhost", "127.0.0.1", "[::1]"].includes(origin.hostname))
18
+ )
19
+ )
20
+ throw new Error(
21
+ "DAILYNOTE_URL must be an HTTPS origin or HTTP loopback origin.",
22
+ );
23
+ const token = env.DAILYNOTE_TOKEN ?? "";
24
+ if (!/^[A-Za-z0-9_-]{43,128}$/.test(token) || token.startsWith("replace-"))
25
+ throw new Error(
26
+ "DAILYNOTE_TOKEN must contain a personal API token from your DailyNote profile.",
27
+ );
28
+ return { endpoint: new URL("/api/mcp/journal-entry", origin), token };
29
+ }
30
+
31
+ const validDate = (value) => {
32
+ if (
33
+ typeof value !== "string" ||
34
+ !/^\d{4}-\d{2}-\d{2}$/.test(value) ||
35
+ Number(value.slice(0, 4)) < 1000
36
+ )
37
+ return false;
38
+ const date = new Date(`${value}T00:00:00Z`);
39
+ return (
40
+ Number.isFinite(date.valueOf()) && date.toISOString().slice(0, 10) === value
41
+ );
42
+ };
43
+
44
+ const safeCodes = new Set([
45
+ "unauthorized",
46
+ "forbidden",
47
+ "rate_limited",
48
+ "invalid_input",
49
+ "invalid_content",
50
+ "conflict",
51
+ "idempotency_conflict",
52
+ "outcome_unknown",
53
+ "payload_too_large",
54
+ "request_timeout",
55
+ "unsupported_media_type",
56
+ ]);
57
+
58
+ export function createJournalServer(config) {
59
+ const server = new McpServer({
60
+ name: packageInfo.name,
61
+ version: packageInfo.version,
62
+ });
63
+ server.registerTool(
64
+ "add_journal_entry",
65
+ {
66
+ description:
67
+ "Append text to the personal token owner's DailyNote journal. Omit format for literal plain text, or use markdown for validated rich text. Creates the date if missing; preserves existing text and media. Date defaults to today in the account timezone. Never reads or returns journal content. On an uncertain result, retry identical text/date/format with the returned requestId; do not generate a new ID.",
68
+ inputSchema: z
69
+ .object({
70
+ text: z
71
+ .string()
72
+ .min(1)
73
+ .max(20_000)
74
+ .refine((text) => text.trim().length > 0)
75
+ .describe(
76
+ "Text to append, up to 20,000 UTF-16 units. Plain mode stores Markdown/HTML literally. Markdown mode supports heading (normalized to level 2), bold, italic, strikethrough, lists, flat task lists, blockquotes, code blocks and safe links. HTML remains literal; tables, images, inline code and horizontal rules are rejected.",
77
+ ),
78
+ format: z
79
+ .enum(["plain", "markdown"])
80
+ .optional()
81
+ .describe(
82
+ "Optional input format, default plain. Use markdown for rich text; requires a DailyNote backend with Markdown support. Preserve this value when retrying.",
83
+ ),
84
+ date: z
85
+ .string()
86
+ .refine(validDate)
87
+ .optional()
88
+ .describe(
89
+ "Optional calendar date YYYY-MM-DD in the account timezone.",
90
+ ),
91
+ requestId: z
92
+ .uuid()
93
+ .optional()
94
+ .describe(
95
+ "Leave empty for a new entry. Reuse the returned ID with identical text/date to safely retry an uncertain request.",
96
+ ),
97
+ })
98
+ .strict(),
99
+ annotations: {
100
+ readOnlyHint: false,
101
+ destructiveHint: false,
102
+ idempotentHint: false,
103
+ openWorldHint: true,
104
+ },
105
+ },
106
+ async ({ text, date, format, requestId = randomUUID() }) => {
107
+ let result;
108
+ let retryAfter;
109
+ try {
110
+ const response = await fetch(config.endpoint, {
111
+ method: "POST",
112
+ redirect: "error",
113
+ signal: AbortSignal.timeout(30_000),
114
+ headers: {
115
+ Authorization: `Bearer ${config.token}`,
116
+ "Content-Type": "application/json",
117
+ },
118
+ body: JSON.stringify({
119
+ text,
120
+ ...(date ? { date } : {}),
121
+ ...(format ? { format } : {}),
122
+ requestId,
123
+ }),
124
+ });
125
+ const body = await response.json();
126
+ if (
127
+ response.ok &&
128
+ body.ok === true &&
129
+ body.data?.status === "saved" &&
130
+ validDate(body.data.date) &&
131
+ body.data.requestId === requestId
132
+ ) {
133
+ result = { date: body.data.date, status: "saved", requestId };
134
+ } else {
135
+ const code = safeCodes.has(body.error?.code)
136
+ ? body.error.code
137
+ : "outcome_unknown";
138
+ retryAfter = Number(response.headers.get("Retry-After"));
139
+ result = { status: "error", code, requestId };
140
+ }
141
+ } catch {
142
+ // A timed-out or lost response may already have committed. Never retry
143
+ // automatically or include remote diagnostics, URLs, token or input text.
144
+ result = { status: "error", code: "outcome_unknown", requestId };
145
+ }
146
+ if (
147
+ result.code === "rate_limited" &&
148
+ Number.isInteger(retryAfter) &&
149
+ retryAfter > 0
150
+ )
151
+ result.retryAfterSeconds = Math.min(retryAfter, 3600);
152
+ return {
153
+ content: [{ type: "text", text: JSON.stringify(result) }],
154
+ structuredContent: result,
155
+ ...(result.status === "error" ? { isError: true } : {}),
156
+ };
157
+ },
158
+ );
159
+ return server;
160
+ }
package/package.json CHANGED
@@ -1,6 +1,43 @@
1
1
  {
2
2
  "name": "dailynote-mcp",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "description": "Connect AI assistants to your DailyNote account through MCP.",
5
+ "license": "UNLICENSED",
6
+ "type": "module",
7
+ "bin": {
8
+ "dailynote-mcp": "server.mjs"
9
+ },
10
+ "files": [
11
+ "server.mjs",
12
+ "journal.mjs",
13
+ "INSTALL_CODEX.md",
14
+ "codex.example.toml",
15
+ "claude-desktop.example.json"
16
+ ],
17
+ "keywords": [
18
+ "dailynote",
19
+ "mcp",
20
+ "model-context-protocol",
21
+ "journal"
22
+ ],
23
+ "publishConfig": {
24
+ "access": "public",
25
+ "registry": "https://registry.npmjs.org/"
26
+ },
27
+ "engines": {
28
+ "node": ">=24 <25"
29
+ },
30
+ "packageManager": "pnpm@11.10.0",
31
+ "scripts": {
32
+ "start": "node server.mjs",
33
+ "test": "node --test server.test.mjs",
34
+ "test:package": "node --test package.test.mjs"
35
+ },
36
+ "dependencies": {
37
+ "@modelcontextprotocol/server": "2.4.0",
38
+ "zod": "4.6.7"
39
+ },
40
+ "devDependencies": {
41
+ "@modelcontextprotocol/client": "2.4.0"
42
+ }
43
+ }
package/server.mjs ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
3
+ import { createJournalServer, readConfig } from "./journal.mjs";
4
+
5
+ try {
6
+ const server = createJournalServer(readConfig(process.env));
7
+ const close = async () => {
8
+ await server.close();
9
+ };
10
+ process.once("SIGINT", close);
11
+ process.once("SIGTERM", close);
12
+ await server.connect(new StdioServerTransport());
13
+ } catch {
14
+ // stdout is exclusively the MCP protocol; diagnostics never include secrets.
15
+ console.error(
16
+ "DailyNote MCP could not start. Set DAILYNOTE_URL and DAILYNOTE_TOKEN in the client environment and check installed dependencies.",
17
+ );
18
+ process.exitCode = 1;
19
+ }