@treeship/mcp 0.1.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.
@@ -0,0 +1,2 @@
1
+ import type { AttestParams } from './types.js';
2
+ export declare function attestAction(params: AttestParams): Promise<string | undefined>;
package/dist/attest.js ADDED
@@ -0,0 +1,33 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ const exec = promisify(execFile);
4
+ export async function attestAction(params) {
5
+ const args = [
6
+ 'attest', 'action',
7
+ '--actor', params.actor,
8
+ '--action', params.action,
9
+ '--format', 'json',
10
+ ];
11
+ if (params.parentId) {
12
+ args.push('--parent', params.parentId);
13
+ }
14
+ const cleanMeta = {};
15
+ for (const [k, v] of Object.entries(params.meta ?? {})) {
16
+ if (v !== undefined && v !== null)
17
+ cleanMeta[k] = v;
18
+ }
19
+ if (Object.keys(cleanMeta).length > 0) {
20
+ args.push('--meta', JSON.stringify(cleanMeta));
21
+ }
22
+ try {
23
+ const { stdout } = await exec('treeship', args, { timeout: 5000 });
24
+ const result = JSON.parse(stdout);
25
+ return result.id || result.artifact_id;
26
+ }
27
+ catch {
28
+ if (process.env.TREESHIP_DEBUG === '1') {
29
+ process.stderr.write(`[treeship] attestAction failed: ${params.action}\n`);
30
+ }
31
+ return undefined;
32
+ }
33
+ }
@@ -0,0 +1,12 @@
1
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
+ import type { CallToolRequest, CallToolResultSchema, CompatibilityCallToolResultSchema, Implementation } from '@modelcontextprotocol/sdk/types.js';
3
+ import type { RequestOptions } from '@modelcontextprotocol/sdk/shared/protocol.js';
4
+ import type { ClientOptions } from '@modelcontextprotocol/sdk/client/index.js';
5
+ export declare class TreeshipMCPClient extends Client {
6
+ private _actor;
7
+ private _disabled;
8
+ constructor(clientInfo: Implementation, options?: ClientOptions);
9
+ callTool(params: CallToolRequest['params'], resultSchema?: typeof CallToolResultSchema | typeof CompatibilityCallToolResultSchema, options?: RequestOptions): Promise<any>;
10
+ private _attestIntent;
11
+ private _attestReceipt;
12
+ }
package/dist/client.js ADDED
@@ -0,0 +1,72 @@
1
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
+ import { attestAction } from './attest.js';
3
+ import { hashPayload } from './utils.js';
4
+ export class TreeshipMCPClient extends Client {
5
+ _actor;
6
+ _disabled;
7
+ constructor(clientInfo, options) {
8
+ super(clientInfo, options);
9
+ this._actor = process.env.TREESHIP_ACTOR ?? `agent://mcp-${clientInfo?.name ?? 'unknown'}`;
10
+ this._disabled = process.env.TREESHIP_DISABLE === '1';
11
+ }
12
+ async callTool(params, resultSchema, options) {
13
+ if (this._disabled) {
14
+ return super.callTool(params, resultSchema, options);
15
+ }
16
+ // Attest INTENT before the call (awaited -- proof of what was about to happen)
17
+ const intentId = await this._attestIntent(params).catch(() => undefined);
18
+ const startMs = Date.now();
19
+ let result;
20
+ let error;
21
+ try {
22
+ result = await super.callTool(params, resultSchema, options);
23
+ }
24
+ catch (e) {
25
+ error = e;
26
+ throw e;
27
+ }
28
+ finally {
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
+ };
40
+ }
41
+ return result;
42
+ }
43
+ async _attestIntent(params) {
44
+ return attestAction({
45
+ actor: this._actor,
46
+ action: `mcp.tool.${params.name}.intent`,
47
+ meta: {
48
+ tool: params.name,
49
+ server: 'mcp',
50
+ args_digest: hashPayload(JSON.stringify(params.arguments ?? {})),
51
+ approval_nonce: process.env.TREESHIP_APPROVAL_NONCE || undefined,
52
+ },
53
+ });
54
+ }
55
+ 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(() => { });
71
+ }
72
+ }
@@ -0,0 +1,3 @@
1
+ export * from '@modelcontextprotocol/sdk/client/index.js';
2
+ export { TreeshipMCPClient as Client } from './client.js';
3
+ export type { ToolReceipt, AttestParams } from './types.js';
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ // Re-export EVERYTHING from @modelcontextprotocol/sdk/client unchanged
2
+ // so existing imports still work after switching to @treeship/mcp
3
+ export * from '@modelcontextprotocol/sdk/client/index.js';
4
+ // Override Client with our wrapped version
5
+ export { TreeshipMCPClient as Client } from './client.js';
@@ -0,0 +1,12 @@
1
+ export interface ToolReceipt {
2
+ intent?: string;
3
+ receipt?: string;
4
+ tool: string;
5
+ actor: string;
6
+ }
7
+ export interface AttestParams {
8
+ actor: string;
9
+ action: string;
10
+ parentId?: string;
11
+ meta?: Record<string, unknown>;
12
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare function hashPayload(content: string): string;
package/dist/utils.js ADDED
@@ -0,0 +1,4 @@
1
+ import { createHash } from 'node:crypto';
2
+ export function hashPayload(content) {
3
+ return 'sha256:' + createHash('sha256').update(content).digest('hex');
4
+ }
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@treeship/mcp",
3
+ "version": "0.1.0",
4
+ "description": "Drop-in Treeship attestation for MCP tool calls",
5
+ "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/zerkerlabs/treeship",
9
+ "directory": "bridges/mcp"
10
+ },
11
+ "homepage": "https://treeship.dev",
12
+ "keywords": ["treeship", "mcp", "attestation", "agents", "tool-calls"],
13
+ "files": ["dist"],
14
+ "type": "module",
15
+ "main": "dist/index.js",
16
+ "types": "dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "import": "./dist/index.js",
20
+ "types": "./dist/index.d.ts"
21
+ }
22
+ },
23
+ "dependencies": {
24
+ "@modelcontextprotocol/sdk": "^1.0.0"
25
+ },
26
+ "devDependencies": {
27
+ "@types/node": "^25.5.0",
28
+ "typescript": "^5.7.0",
29
+ "vitest": "^3.0.0"
30
+ },
31
+ "scripts": {
32
+ "build": "tsc",
33
+ "test": "vitest run"
34
+ }
35
+ }