postmd-mcp-server 2.1.0 → 2.2.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/README.md +27 -11
- package/package.json +1 -1
- package/scripts/smoke-test.mjs +45 -0
- package/server.json +2 -2
- package/src/index.js +75 -21
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@ stdio [Model Context Protocol](https://modelcontextprotocol.io) server for **[Po
|
|
|
4
4
|
|
|
5
5
|
**Publishing needs no account and no key.** With zero configuration this server can already turn Markdown into a shareable page. An API key adds management: updating and deleting your documents, attachments, and groups.
|
|
6
6
|
|
|
7
|
+
**Anonymous documents come with a control token.** Publishing without a key returns `data.controlToken` and `data.retainedUntil`: the document is deleted at that instant, and the token is the only way to update or delete it before then. It is shown once and cannot be reissued, so keep it with the `docCode`. Pass it as `controlToken` to the update and delete tools and they work without an API key.
|
|
8
|
+
|
|
7
9
|
**HTTP reference:** [postmd.turink.com/docs/api](https://postmd.turink.com/docs/api) · machine-readable spec at [/api-docs](https://postmd.turink.com/api-docs)
|
|
8
10
|
|
|
9
11
|
## Requirements
|
|
@@ -36,19 +38,22 @@ Publishing and reading — no key needed:
|
|
|
36
38
|
|
|
37
39
|
Managing documents — key with `documents:write`:
|
|
38
40
|
|
|
41
|
+
Each of the first three also accepts `controlToken` instead of a key, for a document published anonymously.
|
|
42
|
+
|
|
39
43
|
| Tool | Purpose |
|
|
40
44
|
|------|---------|
|
|
41
45
|
| `postmd_update_document` | Replace content and/or metadata; can clear password / end date |
|
|
42
46
|
| `postmd_update_document_from_file` | Same, body read from a local `filePath` |
|
|
43
|
-
| `postmd_delete_document` | Delete
|
|
47
|
+
| `postmd_delete_document` | Delete a document (no undo) |
|
|
44
48
|
| `postmd_upload_attachment` | Upload an image/PDF, get a URL to embed in Markdown |
|
|
45
49
|
| `postmd_create_documents_from_files` | Bulk-publish several `.md` files in one call |
|
|
46
50
|
| `postmd_move_document_to_group` | Move a document into a group / folder |
|
|
47
51
|
|
|
48
52
|
Notes and highlights — key with `documents:read` / `documents:write`. A note is
|
|
49
53
|
text anchored to a quoted passage; a highlight is the same object carrying only
|
|
50
|
-
a colour.
|
|
51
|
-
|
|
54
|
+
a colour. Visibility comes from ownership: on the key member's own document a note
|
|
55
|
+
is `PRIVATE` or `SHARED`, and on anyone else's document it is always `SHARED`.
|
|
56
|
+
Documents nobody owns — anonymous uploads and service-owned pages — take no notes:
|
|
52
57
|
|
|
53
58
|
| Tool | Purpose |
|
|
54
59
|
|------|---------|
|
|
@@ -73,19 +78,21 @@ For uploads: either pass the full Markdown as the `markdown` argument, or pass a
|
|
|
73
78
|
|
|
74
79
|
## Quickstart
|
|
75
80
|
|
|
81
|
+
Nothing to install. `npx` fetches the package and the MCP client spawns it.
|
|
82
|
+
|
|
76
83
|
```bash
|
|
77
|
-
|
|
78
|
-
cd postmd-mcp-server
|
|
79
|
-
npm ci
|
|
80
|
-
node src/index.js # normally spawned by the MCP client; use for debugging
|
|
84
|
+
npx -y postmd-mcp-server
|
|
81
85
|
```
|
|
82
86
|
|
|
87
|
+
Run it by hand only to check that it starts — it speaks MCP over stdin and stdout, so it
|
|
88
|
+
will sit there waiting for a client.
|
|
89
|
+
|
|
83
90
|
## Client configuration
|
|
84
91
|
|
|
85
92
|
Claude Code:
|
|
86
93
|
|
|
87
94
|
```bash
|
|
88
|
-
claude mcp add postmd --
|
|
95
|
+
claude mcp add postmd -- npx -y postmd-mcp-server
|
|
89
96
|
```
|
|
90
97
|
|
|
91
98
|
Cursor (`~/.cursor/mcp.json`) and most other stdio clients:
|
|
@@ -95,14 +102,23 @@ Cursor (`~/.cursor/mcp.json`) and most other stdio clients:
|
|
|
95
102
|
"mcpServers": {
|
|
96
103
|
"PostMD": {
|
|
97
104
|
"type": "stdio",
|
|
98
|
-
"command": "
|
|
99
|
-
"args": ["
|
|
105
|
+
"command": "npx",
|
|
106
|
+
"args": ["-y", "postmd-mcp-server"],
|
|
100
107
|
"env": { "POSTMD_API_KEY": "pmk_…" }
|
|
101
108
|
}
|
|
102
109
|
}
|
|
103
110
|
}
|
|
104
111
|
```
|
|
105
112
|
|
|
113
|
+
To run a checkout instead — changing the code, or debugging against a local PostMD —
|
|
114
|
+
point the client at the file.
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
git clone https://github.com/reinlainer/postmd-mcp-server.git
|
|
118
|
+
cd postmd-mcp-server && npm ci
|
|
119
|
+
claude mcp add postmd-dev -- node "$PWD/src/index.js"
|
|
120
|
+
```
|
|
121
|
+
|
|
106
122
|
Leave `env` out entirely for publish/read-only use. `cp .env.example .env` works too — the server loads its own `.env`.
|
|
107
123
|
|
|
108
124
|
## Smoke test
|
|
@@ -114,7 +130,7 @@ export POSTMD_API_KEY=pmk_…
|
|
|
114
130
|
npm run smoke
|
|
115
131
|
```
|
|
116
132
|
|
|
117
|
-
Creates a group and a passworded document, reads it back, updates it, clears the password, then deletes both.
|
|
133
|
+
Creates a group and a passworded document, reads it back, updates it, clears the password, then deletes both. It also publishes one document with no credential and removes it with the control token.
|
|
118
134
|
|
|
119
135
|
## Stack
|
|
120
136
|
|
package/package.json
CHANGED
package/scripts/smoke-test.mjs
CHANGED
|
@@ -108,5 +108,50 @@ if (groupId != null) {
|
|
|
108
108
|
check("delete group", dropped.json?.resultCode === "200", dropped.text);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/*
|
|
112
|
+
9. 익명 발행과 제어 토큰.
|
|
113
|
+
|
|
114
|
+
자격 증명을 붙이지 않고 부른다. 키를 실으면 그 회원 소유가 되어 토큰이 나오지 않으므로,
|
|
115
|
+
여기서만 Authorization 헤더를 뺀다.
|
|
116
|
+
*/
|
|
117
|
+
const anonForm = new FormData();
|
|
118
|
+
anonForm.append(
|
|
119
|
+
"file",
|
|
120
|
+
new Blob([`# Smoke anon\n\n${MARKER}\n`], { type: "text/markdown" }),
|
|
121
|
+
"smoke-anon.md"
|
|
122
|
+
);
|
|
123
|
+
const anonRes = await fetch(`${base}/api/v1/documents`, { method: "POST", body: anonForm });
|
|
124
|
+
const anon = await anonRes.json().catch(() => null);
|
|
125
|
+
check("publish without a credential", anon?.resultCode === "200", JSON.stringify(anon));
|
|
126
|
+
|
|
127
|
+
const anonCode = anon?.data?.docCode;
|
|
128
|
+
const controlToken = anon?.data?.controlToken;
|
|
129
|
+
check("answer carries a control token", typeof controlToken === "string" && controlToken.startsWith("pmt_"));
|
|
130
|
+
check("answer carries a deletion date", typeof anon?.data?.retainedUntil === "string");
|
|
131
|
+
check("answer explains the terms", typeof anon?.message === "string" && anon.message.length > 0);
|
|
132
|
+
|
|
133
|
+
if (anonCode && controlToken) {
|
|
134
|
+
const titled = new FormData();
|
|
135
|
+
titled.append("title", `mcp smoke anon ${stamp}`);
|
|
136
|
+
const changed = await fetch(`${base}/api/v1/documents/${anonCode}/update`, {
|
|
137
|
+
method: "POST",
|
|
138
|
+
headers: { "X-Document-Token": controlToken },
|
|
139
|
+
body: titled,
|
|
140
|
+
});
|
|
141
|
+
check("token updates the document", (await changed.json().catch(() => null))?.resultCode === "200");
|
|
142
|
+
|
|
143
|
+
const refused = await fetch(`${base}/api/v1/documents/${anonCode}/delete`, {
|
|
144
|
+
method: "POST",
|
|
145
|
+
headers: { "X-Document-Token": "pmt_wrong" },
|
|
146
|
+
});
|
|
147
|
+
check("a wrong token is refused", (await refused.json().catch(() => null))?.resultCode === "E_DOC_0008");
|
|
148
|
+
|
|
149
|
+
const removed = await fetch(`${base}/api/v1/documents/${anonCode}/delete`, {
|
|
150
|
+
method: "POST",
|
|
151
|
+
headers: { "X-Document-Token": controlToken },
|
|
152
|
+
});
|
|
153
|
+
check("token deletes the document", (await removed.json().catch(() => null))?.resultCode === "200");
|
|
154
|
+
}
|
|
155
|
+
|
|
111
156
|
console.log(failures ? `\n${failures} failure(s)` : "\nall good");
|
|
112
157
|
process.exit(failures ? 1 : 0);
|
package/server.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
3
|
"name": "io.github.reinlainer/postmd-mcp-server",
|
|
4
4
|
"description": "Publish Markdown to PostMD and get a shareable web page",
|
|
5
|
-
"version": "2.
|
|
5
|
+
"version": "2.2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"url": "https://github.com/reinlainer/postmd-mcp-server",
|
|
8
8
|
"source": "github"
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "postmd-mcp-server",
|
|
14
|
-
"version": "2.
|
|
14
|
+
"version": "2.2.0",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|
package/src/index.js
CHANGED
|
@@ -17,7 +17,7 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
17
17
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
18
18
|
import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
19
19
|
|
|
20
|
-
const VERSION = "2.
|
|
20
|
+
const VERSION = "2.2.0";
|
|
21
21
|
|
|
22
22
|
/** 기본은 운영이다. 대부분의 사용자는 설정 없이 바로 쓰면 된다. */
|
|
23
23
|
const DEFAULT_BASE_URL = "https://postmd.turink.com";
|
|
@@ -28,7 +28,10 @@ const SERVER_INSTRUCTIONS =
|
|
|
28
28
|
"the HTTP API directly. Creating a document needs no API key; updating, deleting, " +
|
|
29
29
|
"attachments and groups need POSTMD_API_KEY with the matching scope. Pass the full " +
|
|
30
30
|
"Markdown in `markdown`, or pass a local `filePath` so this server reads the file " +
|
|
31
|
-
"itself. A successful create returns data.shareUrl — hand that URL to people."
|
|
31
|
+
"itself. A successful create returns data.shareUrl — hand that URL to people. " +
|
|
32
|
+
"Creating without a key also returns data.controlToken and data.retainedUntil: the " +
|
|
33
|
+
"document is deleted at that instant, and the token is the only way to update or " +
|
|
34
|
+
"delete it. It is shown once, so report it to the person along with the URL.";
|
|
32
35
|
|
|
33
36
|
function isDebug() {
|
|
34
37
|
const v = process.env.POSTMD_DEBUG;
|
|
@@ -176,9 +179,18 @@ const ATTACHMENT_MIME = {
|
|
|
176
179
|
pdf: "application/pdf",
|
|
177
180
|
};
|
|
178
181
|
|
|
179
|
-
/**
|
|
182
|
+
/**
|
|
183
|
+
* 만든 문서에는 나눠 줄 주소를 붙여 준다. 에이전트의 다음 행동이 바로 그것이다.
|
|
184
|
+
*
|
|
185
|
+
* 서버가 이미 `shareUrl` 을 돌려주면 그것을 쓴다. 서버는 요청이 실제로 들어온 주소를
|
|
186
|
+
* 보고 만들고, 여기서는 `POSTMD_BASE_URL` 을 보고 만든다. 자체 호스팅에서 그 둘이
|
|
187
|
+
* 다르면 값이 갈리는데, 사람에게 건너가는 주소는 서버가 아는 쪽이 맞다.
|
|
188
|
+
*
|
|
189
|
+
* 그래도 이 함수를 남겨 둔다. `shareUrl` 을 돌려주지 않는 구 버전 서버를 가리키는
|
|
190
|
+
* 설정이 있을 수 있고, 그때도 에이전트는 건넬 주소를 받아야 한다.
|
|
191
|
+
*/
|
|
180
192
|
function addShareUrl(ctx, data) {
|
|
181
|
-
if (data && typeof data.docCode === "string" && data.docCode) {
|
|
193
|
+
if (data && typeof data.docCode === "string" && data.docCode && !data.shareUrl) {
|
|
182
194
|
data.shareUrl = `${ctx.base}/share/${encodeURIComponent(data.docCode)}`;
|
|
183
195
|
}
|
|
184
196
|
}
|
|
@@ -213,11 +225,40 @@ async function updateDocument(ctx, a, markdownBuffer) {
|
|
|
213
225
|
}
|
|
214
226
|
const r = await apiFetch(ctx, `/documents/${encodeURIComponent(a.docCode)}/update`, {
|
|
215
227
|
method: "POST",
|
|
228
|
+
headers: tokenHeader(a),
|
|
216
229
|
body: form,
|
|
217
230
|
});
|
|
218
231
|
return fromEnvelope(r);
|
|
219
232
|
}
|
|
220
233
|
|
|
234
|
+
/**
|
|
235
|
+
* 익명 발행 문서의 제어 토큰 인자.
|
|
236
|
+
*
|
|
237
|
+
* 키를 대신하는 것이 아니라 그 문서 하나에만 듣는다. 그래서 키가 없어도 이 값이 있으면
|
|
238
|
+
* 도구를 부를 수 있고, 키가 있어도 남의 익명 문서에는 이 값이 있어야 한다.
|
|
239
|
+
*/
|
|
240
|
+
const CONTROL_TOKEN_PROP = {
|
|
241
|
+
type: "string",
|
|
242
|
+
description:
|
|
243
|
+
"Control token from an anonymous publish answer (pmt_...). Lets you act on that one " +
|
|
244
|
+
"document without an API key.",
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
/** 제어 토큰을 헤더로 옮긴다. 서버는 회원 인증 헤더와 나눠서 받는다. */
|
|
248
|
+
function tokenHeader(a) {
|
|
249
|
+
return a.controlToken ? { "X-Document-Token": String(a.controlToken) } : {};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* 키가 없어도 제어 토큰이 있으면 통과시킨다.
|
|
254
|
+
*
|
|
255
|
+
* 토큰만으로 되는 일에 키를 요구하면, 방금 익명으로 발행하고 토큰을 받은 쪽이 자기 문서를
|
|
256
|
+
* 손대지 못한다.
|
|
257
|
+
*/
|
|
258
|
+
function missingKeyUnlessToken(ctx, a, scopes) {
|
|
259
|
+
return a.controlToken ? null : missingKey(ctx, scopes);
|
|
260
|
+
}
|
|
261
|
+
|
|
221
262
|
/** 문서 메타데이터 공통 속성. 만들기·고치기 스키마가 나눠 쓴다. */
|
|
222
263
|
const DOC_META_PROPS = {
|
|
223
264
|
password: { type: "string", description: "Readers must supply this password to see the content." },
|
|
@@ -237,8 +278,11 @@ const TOOL_DEFS = [
|
|
|
237
278
|
name: "postmd_create_document",
|
|
238
279
|
description:
|
|
239
280
|
"Publish Markdown as a PostMD web page. No API key required — anyone can publish. " +
|
|
240
|
-
"Returns docCode and data.shareUrl; hand shareUrl to people.
|
|
241
|
-
"document
|
|
281
|
+
"Returns docCode and data.shareUrl; hand shareUrl to people. Without a key the " +
|
|
282
|
+
"document is anonymous: data.retainedUntil is when it is deleted and " +
|
|
283
|
+
"data.controlToken is the only way to update or delete it, shown once and never " +
|
|
284
|
+
"reissued — report both to the person. With an API key the document belongs to that " +
|
|
285
|
+
"member, has no expiry, needs no token and can collect notes; groupId files it into " +
|
|
242
286
|
"that group instead of the default one (key with documents:write).",
|
|
243
287
|
inputSchema: {
|
|
244
288
|
type: "object",
|
|
@@ -262,7 +306,8 @@ const TOOL_DEFS = [
|
|
|
262
306
|
name: "postmd_create_document_from_file",
|
|
263
307
|
description:
|
|
264
308
|
"Same as postmd_create_document, but reads the Markdown from filePath on the machine " +
|
|
265
|
-
"running this MCP server — use it for large files instead of pasting the body."
|
|
309
|
+
"running this MCP server — use it for large files instead of pasting the body. " +
|
|
310
|
+
"Without an API key it returns data.controlToken and data.retainedUntil, same as above.",
|
|
266
311
|
inputSchema: {
|
|
267
312
|
type: "object",
|
|
268
313
|
properties: {
|
|
@@ -329,9 +374,11 @@ const TOOL_DEFS = [
|
|
|
329
374
|
{
|
|
330
375
|
name: "postmd_update_document",
|
|
331
376
|
description:
|
|
332
|
-
"Update a document
|
|
333
|
-
"`
|
|
334
|
-
"
|
|
377
|
+
"Update a document. Requires an API key with documents:write for a document you own, " +
|
|
378
|
+
"or `controlToken` for an anonymously published one. Include `markdown` to replace " +
|
|
379
|
+
"the stored content; any metadata field replaces that field. clearPassword / " +
|
|
380
|
+
"clearShareEndDate remove the password / end date. Updating does not push back the " +
|
|
381
|
+
"deletion date of an anonymous document.",
|
|
335
382
|
inputSchema: {
|
|
336
383
|
type: "object",
|
|
337
384
|
properties: {
|
|
@@ -345,6 +392,7 @@ const TOOL_DEFS = [
|
|
|
345
392
|
...DOC_META_PROPS,
|
|
346
393
|
clearPassword: { type: "boolean", description: "true removes the password." },
|
|
347
394
|
clearShareEndDate: { type: "boolean", description: "true removes the end date, making sharing open-ended." },
|
|
395
|
+
controlToken: CONTROL_TOKEN_PROP,
|
|
348
396
|
},
|
|
349
397
|
required: ["docCode"],
|
|
350
398
|
},
|
|
@@ -353,7 +401,7 @@ const TOOL_DEFS = [
|
|
|
353
401
|
name: "postmd_update_document_from_file",
|
|
354
402
|
description:
|
|
355
403
|
"Same as postmd_update_document, but reads the new Markdown from filePath on the " +
|
|
356
|
-
"machine running this MCP server.",
|
|
404
|
+
"machine running this MCP server. Takes `controlToken` the same way.",
|
|
357
405
|
inputSchema: {
|
|
358
406
|
type: "object",
|
|
359
407
|
properties: {
|
|
@@ -367,6 +415,7 @@ const TOOL_DEFS = [
|
|
|
367
415
|
...DOC_META_PROPS,
|
|
368
416
|
clearPassword: { type: "boolean", description: "true removes the password." },
|
|
369
417
|
clearShareEndDate: { type: "boolean", description: "true removes the end date, making sharing open-ended." },
|
|
418
|
+
controlToken: CONTROL_TOKEN_PROP,
|
|
370
419
|
},
|
|
371
420
|
required: ["docCode", "filePath"],
|
|
372
421
|
},
|
|
@@ -374,11 +423,13 @@ const TOOL_DEFS = [
|
|
|
374
423
|
{
|
|
375
424
|
name: "postmd_delete_document",
|
|
376
425
|
description:
|
|
377
|
-
"Delete a document
|
|
378
|
-
"
|
|
426
|
+
"Delete a document. Requires an API key with documents:write for a document you own, " +
|
|
427
|
+
"or `controlToken` for an anonymously published one. There is no endpoint to undo " +
|
|
428
|
+
"this: the document stops being served at once and its stored content is erased about " +
|
|
429
|
+
"a month later.",
|
|
379
430
|
inputSchema: {
|
|
380
431
|
type: "object",
|
|
381
|
-
properties: { docCode: { type: "string" } },
|
|
432
|
+
properties: { docCode: { type: "string" }, controlToken: CONTROL_TOKEN_PROP },
|
|
382
433
|
required: ["docCode"],
|
|
383
434
|
},
|
|
384
435
|
},
|
|
@@ -423,9 +474,10 @@ const TOOL_DEFS = [
|
|
|
423
474
|
description:
|
|
424
475
|
"Attach a note or highlight to a document. Requires an API key with documents:write. " +
|
|
425
476
|
"Give `content` for a note, `color` alone for a colour-only highlight (then " +
|
|
426
|
-
"`quotedContent` is required — a highlight must point at a passage).
|
|
427
|
-
"
|
|
428
|
-
"
|
|
477
|
+
"`quotedContent` is required — a highlight must point at a passage). Visibility comes " +
|
|
478
|
+
"from ownership: on the key member's own document choose PRIVATE (only they see it) or " +
|
|
479
|
+
"SHARED; on anyone else's document every note is SHARED, so omit scope. Documents " +
|
|
480
|
+
"nobody owns — anonymous uploads and service-owned pages — take no notes at all.",
|
|
429
481
|
inputSchema: {
|
|
430
482
|
type: "object",
|
|
431
483
|
properties: {
|
|
@@ -447,7 +499,8 @@ const TOOL_DEFS = [
|
|
|
447
499
|
name: "postmd_update_note",
|
|
448
500
|
description:
|
|
449
501
|
"Edit a note you wrote. Requires an API key with documents:write. Omitting scope " +
|
|
450
|
-
"keeps the current one
|
|
502
|
+
"keeps the current one; a scope you do send follows the ownership rule above. The " +
|
|
503
|
+
"note must keep text or a colour.",
|
|
451
504
|
inputSchema: {
|
|
452
505
|
type: "object",
|
|
453
506
|
properties: {
|
|
@@ -655,12 +708,12 @@ async function runTool(ctx, name, args) {
|
|
|
655
708
|
}
|
|
656
709
|
}
|
|
657
710
|
case "postmd_update_document": {
|
|
658
|
-
const denied =
|
|
711
|
+
const denied = missingKeyUnlessToken(ctx, a, "documents:write");
|
|
659
712
|
if (denied) return denied;
|
|
660
713
|
return await updateDocument(ctx, a, a.markdown ?? null);
|
|
661
714
|
}
|
|
662
715
|
case "postmd_update_document_from_file": {
|
|
663
|
-
const denied =
|
|
716
|
+
const denied = missingKeyUnlessToken(ctx, a, "documents:write");
|
|
664
717
|
if (denied) return denied;
|
|
665
718
|
try {
|
|
666
719
|
const { buffer, suggestedName } = await readLocalFile(a.filePath);
|
|
@@ -670,10 +723,11 @@ async function runTool(ctx, name, args) {
|
|
|
670
723
|
}
|
|
671
724
|
}
|
|
672
725
|
case "postmd_delete_document": {
|
|
673
|
-
const denied =
|
|
726
|
+
const denied = missingKeyUnlessToken(ctx, a, "documents:write");
|
|
674
727
|
if (denied) return denied;
|
|
675
728
|
const r = await apiFetch(ctx, `/documents/${encodeURIComponent(a.docCode)}/delete`, {
|
|
676
729
|
method: "POST",
|
|
730
|
+
headers: tokenHeader(a),
|
|
677
731
|
});
|
|
678
732
|
return fromEnvelope(r);
|
|
679
733
|
}
|