@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 +213 -0
- package/dist/chunk-RED5DKGI.mjs +122 -0
- package/dist/index.d.mts +1242 -0
- package/dist/index.d.ts +1242 -0
- package/dist/index.js +1736 -0
- package/dist/index.mjs +1597 -0
- package/dist/oauth-OMPWCI2X.mjs +10 -0
- package/package.json +63 -0
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
|
+
};
|