tunc-mcp 0.1.2 → 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
@@ -128,20 +134,37 @@ await memory.callTool('data_delete', {
128
134
  if_revision: revision,
129
135
  });
130
136
 
131
- // Artifacts: store a file, get a private link
137
+ // Artifacts: store a file, get a private link back.
138
+ // art_read is operation list|get; art_write is operation create; art_delete
139
+ // takes an artifact_id and nothing else.
132
140
  const files = art();
133
- await files.callTool('art_write', {
134
- title: 'quarterly-report.pdf',
141
+ const stored = await files.callTool('art_write', {
142
+ operation: 'create',
143
+ name: 'quarterly-report.pdf',
135
144
  mime_type: 'application/pdf',
136
145
  content_base64: base64Bytes,
137
146
  });
138
-
139
- // Documents: build something a human can open and edit
147
+ // the result nests the artifact, keyed by `id`, with a viewer_url to share
148
+ const artifactId = JSON.parse(stored.content[0].text).artifact.id;
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
+
158
+ // Documents: build something a human can open and edit.
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`.
140
162
  const docs = doc();
141
- await docs.callTool('doc_create', {
163
+ const made = await docs.callTool('doc_create', {
142
164
  title: 'Q4 summary',
143
165
  markdown: '# Q4\n\nRevenue up 12%.',
144
166
  });
167
+ const docId = JSON.parse(made.content[0].text).result.doc.id;
145
168
  ```
146
169
 
147
170
  Discover what any service offers:
@@ -158,7 +181,7 @@ export TUNC_API_KEY=tunc_...
158
181
 
159
182
  npx tunc-mcp services
160
183
  npx tunc-mcp tools --service orb
161
- 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
162
185
  ```
163
186
 
164
187
  ## Wire it into an MCP client
@@ -178,6 +201,30 @@ orchestrator. Point it at the endpoint and pass the key as a bearer header:
178
201
  }
179
202
  ```
180
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
+
181
228
  ## Agent-to-agent
182
229
 
183
230
  Every service publishes an A2A agent card, so agents that speak A2A can discover
@@ -226,6 +273,8 @@ service; the message says which.
226
273
 
227
274
  `data_update` 接受 `if_revision`。两个智能体写同一条记录时不会互相覆盖:后到的写入收到 `revision_conflict`,重新读取后重试。这把多智能体最常见的“静默丢更新”故障,变成一个可以被发现的错误。
228
275
 
276
+ 注意两个与直觉不同的行为:**Doc** 的 `doc_write` 把 `base_revision` 当作合并基线而不是锁,过期版本通常会被合并而不是报错,只有在编辑内容真正冲突时才返回 `doc_conflict`;**Art** 的删除是标记删除,删除之后 `get` 仍然能读到该记录,但 `status` 为 `deleted`、`viewer_url` 为空、内容链接返回 `401`。
277
+
229
278
  ```bash
230
279
  npm install tunc-mcp
231
280
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tunc-mcp",
3
- "version": "0.1.2",
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 });