tunc-mcp 0.1.3 → 0.1.4
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 +44 -2
- package/package.json +1 -1
- package/src/client.js +3 -1
- package/src/index.js +2 -2
package/README.md
CHANGED
|
@@ -36,6 +36,12 @@ lost-update bug — the classic multi-agent failure where a plausible but wrong
|
|
|
36
36
|
number survives — into a loud retry. If you run agent swarms that share state,
|
|
37
37
|
use it.
|
|
38
38
|
|
|
39
|
+
That is Orb, and it is strict — a stale write is refused, nothing is committed.
|
|
40
|
+
**Doc is the opposite on purpose:** `doc_write`'s `base_revision` is a merge
|
|
41
|
+
base, not a lock, so a stale write usually merges into the newer text and
|
|
42
|
+
succeeds. `doc_conflict` appears only when the edits actually collide. Do not
|
|
43
|
+
wrap both in the same "always pass the revision, retry on conflict" helper.
|
|
44
|
+
|
|
39
45
|
## Install
|
|
40
46
|
|
|
41
47
|
```bash
|
|
@@ -141,8 +147,18 @@ const stored = await files.callTool('art_write', {
|
|
|
141
147
|
// the result nests the artifact, keyed by `id`, with a viewer_url to share
|
|
142
148
|
const artifactId = JSON.parse(stored.content[0].text).artifact.id;
|
|
143
149
|
|
|
150
|
+
// Deleting tombstones rather than erases: art_read get still resolves the
|
|
151
|
+
// artifact afterwards, reporting status "deleted", availability "deleted" and
|
|
152
|
+
// viewer_url null, and the content URL answers 401. Do not test for a delete by
|
|
153
|
+
// expecting get to throw — check the status, or that list no longer returns it.
|
|
154
|
+
await files.callTool('art_delete', { artifact_id: artifactId });
|
|
155
|
+
const after = await files.callTool('art_read', { operation: 'get', artifact_id: artifactId });
|
|
156
|
+
console.log(JSON.parse(after.content[0].text).artifact.status); // "deleted"
|
|
157
|
+
|
|
144
158
|
// Documents: build something a human can open and edit.
|
|
145
|
-
// doc_create needs
|
|
159
|
+
// doc_create needs a title AND a markdown body (the schema says only title is
|
|
160
|
+
// required, which is wrong: a doc without markdown gets invalid_arguments).
|
|
161
|
+
// doc_read, doc_write and doc_delete take `doc`.
|
|
146
162
|
const docs = doc();
|
|
147
163
|
const made = await docs.callTool('doc_create', {
|
|
148
164
|
title: 'Q4 summary',
|
|
@@ -165,7 +181,7 @@ export TUNC_API_KEY=tunc_...
|
|
|
165
181
|
|
|
166
182
|
npx tunc-mcp services
|
|
167
183
|
npx tunc-mcp tools --service orb
|
|
168
|
-
npx tunc-mcp call data_read '{"name":"contacts/ada"}' --service orb
|
|
184
|
+
npx tunc-mcp call data_read '{"operation":"get","name":"contacts/ada"}' --service orb
|
|
169
185
|
```
|
|
170
186
|
|
|
171
187
|
## Wire it into an MCP client
|
|
@@ -185,6 +201,30 @@ orchestrator. Point it at the endpoint and pass the key as a bearer header:
|
|
|
185
201
|
}
|
|
186
202
|
```
|
|
187
203
|
|
|
204
|
+
## OAuth instead of a key
|
|
205
|
+
|
|
206
|
+
The servers accept a resource-bound OAuth token as well as an API key. That
|
|
207
|
+
matters for Doc: **`mcp.tuncdoc.com` advertises the same scope names as Orb
|
|
208
|
+
(`orb.read`, `orb.write`, `orb.delete`)**, so a token without a resource binding
|
|
209
|
+
defaults to the Orb audience and Doc rejects it as "not scoped for this service".
|
|
210
|
+
Include the resource in the device-authorization request:
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
resource=https://mcp.tuncdoc.com/mcp
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
That also flips the approval page to the right domain — an unbound grant sends
|
|
217
|
+
people to `tuncorb.com/connect` even when they are authorising Doc.
|
|
218
|
+
|
|
219
|
+
```js
|
|
220
|
+
doc({ apiKey: deviceAccessToken });
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Revoking the token (`POST /oauth/revoke`) returns `200`, but the access token
|
|
224
|
+
keeps working until it expires — about 15 minutes. Revocation kills the refresh
|
|
225
|
+
path, not an already-issued access token, so a successful revoke is not proof
|
|
226
|
+
the credential is dead this second.
|
|
227
|
+
|
|
188
228
|
## Agent-to-agent
|
|
189
229
|
|
|
190
230
|
Every service publishes an A2A agent card, so agents that speak A2A can discover
|
|
@@ -233,6 +273,8 @@ service; the message says which.
|
|
|
233
273
|
|
|
234
274
|
`data_update` 接受 `if_revision`。两个智能体写同一条记录时不会互相覆盖:后到的写入收到 `revision_conflict`,重新读取后重试。这把多智能体最常见的“静默丢更新”故障,变成一个可以被发现的错误。
|
|
235
275
|
|
|
276
|
+
注意两个与直觉不同的行为:**Doc** 的 `doc_write` 把 `base_revision` 当作合并基线而不是锁,过期版本通常会被合并而不是报错,只有在编辑内容真正冲突时才返回 `doc_conflict`;**Art** 的删除是标记删除,删除之后 `get` 仍然能读到该记录,但 `status` 为 `deleted`、`viewer_url` 为空、内容链接返回 `401`。
|
|
277
|
+
|
|
236
278
|
```bash
|
|
237
279
|
npm install tunc-mcp
|
|
238
280
|
```
|
package/package.json
CHANGED
package/src/client.js
CHANGED
|
@@ -57,6 +57,8 @@ export function parseBody(contentType, text) {
|
|
|
57
57
|
return last;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
export const VERSION = '0.1.4';
|
|
61
|
+
|
|
60
62
|
export class TuncMcpClient {
|
|
61
63
|
#endpoint;
|
|
62
64
|
#apiKey;
|
|
@@ -142,7 +144,7 @@ export class TuncMcpClient {
|
|
|
142
144
|
const result = await this.#rpc('initialize', {
|
|
143
145
|
protocolVersion: '2025-06-18',
|
|
144
146
|
capabilities: {},
|
|
145
|
-
clientInfo: { name: 'tunc-mcp', version:
|
|
147
|
+
clientInfo: { name: 'tunc-mcp', version: VERSION },
|
|
146
148
|
});
|
|
147
149
|
// Notifications carry no id and expect no body.
|
|
148
150
|
await fetch(this.#endpoint, {
|
package/src/index.js
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
// Tunc Doc — documents, spreadsheets and slide decks, import/export DOCX,
|
|
8
8
|
// XLSX, PPTX and PDF.
|
|
9
9
|
|
|
10
|
-
import { SERVERS, TuncMcpClient, TuncMcpError, parseBody } from './client.js';
|
|
10
|
+
import { SERVERS, TuncMcpClient, TuncMcpError, VERSION, parseBody } from './client.js';
|
|
11
11
|
|
|
12
|
-
export { SERVERS, TuncMcpClient, TuncMcpError, parseBody };
|
|
12
|
+
export { SERVERS, TuncMcpClient, TuncMcpError, VERSION, parseBody };
|
|
13
13
|
|
|
14
14
|
export function orb(options = {}) {
|
|
15
15
|
return new TuncMcpClient({ service: 'orb', ...options });
|