mcp-for-memos 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +334 -0
  2. package/dist/client.d.ts +15 -0
  3. package/dist/client.d.ts.map +1 -0
  4. package/dist/client.js +155 -0
  5. package/dist/client.js.map +1 -0
  6. package/dist/config.d.ts +18 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/config.js +27 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/http.d.ts +4 -0
  11. package/dist/http.d.ts.map +1 -0
  12. package/dist/http.js +342 -0
  13. package/dist/http.js.map +1 -0
  14. package/dist/index.d.ts +3 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +79 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/prompts/index.d.ts +3 -0
  19. package/dist/prompts/index.d.ts.map +1 -0
  20. package/dist/prompts/index.js +68 -0
  21. package/dist/prompts/index.js.map +1 -0
  22. package/dist/resources/index.d.ts +4 -0
  23. package/dist/resources/index.d.ts.map +1 -0
  24. package/dist/resources/index.js +52 -0
  25. package/dist/resources/index.js.map +1 -0
  26. package/dist/server.d.ts +9 -0
  27. package/dist/server.d.ts.map +1 -0
  28. package/dist/server.js +40 -0
  29. package/dist/server.js.map +1 -0
  30. package/dist/tools/memos.d.ts +9 -0
  31. package/dist/tools/memos.d.ts.map +1 -0
  32. package/dist/tools/memos.js +137 -0
  33. package/dist/tools/memos.js.map +1 -0
  34. package/dist/tools/relations.d.ts +4 -0
  35. package/dist/tools/relations.d.ts.map +1 -0
  36. package/dist/tools/relations.js +108 -0
  37. package/dist/tools/relations.js.map +1 -0
  38. package/dist/tools/resources.d.ts +4 -0
  39. package/dist/tools/resources.d.ts.map +1 -0
  40. package/dist/tools/resources.js +90 -0
  41. package/dist/tools/resources.js.map +1 -0
  42. package/dist/tools/review.d.ts +4 -0
  43. package/dist/tools/review.d.ts.map +1 -0
  44. package/dist/tools/review.js +208 -0
  45. package/dist/tools/review.js.map +1 -0
  46. package/dist/tools/tags.d.ts +4 -0
  47. package/dist/tools/tags.d.ts.map +1 -0
  48. package/dist/tools/tags.js +164 -0
  49. package/dist/tools/tags.js.map +1 -0
  50. package/dist/tools/utils.d.ts +6 -0
  51. package/dist/tools/utils.d.ts.map +1 -0
  52. package/dist/tools/utils.js +68 -0
  53. package/dist/tools/utils.js.map +1 -0
  54. package/dist/types.d.ts +36 -0
  55. package/dist/types.d.ts.map +1 -0
  56. package/dist/types.js +2 -0
  57. package/dist/types.js.map +1 -0
  58. package/package.json +49 -0
package/README.md ADDED
@@ -0,0 +1,334 @@
1
+ # memos-mcp
2
+
3
+ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that turns [Memos](https://github.com/usememos/memos) into a **multi-purpose database** for your AI assistants — notes, projects, tasks, agendas, knowledge base, and more.
4
+
5
+ [中文文档](README_zh.md)
6
+
7
+ ## What is this?
8
+
9
+ **memos-mcp** is a bridge between AI assistants and your Memos instance. Instead of context windows that forget, your AI has a **persistent memory database** it can query, create, and organize notes in.
10
+
11
+ Think of it as:
12
+ - 📝 **Personal knowledge base** — capture ideas, research, learnings
13
+ - 📋 **Task manager** — to-dos, checklists, project tracking
14
+ - 📅 **Agenda/planner** — schedules, meeting notes, deadlines
15
+ - 🏗️ **Project database** — specs, decisions, retrospectives
16
+ - 🔖 **Bookmark system** — save links, quotes, references
17
+
18
+ Your AI assistant can search by date, filter by tags, create notes on the fly, and query your entire history — all through natural language.
19
+
20
+ ---
21
+
22
+ ## What can the AI do with this MCP?
23
+
24
+ Once connected, your AI assistant gains **persistent memory**. Here's what it can do:
25
+
26
+ ### 📝 Create Notes
27
+ - Save meeting notes, ideas, tasks, or anything
28
+ - Use `#hashtags` for automatic tagging
29
+ - Backdate notes to a specific time
30
+ - Markdown support (bold, lists, code blocks, etc.)
31
+
32
+ ### 🔍 Search & Recall
33
+ - "What did I do yesterday?" → searches by date
34
+ - "Show me #project notes" → filters by tag
35
+ - "Find notes about API design" → full-text search
36
+ - "What's on my agenda today?" → today's memos
37
+ - "Notes from last week" → date range search
38
+ - "Show pinned notes" → filter by pin status
39
+ - "Find notes with incomplete tasks" → task filter
40
+
41
+ ### ✏️ Update & Organize
42
+ - Edit existing notes
43
+ - Pin important memos
44
+ - Archive old notes
45
+ - Change visibility (private/public)
46
+
47
+ ### 🗑️ Clean Up
48
+ - Delete notes you no longer need
49
+
50
+ ### 🏷️ Tag Management
51
+ - List all tags with usage counts
52
+ - Browse tag hierarchy (`#work/meeting`, `#project/backend`)
53
+ - Analyze and suggest tag improvements
54
+
55
+ ---
56
+
57
+ ## Tools (6)
58
+
59
+ | Tool | What it does | Key Features |
60
+ |------|-------------|--------------|
61
+ | `search` | Search memos by date, tags, content, visibility, pinned status | Relative dates (`today`, `yesterday`, `next_monday`), date ranges, week view, full-text search |
62
+ | `get` | Get a single memo's full content | By ID or UID, includes attachments metadata |
63
+ | `create` | Create a memo with markdown and #hashtags | Backdate support, visibility control. **Max 13,000 chars** |
64
+ | `update` | Update memo content, visibility, or pin status | Partial updates (only changed fields), archive/restore |
65
+ | `delete` | Permanently delete a memo | Cannot be undone |
66
+ | `tags` | List all tags with usage counts | Hierarchical: top-level, children, or recursive flat list |
67
+
68
+ ### Search Date Examples
69
+
70
+ | You say | The AI calls |
71
+ |---------|-------------|
72
+ | "What did I do yesterday?" | `search(date="yesterday")` |
73
+ | "What's on my agenda today?" | `search(date="today")` |
74
+ | "Notes from last week" | `search(date="last_week", week=true)` |
75
+ | "Show me #project notes" | `search(tags=["project"])` |
76
+ | "Find notes about API design" | `search(query="API design")` |
77
+ | "What happened on June 15?" | `search(date="2025-06-15")` |
78
+ | "March to April overview" | `search(date="2025-03-01", endDate="2025-04-30")` |
79
+ | "Show all my memos" | `search()` |
80
+ | "Find notes with incomplete tasks" | `search(hasIncompleteTasks=true)` |
81
+ | "Show notes with attachments" | `search(hasAttachments=true)` |
82
+
83
+ ### Supported Relative Dates
84
+
85
+ `today`, `yesterday`, `tomorrow`, `this_week`, `next_week`, `last_week`, `this_month`, `next_month`, `last_month`, `next_monday`, `last_friday`, `3_days_ago`, `in_7_days`, `2_weeks_ago`, `in_2_weeks`
86
+
87
+ ---
88
+
89
+ ## Prompts (4)
90
+
91
+ Prompts are pre-built workflows the AI can follow:
92
+
93
+ | Prompt | What it does | Workflow |
94
+ |--------|-------------|----------|
95
+ | `capture` | Quick-save a thought, task, or idea | Asks for content + optional tags, creates memo |
96
+ | `review` | Review memos from a time period | Searches memos, reads each one, summarizes |
97
+ | `on_day` | Check what happened/is planned for a date | Searches by date, presents findings |
98
+ | `tag_overview` | Analyze your tag system | Lists tags, shows hierarchy, suggests cleanup |
99
+
100
+ ---
101
+
102
+ ## What the AI sees (Model Instructions)
103
+
104
+ When the AI connects to this MCP, it receives these tool descriptions:
105
+
106
+ ### `create` tool instructions:
107
+ > "Create a memo with markdown content. Use #hashtags for tags. **CRITICAL LIMIT: Each memo MUST NOT exceed 13,000 characters.** The Memos server enforces a hard limit of ~18K but to avoid errors always stay under 13K. If you need to write more, split into multiple memos with a shared tag (e.g. #project/my-post) and add part numbers."
108
+
109
+ ### `search` tool instructions:
110
+ > "Search memos. Unified tool for date-based, tag-based, and content-based search. Date accepts ISO, relative dates ('today', 'yesterday', 'next_monday'), and week mode. Filters: tags, query, visibility, pinned, state, hasIncompleteTasks, hasAttachments. No date = all memos."
111
+
112
+ ### `tags` tool instructions:
113
+ > "List tags with usage counts extracted from your memo content. Tags are hashtags like #project or #work/meeting. Results are cached for 60 seconds. No args = top-level tags only. parent='work' = children of 'work'. recursive=true = all tags flat."
114
+
115
+ ### `update` tool instructions:
116
+ > "Update memo fields. Only provided fields are changed. CRITICAL: MUST NOT exceed 13,000 chars."
117
+
118
+ ### `get` tool instructions:
119
+ > "Get a single memo by ID or UID."
120
+
121
+ ### `delete` tool instructions:
122
+ > "Permanently delete a memo. Cannot be undone."
123
+
124
+ ---
125
+
126
+ ## Setup
127
+
128
+ ### Prerequisites
129
+
130
+ - Node.js >= 18
131
+ - A running Memos instance (v0.29+)
132
+ - A Memos access token (Settings → Access Tokens)
133
+
134
+ ### Quick Start — Claude Desktop/Code
135
+
136
+ Add to your MCP config (`claude_desktop_config.json` or equivalent):
137
+
138
+ ```json
139
+ {
140
+ "mcpServers": {
141
+ "memos": {
142
+ "command": "npx",
143
+ "args": ["-y", "memos-mcp"],
144
+ "env": {
145
+ "MEMOS_URL": "https://your-memos-instance.com",
146
+ "MEMOS_TOKEN": "your-access-token"
147
+ }
148
+ }
149
+ }
150
+ }
151
+ ```
152
+
153
+ ### Quick Start — OpenCode
154
+
155
+ Add to `opencode.json`:
156
+
157
+ ```json
158
+ "mcp": {
159
+ "memos": {
160
+ "type": "remote",
161
+ "enabled": true,
162
+ "url": "http://your-server:8444/mcp",
163
+ "headers": {
164
+ "Authorization": "Bearer your-memos-access-token"
165
+ }
166
+ }
167
+ }
168
+ ```
169
+
170
+ ### Quick Start — LibreChat
171
+
172
+ Add to `librechat.yaml`:
173
+
174
+ ```yaml
175
+ mcpServers:
176
+ memos:
177
+ type: streamable-http
178
+ url: http://your-server:8444/mcp
179
+ headers:
180
+ Authorization: "Bearer your-memos-access-token"
181
+ timeout: 240000
182
+ ```
183
+
184
+ ---
185
+
186
+ ## Running the Server
187
+
188
+ ### HTTP Mode (Multi-tenant)
189
+
190
+ For servers that support remote MCP (OpenCode, LibreChat, etc.):
191
+
192
+ ```bash
193
+ # Start with npx
194
+ MEMOS_URL=https://your-memos-instance.com npx -y memos-mcp --http
195
+
196
+ # Or with custom port
197
+ MEMOS_URL=https://your-memos-instance.com npx -y memos-mcp --http --port 8443
198
+ ```
199
+
200
+ Each client sends its own Bearer token — no token in the server config.
201
+
202
+ ### Stdio Mode (Single user)
203
+
204
+ For Claude Desktop/Code:
205
+
206
+ ```bash
207
+ MEMOS_URL=https://your-memos-instance.com MEMOS_TOKEN=your-token npx -y memos-mcp
208
+ ```
209
+
210
+ ### Running from Source
211
+
212
+ ```bash
213
+ # 1. Clone and install
214
+ git clone https://github.com/CesarGuzmanLopez/memos-mcp.git
215
+ cd memos-mcp
216
+ pnpm install
217
+
218
+ # 2. Build
219
+ pnpm build
220
+
221
+ # 3. Run in stdio mode
222
+ MEMOS_URL=https://your-memos-instance.com MEMOS_TOKEN=your-token node dist/index.js
223
+
224
+ # 4. Or run in HTTP mode
225
+ MEMOS_URL=https://your-memos-instance.com node dist/index.js --http
226
+ ```
227
+
228
+ Then add to your MCP client config:
229
+
230
+ ```json
231
+ {
232
+ "mcpServers": {
233
+ "memos": {
234
+ "command": "node",
235
+ "args": ["/path/to/memos-mcp/dist/index.js"],
236
+ "env": {
237
+ "MEMOS_URL": "https://your-memos-instance.com",
238
+ "MEMOS_TOKEN": "your-access-token"
239
+ }
240
+ }
241
+ }
242
+ }
243
+ ```
244
+
245
+ ### Docker
246
+
247
+ ```bash
248
+ docker run -d --name memos-mcp \
249
+ -p 8444:8443 \
250
+ -e MEMOS_URL=https://your-memos-instance.com \
251
+ ghcr.io/cesarguzmanlopez/memos-mcp:latest \
252
+ node dist/index.js --http --port 8443
253
+ ```
254
+
255
+ Or with Dockerfile:
256
+
257
+ ```dockerfile
258
+ FROM node:20-alpine
259
+ WORKDIR /app
260
+ COPY dist/ ./
261
+ RUN npm install --omit=dev @modelcontextprotocol/sdk express zod
262
+ EXPOSE 8443
263
+ ENV MEMOS_URL=https://your-memos-instance.com
264
+ CMD ["node", "dist/index.js", "--http", "--port", "8443"]
265
+ ```
266
+
267
+ ### Nginx Reverse Proxy (Production)
268
+
269
+ ```nginx
270
+ server {
271
+ listen 443 ssl;
272
+ server_name memos-mcp.your-domain.com;
273
+
274
+ location / {
275
+ proxy_pass http://127.0.0.1:8444;
276
+ proxy_http_version 1.1;
277
+ proxy_set_header Host $host;
278
+ proxy_set_header X-Real-IP $remote_addr;
279
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
280
+ proxy_set_header X-Forwarded-Proto $scheme;
281
+ proxy_set_header Accept "application/json, text/event-stream";
282
+ }
283
+ }
284
+ ```
285
+
286
+ ---
287
+
288
+ ## Environment Variables
289
+
290
+ | Variable | Required | Description |
291
+ |----------|----------|-------------|
292
+ | `MEMOS_URL` | Yes | Your Memos instance URL |
293
+ | `MEMOS_TOKEN` | Stdio only | Access token for stdio mode |
294
+ | `HTTP_PORT` | No | HTTP server port (default: 3000) |
295
+ | `HTTP_HOST` | No | HTTP server host (default: 127.0.0.1) |
296
+ | `CORS_ORIGIN` | No | Allowed CORS origins (default: *) |
297
+ | `LOG_LEVEL` | No | Log level: debug, info, warn, error (default: info) |
298
+
299
+ ## API Endpoints
300
+
301
+ When running in HTTP mode:
302
+
303
+ | Endpoint | Method | Description |
304
+ |----------|--------|-------------|
305
+ | `/health` | GET | Health check with uptime and memory stats |
306
+ | `/mcp` | POST | Streamable HTTP MCP endpoint |
307
+ | `/sse` | GET | SSE connection endpoint |
308
+ | `/sse/messages` | POST | SSE message endpoint |
309
+
310
+ ## Memos Compatibility
311
+
312
+ | Memos Version | Compatible | Notes |
313
+ |---------------|------------|-------|
314
+ | **0.29.x** | ✅ Yes | Fully supported. Tested with 0.29.1 |
315
+ | **0.28.x** | ⚠️ Partial | API works but UIDs may not be supported |
316
+ | **0.22.x — 0.27.x** | ❌ No | Uses v1 API but without UID support |
317
+ | **< 0.22** | ❌ No | Old API, not compatible |
318
+
319
+ **Minimum required:** Memos **v0.29+** (uses UID-based identifiers `memos/abc123` instead of numeric IDs)
320
+
321
+ **Tested with:** Memos **0.29.1** (neosmemo/memos:stable Docker image)
322
+
323
+ **API used:** REST v1 (`/api/v1/memos`) with CEL filter expressions for queries
324
+
325
+ ## Important Notes
326
+
327
+ - **Content limit:** Each memo has a hard limit of ~18,000 characters (server-side). The MCP enforces a safe limit of **13,000 characters**. For longer content, split into multiple memos with a shared tag.
328
+ - **Multi-tenant:** In HTTP mode, each client sends their own Bearer token. The server uses that token to access Memos as that specific user.
329
+ - **Rate limiting:** 100 requests per minute per IP.
330
+ - **Tags:** Use `#hashtags` in your memo content. The MCP extracts them automatically.
331
+
332
+ ## License
333
+
334
+ MIT
@@ -0,0 +1,15 @@
1
+ export declare class MemosClient {
2
+ readonly baseUrl: string;
3
+ private token;
4
+ private _currentUser;
5
+ constructor(baseUrl: string, token: string);
6
+ validateToken(): Promise<boolean>;
7
+ getCurrentUser(): Promise<string>;
8
+ private headers;
9
+ private fetchWithTimeout;
10
+ get<T>(path: string, params?: Record<string, string>): Promise<T>;
11
+ post<T>(path: string, body?: unknown): Promise<T>;
12
+ patch<T>(path: string, body?: unknown, params?: Record<string, string>): Promise<T>;
13
+ delete<T = unknown>(path: string): Promise<T>;
14
+ }
15
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAiBA,qBAAa,WAAW;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,YAAY,CAAuB;gBAE/B,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAMpC,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IAUjC,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IA0BvC,OAAO,CAAC,OAAO;YAQD,gBAAgB;IAuBxB,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAqBjE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAejD,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAuBnF,MAAM,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;CAcpD"}
package/dist/client.js ADDED
@@ -0,0 +1,155 @@
1
+ // Cache a nivel de módulo para el usuario actual por combinación baseUrl+token
2
+ const userCache = new Map();
3
+ const USER_CACHE_TTL_MS = 300_000; // 5 minutos
4
+ // Sweep periódico del cache de usuarios
5
+ setInterval(() => {
6
+ const now = Date.now();
7
+ for (const [key, entry] of userCache) {
8
+ if (now - entry.timestamp > USER_CACHE_TTL_MS) {
9
+ userCache.delete(key);
10
+ }
11
+ }
12
+ }, 60_000); // Cada minuto
13
+ // Timeout para requests a Memos API (configurable via MEMOS_FETCH_TIMEOUT, default 120s)
14
+ const FETCH_TIMEOUT_MS = parseInt(process.env.MEMOS_FETCH_TIMEOUT || "120000", 10);
15
+ export class MemosClient {
16
+ baseUrl;
17
+ token;
18
+ _currentUser = null;
19
+ constructor(baseUrl, token) {
20
+ this.baseUrl = baseUrl.replace(/\/+$/, "");
21
+ this.token = token;
22
+ }
23
+ // Validate token is working
24
+ async validateToken() {
25
+ try {
26
+ await this.getCurrentUser();
27
+ return true;
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }
33
+ // Obtener usuario actual con cache global
34
+ async getCurrentUser() {
35
+ if (this._currentUser)
36
+ return this._currentUser;
37
+ const cacheKey = `${this.baseUrl}:${this.token}`;
38
+ const cached = userCache.get(cacheKey);
39
+ if (cached && Date.now() - cached.timestamp < USER_CACHE_TTL_MS) {
40
+ this._currentUser = cached.user;
41
+ return this._currentUser;
42
+ }
43
+ const result = await this.get("/api/v1/memos", { pageSize: "1" });
44
+ if (result.memos && result.memos.length > 0) {
45
+ this._currentUser = result.memos[0].creator;
46
+ userCache.set(cacheKey, { user: this._currentUser, timestamp: Date.now() });
47
+ }
48
+ if (!this._currentUser) {
49
+ throw new Error("Could not determine current user. No memos found with this token.");
50
+ }
51
+ return this._currentUser;
52
+ }
53
+ headers() {
54
+ return {
55
+ Authorization: `Bearer ${this.token}`,
56
+ "Content-Type": "application/json",
57
+ };
58
+ }
59
+ // Helper para fetch con timeout
60
+ async fetchWithTimeout(url, options) {
61
+ const controller = new AbortController();
62
+ const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
63
+ try {
64
+ const response = await fetch(url, {
65
+ ...options,
66
+ signal: controller.signal,
67
+ });
68
+ return response;
69
+ }
70
+ catch (error) {
71
+ if (error instanceof DOMException && error.name === "AbortError") {
72
+ throw new Error(`Request to ${url} timed out after ${FETCH_TIMEOUT_MS}ms`);
73
+ }
74
+ throw error;
75
+ }
76
+ finally {
77
+ clearTimeout(timeout);
78
+ }
79
+ }
80
+ async get(path, params) {
81
+ const url = new URL(`${this.baseUrl}${path}`);
82
+ if (params) {
83
+ for (const [key, value] of Object.entries(params)) {
84
+ if (value !== undefined && value !== "") {
85
+ url.searchParams.set(key, value);
86
+ }
87
+ }
88
+ }
89
+ const res = await this.fetchWithTimeout(url.toString(), {
90
+ headers: this.headers(),
91
+ });
92
+ if (!res.ok) {
93
+ const body = await res.text();
94
+ throw new Error(`GET ${path} failed (${res.status}): ${body}`);
95
+ }
96
+ const text = await res.text();
97
+ if (!text)
98
+ return undefined;
99
+ return JSON.parse(text);
100
+ }
101
+ async post(path, body) {
102
+ const res = await this.fetchWithTimeout(`${this.baseUrl}${path}`, {
103
+ method: "POST",
104
+ headers: this.headers(),
105
+ body: body ? JSON.stringify(body) : undefined,
106
+ });
107
+ if (!res.ok) {
108
+ const text = await res.text();
109
+ throw new Error(`POST ${path} failed (${res.status}): ${text}`);
110
+ }
111
+ const text = await res.text();
112
+ if (!text)
113
+ return undefined;
114
+ return JSON.parse(text);
115
+ }
116
+ async patch(path, body, params) {
117
+ const url = new URL(`${this.baseUrl}${path}`);
118
+ if (params) {
119
+ for (const [key, value] of Object.entries(params)) {
120
+ if (value !== undefined && value !== "") {
121
+ url.searchParams.set(key, value);
122
+ }
123
+ }
124
+ }
125
+ const res = await this.fetchWithTimeout(url.toString(), {
126
+ method: "PATCH",
127
+ headers: this.headers(),
128
+ body: body ? JSON.stringify(body) : undefined,
129
+ });
130
+ if (!res.ok) {
131
+ const text = await res.text();
132
+ throw new Error(`PATCH ${path} failed (${res.status}): ${text}`);
133
+ }
134
+ const text = await res.text();
135
+ if (!text)
136
+ return undefined;
137
+ return JSON.parse(text);
138
+ }
139
+ async delete(path) {
140
+ const res = await this.fetchWithTimeout(`${this.baseUrl}${path}`, {
141
+ method: "DELETE",
142
+ headers: this.headers(),
143
+ });
144
+ if (!res.ok) {
145
+ const text = await res.text();
146
+ throw new Error(`DELETE ${path} failed (${res.status}): ${text}`);
147
+ }
148
+ // Memos DELETE may return empty body
149
+ const text = await res.text();
150
+ if (!text)
151
+ return undefined;
152
+ return JSON.parse(text);
153
+ }
154
+ }
155
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { z } from "zod";
2
+ declare const EnvSchema: z.ZodObject<{
3
+ MEMOS_URL: z.ZodString;
4
+ HTTP_PORT: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
5
+ HTTP_HOST: z.ZodDefault<z.ZodString>;
6
+ CORS_ORIGIN: z.ZodDefault<z.ZodString>;
7
+ LOG_LEVEL: z.ZodDefault<z.ZodEnum<{
8
+ error: "error";
9
+ debug: "debug";
10
+ info: "info";
11
+ warn: "warn";
12
+ }>>;
13
+ }, z.core.$strip>;
14
+ export type Config = z.infer<typeof EnvSchema>;
15
+ export declare function loadConfig(): Config;
16
+ export declare function getCorsOrigins(config: Config): string[];
17
+ export {};
18
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,QAAA,MAAM,SAAS;;;;;;;;;;;iBAab,CAAC;AAEH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AAG/C,wBAAgB,UAAU,IAAI,MAAM,CASnC;AAGD,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAEvD"}
package/dist/config.js ADDED
@@ -0,0 +1,27 @@
1
+ import { z } from "zod";
2
+ // Schema de validación para variables de entorno
3
+ const EnvSchema = z.object({
4
+ // Memos connection (required)
5
+ MEMOS_URL: z.string().url().describe("Memos instance URL"),
6
+ // HTTP server options
7
+ HTTP_PORT: z.coerce.number().int().min(1).max(65535).default(3000).describe("HTTP server port"),
8
+ HTTP_HOST: z.string().default("127.0.0.1").describe("HTTP server host"),
9
+ // Security
10
+ CORS_ORIGIN: z.string().default("*").describe("CORS allowed origins (comma-separated)"),
11
+ // Logging
12
+ LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info").describe("Log level"),
13
+ });
14
+ // Cargar configuración desde variables de entorno
15
+ export function loadConfig() {
16
+ const result = EnvSchema.safeParse(process.env);
17
+ if (!result.success) {
18
+ console.error("Configuration error:", result.error.flatten().fieldErrors);
19
+ process.exit(1);
20
+ }
21
+ return result.data;
22
+ }
23
+ // Obtener orígenes CORS como array
24
+ export function getCorsOrigins(config) {
25
+ return config.CORS_ORIGIN.split(",").map((o) => o.trim());
26
+ }
27
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,iDAAiD;AACjD,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC;IACzB,8BAA8B;IAC9B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,oBAAoB,CAAC;IAE1D,sBAAsB;IACtB,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,kBAAkB,CAAC;IAC/F,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,kBAAkB,CAAC;IAEvE,WAAW;IACX,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,wCAAwC,CAAC;IAEvF,UAAU;IACV,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;CAC5F,CAAC,CAAC;AAIH,kDAAkD;AAClD,MAAM,UAAU,UAAU;IACxB,MAAM,MAAM,GAAG,SAAS,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAEhD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,WAAW,CAAC,CAAC;QAC1E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,OAAO,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AAC5D,CAAC"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { Config } from "./config.js";
2
+ export declare function createHttpApp(config: Config): import("express-serve-static-core").Express;
3
+ export declare function startHttpServer(config: Config): import("node:http").Server<typeof import("node:http").IncomingMessage, typeof import("node:http").ServerResponse>;
4
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAAkB,MAAM,aAAa,CAAC;AAgFrD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,+CAgP3C;AAGD,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,qHA4C7C"}