@iflow-mcp/dearcloud09-logseq-mcp 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.
- package/CLAUDE.md +22 -0
- package/LICENSE +135 -0
- package/README.ko.md +345 -0
- package/README.md +302 -0
- package/add-today-dairy.js +165 -0
- package/com.logseq.daily-automation.plist.example +41 -0
- package/dist/graph.d.ts +97 -0
- package/dist/graph.js +627 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +626 -0
- package/dist/types.d.ts +57 -0
- package/dist/types.js +4 -0
- package/dist/weather-scraper.d.ts +22 -0
- package/dist/weather-scraper.js +202 -0
- package/language.json +1 -0
- package/package.json +1 -0
- package/package_name +1 -0
- package/push_info.json +5 -0
- package/run-daily-automation.sh +19 -0
- package/run-daily-automation.sh.example +27 -0
- package/src/graph.ts +710 -0
- package/src/index.ts +697 -0
- package/src/types.ts +66 -0
- package/src/weather-scraper.ts +249 -0
- package/tsconfig.json +17 -0
package/README.md
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# Logseq MCP Server
|
|
2
|
+
|
|
3
|
+
[](https://polyformproject.org/licenses/noncommercial/1.0.0)
|
|
4
|
+
[](https://nodejs.org/)
|
|
5
|
+
[](https://modelcontextprotocol.io/)
|
|
6
|
+
|
|
7
|
+
> **Let AI read and write your Logseq graph directly via MCP**
|
|
8
|
+
|
|
9
|
+
[한국어 README](README.ko.md)
|
|
10
|
+
|
|
11
|
+
Talk to Claude and say "add this to today's journal", "find what I did last week", "show me all pages linked to this one" - and it just works.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Why This?
|
|
16
|
+
|
|
17
|
+
**Problem**: Logseq is a great PKM tool, but integrating with AI assistants requires constant copy-pasting.
|
|
18
|
+
|
|
19
|
+
**Solution**: With this MCP server:
|
|
20
|
+
- Claude **directly** writes to your journal (no copy-paste)
|
|
21
|
+
- **Search and summarize** past entries (maintain context)
|
|
22
|
+
- **Navigate connections** between pages (backlinks, graph)
|
|
23
|
+
- **Auto-generate** daily journals with templates
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
You: "Summarize today's meeting notes and add them to my journal"
|
|
27
|
+
Claude: [writes directly to Logseq via logseq-mcp]
|
|
28
|
+
"Done! Added to today's journal. Anything else?"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Is This For You?
|
|
34
|
+
|
|
35
|
+
### Good fit if you...
|
|
36
|
+
|
|
37
|
+
- Use Logseq as your **primary PKM**
|
|
38
|
+
- Use **Claude Code or Claude Desktop** regularly
|
|
39
|
+
- Want to **delegate** note management to AI
|
|
40
|
+
- Use **local file-based** Logseq (not Logseq Sync)
|
|
41
|
+
|
|
42
|
+
### Not for you if...
|
|
43
|
+
|
|
44
|
+
- Using **Logseq Sync** (requires local file access)
|
|
45
|
+
- **Obsidian** user (different MCP server needed)
|
|
46
|
+
- Have sensitive info in notes and **uncomfortable with AI access**
|
|
47
|
+
- Use **org-mode** instead of Markdown (not yet supported)
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Features
|
|
52
|
+
|
|
53
|
+
| Feature | Description |
|
|
54
|
+
|---------|-------------|
|
|
55
|
+
| **Page CRUD** | Create, read, update, delete pages + property support |
|
|
56
|
+
| **Search** | Full-text search + tag/folder filtering |
|
|
57
|
+
| **Graph Navigation** | Links, backlinks, page relationship traversal |
|
|
58
|
+
| **Journal** | Access today's/specific date journals + templates |
|
|
59
|
+
| **Content Logging** | Log articles, books, movies, exhibitions to journal |
|
|
60
|
+
| **Resources** | Expose graph pages as MCP resources |
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Quick Start
|
|
65
|
+
|
|
66
|
+
### 1. Install
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
git clone https://github.com/dearcloud09/logseq-mcp.git
|
|
70
|
+
cd logseq-mcp
|
|
71
|
+
npm install
|
|
72
|
+
npm run build
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 2. Configure
|
|
76
|
+
|
|
77
|
+
**Claude Code** (`~/.claude/settings.json`):
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"mcpServers": {
|
|
82
|
+
"logseq": {
|
|
83
|
+
"command": "node",
|
|
84
|
+
"args": ["/path/to/logseq-mcp/dist/index.js"],
|
|
85
|
+
"env": {
|
|
86
|
+
"LOGSEQ_GRAPH_PATH": "/path/to/your/logseq/graph"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json`):
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"mcpServers": {
|
|
98
|
+
"logseq": {
|
|
99
|
+
"command": "node",
|
|
100
|
+
"args": ["/path/to/logseq-mcp/dist/index.js"],
|
|
101
|
+
"env": {
|
|
102
|
+
"LOGSEQ_GRAPH_PATH": "/path/to/your/logseq/graph"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 3. Verify
|
|
110
|
+
|
|
111
|
+
Ask Claude: "Show me my Logseq page list"
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Available Tools
|
|
116
|
+
|
|
117
|
+
| Tool | Description |
|
|
118
|
+
|------|-------------|
|
|
119
|
+
| `list_pages` | List all pages with metadata (tags, links, backlinks) |
|
|
120
|
+
| `read_page` | Read page content and metadata |
|
|
121
|
+
| `create_page` | Create new page (with property support) |
|
|
122
|
+
| `update_page` | Update page content |
|
|
123
|
+
| `delete_page` | Delete a page |
|
|
124
|
+
| `append_to_page` | Append content to existing page |
|
|
125
|
+
| `search_pages` | Search by content/title + tag/folder filters |
|
|
126
|
+
| `get_backlinks` | Get pages that reference a specific page |
|
|
127
|
+
| `get_graph` | Get page connection graph data |
|
|
128
|
+
| `get_journal` | Get today's or specific date's journal |
|
|
129
|
+
| `create_journal` | Create journal with optional template |
|
|
130
|
+
| `add_article` | Add article to journal (title, summary, tags, URL, highlights) |
|
|
131
|
+
| `add_book` | Add book to journal (title, author, tags, memo) |
|
|
132
|
+
| `add_movie` | Add movie to journal (title, director, memo) |
|
|
133
|
+
| `add_exhibition` | Add exhibition to journal (title, venue, artist, memo) |
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Usage Examples
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
"Show me today's journal"
|
|
141
|
+
"Add this content to 'Project A' page: ..."
|
|
142
|
+
"Find all pages with #meeting tag"
|
|
143
|
+
"What pages are connected to my Goals page?"
|
|
144
|
+
"Search for TODO items in last week's journals"
|
|
145
|
+
"Create a new page called 'Reading List'"
|
|
146
|
+
"Summarize our conversation and save it as an article in my journal"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Logseq Graph Structure
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
your-graph/
|
|
155
|
+
journals/ # Daily journals (2024_01_15.md format)
|
|
156
|
+
pages/ # Regular pages
|
|
157
|
+
logseq/ # Logseq settings
|
|
158
|
+
whiteboards/ # Whiteboards
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Security
|
|
164
|
+
|
|
165
|
+
- Path traversal protection (graph-only access)
|
|
166
|
+
- Symlink/hardlink attack prevention
|
|
167
|
+
- Input validation and sanitization
|
|
168
|
+
- DoS protection (content size limits)
|
|
169
|
+
- Error message sanitization
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Troubleshooting
|
|
174
|
+
|
|
175
|
+
### "LOGSEQ_GRAPH_PATH environment variable is required"
|
|
176
|
+
|
|
177
|
+
Set `LOGSEQ_GRAPH_PATH` in your configuration file.
|
|
178
|
+
|
|
179
|
+
### MCP server not recognized by Claude
|
|
180
|
+
|
|
181
|
+
1. Restart Claude Code/Desktop
|
|
182
|
+
2. Verify path is absolute (`/Users/...` format)
|
|
183
|
+
3. Ensure `npm run build` was executed
|
|
184
|
+
|
|
185
|
+
### Pages not showing up
|
|
186
|
+
|
|
187
|
+
- Check if `.md` files exist in `journals/` or `pages/`
|
|
188
|
+
- Verify you're using **local graph** (not Logseq Sync)
|
|
189
|
+
|
|
190
|
+
### org-mode files not reading
|
|
191
|
+
|
|
192
|
+
Currently **Markdown only**. org-mode support planned for future.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Korean-Specific Features
|
|
197
|
+
|
|
198
|
+
This project includes features optimized for Korean users:
|
|
199
|
+
|
|
200
|
+
### Daily Automation (Optional)
|
|
201
|
+
|
|
202
|
+
Auto-generate daily journal with weather (Korea only - uses Naver Weather) and diary template.
|
|
203
|
+
|
|
204
|
+
1. Copy and edit plist file:
|
|
205
|
+
```bash
|
|
206
|
+
cp com.logseq.daily-automation.plist.example ~/Library/LaunchAgents/com.logseq.daily-automation.plist
|
|
207
|
+
# Edit the file to replace /path/to/ with your actual paths
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
2. Load launchd agent:
|
|
211
|
+
```bash
|
|
212
|
+
launchctl load ~/Library/LaunchAgents/com.logseq.daily-automation.plist
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
3. Test manually:
|
|
216
|
+
```bash
|
|
217
|
+
./run-daily-automation.sh
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Generated template structure:
|
|
221
|
+
```markdown
|
|
222
|
+
- [[일기]]
|
|
223
|
+
- [[날씨]]
|
|
224
|
+
- {weather info}
|
|
225
|
+
- [[오늘의 일기]]
|
|
226
|
+
- [[행복도]]
|
|
227
|
+
- [[오늘의 행복]]
|
|
228
|
+
- [[오늘의 컨디션]]
|
|
229
|
+
- [[수면]]
|
|
230
|
+
- 취침:
|
|
231
|
+
- 기상:
|
|
232
|
+
- 질: /5
|
|
233
|
+
- [[오늘의 생각]]
|
|
234
|
+
- [[Tasks]]
|
|
235
|
+
- TODO
|
|
236
|
+
- [[오늘 잘 해낸 일]]
|
|
237
|
+
- [[TIL]]
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
See [Korean README](README.ko.md) for more details.
|
|
241
|
+
|
|
242
|
+
### Cultural Content Structure
|
|
243
|
+
|
|
244
|
+
`add_book`, `add_movie`, `add_exhibition` tools use Korean wikilink structure (`[[문화]]`). Customize the templates in `src/index.ts` for your language.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Development
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
# Development mode (watch)
|
|
252
|
+
npm run dev
|
|
253
|
+
|
|
254
|
+
# TypeScript build
|
|
255
|
+
npm run build
|
|
256
|
+
|
|
257
|
+
# Production run
|
|
258
|
+
npm start
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Project Structure
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
src/
|
|
265
|
+
index.ts # MCP server entry point, tool handlers
|
|
266
|
+
types.ts # TypeScript type definitions
|
|
267
|
+
graph.ts # Graph filesystem operations
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Contributing
|
|
273
|
+
|
|
274
|
+
Issues and PRs welcome!
|
|
275
|
+
|
|
276
|
+
1. Fork this repo
|
|
277
|
+
2. Create feature branch (`git checkout -b feature/amazing`)
|
|
278
|
+
3. Commit changes (`git commit -m 'Add amazing feature'`)
|
|
279
|
+
4. Push to branch (`git push origin feature/amazing`)
|
|
280
|
+
5. Open a Pull Request
|
|
281
|
+
|
|
282
|
+
### Ideas for contribution
|
|
283
|
+
|
|
284
|
+
- [ ] org-mode support
|
|
285
|
+
- [ ] Logseq property search
|
|
286
|
+
- [ ] Whiteboard support
|
|
287
|
+
- [ ] Better graph visualization data
|
|
288
|
+
- [ ] i18n for templates
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## License
|
|
293
|
+
|
|
294
|
+
[Polyform Noncommercial 1.0.0](LICENSE) - Free for personal and noncommercial use.
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Related
|
|
299
|
+
|
|
300
|
+
- [Model Context Protocol](https://modelcontextprotocol.io/)
|
|
301
|
+
- [Logseq](https://logseq.com/)
|
|
302
|
+
- [Claude Code](https://claude.ai/code)
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logseq Daily Note 자동 생성 스크립트
|
|
3
|
+
* 매일 실행하여 오늘의 일기 템플릿 + 날씨 정보를 자동으로 추가
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
8
|
+
import { join, dirname } from 'node:path';
|
|
9
|
+
import { WeatherScraper } from './dist/weather-scraper.js';
|
|
10
|
+
|
|
11
|
+
// 설정
|
|
12
|
+
const LOGSEQ_GRAPH_PATH = process.env.LOGSEQ_GRAPH_PATH;
|
|
13
|
+
if (!LOGSEQ_GRAPH_PATH) {
|
|
14
|
+
console.error('Error: LOGSEQ_GRAPH_PATH environment variable is required');
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
const JOURNALS_PATH = join(LOGSEQ_GRAPH_PATH, 'journals');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* KST 기준 오늘 날짜 가져오기
|
|
21
|
+
*/
|
|
22
|
+
function getKSTDate() {
|
|
23
|
+
const now = new Date();
|
|
24
|
+
const kstOffset = 9 * 60; // KST는 UTC+9
|
|
25
|
+
const utcTime = now.getTime() + now.getTimezoneOffset() * 60000;
|
|
26
|
+
return new Date(utcTime + kstOffset * 60000);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 날짜를 저널 파일명 형식으로 변환 (yyyy_MM_dd.md)
|
|
31
|
+
*/
|
|
32
|
+
function formatDateForFilename(date) {
|
|
33
|
+
const year = date.getFullYear();
|
|
34
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
35
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
36
|
+
return `${year}_${month}_${day}.md`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 일기 템플릿 생성 (날씨 정보 포함)
|
|
41
|
+
*/
|
|
42
|
+
async function generateTemplate(weatherLines) {
|
|
43
|
+
const lines = ['- [[일기]]'];
|
|
44
|
+
|
|
45
|
+
// 날씨 섹션
|
|
46
|
+
lines.push('\t- [[날씨]]');
|
|
47
|
+
if (weatherLines && weatherLines.length > 0) {
|
|
48
|
+
for (const line of weatherLines) {
|
|
49
|
+
lines.push(`\t\t- ${line}`);
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
lines.push('\t\t- 날씨 정보 없음');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 오늘의 일기 섹션
|
|
56
|
+
lines.push('\t- [[오늘의 일기]]');
|
|
57
|
+
lines.push('\t\t- [[행복도]]');
|
|
58
|
+
lines.push('\t\t\t- [[오늘의 행복]]');
|
|
59
|
+
lines.push('\t\t- [[오늘의 컨디션]]');
|
|
60
|
+
lines.push('\t\t\t- [[수면]]');
|
|
61
|
+
lines.push('\t\t\t\t- 취침:');
|
|
62
|
+
lines.push('\t\t\t\t- 기상:');
|
|
63
|
+
lines.push('\t\t\t\t- 질: /5');
|
|
64
|
+
lines.push('\t\t- [[오늘의 생각]]');
|
|
65
|
+
|
|
66
|
+
// Tasks 섹션
|
|
67
|
+
lines.push('\t- [[Tasks]]');
|
|
68
|
+
lines.push('\t\t- TODO ');
|
|
69
|
+
lines.push('\t\t- [[오늘 잘 해낸 일]]');
|
|
70
|
+
|
|
71
|
+
// TIL 섹션
|
|
72
|
+
lines.push('\t- [[TIL]]');
|
|
73
|
+
|
|
74
|
+
return lines.join('\n');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 기존 저널에 템플릿이 있는지 확인
|
|
79
|
+
*/
|
|
80
|
+
function hasTemplate(content) {
|
|
81
|
+
return content.includes('[[일기]]') || content.includes('[[날씨]]');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 메인 함수
|
|
86
|
+
*/
|
|
87
|
+
async function addTodayDairy() {
|
|
88
|
+
const today = getKSTDate();
|
|
89
|
+
const filename = formatDateForFilename(today);
|
|
90
|
+
const filePath = join(JOURNALS_PATH, filename);
|
|
91
|
+
|
|
92
|
+
console.log(`[${new Date().toISOString()}] Logseq Daily Note 자동 생성 시작`);
|
|
93
|
+
console.log(`대상 파일: ${filePath}`);
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
// journals 폴더 확인
|
|
97
|
+
if (!existsSync(JOURNALS_PATH)) {
|
|
98
|
+
await mkdir(JOURNALS_PATH, { recursive: true });
|
|
99
|
+
console.log(`journals 폴더 생성: ${JOURNALS_PATH}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 기존 파일 내용 확인
|
|
103
|
+
let existingContent = '';
|
|
104
|
+
if (existsSync(filePath)) {
|
|
105
|
+
existingContent = await readFile(filePath, 'utf-8');
|
|
106
|
+
|
|
107
|
+
// 이미 템플릿이 있으면 건너뜀
|
|
108
|
+
if (hasTemplate(existingContent)) {
|
|
109
|
+
console.log('이미 일기 템플릿이 존재합니다. 건너뜁니다.');
|
|
110
|
+
return { success: true, message: '이미 템플릿 존재' };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 날씨 정보 가져오기 (3회 재시도)
|
|
115
|
+
let weatherLines = null;
|
|
116
|
+
const weatherScraper = new WeatherScraper();
|
|
117
|
+
|
|
118
|
+
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
119
|
+
try {
|
|
120
|
+
console.log(`날씨 정보 가져오기 시도 ${attempt}/3...`);
|
|
121
|
+
const weather = await weatherScraper.getWeatherForDongcheon();
|
|
122
|
+
if (weather) {
|
|
123
|
+
weatherLines = weatherScraper.formatWeatherForLogseq(weather);
|
|
124
|
+
console.log('날씨 정보 수집 성공');
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.error(`날씨 수집 실패 (시도 ${attempt}):`, error.message);
|
|
129
|
+
if (attempt < 3) {
|
|
130
|
+
const waitMs = Math.min(1000 * Math.pow(2, attempt - 1), 5000);
|
|
131
|
+
await new Promise(resolve => setTimeout(resolve, waitMs));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 템플릿 생성
|
|
137
|
+
const template = await generateTemplate(weatherLines);
|
|
138
|
+
|
|
139
|
+
// 파일 작성
|
|
140
|
+
const newContent = existingContent
|
|
141
|
+
? existingContent + '\n' + template
|
|
142
|
+
: template;
|
|
143
|
+
|
|
144
|
+
await writeFile(filePath, newContent, 'utf-8');
|
|
145
|
+
console.log('Daily Note 템플릿 생성 완료!');
|
|
146
|
+
console.log(`파일: ${filePath}`);
|
|
147
|
+
|
|
148
|
+
return { success: true, message: '템플릿 생성 완료' };
|
|
149
|
+
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.error('오류 발생:', error.message);
|
|
152
|
+
return { success: false, message: error.message };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 실행
|
|
157
|
+
addTodayDairy()
|
|
158
|
+
.then(result => {
|
|
159
|
+
console.log('결과:', result);
|
|
160
|
+
process.exit(result.success ? 0 : 1);
|
|
161
|
+
})
|
|
162
|
+
.catch(error => {
|
|
163
|
+
console.error('치명적 오류:', error);
|
|
164
|
+
process.exit(1);
|
|
165
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>Label</key>
|
|
6
|
+
<string>com.logseq.daily-automation</string>
|
|
7
|
+
|
|
8
|
+
<key>ProgramArguments</key>
|
|
9
|
+
<array>
|
|
10
|
+
<string>/bin/bash</string>
|
|
11
|
+
<string>/path/to/logseq-mcp/run-daily-automation.sh</string>
|
|
12
|
+
</array>
|
|
13
|
+
|
|
14
|
+
<key>StartCalendarInterval</key>
|
|
15
|
+
<dict>
|
|
16
|
+
<key>Hour</key>
|
|
17
|
+
<integer>6</integer>
|
|
18
|
+
<key>Minute</key>
|
|
19
|
+
<integer>0</integer>
|
|
20
|
+
</dict>
|
|
21
|
+
|
|
22
|
+
<key>StandardOutPath</key>
|
|
23
|
+
<string>/path/to/logseq-mcp/launchd.log</string>
|
|
24
|
+
|
|
25
|
+
<key>StandardErrorPath</key>
|
|
26
|
+
<string>/path/to/logseq-mcp/launchd.error.log</string>
|
|
27
|
+
|
|
28
|
+
<key>RunAtLoad</key>
|
|
29
|
+
<false/>
|
|
30
|
+
|
|
31
|
+
<key>EnvironmentVariables</key>
|
|
32
|
+
<dict>
|
|
33
|
+
<key>LOGSEQ_GRAPH_PATH</key>
|
|
34
|
+
<string>/path/to/your/logseq/graph</string>
|
|
35
|
+
<key>WEATHER_LOCATION</key>
|
|
36
|
+
<string>서울</string>
|
|
37
|
+
<key>PATH</key>
|
|
38
|
+
<string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin</string>
|
|
39
|
+
</dict>
|
|
40
|
+
</dict>
|
|
41
|
+
</plist>
|
package/dist/graph.d.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logseq Graph Service
|
|
3
|
+
* Handles file system operations for Logseq graphs
|
|
4
|
+
*/
|
|
5
|
+
import type { Page, PageMetadata, SearchResult, Graph } from './types.js';
|
|
6
|
+
export declare class GraphService {
|
|
7
|
+
private graphPath;
|
|
8
|
+
private pagesPath;
|
|
9
|
+
private journalsPath;
|
|
10
|
+
constructor(graphPath: string);
|
|
11
|
+
/**
|
|
12
|
+
* List all pages in the graph
|
|
13
|
+
*/
|
|
14
|
+
listPages(folder?: string): Promise<PageMetadata[]>;
|
|
15
|
+
private collectAllPages;
|
|
16
|
+
/**
|
|
17
|
+
* Read a page by path or name
|
|
18
|
+
*/
|
|
19
|
+
readPage(pathOrName: string): Promise<Page>;
|
|
20
|
+
/**
|
|
21
|
+
* Validate page name (whitelist approach for security)
|
|
22
|
+
*/
|
|
23
|
+
private validatePageName;
|
|
24
|
+
/**
|
|
25
|
+
* Create a new page
|
|
26
|
+
*/
|
|
27
|
+
createPage(name: string, content: string, properties?: Record<string, unknown>): Promise<Page>;
|
|
28
|
+
/**
|
|
29
|
+
* Update an existing page
|
|
30
|
+
*/
|
|
31
|
+
updatePage(pathOrName: string, content: string, properties?: Record<string, unknown>): Promise<Page>;
|
|
32
|
+
/**
|
|
33
|
+
* Delete a page
|
|
34
|
+
*/
|
|
35
|
+
deletePage(pathOrName: string): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Append content to a page
|
|
38
|
+
*/
|
|
39
|
+
appendToPage(pathOrName: string, content: string): Promise<Page>;
|
|
40
|
+
/**
|
|
41
|
+
* Search pages
|
|
42
|
+
*/
|
|
43
|
+
searchPages(query: string, options?: {
|
|
44
|
+
tags?: string[];
|
|
45
|
+
folder?: string;
|
|
46
|
+
}): Promise<SearchResult[]>;
|
|
47
|
+
/**
|
|
48
|
+
* Get backlinks for a page
|
|
49
|
+
*/
|
|
50
|
+
getBacklinks(pathOrName: string): Promise<PageMetadata[]>;
|
|
51
|
+
/**
|
|
52
|
+
* Get graph data
|
|
53
|
+
*/
|
|
54
|
+
getGraph(options?: {
|
|
55
|
+
center?: string;
|
|
56
|
+
depth?: number;
|
|
57
|
+
}): Promise<Graph>;
|
|
58
|
+
/**
|
|
59
|
+
* Get journal page for a date
|
|
60
|
+
*/
|
|
61
|
+
getJournalPage(date?: string): Promise<Page | null>;
|
|
62
|
+
/**
|
|
63
|
+
* Create journal page
|
|
64
|
+
*/
|
|
65
|
+
createJournalPage(date?: string, template?: string): Promise<Page>;
|
|
66
|
+
/**
|
|
67
|
+
* Append content to journal page (creates if doesn't exist)
|
|
68
|
+
*/
|
|
69
|
+
appendToJournalPage(date?: string, content?: string): Promise<Page>;
|
|
70
|
+
/**
|
|
71
|
+
* Validate content size (prevents DoS attacks)
|
|
72
|
+
*/
|
|
73
|
+
private validateContentSize;
|
|
74
|
+
/**
|
|
75
|
+
* Check if path is a symlink (prevents symlink escape attacks)
|
|
76
|
+
*/
|
|
77
|
+
private checkSymlink;
|
|
78
|
+
/**
|
|
79
|
+
* Check if path is a regular file (prevents symlink/hardlink attacks)
|
|
80
|
+
* More strict than checkSymlink - also detects hardlinks to external files
|
|
81
|
+
*/
|
|
82
|
+
private checkRegularFile;
|
|
83
|
+
/**
|
|
84
|
+
* Validate that a path is within the graph directory (prevents path traversal attacks)
|
|
85
|
+
*/
|
|
86
|
+
private validatePath;
|
|
87
|
+
private resolvePath;
|
|
88
|
+
private extractTags;
|
|
89
|
+
private extractLinks;
|
|
90
|
+
private parseProperties;
|
|
91
|
+
/**
|
|
92
|
+
* Validate and sanitize properties (prevents injection attacks)
|
|
93
|
+
*/
|
|
94
|
+
private validateProperties;
|
|
95
|
+
private buildContent;
|
|
96
|
+
private getTodayString;
|
|
97
|
+
}
|