@treeship/mcp 0.2.1 → 0.3.1

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/dist/attest.d.ts CHANGED
@@ -1,2 +1,3 @@
1
- import type { AttestParams } from './types.js';
1
+ import type { AttestParams, AttestReceiptParams } from './types.js';
2
2
  export declare function attestAction(params: AttestParams): Promise<string | undefined>;
3
+ export declare function attestReceipt(params: AttestReceiptParams): Promise<string | undefined>;
package/dist/attest.js CHANGED
@@ -11,6 +11,9 @@ export async function attestAction(params) {
11
11
  if (params.parentId) {
12
12
  args.push('--parent', params.parentId);
13
13
  }
14
+ if (params.approvalNonce) {
15
+ args.push('--approval-nonce', params.approvalNonce);
16
+ }
14
17
  const cleanMeta = {};
15
18
  for (const [k, v] of Object.entries(params.meta ?? {})) {
16
19
  if (v !== undefined && v !== null)
@@ -31,3 +34,28 @@ export async function attestAction(params) {
31
34
  return undefined;
32
35
  }
33
36
  }
37
+ export async function attestReceipt(params) {
38
+ const args = [
39
+ 'attest', 'receipt',
40
+ '--system', params.system,
41
+ '--kind', params.kind,
42
+ '--format', 'json',
43
+ ];
44
+ if (params.subject) {
45
+ args.push('--subject', params.subject);
46
+ }
47
+ if (params.payload && Object.keys(params.payload).length > 0) {
48
+ args.push('--payload', JSON.stringify(params.payload));
49
+ }
50
+ try {
51
+ const { stdout } = await exec('treeship', args, { timeout: 5000 });
52
+ const result = JSON.parse(stdout);
53
+ return result.id || result.artifact_id;
54
+ }
55
+ catch (e) {
56
+ if (process.env.TREESHIP_DEBUG === '1') {
57
+ process.stderr.write(`[treeship] attestReceipt failed: ${params.kind}\n`);
58
+ }
59
+ return undefined;
60
+ }
61
+ }
package/dist/client.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
- import { attestAction } from './attest.js';
2
+ import { attestAction, attestReceipt } from './attest.js';
3
3
  import { hashPayload } from './utils.js';
4
4
  export class TreeshipMCPClient extends Client {
5
5
  _actor;
@@ -27,16 +27,26 @@ export class TreeshipMCPClient extends Client {
27
27
  }
28
28
  finally {
29
29
  const elapsedMs = Date.now() - startMs;
30
- // Attest RECEIPT after the call (fire-and-forget -- never blocks response)
31
- this._attestReceipt(params, result, intentId, elapsedMs, error).catch(() => { });
32
- }
33
- // Attach treeship metadata to result
34
- if (intentId && result) {
35
- result._treeship = {
36
- intent: intentId,
37
- tool: params.name,
38
- actor: this._actor,
39
- };
30
+ // Attach treeship metadata to result immediately (receipt comes async)
31
+ if (result) {
32
+ const receipt = { resolve: (_id) => { } };
33
+ const receiptPromise = new Promise(r => { receipt.resolve = r; });
34
+ result._treeship = {
35
+ intent: intentId,
36
+ receipt: undefined,
37
+ receiptReady: receiptPromise,
38
+ tool: params.name,
39
+ actor: this._actor,
40
+ };
41
+ // Fire-and-forget: receipt attestation happens off the hot path.
42
+ // Callers who need the receipt ID can await result._treeship.receiptReady.
43
+ this._attestReceipt(params, result, intentId, elapsedMs, error)
44
+ .then(id => {
45
+ result._treeship.receipt = id;
46
+ receipt.resolve(id);
47
+ })
48
+ .catch(() => receipt.resolve(undefined));
49
+ }
40
50
  }
41
51
  return result;
42
52
  }
@@ -44,29 +54,35 @@ export class TreeshipMCPClient extends Client {
44
54
  return attestAction({
45
55
  actor: this._actor,
46
56
  action: `mcp.tool.${params.name}.intent`,
57
+ approvalNonce: process.env.TREESHIP_APPROVAL_NONCE || undefined,
47
58
  meta: {
48
59
  tool: params.name,
49
60
  server: 'mcp',
50
61
  args_digest: hashPayload(JSON.stringify(params.arguments ?? {})),
51
- approval_nonce: process.env.TREESHIP_APPROVAL_NONCE || undefined,
52
62
  },
53
63
  });
54
64
  }
55
65
  async _attestReceipt(params, result, intentId, elapsedMs, error) {
56
- await attestAction({
57
- actor: this._actor,
58
- action: `mcp.tool.${params.name}.receipt`,
59
- parentId: intentId,
60
- meta: {
61
- tool: params.name,
62
- elapsed_ms: elapsedMs,
63
- exit_code: error ? 1 : 0,
64
- is_error: result?.isError ?? !!error,
65
- output_digest: result
66
- ? hashPayload(JSON.stringify(result.content ?? result))
67
- : undefined,
68
- error_message: error?.message,
69
- },
70
- }).catch(() => { });
66
+ try {
67
+ return await attestReceipt({
68
+ system: this._actor,
69
+ kind: 'tool.result',
70
+ subject: intentId,
71
+ payload: {
72
+ tool: params.name,
73
+ elapsed_ms: elapsedMs,
74
+ exit_code: error ? 1 : 0,
75
+ is_error: result?.isError ?? !!error,
76
+ output_digest: result
77
+ ? hashPayload(JSON.stringify(result.content ?? result))
78
+ : undefined,
79
+ error_message: error?.message,
80
+ },
81
+ });
82
+ }
83
+ catch (e) {
84
+ process.stderr.write(`[treeship] attestReceipt failed for ${params.name}: ${e.message}\n`);
85
+ return undefined;
86
+ }
71
87
  }
72
88
  }
package/dist/types.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export interface ToolReceipt {
2
2
  intent?: string;
3
3
  receipt?: string;
4
+ /** Resolves with the receipt artifact ID once attestation completes (async). */
5
+ receiptReady?: Promise<string | undefined>;
4
6
  tool: string;
5
7
  actor: string;
6
8
  }
@@ -8,5 +10,12 @@ export interface AttestParams {
8
10
  actor: string;
9
11
  action: string;
10
12
  parentId?: string;
13
+ approvalNonce?: string;
11
14
  meta?: Record<string, unknown>;
12
15
  }
16
+ export interface AttestReceiptParams {
17
+ system: string;
18
+ kind: string;
19
+ subject?: string;
20
+ payload?: Record<string, unknown>;
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treeship/mcp",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "Drop-in Treeship attestation for MCP tool calls",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {