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 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 only a title; doc_read, doc_write and doc_delete take `doc`.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tunc-mcp",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "MCP client for Tunc Orb (agent memory database), Tunc Art (artifact storage and private file sharing) and Tunc Doc (documents, spreadsheets, decks).",
5
5
  "keywords": [
6
6
  "mcp",
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: '0.1.0' },
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 });