@dayby/mcp-server 0.3.1 → 0.3.2
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/README.md +49 -39
- package/dist/auth.js +2 -1
- package/dist/dayby-client.d.ts +6 -0
- package/dist/dayby-client.js +3 -0
- package/dist/index.js +58 -5
- package/dist/sanitizer.test.js +3 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,14 +17,14 @@ draft_post → sanitized locally, never touches network
|
|
|
17
17
|
|
|
18
18
|
| Tool | What it does | Touches network? |
|
|
19
19
|
|---|---|---|
|
|
20
|
-
| `draft_post` | Creates a sanitized draft from your description |
|
|
21
|
-
| `edit_draft` | Modify a draft before publishing |
|
|
22
|
-
| `check_content` | Dry-run: see what would get stripped |
|
|
23
|
-
| `publish_post` | Publish an approved draft to DayBy |
|
|
24
|
-
| `list_posts` | List your recent DayBy posts |
|
|
25
|
-
| `get_post` | Fetch a single post by slug |
|
|
26
|
-
| `update_post` | Update title, content, or visibility |
|
|
27
|
-
| `delete_post` | Permanently delete a post |
|
|
20
|
+
| `draft_post` | Creates a sanitized draft from your description | No |
|
|
21
|
+
| `edit_draft` | Modify a draft before publishing | No |
|
|
22
|
+
| `check_content` | Dry-run: see what would get stripped | No |
|
|
23
|
+
| `publish_post` | Publish an approved draft to DayBy | Yes (sanitized only) |
|
|
24
|
+
| `list_posts` | List your recent DayBy posts | Yes |
|
|
25
|
+
| `get_post` | Fetch a single post by slug | Yes |
|
|
26
|
+
| `update_post` | Update title, content, or visibility | Yes |
|
|
27
|
+
| `delete_post` | Permanently delete a post | Yes |
|
|
28
28
|
|
|
29
29
|
## What Gets Stripped (Automatically)
|
|
30
30
|
|
|
@@ -35,30 +35,11 @@ draft_post → sanitized locally, never touches network
|
|
|
35
35
|
- SSH keys, JWTs, GitHub tokens
|
|
36
36
|
- Database connection URLs
|
|
37
37
|
- File paths with usernames
|
|
38
|
-
- Plus anything you configure in blocklist
|
|
38
|
+
- Plus anything you configure in blocklist
|
|
39
39
|
|
|
40
40
|
## Setup
|
|
41
41
|
|
|
42
|
-
### 1.
|
|
43
|
-
|
|
44
|
-
1. Sign up at [dayby.dev](https://dayby.dev)
|
|
45
|
-
2. Go to Settings → API
|
|
46
|
-
3. Enable API access and generate a key
|
|
47
|
-
|
|
48
|
-
### 2. Configure Sanitizer (Optional but Recommended)
|
|
49
|
-
|
|
50
|
-
Create `~/.dayby/sanitizer.json`:
|
|
51
|
-
|
|
52
|
-
```json
|
|
53
|
-
{
|
|
54
|
-
"blockedTerms": ["YourCompany", "ProjectCodename"],
|
|
55
|
-
"blockedDomains": ["internal.yourcompany.com"],
|
|
56
|
-
"blockedNames": ["Your Boss Name"],
|
|
57
|
-
"customPatterns": ["JIRA-\\d+", "INTERNAL-\\d+"]
|
|
58
|
-
}
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### 3. Install
|
|
42
|
+
### 1. Install
|
|
62
43
|
|
|
63
44
|
**Option A — npx (no install needed):**
|
|
64
45
|
|
|
@@ -80,7 +61,25 @@ cd dayby-mcp-server
|
|
|
80
61
|
npm install && npm run build
|
|
81
62
|
```
|
|
82
63
|
|
|
83
|
-
###
|
|
64
|
+
### 2. Authenticate
|
|
65
|
+
|
|
66
|
+
Run the auth command to connect your DayBy account:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
dayby-mcp auth
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
This opens your browser for a one-click authorization. Your token is saved locally at `~/.dayby/credentials.json`.
|
|
73
|
+
|
|
74
|
+
To log out:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
dayby-mcp auth --logout
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Alternatively, you can set the `DAYBY_API_KEY` environment variable (from Settings > API on dayby.dev).
|
|
81
|
+
|
|
82
|
+
### 3. Add to your MCP client
|
|
84
83
|
|
|
85
84
|
**Claude Code (simplest):**
|
|
86
85
|
|
|
@@ -101,10 +100,7 @@ claude mcp add dayby -- npx @dayby/mcp-server
|
|
|
101
100
|
"mcpServers": {
|
|
102
101
|
"dayby": {
|
|
103
102
|
"command": "npx",
|
|
104
|
-
"args": ["@dayby/mcp-server"]
|
|
105
|
-
"env": {
|
|
106
|
-
"DAYBY_API_KEY": "your-api-key-here"
|
|
107
|
-
}
|
|
103
|
+
"args": ["@dayby/mcp-server"]
|
|
108
104
|
}
|
|
109
105
|
}
|
|
110
106
|
}
|
|
@@ -117,15 +113,25 @@ claude mcp add dayby -- npx @dayby/mcp-server
|
|
|
117
113
|
"mcpServers": {
|
|
118
114
|
"dayby": {
|
|
119
115
|
"command": "npx",
|
|
120
|
-
"args": ["@dayby/mcp-server"]
|
|
121
|
-
"env": {
|
|
122
|
-
"DAYBY_API_KEY": "your-api-key-here"
|
|
123
|
-
}
|
|
116
|
+
"args": ["@dayby/mcp-server"]
|
|
124
117
|
}
|
|
125
118
|
}
|
|
126
119
|
}
|
|
127
120
|
```
|
|
128
121
|
|
|
122
|
+
### 4. Configure Sanitizer (Optional but Recommended)
|
|
123
|
+
|
|
124
|
+
Create `~/.dayby/sanitizer.json`:
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"blockedTerms": ["YourCompany", "ProjectCodename"],
|
|
129
|
+
"blockedDomains": ["internal.yourcompany.com"],
|
|
130
|
+
"blockedNames": ["Your Boss Name"],
|
|
131
|
+
"customPatterns": ["JIRA-\\d+", "INTERNAL-\\d+"]
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
129
135
|
## Usage Examples
|
|
130
136
|
|
|
131
137
|
**While coding:**
|
|
@@ -143,7 +149,7 @@ Claude will use `draft_post` to sanitize locally, show you a preview, and only p
|
|
|
143
149
|
|
|
144
150
|
| Variable | Description | Default |
|
|
145
151
|
|---|---|---|
|
|
146
|
-
| `DAYBY_API_KEY` | Your DayBy API key | (
|
|
152
|
+
| `DAYBY_API_KEY` | Your DayBy API key (alternative to `dayby-mcp auth`) | (none) |
|
|
147
153
|
| `DAYBY_API_URL` | DayBy API URL | `https://dayby.dev` |
|
|
148
154
|
| `DAYBY_BLOCKED_TERMS` | Comma-separated blocked terms | (none) |
|
|
149
155
|
| `DAYBY_BLOCKED_DOMAINS` | Comma-separated blocked domains | (none) |
|
|
@@ -170,6 +176,10 @@ Then restart your terminal or run `source ~/.bashrc` again.
|
|
|
170
176
|
|
|
171
177
|
Restart Claude Code / Claude Desktop after adding the MCP config.
|
|
172
178
|
|
|
179
|
+
**`Not authenticated` errors**
|
|
180
|
+
|
|
181
|
+
Run `dayby-mcp auth` to connect your account, or set `DAYBY_API_KEY` in your environment.
|
|
182
|
+
|
|
173
183
|
## License
|
|
174
184
|
|
|
175
185
|
MIT
|
package/dist/auth.js
CHANGED
|
@@ -45,11 +45,12 @@ exports.clearCredentials = clearCredentials;
|
|
|
45
45
|
exports.getStoredToken = getStoredToken;
|
|
46
46
|
exports.runAuthFlow = runAuthFlow;
|
|
47
47
|
const fs = __importStar(require("fs"));
|
|
48
|
+
const os = __importStar(require("os"));
|
|
48
49
|
const path = __importStar(require("path"));
|
|
49
50
|
const child_process_1 = require("child_process");
|
|
50
51
|
// --- Config ---
|
|
51
52
|
const DEFAULT_API_URL = 'https://dayby.dev';
|
|
52
|
-
const CREDENTIALS_DIR = path.join(
|
|
53
|
+
const CREDENTIALS_DIR = path.join(os.homedir(), '.dayby');
|
|
53
54
|
const CREDENTIALS_FILE = path.join(CREDENTIALS_DIR, 'credentials.json');
|
|
54
55
|
const POLL_INTERVAL_MS = 2000;
|
|
55
56
|
// --- Storage ---
|
package/dist/dayby-client.d.ts
CHANGED
|
@@ -42,6 +42,7 @@ export declare class DayByClient {
|
|
|
42
42
|
title: string;
|
|
43
43
|
content: string;
|
|
44
44
|
visibility?: string;
|
|
45
|
+
tags?: string[];
|
|
45
46
|
}): Promise<{
|
|
46
47
|
post: DayByPost;
|
|
47
48
|
}>;
|
|
@@ -49,9 +50,14 @@ export declare class DayByClient {
|
|
|
49
50
|
title?: string;
|
|
50
51
|
content?: string;
|
|
51
52
|
visibility?: string;
|
|
53
|
+
tags?: string[];
|
|
52
54
|
}): Promise<{
|
|
53
55
|
post: DayByPost;
|
|
54
56
|
}>;
|
|
57
|
+
updateArticle(slug: string, htmlArticle: string): Promise<{
|
|
58
|
+
post: DayByPost;
|
|
59
|
+
message: string;
|
|
60
|
+
}>;
|
|
55
61
|
deletePost(slug: string): Promise<{
|
|
56
62
|
message: string;
|
|
57
63
|
}>;
|
package/dist/dayby-client.js
CHANGED
|
@@ -53,6 +53,9 @@ class DayByClient {
|
|
|
53
53
|
post: params,
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
|
+
async updateArticle(slug, htmlArticle) {
|
|
57
|
+
return this.request('PUT', `/api/v2/posts/${slug}/article`, { html_article: htmlArticle });
|
|
58
|
+
}
|
|
56
59
|
async deletePost(slug) {
|
|
57
60
|
return this.request('DELETE', `/api/v2/posts/${slug}`);
|
|
58
61
|
}
|
package/dist/index.js
CHANGED
|
@@ -54,6 +54,7 @@ const dayby_client_js_1 = require("./dayby-client.js");
|
|
|
54
54
|
const auth_js_1 = require("./auth.js");
|
|
55
55
|
const visibility_js_1 = require("./visibility.js");
|
|
56
56
|
const fs = __importStar(require("fs"));
|
|
57
|
+
const os = __importStar(require("os"));
|
|
57
58
|
const path = __importStar(require("path"));
|
|
58
59
|
// --- Auth subcommand ---
|
|
59
60
|
async function handleAuthCommand() {
|
|
@@ -76,7 +77,7 @@ function loadConfig() {
|
|
|
76
77
|
// 3. Load sanitizer config from file if it exists
|
|
77
78
|
let sanitizerConfig = {};
|
|
78
79
|
const configPaths = [
|
|
79
|
-
path.join(
|
|
80
|
+
path.join(os.homedir(), '.dayby', 'sanitizer.json'),
|
|
80
81
|
path.join(process.cwd(), '.dayby-sanitizer.json'),
|
|
81
82
|
];
|
|
82
83
|
for (const configPath of configPaths) {
|
|
@@ -137,7 +138,8 @@ async function main() {
|
|
|
137
138
|
title: zod_1.z.string().describe('Post title — focus on the technology/skill learned'),
|
|
138
139
|
content: zod_1.z.string().describe('Post content — describe what you learned, built, or solved. The sanitizer will strip any sensitive data automatically.'),
|
|
139
140
|
visibility: zod_1.z.enum(['published', 'draft']).default('published').describe('Post visibility on DayBy'),
|
|
140
|
-
|
|
141
|
+
tags: zod_1.z.array(zod_1.z.string()).optional().describe('Tags/project names for this post (e.g., ["playflow", "rust"]). Used to filter posts by project in the public API.'),
|
|
142
|
+
}, async ({ title, content, visibility, tags }) => {
|
|
141
143
|
// Sanitize both title and content locally
|
|
142
144
|
const titleResult = sanitizer.sanitize(title);
|
|
143
145
|
const contentResult = sanitizer.sanitize(content);
|
|
@@ -150,6 +152,7 @@ async function main() {
|
|
|
150
152
|
sanitizedContent: contentResult.clean,
|
|
151
153
|
strippedItems: allStripped,
|
|
152
154
|
visibility,
|
|
155
|
+
tags: tags || [],
|
|
153
156
|
createdAt: new Date(),
|
|
154
157
|
};
|
|
155
158
|
drafts.set(draftId, draft);
|
|
@@ -158,6 +161,9 @@ async function main() {
|
|
|
158
161
|
response += `**Title:** ${draft.sanitizedTitle}\n\n`;
|
|
159
162
|
response += `**Content:**\n${draft.sanitizedContent}\n\n`;
|
|
160
163
|
response += `**Visibility:** ${visibility}\n`;
|
|
164
|
+
if (draft.tags.length > 0) {
|
|
165
|
+
response += `**Tags:** ${draft.tags.join(', ')}\n`;
|
|
166
|
+
}
|
|
161
167
|
if (allStripped.length > 0) {
|
|
162
168
|
response += `\n⚠️ **Sanitizer removed ${allStripped.length} sensitive item(s):**\n`;
|
|
163
169
|
for (const item of allStripped.slice(0, 10)) {
|
|
@@ -183,7 +189,8 @@ async function main() {
|
|
|
183
189
|
title: zod_1.z.string().optional().describe('Updated title (will be re-sanitized)'),
|
|
184
190
|
content: zod_1.z.string().optional().describe('Updated content (will be re-sanitized)'),
|
|
185
191
|
visibility: zod_1.z.enum(['published', 'draft']).optional().describe('Updated visibility'),
|
|
186
|
-
|
|
192
|
+
tags: zod_1.z.array(zod_1.z.string()).optional().describe('Updated tags/project names'),
|
|
193
|
+
}, async ({ draft_id, title, content, visibility, tags }) => {
|
|
187
194
|
const draft = drafts.get(draft_id);
|
|
188
195
|
if (!draft) {
|
|
189
196
|
return {
|
|
@@ -204,6 +211,9 @@ async function main() {
|
|
|
204
211
|
if (visibility) {
|
|
205
212
|
draft.visibility = visibility;
|
|
206
213
|
}
|
|
214
|
+
if (tags) {
|
|
215
|
+
draft.tags = tags;
|
|
216
|
+
}
|
|
207
217
|
let response = `✏️ **Draft Updated** (ID: ${draft_id})\n\n`;
|
|
208
218
|
response += `**Title:** ${draft.sanitizedTitle}\n\n`;
|
|
209
219
|
response += `**Content:**\n${draft.sanitizedContent}\n\n`;
|
|
@@ -240,6 +250,7 @@ async function main() {
|
|
|
240
250
|
title: draft.sanitizedTitle,
|
|
241
251
|
content: draft.sanitizedContent,
|
|
242
252
|
visibility: (0, visibility_js_1.toApiVisibility)(draft.visibility),
|
|
253
|
+
tags: draft.tags.length > 0 ? draft.tags : undefined,
|
|
243
254
|
});
|
|
244
255
|
let response = `✅ **Published to DayBy!**\n\n`;
|
|
245
256
|
response += `**Title:** ${result.post.title}\n`;
|
|
@@ -332,7 +343,8 @@ async function main() {
|
|
|
332
343
|
title: zod_1.z.string().optional().describe('New title (will be sanitized)'),
|
|
333
344
|
content: zod_1.z.string().optional().describe('New content (will be sanitized)'),
|
|
334
345
|
visibility: zod_1.z.enum(['published', 'draft']).optional().describe('New visibility'),
|
|
335
|
-
|
|
346
|
+
tags: zod_1.z.array(zod_1.z.string()).optional().describe('Updated tags/project names'),
|
|
347
|
+
}, async ({ slug, title, content, visibility, tags }) => {
|
|
336
348
|
if (!(process.env.DAYBY_API_KEY || (0, auth_js_1.getStoredToken)(config.apiUrl) || config.apiKey)) {
|
|
337
349
|
return { content: [{ type: 'text', text: '❌ Not authenticated. Run `dayby-mcp auth` to connect your DayBy account.' }] };
|
|
338
350
|
}
|
|
@@ -343,8 +355,10 @@ async function main() {
|
|
|
343
355
|
params.content = sanitizer.sanitize(content).clean;
|
|
344
356
|
if (visibility)
|
|
345
357
|
params.visibility = (0, visibility_js_1.toApiVisibility)(visibility);
|
|
358
|
+
if (tags)
|
|
359
|
+
params.tags = tags;
|
|
346
360
|
if (Object.keys(params).length === 0) {
|
|
347
|
-
return { content: [{ type: 'text', text: '❌ Provide at least one field to update (title, content, or
|
|
361
|
+
return { content: [{ type: 'text', text: '❌ Provide at least one field to update (title, content, visibility, or tags).' }] };
|
|
348
362
|
}
|
|
349
363
|
try {
|
|
350
364
|
const result = await client.updatePost(slug, params);
|
|
@@ -362,6 +376,45 @@ async function main() {
|
|
|
362
376
|
}
|
|
363
377
|
});
|
|
364
378
|
// ========================================
|
|
379
|
+
// Tool: update_article
|
|
380
|
+
// Let the user's AI generate or edit the HTML article directly.
|
|
381
|
+
// ========================================
|
|
382
|
+
server.tool('update_article', `Set or replace the HTML article for a DayBy post. Use this to write custom articles with CTAs, rich formatting, or any content the user wants.
|
|
383
|
+
|
|
384
|
+
DayBy article HTML rules:
|
|
385
|
+
- Return clean HTML fragments only (no wrapper divs, article tags, doctype, html, head, body)
|
|
386
|
+
- Use <p> tags for paragraphs (no classes needed)
|
|
387
|
+
- Use <h2> for section headings, <h3> for sub-sections
|
|
388
|
+
- Use <strong> for key terms, <em> for emphasis
|
|
389
|
+
- Use <blockquote> for pull quotes
|
|
390
|
+
- Use <pre><code> for code blocks, <code> inline for references
|
|
391
|
+
- Use <ul>/<ol> for lists (prefer prose over lists)
|
|
392
|
+
- Use <a href="..." target="_blank"> for links and CTAs
|
|
393
|
+
- No CSS classes on elements (the page stylesheet handles typography)
|
|
394
|
+
- Target length: 600-1000 words
|
|
395
|
+
- Keep the author's voice and perspective from the original post`, {
|
|
396
|
+
slug: zod_1.z.string().describe('The post slug to update'),
|
|
397
|
+
html_article: zod_1.z.string().describe('The HTML article content. Follow DayBy article HTML rules in the tool description.'),
|
|
398
|
+
}, async ({ slug, html_article }) => {
|
|
399
|
+
if (!(process.env.DAYBY_API_KEY || (0, auth_js_1.getStoredToken)(config.apiUrl) || config.apiKey)) {
|
|
400
|
+
return { content: [{ type: 'text', text: '❌ Not authenticated. Run `dayby-mcp auth` to connect your DayBy account.' }] };
|
|
401
|
+
}
|
|
402
|
+
try {
|
|
403
|
+
const result = await client.updateArticle(slug, html_article);
|
|
404
|
+
const post = result.post;
|
|
405
|
+
let response = `✅ **Article Updated**\n\n`;
|
|
406
|
+
response += `**Title:** ${post.title}\n`;
|
|
407
|
+
response += `**URL:** ${post.url}\n`;
|
|
408
|
+
response += `${result.message}\n`;
|
|
409
|
+
return { content: [{ type: 'text', text: response }] };
|
|
410
|
+
}
|
|
411
|
+
catch (e) {
|
|
412
|
+
return {
|
|
413
|
+
content: [{ type: 'text', text: `❌ Failed to update article: ${e instanceof Error ? e.message : 'Unknown error'}` }],
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
// ========================================
|
|
365
418
|
// Tool: delete_post
|
|
366
419
|
// ========================================
|
|
367
420
|
server.tool('delete_post', 'Permanently delete a DayBy post by its slug.', {
|
package/dist/sanitizer.test.js
CHANGED
|
@@ -6,8 +6,9 @@ const sanitizer_js_1 = require("./sanitizer.js");
|
|
|
6
6
|
const sanitizer = new sanitizer_js_1.Sanitizer();
|
|
7
7
|
(0, vitest_1.describe)('default patterns', () => {
|
|
8
8
|
(0, vitest_1.it)('strips API keys', () => {
|
|
9
|
-
const
|
|
10
|
-
|
|
9
|
+
const fakeKey = 'xk_test_' + 'a1b2c3d4e5f6g7h8i9j0k1l2';
|
|
10
|
+
const result = sanitizer.sanitize(`My api_key=${fakeKey}`);
|
|
11
|
+
(0, vitest_1.expect)(result.clean).not.toContain(fakeKey);
|
|
11
12
|
(0, vitest_1.expect)(result.stripped.length).toBeGreaterThan(0);
|
|
12
13
|
});
|
|
13
14
|
(0, vitest_1.it)('strips AWS access keys', () => {
|
package/package.json
CHANGED