@atrib/agent 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.
Files changed (39) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +333 -0
  3. package/dist/adapters/cloudflare-agent.d.ts +46 -0
  4. package/dist/adapters/cloudflare-agent.d.ts.map +1 -0
  5. package/dist/adapters/cloudflare-agent.js +124 -0
  6. package/dist/adapters/cloudflare-agent.js.map +1 -0
  7. package/dist/adapters/langchain-mcp.d.ts +172 -0
  8. package/dist/adapters/langchain-mcp.d.ts.map +1 -0
  9. package/dist/adapters/langchain-mcp.js +145 -0
  10. package/dist/adapters/langchain-mcp.js.map +1 -0
  11. package/dist/adapters/mcp-client.d.ts +94 -0
  12. package/dist/adapters/mcp-client.d.ts.map +1 -0
  13. package/dist/adapters/mcp-client.js +91 -0
  14. package/dist/adapters/mcp-client.js.map +1 -0
  15. package/dist/adapters/vercel-ai-sdk-mcp.d.ts +129 -0
  16. package/dist/adapters/vercel-ai-sdk-mcp.d.ts.map +1 -0
  17. package/dist/adapters/vercel-ai-sdk-mcp.js +115 -0
  18. package/dist/adapters/vercel-ai-sdk-mcp.js.map +1 -0
  19. package/dist/index.d.ts +18 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +27 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/middleware.d.ts +53 -0
  24. package/dist/middleware.d.ts.map +1 -0
  25. package/dist/middleware.js +268 -0
  26. package/dist/middleware.js.map +1 -0
  27. package/dist/policy.d.ts +28 -0
  28. package/dist/policy.d.ts.map +1 -0
  29. package/dist/policy.js +196 -0
  30. package/dist/policy.js.map +1 -0
  31. package/dist/session.d.ts +50 -0
  32. package/dist/session.d.ts.map +1 -0
  33. package/dist/session.js +141 -0
  34. package/dist/session.js.map +1 -0
  35. package/dist/transaction.d.ts +52 -0
  36. package/dist/transaction.d.ts.map +1 -0
  37. package/dist/transaction.js +123 -0
  38. package/dist/transaction.js.map +1 -0
  39. package/package.json +50 -0
@@ -0,0 +1,124 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * Adapter: attribute MCP tool calls flowing through a Cloudflare Agent.
4
+ *
5
+ * Cloudflare's `agents` package exposes two MCP integration surfaces:
6
+ *
7
+ * 1. **Server-side `McpAgent`**. you build an MCP server inside a Worker
8
+ * by extending `McpAgent` and defining tools on `this.server`. Because
9
+ * `this.server` is a real `McpServer` from `@modelcontextprotocol/sdk`,
10
+ * you wrap it directly with `atrib()` from `@atrib/mcp`. no helper
11
+ * needed. See `packages/integration/examples/cloudflare-agents/` for
12
+ * the runnable McpAgent example.
13
+ *
14
+ * 2. **Client-side `Agent.addMcpServer`**. your `Agent` (or `AIChatAgent`)
15
+ * connects to one or more upstream MCP servers via `this.addMcpServer(name, url)`.
16
+ * Cloudflare's `MCPClientManager` constructs an `@modelcontextprotocol/sdk`
17
+ * Client per upstream and stores it on `agent.mcp.mcpConnections[name].client`.
18
+ * Tool invocations flow through `MCPClientManager.callTool()` which
19
+ * delegates straight to `mcpConnections[serverId].client.callTool(...)`
20
+ * (verified against `agents@0.9.0` `dist/client-BwgM3cRz.js` line 1444).
21
+ *
22
+ * This file is the helper for surface (2). It walks `agent.mcp.mcpConnections`
23
+ * after the agent has finished registering its upstream MCP servers and
24
+ * replaces each connection's `client` field with one wrapped by `wrapMcpClient`.
25
+ * Subsequent tool calls go through atrib's interceptor lifecycle.
26
+ *
27
+ * Usage:
28
+ *
29
+ * import { Agent } from 'agents'
30
+ * import { atrib, attributeCloudflareAgentMcp } from '@atrib/agent'
31
+ *
32
+ * class WeatherAgent extends Agent<Env> {
33
+ * interceptor = atrib({
34
+ * creatorKey: this.env.ATRIB_PRIVATE_KEY,
35
+ * merchantDomain: 'https://merchant.example.com',
36
+ * serverUrls: ['https://weather-mcp.example.com'],
37
+ * })
38
+ *
39
+ * async onStart() {
40
+ * await this.addMcpServer('weather', 'https://weather-mcp.example.com/mcp', {
41
+ * transport: { type: 'streamable-http' },
42
+ * })
43
+ *
44
+ * // After all addMcpServer() calls complete, attribute the connections.
45
+ * attributeCloudflareAgentMcp(this, { interceptor: this.interceptor })
46
+ * }
47
+ * }
48
+ *
49
+ * If you call `addMcpServer` again later (e.g. in a message handler or after
50
+ * an OAuth flow completes), call `attributeCloudflareAgentMcp` again. The
51
+ * helper is idempotent. connections that are already wrapped are skipped.
52
+ *
53
+ * Per spec §5.8 (degradation contract), if any single connection fails to wrap
54
+ * (missing `client` field, unexpected shape), the helper logs a warning with
55
+ * the `atrib:` prefix and skips it without throwing. The agent's tool calls
56
+ * continue to work. they just won't be attributed for that connection.
57
+ */
58
+ import { wrapMcpClient } from './mcp-client.js';
59
+ /** Runtime check that an unknown value structurally matches MinimalMcpClient. */
60
+ function isMinimalMcpClient(v) {
61
+ return (v != null &&
62
+ typeof v === 'object' &&
63
+ typeof v.callTool === 'function');
64
+ }
65
+ /**
66
+ * Marker symbol set on a wrapped client to make repeated calls to
67
+ * `attributeCloudflareAgentMcp` idempotent. The Proxy returned by
68
+ * `wrapMcpClient` carries this property; the unwrapped Client does not.
69
+ */
70
+ const ATRIB_WRAPPED = Symbol.for('atrib.cloudflare.wrapped');
71
+ /**
72
+ * Wrap every currently-connected MCP client on a Cloudflare Agent with atrib
73
+ * attribution. Returns the number of connections wrapped (excluding ones that
74
+ * were already wrapped). Idempotent. safe to call multiple times.
75
+ *
76
+ * Call this in `onStart()` after your `addMcpServer()` calls. If you add more
77
+ * MCP servers later (in a message handler, after OAuth, etc.), call again.
78
+ */
79
+ export function attributeCloudflareAgentMcp(agent, options) {
80
+ const connections = agent.mcp?.mcpConnections;
81
+ if (!connections || typeof connections !== 'object') {
82
+ console.warn('atrib: attributeCloudflareAgentMcp called on an agent with no mcp.mcpConnections. ' +
83
+ "the Cloudflare 'agents' package shape may have changed. Skipping.");
84
+ return 0;
85
+ }
86
+ let wrapped = 0;
87
+ for (const [name, conn] of Object.entries(connections)) {
88
+ try {
89
+ if (!conn || !isMinimalMcpClient(conn.client)) {
90
+ console.warn(`atrib: connection '${name}' has no client field, skipping`);
91
+ continue;
92
+ }
93
+ // Skip already-wrapped clients (idempotency)
94
+ if (conn.client[ATRIB_WRAPPED] === true) {
95
+ continue;
96
+ }
97
+ // Derive serverUrl: explicit override > connection.url.origin > undefined
98
+ let serverUrl = options.serverUrls?.[name];
99
+ if (!serverUrl && conn.url) {
100
+ try {
101
+ const u = conn.url instanceof URL ? conn.url : new URL(conn.url);
102
+ serverUrl = u.origin;
103
+ }
104
+ catch {
105
+ // URL parse failed; let wrapMcpClient fall back to no serverUrl
106
+ }
107
+ }
108
+ const wrappedClient = wrapMcpClient(conn.client, options.interceptor, serverUrl ? { serverUrl } : {});
109
+ wrappedClient[ATRIB_WRAPPED] = true;
110
+ // Replace the client field in place. MCPClientManager.callTool reads
111
+ // mcpConnections[serverId].client at invocation time, so subsequent
112
+ // tool calls will go through the wrapped client.
113
+ conn.client = wrappedClient;
114
+ wrapped++;
115
+ }
116
+ catch (err) {
117
+ // §5.8 degradation contract: never let a single bad connection break
118
+ // the whole agent. Log and continue.
119
+ console.warn(`atrib: failed to wrap MCP connection '${name}', skipping:`, err);
120
+ }
121
+ }
122
+ return wrapped;
123
+ }
124
+ //# sourceMappingURL=cloudflare-agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflare-agent.js","sourceRoot":"","sources":["../../src/adapters/cloudflare-agent.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAEH,OAAO,EAAE,aAAa,EAAyB,MAAM,iBAAiB,CAAA;AAGtE,iFAAiF;AACjF,SAAS,kBAAkB,CAAC,CAAU;IACpC,OAAO,CACL,CAAC,IAAI,IAAI;QACT,OAAO,CAAC,KAAK,QAAQ;QACrB,OAAQ,CAA4B,CAAC,QAAQ,KAAK,UAAU,CAC7D,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,aAAa,GAAG,MAAM,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAA;AA2C5D;;;;;;;GAOG;AACH,MAAM,UAAU,2BAA2B,CACzC,KAA0B,EAC1B,OAA2C;IAE3C,MAAM,WAAW,GAAG,KAAK,CAAC,GAAG,EAAE,cAAc,CAAA;IAC7C,IAAI,CAAC,WAAW,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;QACpD,OAAO,CAAC,IAAI,CACV,oFAAoF;YAClF,mEAAmE,CACtE,CAAA;QACD,OAAO,CAAC,CAAA;IACV,CAAC;IAED,IAAI,OAAO,GAAG,CAAC,CAAA;IAEf,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QACvD,IAAI,CAAC;YACH,IAAI,CAAC,IAAI,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC9C,OAAO,CAAC,IAAI,CAAC,sBAAsB,IAAI,iCAAiC,CAAC,CAAA;gBACzE,SAAQ;YACV,CAAC;YAED,6CAA6C;YAC7C,IAAK,IAAI,CAAC,MAA6C,CAAC,aAAa,CAAC,KAAK,IAAI,EAAE,CAAC;gBAChF,SAAQ;YACV,CAAC;YAED,0EAA0E;YAC1E,IAAI,SAAS,GAAuB,OAAO,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAA;YAC9D,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;gBAC3B,IAAI,CAAC;oBACH,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,YAAY,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;oBAChE,SAAS,GAAG,CAAC,CAAC,MAAM,CAAA;gBACtB,CAAC;gBAAC,MAAM,CAAC;oBACP,gEAAgE;gBAClE,CAAC;YACH,CAAC;YAED,MAAM,aAAa,GAAG,aAAa,CACjC,IAAI,CAAC,MAAM,EACX,OAAO,CAAC,WAAW,EACnB,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAC/B,CAMA;YAAC,aAAoD,CAAC,aAAa,CAAC,GAAG,IAAI,CAAA;YAE5E,qEAAqE;YACrE,oEAAoE;YACpE,iDAAiD;YACjD,IAAI,CAAC,MAAM,GAAG,aAAa,CAAA;YAC3B,OAAO,EAAE,CAAA;QACX,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,qEAAqE;YACrE,qCAAqC;YACrC,OAAO,CAAC,IAAI,CAAC,yCAAyC,IAAI,cAAc,EAAE,GAAG,CAAC,CAAA;QAChF,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAA;AAChB,CAAC"}
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Adapter: attribute MCP tool calls flowing through a LangChain JS MCP client.
3
+ *
4
+ * LangChain exposes MCP integration via the `@langchain/mcp-adapters` package
5
+ * (verified against `@langchain/mcp-adapters@1.1.3`). Two APIs are supported:
6
+ *
7
+ * 1. **High-level: `new MultiServerMCPClient({ mcpServers: {...} }).getTools()`**
8
+ * . config-driven, the client owns its internal
9
+ * `@modelcontextprotocol/sdk` Client instances behind `#private` fields.
10
+ * Users never see the Client reference directly; there is no public
11
+ * setter to inject a `wrapMcpClient`-style Proxy replacement. The
12
+ * `getClient(serverName)` getter returns the internal Client, so we
13
+ * can reach it. but we cannot substitute it.
14
+ *
15
+ * 2. **Low-level: `loadMcpTools(serverName, rawClient)`**. accepts a raw
16
+ * `@modelcontextprotocol/sdk` Client directly (verified at
17
+ * `dist/tools.d.ts:28`). For this path, users can construct their own
18
+ * Client, wrap with the existing `wrapMcpClient` helper, and pass it
19
+ * in. No new code required. see the example in
20
+ * `packages/integration/examples/langchain-js/` for the pattern.
21
+ *
22
+ * This adapter handles the high-level `MultiServerMCPClient` path. It walks
23
+ * the multi-client's configured servers, calls `getClient()` on each to reach
24
+ * the internal Client, and monkey-patches `callTool` in place. Structurally
25
+ * the same pattern as `attributeVercelAiSdkMcp` (see D023), with one
26
+ * additional concern documented below.
27
+ *
28
+ * ## Per-call fork propagation
29
+ *
30
+ * LangChain's internal `_callTool` function (verified at `dist/tools.js:384`)
31
+ * supports per-call header changes via `client.fork(headers)`. this creates
32
+ * a new Client instance with different HTTP headers and calls `callTool` on
33
+ * the forked instance, not the original. A naive monkey-patch on the original
34
+ * Client would silently lose attribution for any LangChain user whose tools
35
+ * set per-call headers (the dominant pattern for per-user authentication).
36
+ *
37
+ * The patch therefore ALSO wraps `fork()` when present, so the forked Client
38
+ * is recursively patched before being returned to `_callTool`. This means
39
+ * every forked instance also flows through the atrib interceptor. no silent
40
+ * attribution drop for header-changing tools.
41
+ *
42
+ * ## Idempotency
43
+ *
44
+ * Each patched Client is marked with `Symbol.for('atrib.langchain-mcp.patched')`.
45
+ * Repeat calls to the helper on the same multi-client, and repeat `fork()`
46
+ * calls on already-patched Clients, are no-ops.
47
+ *
48
+ * ## Order sensitivity
49
+ *
50
+ * The helper is SAFE to call AFTER `multiClient.getTools()` because LangChain's
51
+ * tool `func` captures `client` by closure but dereferences `client.callTool`
52
+ * at invocation time (not at tool construction time. see `dist/tools.js:391`).
53
+ * Patching the client after tools are built still takes effect on the next
54
+ * tool invocation.
55
+ *
56
+ * The helper is also safe to call BEFORE `getTools()`, but in that case
57
+ * `multiClient.getClient(serverName)` will lazily initialize the connection
58
+ * if it hasn't happened yet. For predictable startup, most users will call
59
+ * `await multiClient.initializeConnections()` first, then
60
+ * `await attributeLangchainMcp(multiClient, { interceptor })`.
61
+ */
62
+ import type { ToolCallInterceptor } from '../middleware.js';
63
+ /**
64
+ * Minimal structural type for a LangChain-extended MCP Client (from
65
+ * `@langchain/mcp-adapters`'s `connection.ts`). This is the MCP SDK Client
66
+ * with an added `fork()` method for per-call header changes. We don't import
67
+ * from `@langchain/mcp-adapters` to avoid a hard dependency on the LangChain
68
+ * ecosystem from `@atrib/agent`.
69
+ *
70
+ * The `callTool` signature mirrors `@modelcontextprotocol/sdk`'s
71
+ * `Client.callTool(params, resultSchema?, options?)`.
72
+ */
73
+ export interface LangchainMcpClientLike {
74
+ callTool(params: {
75
+ name: string;
76
+ arguments?: Record<string, unknown> | undefined;
77
+ _meta?: Record<string, unknown> | undefined;
78
+ }, resultSchema?: unknown, options?: {
79
+ signal?: AbortSignal | undefined;
80
+ timeout?: number | undefined;
81
+ onprogress?: ((progress: unknown) => void) | undefined;
82
+ }): Promise<unknown>;
83
+ fork?(headers: Record<string, string>): Promise<LangchainMcpClientLike>;
84
+ }
85
+ /**
86
+ * Minimal structural type for a LangChain `MultiServerMCPClient`. Mirrors the
87
+ * subset of the public surface we depend on (verified against
88
+ * `@langchain/mcp-adapters@1.1.3` `dist/client.d.ts`).
89
+ *
90
+ * The `config` getter returns a cloned `ClientConfig` whose `mcpServers`
91
+ * field enumerates every configured server by name. this is how we
92
+ * discover which servers to patch without requiring the caller to list
93
+ * them explicitly.
94
+ */
95
+ export interface LangchainMultiServerMcpClientLike {
96
+ readonly config: {
97
+ mcpServers?: Record<string, unknown> | undefined;
98
+ };
99
+ getClient(serverName: string, options?: unknown): Promise<LangchainMcpClientLike | undefined>;
100
+ }
101
+ /** Options for `attributeLangchainMcp`. */
102
+ export interface AttributeLangchainMcpOptions {
103
+ /** The atrib interceptor that observes tool calls on this client. */
104
+ interceptor: ToolCallInterceptor;
105
+ /**
106
+ * Optional per-server canonical URL map. Used by the interceptor for
107
+ * content_id derivation when emitting Path 2 transaction records (§5.4.5).
108
+ * Keys are the server names as they appear in the multi-client's
109
+ * `mcpServers` config; values are canonical URLs (e.g.
110
+ * `'https://search.example.com'`).
111
+ */
112
+ serverUrls?: Record<string, string>;
113
+ /**
114
+ * Optional list of server names to patch. If omitted, the helper patches
115
+ * every server in the multi-client's `config.mcpServers`. Provide this
116
+ * when you want to selectively attribute only a subset of configured
117
+ * servers (rare. usually you want all of them).
118
+ */
119
+ servers?: string[];
120
+ }
121
+ /**
122
+ * Patch a LangChain `MultiServerMCPClient` so every outbound `tools/call`
123
+ * flows through atrib's interceptor lifecycle.
124
+ *
125
+ * Walks the multi-client's configured servers, calls `getClient(serverName)`
126
+ * on each to reach the internal `@modelcontextprotocol/sdk` Client, and
127
+ * monkey-patches `callTool` (and `fork` when present) in place.
128
+ *
129
+ * Idempotent: calling the helper twice on the same multi-client, or calling
130
+ * it on a multi-client with some already-patched servers, is safe. Repeat
131
+ * invocations on already-patched clients are no-ops.
132
+ *
133
+ * Order independent: this helper can be called BEFORE or AFTER
134
+ * `multiClient.getTools()` because LangChain's tool `func` dereferences
135
+ * `client.callTool` at invocation time, not at build time.
136
+ *
137
+ * Returns the number of clients that were newly patched on this call
138
+ * (excludes clients that were already patched from a previous call).
139
+ *
140
+ * Usage:
141
+ *
142
+ * import { MultiServerMCPClient } from '@langchain/mcp-adapters'
143
+ * import { ChatAnthropic } from '@langchain/anthropic'
144
+ * import { createReactAgent } from '@langchain/langgraph/prebuilt'
145
+ * import { atrib, attributeLangchainMcp } from '@atrib/agent'
146
+ *
147
+ * const interceptor = atrib({
148
+ * creatorKey: process.env.ATRIB_PRIVATE_KEY!,
149
+ * merchantDomain: 'https://merchant.example.com',
150
+ * serverUrls: ['https://search.example.com'],
151
+ * })
152
+ *
153
+ * const multi = new MultiServerMCPClient({
154
+ * mcpServers: {
155
+ * search: { transport: 'http', url: 'https://search.example.com/mcp' },
156
+ * },
157
+ * })
158
+ *
159
+ * await multi.initializeConnections()
160
+ * await attributeLangchainMcp(multi, {
161
+ * interceptor,
162
+ * serverUrls: { search: 'https://search.example.com' },
163
+ * })
164
+ *
165
+ * const tools = await multi.getTools()
166
+ * const agent = createReactAgent({
167
+ * llm: new ChatAnthropic({ model: 'claude-sonnet-4-6' }),
168
+ * tools,
169
+ * })
170
+ */
171
+ export declare function attributeLangchainMcp(multiClient: LangchainMultiServerMcpClientLike, options: AttributeLangchainMcpOptions): Promise<number>;
172
+ //# sourceMappingURL=langchain-mcp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"langchain-mcp.d.ts","sourceRoot":"","sources":["../../src/adapters/langchain-mcp.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAO3D;;;;;;;;;GASG;AACH,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CACN,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAA;QACZ,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;QAC/C,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;KAC5C,EACD,YAAY,CAAC,EAAE,OAAO,EACtB,OAAO,CAAC,EAAE;QACR,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;QAChC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QAC5B,UAAU,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;KACvD,GACA,OAAO,CAAC,OAAO,CAAC,CAAA;IACnB,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAA;CACxE;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,iCAAiC;IAChD,QAAQ,CAAC,MAAM,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;KAAE,CAAA;IACrE,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,sBAAsB,GAAG,SAAS,CAAC,CAAA;CAC9F;AAED,2CAA2C;AAC3C,MAAM,WAAW,4BAA4B;IAC3C,qEAAqE;IACrE,WAAW,EAAE,mBAAmB,CAAA;IAEhC;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAEnC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,iCAAiC,EAC9C,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,MAAM,CAAC,CA2BjB"}
@@ -0,0 +1,145 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * Marker symbol set on a patched client to make repeated patches idempotent.
4
+ */
5
+ const ATRIB_PATCHED = Symbol.for('atrib.langchain-mcp.patched');
6
+ /**
7
+ * Patch a LangChain `MultiServerMCPClient` so every outbound `tools/call`
8
+ * flows through atrib's interceptor lifecycle.
9
+ *
10
+ * Walks the multi-client's configured servers, calls `getClient(serverName)`
11
+ * on each to reach the internal `@modelcontextprotocol/sdk` Client, and
12
+ * monkey-patches `callTool` (and `fork` when present) in place.
13
+ *
14
+ * Idempotent: calling the helper twice on the same multi-client, or calling
15
+ * it on a multi-client with some already-patched servers, is safe. Repeat
16
+ * invocations on already-patched clients are no-ops.
17
+ *
18
+ * Order independent: this helper can be called BEFORE or AFTER
19
+ * `multiClient.getTools()` because LangChain's tool `func` dereferences
20
+ * `client.callTool` at invocation time, not at build time.
21
+ *
22
+ * Returns the number of clients that were newly patched on this call
23
+ * (excludes clients that were already patched from a previous call).
24
+ *
25
+ * Usage:
26
+ *
27
+ * import { MultiServerMCPClient } from '@langchain/mcp-adapters'
28
+ * import { ChatAnthropic } from '@langchain/anthropic'
29
+ * import { createReactAgent } from '@langchain/langgraph/prebuilt'
30
+ * import { atrib, attributeLangchainMcp } from '@atrib/agent'
31
+ *
32
+ * const interceptor = atrib({
33
+ * creatorKey: process.env.ATRIB_PRIVATE_KEY!,
34
+ * merchantDomain: 'https://merchant.example.com',
35
+ * serverUrls: ['https://search.example.com'],
36
+ * })
37
+ *
38
+ * const multi = new MultiServerMCPClient({
39
+ * mcpServers: {
40
+ * search: { transport: 'http', url: 'https://search.example.com/mcp' },
41
+ * },
42
+ * })
43
+ *
44
+ * await multi.initializeConnections()
45
+ * await attributeLangchainMcp(multi, {
46
+ * interceptor,
47
+ * serverUrls: { search: 'https://search.example.com' },
48
+ * })
49
+ *
50
+ * const tools = await multi.getTools()
51
+ * const agent = createReactAgent({
52
+ * llm: new ChatAnthropic({ model: 'claude-sonnet-4-6' }),
53
+ * tools,
54
+ * })
55
+ */
56
+ export async function attributeLangchainMcp(multiClient, options) {
57
+ const { interceptor, serverUrls = {}, servers } = options;
58
+ const configuredServers = servers ?? Object.keys(multiClient.config.mcpServers ?? {});
59
+ let newlyPatched = 0;
60
+ for (const serverName of configuredServers) {
61
+ let client;
62
+ try {
63
+ client = await multiClient.getClient(serverName);
64
+ }
65
+ catch (err) {
66
+ console.warn(`atrib: langchain-mcp could not resolve client for server '${serverName}', skipping`, err);
67
+ continue;
68
+ }
69
+ if (!client) {
70
+ console.warn(`atrib: langchain-mcp server '${serverName}' has no client (not initialized?), skipping`);
71
+ continue;
72
+ }
73
+ if (patchClient(client, interceptor, serverUrls[serverName])) {
74
+ newlyPatched++;
75
+ }
76
+ }
77
+ return newlyPatched;
78
+ }
79
+ /**
80
+ * Patch a single LangChain MCP Client in place. Returns true if the client
81
+ * was newly patched, false if it was already patched from a previous call.
82
+ *
83
+ * Recursive on `fork()`: when the patched `fork` is invoked, the returned
84
+ * forked client is itself passed through `patchClient` before being handed
85
+ * back to LangChain's `_callTool`. This ensures that per-call-header
86
+ * workflows (common for per-user authentication) don't silently lose
87
+ * attribution.
88
+ */
89
+ function patchClient(client, interceptor, serverUrl) {
90
+ if (client[ATRIB_PATCHED] === true) {
91
+ return false;
92
+ }
93
+ const originalCallTool = client.callTool.bind(client);
94
+ const patchedCallTool = async (params, resultSchema, opts) => {
95
+ const toolName = params.name;
96
+ const existingMeta = (params._meta ?? {});
97
+ // §5.4.3: Build outbound _meta via the interceptor. The interceptor
98
+ // returns a record that includes any existing _meta keys plus atrib's
99
+ // own (atrib token, traceparent, tracestate, baggage, X-Atrib-Chain).
100
+ let outboundMeta;
101
+ try {
102
+ outboundMeta = await interceptor.onBeforeToolCall(toolName, existingMeta);
103
+ }
104
+ catch (err) {
105
+ // §5.8 degradation: pass through with the original params on failure
106
+ console.warn('atrib: langchain-mcp onBeforeToolCall failed, passing through', err);
107
+ outboundMeta = undefined;
108
+ }
109
+ // Construct new params rather than mutating. caller's reference is preserved.
110
+ const forwardedParams = outboundMeta !== undefined ? { ...params, _meta: outboundMeta } : params;
111
+ const result = await originalCallTool(forwardedParams, resultSchema, opts);
112
+ // §5.4.4: Update session state from the response _meta.
113
+ try {
114
+ const responseMeta = (result?._meta ?? {});
115
+ const responseOptions = {
116
+ ...(serverUrl !== undefined ? { serverUrl } : {}),
117
+ isError: result?.isError === true,
118
+ };
119
+ interceptor.onAfterToolResponse(toolName, result, responseMeta, responseOptions);
120
+ }
121
+ catch (err) {
122
+ console.warn('atrib: langchain-mcp onAfterToolResponse failed, passing through', err);
123
+ }
124
+ return result;
125
+ };
126
+ client.callTool = patchedCallTool;
127
+ // Fork propagation: LangChain's _callTool creates a new Client via fork()
128
+ // when per-call header changes are requested (dist/tools.js:384). The
129
+ // forked client needs its own callTool patch, or it silently bypasses
130
+ // atrib. We wrap fork so the returned forked client is patched recursively
131
+ // before being handed back to the caller.
132
+ if (typeof client.fork === 'function') {
133
+ const originalFork = client.fork.bind(client);
134
+ const patchedFork = async (headers) => {
135
+ const forkedClient = await originalFork(headers);
136
+ patchClient(forkedClient, interceptor, serverUrl);
137
+ return forkedClient;
138
+ };
139
+ client.fork = patchedFork;
140
+ }
141
+ ;
142
+ client[ATRIB_PATCHED] = true;
143
+ return true;
144
+ }
145
+ //# sourceMappingURL=langchain-mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"langchain-mcp.js","sourceRoot":"","sources":["../../src/adapters/langchain-mcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Adapter: wrap a raw `@modelcontextprotocol/sdk` Client with atrib attribution.
3
+ *
4
+ * The MCP TypeScript SDK exposes a `Client` class with a `callTool(params, ...)`
5
+ * method. This adapter returns a thin proxy around such a client whose
6
+ * `callTool` is wrapped to:
7
+ *
8
+ * 1. Call `interceptor.onBeforeToolCall(toolName, existingMeta)` to get the
9
+ * attribution `_meta` block (traceparent, atrib token, baggage, etc.)
10
+ * 2. Forward the request through the underlying client with the new
11
+ * `_meta` merged into `params._meta`
12
+ * 3. Call `interceptor.onAfterToolResponse(toolName, result, result._meta, ...)`
13
+ * so the interceptor can update its session state, detect transactions,
14
+ * and emit Path 2 records when the tool server has no @atrib/mcp
15
+ *
16
+ * The adapter is intentionally narrow. it only proxies `callTool`. All other
17
+ * client methods (`listTools`, `close`, `connect`, `request`, etc.) are
18
+ * forwarded unchanged. This means a developer can drop the adapter into any
19
+ * existing code that uses the MCP SDK Client directly without changing
20
+ * anything else.
21
+ *
22
+ * If the MCP SDK ever changes the `callTool` signature, the adapter will
23
+ * fail loudly because TypeScript will complain about the proxied parameters
24
+ * shape. see the `MinimalMcpClient` interface below.
25
+ */
26
+ import type { ToolCallInterceptor } from '../middleware.js';
27
+ /**
28
+ * The minimal subset of `@modelcontextprotocol/sdk/client/index.js` Client
29
+ * that this adapter depends on. We do not import the SDK type directly to
30
+ * avoid making `@modelcontextprotocol/sdk` a hard dependency of `@atrib/agent`
31
+ *. the SDK is only needed if you actually use this adapter, not for the
32
+ * core interceptor API.
33
+ */
34
+ export interface MinimalMcpClient {
35
+ callTool(params: {
36
+ name: string;
37
+ arguments?: Record<string, unknown>;
38
+ _meta?: Record<string, unknown>;
39
+ [key: string]: unknown;
40
+ }, resultSchema?: unknown, options?: unknown): Promise<{
41
+ content?: unknown;
42
+ _meta?: Record<string, unknown>;
43
+ isError?: boolean;
44
+ [key: string]: unknown;
45
+ }>;
46
+ }
47
+ /** Options for `wrapMcpClient`. */
48
+ export interface WrapMcpClientOptions {
49
+ /**
50
+ * The canonical URL of the MCP server this client connects to. Used by
51
+ * the interceptor for content_id derivation when emitting Path 2
52
+ * transaction records (the agent fallback path described in spec §5.4.5).
53
+ * If omitted, the interceptor will fall back to the tool's MCP server
54
+ * URL only when it can be derived elsewhere. for many transports
55
+ * (especially stdio) it cannot, and content_id values for transactions
56
+ * will be less specific.
57
+ */
58
+ serverUrl?: string;
59
+ }
60
+ /**
61
+ * Wrap a raw MCP Client so every `callTool` invocation participates in
62
+ * atrib attribution.
63
+ *
64
+ * Returns a Proxy over the client. All non-`callTool` methods are forwarded
65
+ * unchanged via the prototype, so the wrapped client is API-compatible with
66
+ * the original.
67
+ *
68
+ * Usage:
69
+ *
70
+ * import { Client } from '@modelcontextprotocol/sdk/client/index.js'
71
+ * import { atrib, wrapMcpClient } from '@atrib/agent'
72
+ *
73
+ * const interceptor = atrib({
74
+ * creatorKey: process.env.ATRIB_PRIVATE_KEY,
75
+ * merchantDomain: 'https://merchant.example.com',
76
+ * serverUrls: ['https://my-tool.example'],
77
+ * })
78
+ *
79
+ * const rawClient = new Client({ name: 'my-agent', version: '1.0.0' })
80
+ * await rawClient.connect(transport)
81
+ *
82
+ * const client = wrapMcpClient(rawClient, interceptor, {
83
+ * serverUrl: 'https://my-tool.example',
84
+ * })
85
+ *
86
+ * // Use `client` exactly like the raw MCP Client.
87
+ * const result = await client.callTool({ name: 'search', arguments: { q: 'foo' } })
88
+ *
89
+ * // On shutdown:
90
+ * await interceptor.flush()
91
+ * await rawClient.close()
92
+ */
93
+ export declare function wrapMcpClient<C extends MinimalMcpClient>(client: C, interceptor: ToolCallInterceptor, options?: WrapMcpClientOptions): C;
94
+ //# sourceMappingURL=mcp-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-client.d.ts","sourceRoot":"","sources":["../../src/adapters/mcp-client.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAE3D;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CACN,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAA;QACZ,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QACnC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QAC/B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KACvB,EACD,YAAY,CAAC,EAAE,OAAO,EACtB,OAAO,CAAC,EAAE,OAAO,GAChB,OAAO,CAAC;QACT,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QAC/B,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KACvB,CAAC,CAAA;CACH;AAED,mCAAmC;AACnC,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,gBAAgB,EACtD,MAAM,EAAE,CAAC,EACT,WAAW,EAAE,mBAAmB,EAChC,OAAO,GAAE,oBAAyB,GACjC,CAAC,CAiEH"}
@@ -0,0 +1,91 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * Wrap a raw MCP Client so every `callTool` invocation participates in
4
+ * atrib attribution.
5
+ *
6
+ * Returns a Proxy over the client. All non-`callTool` methods are forwarded
7
+ * unchanged via the prototype, so the wrapped client is API-compatible with
8
+ * the original.
9
+ *
10
+ * Usage:
11
+ *
12
+ * import { Client } from '@modelcontextprotocol/sdk/client/index.js'
13
+ * import { atrib, wrapMcpClient } from '@atrib/agent'
14
+ *
15
+ * const interceptor = atrib({
16
+ * creatorKey: process.env.ATRIB_PRIVATE_KEY,
17
+ * merchantDomain: 'https://merchant.example.com',
18
+ * serverUrls: ['https://my-tool.example'],
19
+ * })
20
+ *
21
+ * const rawClient = new Client({ name: 'my-agent', version: '1.0.0' })
22
+ * await rawClient.connect(transport)
23
+ *
24
+ * const client = wrapMcpClient(rawClient, interceptor, {
25
+ * serverUrl: 'https://my-tool.example',
26
+ * })
27
+ *
28
+ * // Use `client` exactly like the raw MCP Client.
29
+ * const result = await client.callTool({ name: 'search', arguments: { q: 'foo' } })
30
+ *
31
+ * // On shutdown:
32
+ * await interceptor.flush()
33
+ * await rawClient.close()
34
+ */
35
+ export function wrapMcpClient(client, interceptor, options = {}) {
36
+ const { serverUrl } = options;
37
+ const wrappedCallTool = async (params, resultSchema, requestOptions) => {
38
+ const toolName = params.name;
39
+ // §5.4.3: Build outbound _meta. The interceptor returns a record that
40
+ // includes any existing _meta keys from the caller plus atrib's own
41
+ // (atrib token, traceparent, tracestate, baggage, X-Atrib-Chain).
42
+ let outboundMeta;
43
+ try {
44
+ const existingMeta = (params._meta ?? {});
45
+ outboundMeta = await interceptor.onBeforeToolCall(toolName, existingMeta);
46
+ }
47
+ catch (err) {
48
+ // §5.8 degradation contract: never let attribution failures break the
49
+ // primary tool call. Fall through to the original params unchanged.
50
+ console.warn('atrib: wrapMcpClient onBeforeToolCall failed, passing through', err);
51
+ outboundMeta = undefined;
52
+ }
53
+ const forwardedParams = outboundMeta !== undefined ? { ...params, _meta: outboundMeta } : params;
54
+ // Forward to the underlying client with the merged _meta.
55
+ const result = await client.callTool(forwardedParams, resultSchema, requestOptions);
56
+ // §5.4.4: Update session state from the response _meta. Also runs Path 1/2
57
+ // transaction detection if the response shape matches a known protocol.
58
+ try {
59
+ const responseMeta = (result?._meta ?? {});
60
+ // exactOptionalPropertyTypes: only include serverUrl when it's set,
61
+ // because the interceptor's option type doesn't accept undefined.
62
+ const responseOptions = {
63
+ ...(serverUrl !== undefined ? { serverUrl } : {}),
64
+ isError: result?.isError === true,
65
+ };
66
+ interceptor.onAfterToolResponse(toolName, result, responseMeta, responseOptions);
67
+ }
68
+ catch (err) {
69
+ console.warn('atrib: wrapMcpClient onAfterToolResponse failed, passing through', err);
70
+ }
71
+ return result;
72
+ };
73
+ // Proxy the client so all other methods (listTools, close, connect, etc.)
74
+ // pass through unchanged. We can't simply spread the client because the
75
+ // SDK's Client class methods rely on `this` being the original instance.
76
+ return new Proxy(client, {
77
+ get(target, prop, receiver) {
78
+ if (prop === 'callTool') {
79
+ return wrappedCallTool;
80
+ }
81
+ const value = Reflect.get(target, prop, receiver);
82
+ // If the property is a function, bind it to the original target so
83
+ // method calls like `client.listTools()` still work correctly.
84
+ if (typeof value === 'function') {
85
+ return value.bind(target);
86
+ }
87
+ return value;
88
+ },
89
+ });
90
+ }
91
+ //# sourceMappingURL=mcp-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-client.js","sourceRoot":"","sources":["../../src/adapters/mcp-client.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAqEtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,UAAU,aAAa,CAC3B,MAAS,EACT,WAAgC,EAChC,UAAgC,EAAE;IAElC,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAA;IAE7B,MAAM,eAAe,GAAiC,KAAK,EACzD,MAAM,EACN,YAAY,EACZ,cAAc,EACd,EAAE;QACF,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAA;QAE5B,sEAAsE;QACtE,oEAAoE;QACpE,kEAAkE;QAClE,IAAI,YAAiD,CAAA;QACrD,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAA4B,CAAA;YACpE,YAAY,GAAG,MAAM,WAAW,CAAC,gBAAgB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAA;QAC3E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,sEAAsE;YACtE,oEAAoE;YACpE,OAAO,CAAC,IAAI,CAAC,+DAA+D,EAAE,GAAG,CAAC,CAAA;YAClF,YAAY,GAAG,SAAS,CAAA;QAC1B,CAAC;QAED,MAAM,eAAe,GACnB,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,MAAM,CAAA;QAE1E,0DAA0D;QAC1D,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,eAAe,EAAE,YAAY,EAAE,cAAc,CAAC,CAAA;QAEnF,2EAA2E;QAC3E,wEAAwE;QACxE,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE,CAA4B,CAAA;YACrE,oEAAoE;YACpE,kEAAkE;YAClE,MAAM,eAAe,GAAG;gBACtB,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjD,OAAO,EAAG,MAAkC,EAAE,OAAO,KAAK,IAAI;aAC/D,CAAA;YACD,WAAW,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,eAAe,CAAC,CAAA;QAClF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,kEAAkE,EAAE,GAAG,CAAC,CAAA;QACvF,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC,CAAA;IAED,0EAA0E;IAC1E,wEAAwE;IACxE,yEAAyE;IACzE,OAAO,IAAI,KAAK,CAAC,MAAM,EAAE;QACvB,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ;YACxB,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;gBACxB,OAAO,eAAe,CAAA;YACxB,CAAC;YACD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;YACjD,mEAAmE;YACnE,+DAA+D;YAC/D,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;gBAChC,OAAQ,KAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;YACzC,CAAC;YACD,OAAO,KAAK,CAAA;QACd,CAAC;KACF,CAAC,CAAA;AACJ,CAAC"}