note-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/LICENSE +21 -0
- package/README.md +93 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +263 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 new-village
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# note-mcp
|
|
2
|
+
|
|
3
|
+
Unofficial stdio MCP server for note.com. It uses cookie-based access to note.com's internal APIs.
|
|
4
|
+
|
|
5
|
+
> [!WARNING]
|
|
6
|
+
> This project is unofficial and not affiliated with note.com. Internal APIs can change without notice. Keep cookies local and never commit them to GitHub, npm, logs, or issue reports.
|
|
7
|
+
|
|
8
|
+
## Install / run
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
NOTE_COOKIE='your note.com cookie string' npx note-mcp
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
For local development:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install
|
|
18
|
+
npm run build
|
|
19
|
+
NOTE_COOKIE='your note.com cookie string' node dist/index.js
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## MCP client configuration
|
|
23
|
+
|
|
24
|
+
Example:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"mcpServers": {
|
|
29
|
+
"note": {
|
|
30
|
+
"command": "npx",
|
|
31
|
+
"args": ["-y", "note-mcp"],
|
|
32
|
+
"env": {
|
|
33
|
+
"NOTE_COOKIE": "your note.com cookie string"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Tools
|
|
41
|
+
|
|
42
|
+
- `note_auth_check` — verify cookie-based access to note.com internal APIs
|
|
43
|
+
- `note_list_my_notes` — list notes for the authenticated account
|
|
44
|
+
- `note_list_drafts` — list drafts for the authenticated account
|
|
45
|
+
- `note_get_note` — fetch a note by note key, e.g. `n1a0b26f944f4`
|
|
46
|
+
- `note_create_draft` — create a draft
|
|
47
|
+
- `note_update_draft` — update a draft by draft id
|
|
48
|
+
|
|
49
|
+
## API basis
|
|
50
|
+
|
|
51
|
+
The initial endpoints are based on public, unofficial note API references, including:
|
|
52
|
+
|
|
53
|
+
- <https://note.com/ego_station/n/n1a0b26f944f4>
|
|
54
|
+
|
|
55
|
+
Known endpoint basis:
|
|
56
|
+
|
|
57
|
+
- Base URL: `https://note.com/api`
|
|
58
|
+
- Note detail: `GET /v3/notes/{noteKey}`
|
|
59
|
+
- Own contents: `GET /v2/creators/info/contents?kind=note&page=1`
|
|
60
|
+
- Draft save: `POST /v1/text_notes/draft_save?id={draftId}`
|
|
61
|
+
- Auth smoke test: `GET /v3/notice_counts`
|
|
62
|
+
|
|
63
|
+
## Authentication
|
|
64
|
+
|
|
65
|
+
Set one of these environment variables before launching the server:
|
|
66
|
+
|
|
67
|
+
- `NOTE_COOKIE`
|
|
68
|
+
- `NOTE_SESSION_COOKIE`
|
|
69
|
+
|
|
70
|
+
Use the full Cookie header value from an authenticated browser session.
|
|
71
|
+
|
|
72
|
+
## Release
|
|
73
|
+
|
|
74
|
+
Releases are handled by GitHub Actions + semantic-release.
|
|
75
|
+
|
|
76
|
+
- Push or merge Conventional Commits into `main`.
|
|
77
|
+
- GitHub Actions runs CI.
|
|
78
|
+
- The release workflow creates GitHub tags/releases and publishes to npm.
|
|
79
|
+
|
|
80
|
+
npm publishing uses npm Trusted Publishing with GitHub Actions OIDC. Configure `new-village/note-mcp` and `.github/workflows/release.yml` as a trusted publisher on npmjs.com. No `NPM_TOKEN` repository secret is required.
|
|
81
|
+
|
|
82
|
+
## Development
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npm run typecheck
|
|
86
|
+
npm test
|
|
87
|
+
npm run build
|
|
88
|
+
npm run lint
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## License
|
|
92
|
+
|
|
93
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
|
|
8
|
+
// src/note/auth.ts
|
|
9
|
+
var COOKIE_ENV_KEYS = ["NOTE_COOKIE", "NOTE_SESSION_COOKIE"];
|
|
10
|
+
function readCookieFromEnv(env = process.env) {
|
|
11
|
+
for (const key of COOKIE_ENV_KEYS) {
|
|
12
|
+
const value = env[key];
|
|
13
|
+
if (value?.trim()) return value.trim();
|
|
14
|
+
}
|
|
15
|
+
throw new Error(
|
|
16
|
+
`Missing note.com cookie. Set ${COOKIE_ENV_KEYS.join(" or ")} before starting note-mcp.`
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/note/errors.ts
|
|
21
|
+
var NoteApiError = class extends Error {
|
|
22
|
+
constructor(message, status, body) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.status = status;
|
|
25
|
+
this.body = body;
|
|
26
|
+
this.name = "NoteApiError";
|
|
27
|
+
}
|
|
28
|
+
status;
|
|
29
|
+
body;
|
|
30
|
+
};
|
|
31
|
+
function toErrorMessage(error) {
|
|
32
|
+
if (error instanceof Error) return error.message;
|
|
33
|
+
return String(error);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/note/client.ts
|
|
37
|
+
var BASE_URL = "https://note.com/api";
|
|
38
|
+
var DEFAULT_USER_AGENT = "note-mcp/0.0.0 (+https://github.com/new-village/note-mcp)";
|
|
39
|
+
var NoteClient = class {
|
|
40
|
+
cookie;
|
|
41
|
+
userAgent;
|
|
42
|
+
fetchImpl;
|
|
43
|
+
constructor(options) {
|
|
44
|
+
this.cookie = options.cookie;
|
|
45
|
+
this.userAgent = options.userAgent ?? DEFAULT_USER_AGENT;
|
|
46
|
+
this.fetchImpl = options.fetch ?? fetch;
|
|
47
|
+
}
|
|
48
|
+
async authCheck() {
|
|
49
|
+
return this.request("/v3/notice_counts");
|
|
50
|
+
}
|
|
51
|
+
async listMyNotes(page = 1) {
|
|
52
|
+
return this.request(`/v2/creators/info/contents?kind=note&page=${page}`);
|
|
53
|
+
}
|
|
54
|
+
async listDrafts(page = 1) {
|
|
55
|
+
return this.request(`/v2/creators/info/contents?kind=draft&page=${page}`);
|
|
56
|
+
}
|
|
57
|
+
async getNote(noteKey) {
|
|
58
|
+
return this.request(`/v3/notes/${encodeURIComponent(noteKey)}`);
|
|
59
|
+
}
|
|
60
|
+
async createDraft(payload) {
|
|
61
|
+
return this.saveDraft(payload);
|
|
62
|
+
}
|
|
63
|
+
async updateDraft(payload) {
|
|
64
|
+
return this.saveDraft(payload);
|
|
65
|
+
}
|
|
66
|
+
async saveDraft(payload) {
|
|
67
|
+
const query = payload.draftId ? `?id=${encodeURIComponent(payload.draftId)}` : "";
|
|
68
|
+
return this.request(`/v1/text_notes/draft_save${query}`, {
|
|
69
|
+
method: "POST",
|
|
70
|
+
body: JSON.stringify({
|
|
71
|
+
title: payload.title,
|
|
72
|
+
body: payload.body,
|
|
73
|
+
hashtags: payload.hashtags ?? []
|
|
74
|
+
})
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
async request(path, init = {}) {
|
|
78
|
+
const headers = new Headers(init.headers);
|
|
79
|
+
headers.set("accept", "application/json");
|
|
80
|
+
headers.set("cookie", this.cookie);
|
|
81
|
+
headers.set("user-agent", this.userAgent);
|
|
82
|
+
headers.set("x-requested-with", "XMLHttpRequest");
|
|
83
|
+
if (init.body && !headers.has("content-type")) {
|
|
84
|
+
headers.set("content-type", "application/json");
|
|
85
|
+
}
|
|
86
|
+
const response = await this.fetchImpl(`${BASE_URL}${path}`, {
|
|
87
|
+
...init,
|
|
88
|
+
headers
|
|
89
|
+
});
|
|
90
|
+
const body = await parseBody(response);
|
|
91
|
+
if (!response.ok) {
|
|
92
|
+
throw new NoteApiError(
|
|
93
|
+
`note.com API request failed: ${response.status} ${response.statusText}`,
|
|
94
|
+
response.status,
|
|
95
|
+
body
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
return body;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
async function parseBody(response) {
|
|
102
|
+
const text = await response.text();
|
|
103
|
+
if (!text) return null;
|
|
104
|
+
try {
|
|
105
|
+
return JSON.parse(text);
|
|
106
|
+
} catch {
|
|
107
|
+
return text;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/index.ts
|
|
112
|
+
var server = new McpServer({
|
|
113
|
+
name: "note-mcp",
|
|
114
|
+
version: "0.0.0-development"
|
|
115
|
+
});
|
|
116
|
+
function createClient() {
|
|
117
|
+
return new NoteClient({ cookie: readCookieFromEnv() });
|
|
118
|
+
}
|
|
119
|
+
function jsonText(value) {
|
|
120
|
+
return JSON.stringify(value, null, 2);
|
|
121
|
+
}
|
|
122
|
+
function result(value) {
|
|
123
|
+
return {
|
|
124
|
+
content: [
|
|
125
|
+
{
|
|
126
|
+
type: "text",
|
|
127
|
+
text: jsonText(value)
|
|
128
|
+
}
|
|
129
|
+
]
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function errorResult(error) {
|
|
133
|
+
const detail = error instanceof NoteApiError ? { message: error.message, status: error.status, body: error.body } : { message: toErrorMessage(error) };
|
|
134
|
+
return {
|
|
135
|
+
isError: true,
|
|
136
|
+
content: [
|
|
137
|
+
{
|
|
138
|
+
type: "text",
|
|
139
|
+
text: jsonText(detail)
|
|
140
|
+
}
|
|
141
|
+
]
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
server.registerTool(
|
|
145
|
+
"note_auth_check",
|
|
146
|
+
{
|
|
147
|
+
title: "Check note.com authentication",
|
|
148
|
+
description: "Checks whether NOTE_COOKIE / NOTE_SESSION_COOKIE can access note.com internal APIs.",
|
|
149
|
+
inputSchema: {}
|
|
150
|
+
},
|
|
151
|
+
async () => {
|
|
152
|
+
try {
|
|
153
|
+
return result(await createClient().authCheck());
|
|
154
|
+
} catch (error) {
|
|
155
|
+
return errorResult(error);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
server.registerTool(
|
|
160
|
+
"note_list_my_notes",
|
|
161
|
+
{
|
|
162
|
+
title: "List my note.com notes",
|
|
163
|
+
description: "Lists notes for the authenticated note.com account.",
|
|
164
|
+
inputSchema: {
|
|
165
|
+
page: z.number().int().positive().default(1)
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
async ({ page }) => {
|
|
169
|
+
try {
|
|
170
|
+
return result(await createClient().listMyNotes(page));
|
|
171
|
+
} catch (error) {
|
|
172
|
+
return errorResult(error);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
);
|
|
176
|
+
server.registerTool(
|
|
177
|
+
"note_list_drafts",
|
|
178
|
+
{
|
|
179
|
+
title: "List note.com drafts",
|
|
180
|
+
description: "Lists drafts for the authenticated note.com account. This uses an unofficial internal API and may need adjustment if note.com changes endpoints.",
|
|
181
|
+
inputSchema: {
|
|
182
|
+
page: z.number().int().positive().default(1)
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
async ({ page }) => {
|
|
186
|
+
try {
|
|
187
|
+
return result(await createClient().listDrafts(page));
|
|
188
|
+
} catch (error) {
|
|
189
|
+
return errorResult(error);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
server.registerTool(
|
|
194
|
+
"note_get_note",
|
|
195
|
+
{
|
|
196
|
+
title: "Get note.com note",
|
|
197
|
+
description: "Fetches a note by note key, e.g. n1a0b26f944f4.",
|
|
198
|
+
inputSchema: {
|
|
199
|
+
noteKey: z.string().min(1)
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
async ({ noteKey }) => {
|
|
203
|
+
try {
|
|
204
|
+
return result(await createClient().getNote(noteKey));
|
|
205
|
+
} catch (error) {
|
|
206
|
+
return errorResult(error);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
);
|
|
210
|
+
server.registerTool(
|
|
211
|
+
"note_create_draft",
|
|
212
|
+
{
|
|
213
|
+
title: "Create note.com draft",
|
|
214
|
+
description: "Creates a note.com draft with title/body/hashtags using an unofficial internal API.",
|
|
215
|
+
inputSchema: {
|
|
216
|
+
title: z.string().min(1),
|
|
217
|
+
body: z.string().min(1),
|
|
218
|
+
hashtags: z.array(z.string().min(1)).optional()
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
async ({ title, body, hashtags }) => {
|
|
222
|
+
try {
|
|
223
|
+
return result(
|
|
224
|
+
await createClient().createDraft({
|
|
225
|
+
title,
|
|
226
|
+
body,
|
|
227
|
+
...hashtags ? { hashtags } : {}
|
|
228
|
+
})
|
|
229
|
+
);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
return errorResult(error);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
server.registerTool(
|
|
236
|
+
"note_update_draft",
|
|
237
|
+
{
|
|
238
|
+
title: "Update note.com draft",
|
|
239
|
+
description: "Updates a note.com draft by draft id using an unofficial internal API.",
|
|
240
|
+
inputSchema: {
|
|
241
|
+
draftId: z.string().min(1),
|
|
242
|
+
title: z.string().min(1),
|
|
243
|
+
body: z.string().min(1),
|
|
244
|
+
hashtags: z.array(z.string().min(1)).optional()
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
async ({ draftId, title, body, hashtags }) => {
|
|
248
|
+
try {
|
|
249
|
+
return result(
|
|
250
|
+
await createClient().updateDraft({
|
|
251
|
+
draftId,
|
|
252
|
+
title,
|
|
253
|
+
body,
|
|
254
|
+
...hashtags ? { hashtags } : {}
|
|
255
|
+
})
|
|
256
|
+
);
|
|
257
|
+
} catch (error) {
|
|
258
|
+
return errorResult(error);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
);
|
|
262
|
+
var transport = new StdioServerTransport();
|
|
263
|
+
await server.connect(transport);
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "note-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Unofficial stdio MCP server for note.com using cookie-based internal APIs.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"note-mcp": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
16
|
+
"dev": "tsx src/index.ts",
|
|
17
|
+
"lint": "eslint .",
|
|
18
|
+
"format": "prettier --check .",
|
|
19
|
+
"format:write": "prettier --write .",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"prepack": "npm run build",
|
|
23
|
+
"release": "semantic-release"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"mcp",
|
|
27
|
+
"model-context-protocol",
|
|
28
|
+
"note",
|
|
29
|
+
"note.com",
|
|
30
|
+
"stdio"
|
|
31
|
+
],
|
|
32
|
+
"author": "new-village",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/new-village/note-mcp.git"
|
|
37
|
+
},
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/new-village/note-mcp/issues"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/new-village/note-mcp#readme",
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=20"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
50
|
+
"zod": "^4.4.3"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
54
|
+
"@semantic-release/git": "^10.0.1",
|
|
55
|
+
"@types/node": "^26.0.0",
|
|
56
|
+
"eslint": "^10.5.0",
|
|
57
|
+
"prettier": "^3.8.4",
|
|
58
|
+
"semantic-release": "^25.0.2",
|
|
59
|
+
"tsup": "^8.5.1",
|
|
60
|
+
"tsx": "^4.22.4",
|
|
61
|
+
"typescript": "^6.0.3",
|
|
62
|
+
"typescript-eslint": "^8.61.1",
|
|
63
|
+
"vitest": "^4.1.9"
|
|
64
|
+
}
|
|
65
|
+
}
|