dailynote-mcp 0.0.0-stage → 0.1.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.
- package/INSTALL_CODEX.md +158 -0
- package/README.md +82 -2
- package/claude-desktop.example.json +8 -0
- package/codex.example.toml +6 -0
- package/journal.mjs +149 -0
- package/package.json +41 -4
- package/server.mjs +19 -0
package/INSTALL_CODEX.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
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
|
+
## 1. Siapkan Node.js dan aplikasi DailyNote
|
|
8
|
+
|
|
9
|
+
Pasang Node.js 24 beserta npm dari [nodejs.org](https://nodejs.org/).
|
|
10
|
+
Periksa dari terminal:
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
node --version
|
|
14
|
+
npx --version
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Node harus menunjukkan versi `v24.x`. Pengguna tidak perlu clone proyek,
|
|
18
|
+
memasang pnpm, menjalankan database, atau melakukan migration.
|
|
19
|
+
Aplikasi DailyNote tujuan harus sudah berjalan dan mendukung fitur Token API
|
|
20
|
+
serta endpoint MCP. Pengembang aplikasi menyiapkan backend tersebut.
|
|
21
|
+
|
|
22
|
+
## 2. Gunakan package npm
|
|
23
|
+
|
|
24
|
+
Konfigurasi berikut memakai `dailynote-mcp@0.1.0` dan berlaku setelah versi
|
|
25
|
+
tersebut tersedia di npm. `npx` akan mengambil package serta dependency ketika
|
|
26
|
+
Codex menjalankan koneksi pertama kali. Akses registry diperlukan untuk
|
|
27
|
+
pengunduhan awal. Tidak perlu menjalankan proses MCP di terminal terpisah.
|
|
28
|
+
|
|
29
|
+
## 3. Buat token pribadi
|
|
30
|
+
|
|
31
|
+
Login ke DailyNote, buka **Profil → Token API → Buat token**, lalu salin token.
|
|
32
|
+
Token lengkap hanya ditampilkan sekali. Jika perlu mengganti token yang sudah
|
|
33
|
+
ada, pilih **Buat token baru**; token lama akan dinonaktifkan.
|
|
34
|
+
|
|
35
|
+
Jangan kirim token ke chat, Git, atau GitHub Actions. Pengguna lain melakukan
|
|
36
|
+
langkah yang sama memakai akun dan komputernya sendiri.
|
|
37
|
+
|
|
38
|
+
## 4. Isi environment sesi macOS
|
|
39
|
+
|
|
40
|
+
Contoh berikut memakai terminal **zsh** bawaan macOS. Tempel token ketika diminta;
|
|
41
|
+
input disembunyikan dan token tidak ditulis secara literal dalam riwayat command.
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
export DAILYNOTE_URL="https://alamat-aplikasi-anda.example"
|
|
45
|
+
read -rs 'DAILYNOTE_TOKEN?Token API DailyNote: '
|
|
46
|
+
printf '\n'
|
|
47
|
+
export DAILYNOTE_TOKEN
|
|
48
|
+
launchctl setenv DAILYNOTE_URL "$DAILYNOTE_URL"
|
|
49
|
+
launchctl setenv DAILYNOTE_TOKEN "$DAILYNOTE_TOKEN"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Ganti alamat contoh dengan origin HTTPS aplikasi DailyNote Anda, tanpa path,
|
|
53
|
+
query, atau fragment. Token harus berasal dari akun pada aplikasi tujuan
|
|
54
|
+
tersebut. `http://localhost:3000` hanya untuk development lokal.
|
|
55
|
+
|
|
56
|
+
`export` mengatur terminal saat ini. `launchctl setenv` membuat aplikasi desktop
|
|
57
|
+
yang dibuka setelahnya menerima variabel pada sesi login macOS ini. Variabel
|
|
58
|
+
tersebut harus diisi ulang setelah logout/restart komputer. Jangan menambahkan
|
|
59
|
+
token ke `.zshrc`, `.env`, atau `config.toml` bila ingin tetap tanpa file token.
|
|
60
|
+
|
|
61
|
+
Untuk Codex CLI, jalankan `codex` dari terminal yang sudah menerima `export`.
|
|
62
|
+
Untuk Codex desktop, tutup penuh aplikasi dengan **Quit Codex**, lalu buka lagi
|
|
63
|
+
setelah langkah ini. Aplikasi yang sudah berjalan belum menerima environment baru.
|
|
64
|
+
|
|
65
|
+
## 5. Daftarkan MCP
|
|
66
|
+
|
|
67
|
+
Buka konfigurasi Codex `~/.codex/config.toml`. Tambahkan blok berikut, atau
|
|
68
|
+
perbarui blok `dailynote-journal` yang sudah ada; pertahankan konfigurasi lainnya.
|
|
69
|
+
|
|
70
|
+
```toml
|
|
71
|
+
[mcp_servers.dailynote-journal]
|
|
72
|
+
command = "npx"
|
|
73
|
+
args = ["-y", "dailynote-mcp@0.1.0"]
|
|
74
|
+
env_vars = ["DAILYNOTE_URL", "DAILYNOTE_TOKEN"]
|
|
75
|
+
enabled_tools = ["add_journal_entry"]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Jika Codex tidak menemukan `npx`, ganti `command` dengan path absolut hasil
|
|
79
|
+
`command -v npx`. Tidak ada path source code DailyNote dalam konfigurasi ini.
|
|
80
|
+
`env_vars` berisi nama variabel yang diteruskan Codex,
|
|
81
|
+
bukan nilai token. Konfigurasi ini mengikuti
|
|
82
|
+
[dokumentasi resmi OpenAI](https://developers.openai.com/codex/mcp/).
|
|
83
|
+
|
|
84
|
+
Jika sebelumnya memakai source lokal, ganti `command`/`args` dalam blok yang
|
|
85
|
+
sama dengan konfigurasi di atas setelah versi npm tersedia. Pertahankan
|
|
86
|
+
`env_vars`; jangan membuat koneksi kedua karena tool akan muncul ganda.
|
|
87
|
+
|
|
88
|
+
## 6. Periksa koneksi
|
|
89
|
+
|
|
90
|
+
Dari terminal mana pun:
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
codex mcp get dailynote-journal
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Hasil seharusnya menunjukkan `enabled: true`, `transport: stdio`, argumen
|
|
97
|
+
`-y dailynote-mcp@0.1.0`, dan nama kedua environment variable. Token mentah tidak
|
|
98
|
+
perlu tampil.
|
|
99
|
+
Setelah Codex desktop dibuka ulang, buka chat baru dan pastikan tool
|
|
100
|
+
`add_journal_entry` tersedia.
|
|
101
|
+
|
|
102
|
+
## 7. Coba input jurnal
|
|
103
|
+
|
|
104
|
+
Di chat yang sudah memuat MCP:
|
|
105
|
+
|
|
106
|
+
> Gunakan DailyNote MCP untuk menambahkan "Tes koneksi MCP dari Codex" ke jurnal hari ini.
|
|
107
|
+
|
|
108
|
+
Tindakan ini menambahkan isi jurnal akun pemilik token. Hasil yang diharapkan
|
|
109
|
+
berisi tanggal, status `saved`, dan `requestId`. Periksa hasil di DailyNote;
|
|
110
|
+
tanggal default mengikuti timezone akun.
|
|
111
|
+
|
|
112
|
+
Jika hasil belum pasti karena koneksi terputus, ulangi dengan requestId, teks
|
|
113
|
+
dan tanggal yang sama. Jangan langsung mengirim entri baru dengan ID berbeda.
|
|
114
|
+
|
|
115
|
+
## Mengganti atau melepas koneksi
|
|
116
|
+
|
|
117
|
+
Untuk mengganti token, buat token baru di profil, ulangi langkah 4 dan buka ulang
|
|
118
|
+
Codex. Untuk mencabut akses, gunakan **Cabut token** di DailyNote.
|
|
119
|
+
|
|
120
|
+
Untuk menghapus pendaftaran MCP dan variabel sesi macOS:
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
codex mcp remove dailynote-journal
|
|
124
|
+
launchctl unsetenv DAILYNOTE_URL
|
|
125
|
+
launchctl unsetenv DAILYNOTE_TOKEN
|
|
126
|
+
unset DAILYNOTE_URL DAILYNOTE_TOKEN
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Proses yang sudah berjalan tetap memegang salinan environment lamanya sampai
|
|
130
|
+
ditutup. Menghapus konfigurasi lokal tidak mencabut token atau menghapus jurnal.
|
|
131
|
+
|
|
132
|
+
## Jika bermasalah
|
|
133
|
+
|
|
134
|
+
- Server tidak bisa dimulai: periksa Node.js 24, path npx, akses registry, dan kedua
|
|
135
|
+
environment variable.
|
|
136
|
+
- `unauthorized`: token dicabut/diganti, salah disalin, atau berasal dari aplikasi
|
|
137
|
+
tujuan lain.
|
|
138
|
+
- Koneksi gagal: pastikan aplikasi tujuan hidup, URL/port tepat, dan gunakan HTTPS
|
|
139
|
+
untuk production.
|
|
140
|
+
- `outcome_unknown`: periksa jurnal atau retry dengan requestId lama.
|
|
141
|
+
- Tool belum terlihat: quit dan buka ulang Codex setelah environment diisi;
|
|
142
|
+
buka chat baru.
|
|
143
|
+
|
|
144
|
+
## Commands to run
|
|
145
|
+
|
|
146
|
+
Wajib: ikuti langkah 1–6 berurutan. Semua command pemasangan pengguna dapat
|
|
147
|
+
dijalankan dari terminal lokal mana pun, tanpa folder proyek. Pemeriksaan akhir:
|
|
148
|
+
|
|
149
|
+
```sh
|
|
150
|
+
codex mcp get dailynote-journal
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Hasil yang diharapkan: koneksi stdio aktif, versi npm tertentu pada argumen,
|
|
154
|
+
dan satu tool `add_journal_entry` tersedia pada chat baru.
|
|
155
|
+
|
|
156
|
+
Pengguna package tidak perlu menjalankan suite tes atau build aplikasi. Jika
|
|
157
|
+
pemasangan gagal, kirim command, exit status, dan cuplikan error relevan tanpa
|
|
158
|
+
token atau isi jurnal privat.
|
package/README.md
CHANGED
|
@@ -1,3 +1,83 @@
|
|
|
1
|
-
#
|
|
1
|
+
# DailyNote MCP
|
|
2
2
|
|
|
3
|
-
|
|
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.1.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.1.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`: teks biasa, maksimum 20.000 unit UTF-16. Baris baru menjadi paragraf;
|
|
52
|
+
HTML dan Markdown disimpan sebagai teks literal.
|
|
53
|
+
- `date`: opsional, format `YYYY-MM-DD`. Default hari ini mengikuti timezone akun.
|
|
54
|
+
- `requestId`: opsional untuk entri baru; gunakan kembali ID yang sama saat retry.
|
|
55
|
+
|
|
56
|
+
Tool membuat jurnal tanggal tersebut bila belum ada, atau menambahkan teks di
|
|
57
|
+
akhir jurnal yang ada. Isi, format, dan media lama tetap dipertahankan. Tidak
|
|
58
|
+
menyediakan pembacaan/penghapusan jurnal, catatan, tugas, atau upload file.
|
|
59
|
+
|
|
60
|
+
Contoh permintaan di Codex:
|
|
61
|
+
|
|
62
|
+
> Gunakan DailyNote MCP untuk menambahkan "Tes koneksi dari Codex" ke jurnal hari ini.
|
|
63
|
+
|
|
64
|
+
Permintaan tersebut menulis jurnal akun pemilik token. Hasil sukses berisi
|
|
65
|
+
tanggal, status `saved`, dan `requestId`; isi jurnal tidak dikembalikan.
|
|
66
|
+
|
|
67
|
+
## Token dan retry
|
|
68
|
+
|
|
69
|
+
Setiap pengguna memakai token pribadi dari akunnya sendiri. Server membaca
|
|
70
|
+
`DAILYNOTE_URL` dan `DAILYNOTE_TOKEN` dari environment; tidak memuat file `.env`.
|
|
71
|
+
Jangan kirim token ke chat atau memasukkannya ke konfigurasi publik.
|
|
72
|
+
|
|
73
|
+
**Buat token baru** menonaktifkan token lama. Setelah rotasi, perbarui environment
|
|
74
|
+
dan buka ulang client. **Cabut token** di profil menghentikan akses token tersebut.
|
|
75
|
+
Scope token saat ini hanya `journal:append`.
|
|
76
|
+
|
|
77
|
+
Jika hasil `outcome_unknown`, periksa jurnal di aplikasi atau ulangi dengan
|
|
78
|
+
**requestId, text, dan date yang sama**, termasuk tetap tanpa `date` bila sebelumnya
|
|
79
|
+
kosong. Tidak ada retry otomatis. Mengirim payload berbeda dengan ID yang sama
|
|
80
|
+
ditolak; mengganti token tidak menghilangkan deduplikasi akun.
|
|
81
|
+
|
|
82
|
+
Jika proses berhenti sebelum requestId diterima, periksa jurnal lewat aplikasi
|
|
83
|
+
sebelum mengirim ulang.
|
package/journal.mjs
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
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 plain text to the personal token owner's DailyNote journal. 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 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
|
+
"Plain text to append. Line breaks become paragraphs; Markdown/HTML are stored as literal text.",
|
|
77
|
+
),
|
|
78
|
+
date: z
|
|
79
|
+
.string()
|
|
80
|
+
.refine(validDate)
|
|
81
|
+
.optional()
|
|
82
|
+
.describe(
|
|
83
|
+
"Optional calendar date YYYY-MM-DD in the account timezone.",
|
|
84
|
+
),
|
|
85
|
+
requestId: z
|
|
86
|
+
.uuid()
|
|
87
|
+
.optional()
|
|
88
|
+
.describe(
|
|
89
|
+
"Leave empty for a new entry. Reuse the returned ID with identical text/date to safely retry an uncertain request.",
|
|
90
|
+
),
|
|
91
|
+
})
|
|
92
|
+
.strict(),
|
|
93
|
+
annotations: {
|
|
94
|
+
readOnlyHint: false,
|
|
95
|
+
destructiveHint: false,
|
|
96
|
+
idempotentHint: false,
|
|
97
|
+
openWorldHint: true,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
async ({ text, date, requestId = randomUUID() }) => {
|
|
101
|
+
let result;
|
|
102
|
+
let retryAfter;
|
|
103
|
+
try {
|
|
104
|
+
const response = await fetch(config.endpoint, {
|
|
105
|
+
method: "POST",
|
|
106
|
+
redirect: "error",
|
|
107
|
+
signal: AbortSignal.timeout(30_000),
|
|
108
|
+
headers: {
|
|
109
|
+
Authorization: `Bearer ${config.token}`,
|
|
110
|
+
"Content-Type": "application/json",
|
|
111
|
+
},
|
|
112
|
+
body: JSON.stringify({ text, ...(date ? { date } : {}), requestId }),
|
|
113
|
+
});
|
|
114
|
+
const body = await response.json();
|
|
115
|
+
if (
|
|
116
|
+
response.ok &&
|
|
117
|
+
body.ok === true &&
|
|
118
|
+
body.data?.status === "saved" &&
|
|
119
|
+
validDate(body.data.date) &&
|
|
120
|
+
body.data.requestId === requestId
|
|
121
|
+
) {
|
|
122
|
+
result = { date: body.data.date, status: "saved", requestId };
|
|
123
|
+
} else {
|
|
124
|
+
const code = safeCodes.has(body.error?.code)
|
|
125
|
+
? body.error.code
|
|
126
|
+
: "outcome_unknown";
|
|
127
|
+
retryAfter = Number(response.headers.get("Retry-After"));
|
|
128
|
+
result = { status: "error", code, requestId };
|
|
129
|
+
}
|
|
130
|
+
} catch {
|
|
131
|
+
// A timed-out or lost response may already have committed. Never retry
|
|
132
|
+
// automatically or include remote diagnostics, URLs, token or input text.
|
|
133
|
+
result = { status: "error", code: "outcome_unknown", requestId };
|
|
134
|
+
}
|
|
135
|
+
if (
|
|
136
|
+
result.code === "rate_limited" &&
|
|
137
|
+
Number.isInteger(retryAfter) &&
|
|
138
|
+
retryAfter > 0
|
|
139
|
+
)
|
|
140
|
+
result.retryAfterSeconds = Math.min(retryAfter, 3600);
|
|
141
|
+
return {
|
|
142
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
143
|
+
structuredContent: result,
|
|
144
|
+
...(result.status === "error" ? { isError: true } : {}),
|
|
145
|
+
};
|
|
146
|
+
},
|
|
147
|
+
);
|
|
148
|
+
return server;
|
|
149
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dailynote-mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.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
|
+
}
|