@datagrout/conduit 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.
package/README.md ADDED
@@ -0,0 +1,213 @@
1
+ # DataGrout Conduit — TypeScript SDK
2
+
3
+ Production-ready MCP client with mTLS identity, OAuth 2.1, semantic discovery, and cost tracking.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @datagrout/conduit
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { Client } from '@datagrout/conduit';
15
+
16
+ const client = new Client('https://gateway.datagrout.ai/servers/{uuid}/mcp');
17
+ await client.connect();
18
+
19
+ const tools = await client.listTools();
20
+ const result = await client.callTool('salesforce@1/get_lead@1', { id: '123' });
21
+
22
+ await client.disconnect();
23
+ ```
24
+
25
+ ## Authentication
26
+
27
+ ### Bearer Token
28
+
29
+ ```typescript
30
+ const client = new Client({
31
+ url: 'https://gateway.datagrout.ai/servers/{uuid}/mcp',
32
+ auth: { bearer: 'your-access-token' },
33
+ });
34
+ ```
35
+
36
+ ### OAuth 2.1 (client_credentials)
37
+
38
+ ```typescript
39
+ const client = new Client({
40
+ url: 'https://gateway.datagrout.ai/servers/{uuid}/mcp',
41
+ auth: {
42
+ clientCredentials: {
43
+ clientId: 'your-client-id',
44
+ clientSecret: 'your-client-secret',
45
+ },
46
+ },
47
+ });
48
+ ```
49
+
50
+ The SDK automatically fetches, caches, and refreshes JWTs before they expire.
51
+
52
+ ### mTLS (Mutual TLS)
53
+
54
+ After bootstrapping, the client certificate handles authentication at the TLS layer — no tokens needed.
55
+
56
+ ```typescript
57
+ import { Client, ConduitIdentity } from '@datagrout/conduit';
58
+
59
+ // Auto-discover from env vars, CONDUIT_IDENTITY_DIR, or ~/.conduit/
60
+ const client = new Client({
61
+ url: 'https://gateway.datagrout.ai/servers/{uuid}/mcp',
62
+ identityAuto: true,
63
+ });
64
+
65
+ // Explicit identity from files
66
+ const identity = ConduitIdentity.fromPaths('certs/client.pem', 'certs/client_key.pem');
67
+ const client = new Client({ url: '...', identity });
68
+
69
+ // Multiple agents on one machine
70
+ const client = new Client({
71
+ url: '...',
72
+ identityDir: '/opt/agents/agent-a/.conduit',
73
+ identityAuto: true,
74
+ });
75
+ ```
76
+
77
+ #### Identity Auto-Discovery Order
78
+
79
+ 1. `identityDir` option (if provided)
80
+ 2. `CONDUIT_MTLS_CERT` + `CONDUIT_MTLS_KEY` environment variables (inline PEM)
81
+ 3. `CONDUIT_IDENTITY_DIR` environment variable (directory path)
82
+ 4. `~/.conduit/identity.pem` + `~/.conduit/identity_key.pem`
83
+ 5. `.conduit/` relative to the current working directory
84
+
85
+ For DataGrout URLs (`*.datagrout.ai`), auto-discovery runs silently even without `identityAuto: true`.
86
+
87
+ #### Bootstrapping an mTLS Identity
88
+
89
+ One-call provisioning — generates a keypair, registers with the DataGrout CA, saves certs locally, and returns a connected client. After this, the token is never needed again.
90
+
91
+ ```typescript
92
+ import { Client } from '@datagrout/conduit';
93
+
94
+ // One-call bootstrap with a one-time access token
95
+ const client = await Client.bootstrapIdentity({
96
+ url: 'https://gateway.datagrout.ai/servers/{uuid}/mcp',
97
+ authToken: 'your-one-time-token',
98
+ name: 'my-agent',
99
+ });
100
+
101
+ // All subsequent runs: mTLS auto-discovered from ~/.conduit/
102
+ const client = new Client('https://gateway.datagrout.ai/servers/{uuid}/mcp');
103
+ await client.connect();
104
+ ```
105
+
106
+ You can also use the registration functions directly:
107
+
108
+ ```typescript
109
+ import { generateKeypair, registerIdentity, saveIdentityToDir } from '@datagrout/conduit';
110
+
111
+ const { publicKeyPem, privateKeyPem } = generateKeypair();
112
+ const reg = await registerIdentity(publicKeyPem, {
113
+ authToken: 'your-access-token',
114
+ name: 'my-agent',
115
+ });
116
+ await saveIdentityToDir(reg.certPem, privateKeyPem, '~/.conduit', reg.caCertPem);
117
+ ```
118
+
119
+ ## Semantic Discovery & Intelligent Interface
120
+
121
+ For DataGrout URLs, the Intelligent Interface is enabled by default — `listTools()` returns only `data-grout@1/discovery.discover@1` and `data-grout@1/discovery.perform@1`. Agents use semantic search instead of enumerating raw integrations:
122
+
123
+ ```typescript
124
+ // Intelligent Interface auto-enabled for DG URLs
125
+ const client = new Client('https://gateway.datagrout.ai/servers/{uuid}/mcp');
126
+ await client.connect();
127
+
128
+ // Semantic search across all connected integrations
129
+ const results = await client.discover({ query: 'find unpaid invoices', limit: 5 });
130
+
131
+ // Direct execution with cost tracking
132
+ const result = await client.perform('salesforce@1/get_lead@1', { id: '123' });
133
+ ```
134
+
135
+ Pass `useIntelligentInterface: false` to opt out and see all raw tools.
136
+
137
+ ## Cost Tracking
138
+
139
+ Every tool call returns a receipt with credit usage:
140
+
141
+ ```typescript
142
+ import { extractMeta } from '@datagrout/conduit';
143
+
144
+ const result = await client.callTool('salesforce@1/get_lead@1', { id: '123' });
145
+ const meta = extractMeta(result);
146
+
147
+ if (meta) {
148
+ console.log(`Credits: ${meta.receipt.netCredits}`);
149
+ console.log(`Savings: ${meta.receipt.savings}`);
150
+ }
151
+ ```
152
+
153
+ ## Transports
154
+
155
+ ```typescript
156
+ // MCP (default) — full MCP protocol over Streamable HTTP
157
+ const client = new Client({ url });
158
+
159
+ // JSONRPC — lightweight, stateless, same tools and auth
160
+ const client = new Client({ url, transport: 'jsonrpc' });
161
+ ```
162
+
163
+ ## API Reference
164
+
165
+ ### Client Options
166
+
167
+ ```typescript
168
+ new Client(options: {
169
+ url: string;
170
+ auth?: { bearer?: string; apiKey?: string; clientCredentials?: {...} };
171
+ transport?: 'mcp' | 'jsonrpc';
172
+ useIntelligentInterface?: boolean;
173
+ identity?: ConduitIdentity;
174
+ identityAuto?: boolean;
175
+ identityDir?: string;
176
+ disableMtls?: boolean;
177
+ timeout?: number;
178
+ });
179
+ ```
180
+
181
+ ### Standard MCP Methods
182
+
183
+ | Method | Description |
184
+ |---|---|
185
+ | `connect()` | Initialize connection |
186
+ | `disconnect()` | Close connection |
187
+ | `listTools()` | List available tools |
188
+ | `callTool(name, args)` | Execute a tool |
189
+ | `listResources()` | List resources |
190
+ | `readResource(uri)` | Read a resource |
191
+ | `listPrompts()` | List prompts |
192
+ | `getPrompt(name, args)` | Get a prompt |
193
+
194
+ ### DataGrout Extensions
195
+
196
+ | Method | Description |
197
+ |---|---|
198
+ | `discover(options)` | Semantic tool search |
199
+ | `perform(options)` | Direct tool execution with tracking |
200
+ | `performBatch(calls)` | Parallel tool execution |
201
+ | `guide(options)` | Guided multi-step workflow |
202
+ | `flowInto(options)` | Workflow orchestration |
203
+ | `prismFocus(options)` | Type transformation |
204
+ | `estimateCost(tool, args)` | Pre-execution credit estimate |
205
+
206
+ ## Requirements
207
+
208
+ - Node.js 18+
209
+ - TypeScript 5.0+ (for TypeScript users)
210
+
211
+ ## License
212
+
213
+ MIT
@@ -0,0 +1,122 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
6
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
7
+ }) : x)(function(x) {
8
+ if (typeof require !== "undefined") return require.apply(this, arguments);
9
+ throw Error('Dynamic require of "' + x + '" is not supported');
10
+ });
11
+ var __esm = (fn, res) => function __init() {
12
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
13
+ };
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+
28
+ // src/oauth.ts
29
+ var oauth_exports = {};
30
+ __export(oauth_exports, {
31
+ OAuthTokenProvider: () => OAuthTokenProvider,
32
+ deriveTokenEndpoint: () => deriveTokenEndpoint
33
+ });
34
+ function deriveTokenEndpoint(mcpUrl) {
35
+ const mcpIdx = mcpUrl.indexOf("/mcp");
36
+ const base = mcpIdx !== -1 ? mcpUrl.slice(0, mcpIdx) : mcpUrl.replace(/\/$/, "");
37
+ return `${base}/oauth/token`;
38
+ }
39
+ var OAuthTokenProvider;
40
+ var init_oauth = __esm({
41
+ "src/oauth.ts"() {
42
+ OAuthTokenProvider = class {
43
+ clientId;
44
+ clientSecret;
45
+ tokenEndpoint;
46
+ scope;
47
+ cached = null;
48
+ fetchPromise = null;
49
+ constructor(opts) {
50
+ this.clientId = opts.clientId;
51
+ this.clientSecret = opts.clientSecret;
52
+ this.tokenEndpoint = opts.tokenEndpoint;
53
+ this.scope = opts.scope;
54
+ }
55
+ /**
56
+ * Return the current bearer token, fetching a fresh one if necessary.
57
+ * Concurrent callers share a single in-flight fetch.
58
+ */
59
+ async getToken() {
60
+ const now = Date.now();
61
+ if (this.cached && this.cached.expiresAt - now > 6e4) {
62
+ return this.cached.accessToken;
63
+ }
64
+ if (!this.fetchPromise) {
65
+ this.fetchPromise = this.fetchToken().finally(() => {
66
+ this.fetchPromise = null;
67
+ });
68
+ }
69
+ const cached = await this.fetchPromise;
70
+ return cached.accessToken;
71
+ }
72
+ /** Invalidate the cached token (e.g. after receiving a 401). */
73
+ invalidate() {
74
+ this.cached = null;
75
+ }
76
+ // ─── Private ───────────────────────────────────────────────────────────────
77
+ async fetchToken() {
78
+ const body = new URLSearchParams({
79
+ grant_type: "client_credentials",
80
+ client_id: this.clientId,
81
+ client_secret: this.clientSecret
82
+ });
83
+ if (this.scope) {
84
+ body.set("scope", this.scope);
85
+ }
86
+ let response;
87
+ try {
88
+ response = await fetch(this.tokenEndpoint, {
89
+ method: "POST",
90
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
91
+ body: body.toString()
92
+ });
93
+ } catch (err) {
94
+ throw new Error(`OAuth token request failed: ${err}`);
95
+ }
96
+ if (!response.ok) {
97
+ const text = await response.text().catch(() => "");
98
+ throw new Error(`OAuth token endpoint returned ${response.status}: ${text}`);
99
+ }
100
+ const data = await response.json();
101
+ const expiresIn = data.expires_in ?? 3600;
102
+ const cached = {
103
+ accessToken: data.access_token,
104
+ expiresAt: Date.now() + expiresIn * 1e3
105
+ };
106
+ this.cached = cached;
107
+ return cached;
108
+ }
109
+ };
110
+ }
111
+ });
112
+
113
+ export {
114
+ __require,
115
+ __esm,
116
+ __export,
117
+ __toCommonJS,
118
+ deriveTokenEndpoint,
119
+ OAuthTokenProvider,
120
+ oauth_exports,
121
+ init_oauth
122
+ };