@narrative.io/data-collaboration-sdk-ts 3.4.0 → 3.5.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.
@@ -25,6 +25,13 @@ export interface McpServerConfig {
25
25
  alias: string;
26
26
  url: string;
27
27
  description?: string;
28
+ /**
29
+ * Reference to a registered external-MCP OAuth connection (from `POST /mcp-connections`,
30
+ * see {@link McpConnectionsApi}). Set for an external server that requires user
31
+ * authorization — the platform resolves and refreshes the connection's bearer token
32
+ * server-side; it never appears in the payload. Omit for public or Narrative-owned MCPs.
33
+ */
34
+ connection_id?: string;
28
35
  }
29
36
  export interface ToolSpec {
30
37
  /** Caller-declared tools must NOT contain a dash. MCP tools' bare name. */
package/build/index.d.ts CHANGED
@@ -30,6 +30,8 @@ export * from "./jobs";
30
30
  export { default as JsonBigNumber } from "./json-big-number";
31
31
  export * from "./mappings";
32
32
  export * from "./mappings/types";
33
+ export * from "./mcp-connections";
34
+ export * from "./mcp-connections/types";
33
35
  export * from "./model-inference";
34
36
  export * from "./model-training";
35
37
  export * from "./models";
@@ -77,6 +79,7 @@ import { HealthCheckApi } from "./health";
77
79
  import { InstallationsApi } from "./installations";
78
80
  import { JobsApi } from "./jobs";
79
81
  import { MappingsApi } from "./mappings";
82
+ import { McpConnectionsApi } from "./mcp-connections";
80
83
  import { ModelInferenceApi } from "./model-inference";
81
84
  import { ModelTrainingApi } from "./model-training";
82
85
  import { ModelsApi } from "./models";
@@ -92,6 +95,6 @@ import { WhoAmIApi } from "./whoami";
92
95
  import { WorkflowsApi } from "./workflows";
93
96
  declare class NarrativeApi extends BaseApi {
94
97
  }
95
- interface NarrativeApi extends BaseApi, HealthCheckApi, AccessTokensApi, DataPlaneApi, DatasetApi, RosettaStoneApi, AttributeApi, AttributeApiV2, PingApi, CompanyInfoApi, InstallationsApi, ConnectionsApi, UploadsApi, ResourceApi, NqlApi, DataStreamsApi, ForecastApi, ContractsApi, AuthenticationApi, MappingsApi, AccessRulesApi, AppsApi, SubscriptionsApi, JobsApi, QueriesApi, ViewsApi, ModelsApi, ModelTrainingApi, ModelInferenceApi, EncryptionMaterialApi, WhoAmIApi, WorkflowsApi, ComputePoolsApi, AgentsApi, CompaniesApi {
98
+ interface NarrativeApi extends BaseApi, HealthCheckApi, AccessTokensApi, DataPlaneApi, DatasetApi, RosettaStoneApi, AttributeApi, AttributeApiV2, PingApi, CompanyInfoApi, InstallationsApi, ConnectionsApi, UploadsApi, ResourceApi, NqlApi, DataStreamsApi, ForecastApi, ContractsApi, AuthenticationApi, MappingsApi, AccessRulesApi, AppsApi, SubscriptionsApi, JobsApi, QueriesApi, ViewsApi, ModelsApi, ModelTrainingApi, ModelInferenceApi, EncryptionMaterialApi, WhoAmIApi, WorkflowsApi, ComputePoolsApi, AgentsApi, CompaniesApi, McpConnectionsApi {
96
99
  }
97
100
  export { NarrativeApi };
package/build/index.js CHANGED
@@ -31,6 +31,8 @@ export * from "./jobs";
31
31
  export { default as JsonBigNumber } from "./json-big-number";
32
32
  export * from "./mappings";
33
33
  export * from "./mappings/types";
34
+ export * from "./mcp-connections";
35
+ export * from "./mcp-connections/types";
34
36
  export * from "./model-inference";
35
37
  export * from "./model-training";
36
38
  export * from "./models";
@@ -78,6 +80,7 @@ import { HealthCheckApi } from "./health";
78
80
  import { InstallationsApi } from "./installations";
79
81
  import { JobsApi } from "./jobs";
80
82
  import { MappingsApi } from "./mappings";
83
+ import { McpConnectionsApi } from "./mcp-connections";
81
84
  import { ModelInferenceApi } from "./model-inference";
82
85
  import { ModelTrainingApi } from "./model-training";
83
86
  import { ModelsApi } from "./models";
@@ -130,5 +133,6 @@ applyMixins(NarrativeApi, [
130
133
  ComputePoolsApi,
131
134
  AgentsApi,
132
135
  CompaniesApi,
136
+ McpConnectionsApi,
133
137
  ]);
134
138
  export { NarrativeApi };
@@ -0,0 +1,38 @@
1
+ import { BaseApi } from "../base-api";
2
+ import type { CreateMcpConnectionRequest, ListMcpConnectionsResponse, McpConnectionCreatedResponse, McpConnectionSummary } from "./types";
3
+ /**
4
+ * `McpConnectionsApi` wraps the external-MCP connection endpoints. A connection is a per-user
5
+ * OAuth link to an external (non-Narrative) MCP server; once `connected`, an agent conversation
6
+ * can reference it by id via `mcp_servers[].connection_id`. Tokens are never returned by the API.
7
+ *
8
+ * The OAuth callback (`GET /mcp-connections/callback`) is a browser redirect, not an SDK call, so
9
+ * it is intentionally not wrapped here.
10
+ */
11
+ export declare class McpConnectionsApi extends BaseApi {
12
+ /**
13
+ * Lists the calling user's MCP connections with their status. Credential-free.
14
+ * @returns {Promise<ListMcpConnectionsResponse>} The caller's connections (empty if none).
15
+ */
16
+ listMcpConnections(): Promise<ListMcpConnectionsResponse>;
17
+ /**
18
+ * Begins connecting an external MCP server (OAuth discovery + dynamic client registration).
19
+ * @param {CreateMcpConnectionRequest} request - The server `url` and routing `alias`.
20
+ * @returns {Promise<McpConnectionCreatedResponse>} The pending `connection_id` and the
21
+ * `authorization_url` the user must visit to consent.
22
+ */
23
+ createMcpConnection(request: CreateMcpConnectionRequest): Promise<McpConnectionCreatedResponse>;
24
+ /**
25
+ * Fetches one of the calling user's connections.
26
+ * @param {string} connectionId - The connection id.
27
+ * @returns {Promise<McpConnectionSummary>} The connection's status view. Rejects with 404 if it
28
+ * does not exist or belongs to another user.
29
+ */
30
+ getMcpConnection(connectionId: string): Promise<McpConnectionSummary>;
31
+ /**
32
+ * Deletes one of the calling user's connections and its stored tokens.
33
+ * @param {string} connectionId - The connection id.
34
+ * @returns {Promise<void>} Resolves when deleted. Rejects with 404 if it does not exist or
35
+ * belongs to another user.
36
+ */
37
+ deleteMcpConnection(connectionId: string): Promise<void>;
38
+ }
@@ -0,0 +1,46 @@
1
+ import { BaseApi } from "../base-api";
2
+ const resourceName = "mcp-connections";
3
+ /**
4
+ * `McpConnectionsApi` wraps the external-MCP connection endpoints. A connection is a per-user
5
+ * OAuth link to an external (non-Narrative) MCP server; once `connected`, an agent conversation
6
+ * can reference it by id via `mcp_servers[].connection_id`. Tokens are never returned by the API.
7
+ *
8
+ * The OAuth callback (`GET /mcp-connections/callback`) is a browser redirect, not an SDK call, so
9
+ * it is intentionally not wrapped here.
10
+ */
11
+ export class McpConnectionsApi extends BaseApi {
12
+ /**
13
+ * Lists the calling user's MCP connections with their status. Credential-free.
14
+ * @returns {Promise<ListMcpConnectionsResponse>} The caller's connections (empty if none).
15
+ */
16
+ async listMcpConnections() {
17
+ return await this.get(resourceName);
18
+ }
19
+ /**
20
+ * Begins connecting an external MCP server (OAuth discovery + dynamic client registration).
21
+ * @param {CreateMcpConnectionRequest} request - The server `url` and routing `alias`.
22
+ * @returns {Promise<McpConnectionCreatedResponse>} The pending `connection_id` and the
23
+ * `authorization_url` the user must visit to consent.
24
+ */
25
+ async createMcpConnection(request) {
26
+ return await this.post(resourceName, request);
27
+ }
28
+ /**
29
+ * Fetches one of the calling user's connections.
30
+ * @param {string} connectionId - The connection id.
31
+ * @returns {Promise<McpConnectionSummary>} The connection's status view. Rejects with 404 if it
32
+ * does not exist or belongs to another user.
33
+ */
34
+ async getMcpConnection(connectionId) {
35
+ return await this.get(`${resourceName}/${connectionId}`);
36
+ }
37
+ /**
38
+ * Deletes one of the calling user's connections and its stored tokens.
39
+ * @param {string} connectionId - The connection id.
40
+ * @returns {Promise<void>} Resolves when deleted. Rejects with 404 if it does not exist or
41
+ * belongs to another user.
42
+ */
43
+ async deleteMcpConnection(connectionId) {
44
+ await this.delete(`${resourceName}/${connectionId}`);
45
+ }
46
+ }
@@ -0,0 +1,22 @@
1
+ export type McpConnectionStatus = "pending" | "connected" | "error";
2
+ export interface CreateMcpConnectionRequest {
3
+ url: string;
4
+ alias: string;
5
+ }
6
+ export interface McpConnectionCreatedResponse {
7
+ connection_id: string;
8
+ authorization_url: string;
9
+ }
10
+ export interface McpConnectionSummary {
11
+ connection_id: string;
12
+ server_url: string;
13
+ alias: string;
14
+ authorization_server: string;
15
+ status: McpConnectionStatus;
16
+ expires_at?: string;
17
+ created_at: string;
18
+ updated_at: string;
19
+ }
20
+ export interface ListMcpConnectionsResponse {
21
+ connections: McpConnectionSummary[];
22
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -36,8 +36,8 @@ export interface Select {
36
36
  from: Table[] | Raw;
37
37
  where: BooleanExpression | Raw | null;
38
38
  }
39
- export type Output = ColumnRef | Lit | Raw | ast.NqlPlaceholder<ast.NqlAstType>;
40
- export type Expression = BooleanExpression | ColumnRef | Lit | Raw | ast.NqlPlaceholder<ast.NqlAstType>;
39
+ export type Output = ColumnRef | Lit | Func | Raw | ast.NqlPlaceholder<ast.NqlAstType>;
40
+ export type Expression = BooleanExpression | ColumnRef | Lit | Func | Raw | ast.NqlPlaceholder<ast.NqlAstType>;
41
41
  export interface Raw {
42
42
  type: "nql";
43
43
  as?: string;
@@ -99,6 +99,12 @@ export interface Lit {
99
99
  value_type: ast.NqlSimpleType;
100
100
  value: string | null;
101
101
  }
102
+ export interface Func {
103
+ type: "function";
104
+ as?: string;
105
+ name: string;
106
+ args: Expression[];
107
+ }
102
108
  export interface And {
103
109
  type: "and";
104
110
  as?: string;
@@ -175,13 +175,52 @@ export function parseDeduplication(n) {
175
175
  return n.qualify !== null ? nqlOrFail(n.qualify) : null;
176
176
  }
177
177
  export function parseExpression(n) {
178
- const parse = oneOf(parseBooleanExpression, parseColumnRef, parseLit, parseStringExpression, parsePlaceholder);
178
+ const parse = oneOf(parseBooleanExpression, parseColumnRef, parseLit, parseFunction, parseStringExpression, parsePlaceholder);
179
179
  return parse(n);
180
180
  }
181
181
  export function parseOutput(n) {
182
- const parse = oneOf(parseColumnRef, parseLit, parsePlaceholder);
182
+ const parse = oneOf(parseColumnRef, parseLit, parseFunction, parsePlaceholder);
183
183
  return parse(n);
184
184
  }
185
+ function escapeRegExp(s) {
186
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
187
+ }
188
+ function parseFunction(n) {
189
+ if (n.type !== "function") {
190
+ return nqlOrFail(n);
191
+ }
192
+ // Only structure functions whose original text is the standard call
193
+ // syntax `NAME(arg, arg, ...)`, optionally followed by an alias — the
194
+ // server serializes `expr AS alias` as the inner node with the alias
195
+ // wrapper's `as`/`nql` merged on top, so an aliased function's `nql`
196
+ // span reads `NAME(args) AS alias`. Functions with special syntax —
197
+ // EXTRACT(unit FROM x), CAST(x AS type), TRIM(BOTH ' ' FROM x) — cannot
198
+ // be regenerated from name + args and must stay Raw passthroughs, or
199
+ // compiling would emit comma syntax and corrupt the query. The match
200
+ // tolerates whitespace differences (and a missing/implicit AS keyword)
201
+ // because compiling name + args back to `NAME(arg, arg, ...) AS "alias"`
202
+ // only ever normalizes those, never changes semantics.
203
+ const argTexts = n.args.map((a) => "nql" in a ? a.nql : undefined);
204
+ if (argTexts.some((t) => t === undefined)) {
205
+ return nqlOrFail(n);
206
+ }
207
+ const aliasSuffix = n.as !== undefined
208
+ ? `(?:\\s+(?:AS\\s+)?(?:"${escapeRegExp(n.as)}"|${escapeRegExp(n.as)}))?`
209
+ : "";
210
+ const standardCall = new RegExp(`^${escapeRegExp(n.name)}\\s*\\(\\s*${argTexts
211
+ .map(escapeRegExp)
212
+ .join("\\s*,\\s*")}\\s*\\)${aliasSuffix}$`, "i");
213
+ if (!standardCall.test(n.nql)) {
214
+ return nqlOrFail(n);
215
+ }
216
+ const func = {
217
+ type: "function",
218
+ as: n.as,
219
+ name: n.name,
220
+ args: n.args.map(parseExpression),
221
+ };
222
+ return func;
223
+ }
185
224
  function parseBooleanExpression(n) {
186
225
  switch (n.type) {
187
226
  case "binary_op": {
@@ -586,10 +625,18 @@ function oneOf(...fs) {
586
625
  function nqlOrFail(n) {
587
626
  if ("nql" in n) {
588
627
  const as = "as" in n ? n.as : undefined;
628
+ // The server serializes `expr AS alias` by merging the alias wrapper's
629
+ // `as`/`nql` onto the inner node, so an aliased node's span carries a
630
+ // trailing ` AS alias`. Strip it: a Raw node's `nql` holds the bare
631
+ // expression and `as` holds the alias — otherwise compiling the node
632
+ // re-appends the alias and emits a corrupt double alias.
633
+ const nqlText = as !== undefined
634
+ ? n.nql.replace(new RegExp(`\\s+(?:AS\\s+)?(?:"${escapeRegExp(as)}"|${escapeRegExp(as)})\\s*$`, "i"), "")
635
+ : n.nql;
589
636
  const nql = {
590
637
  type: "nql",
591
638
  as,
592
- nql: n.nql,
639
+ nql: nqlText,
593
640
  };
594
641
  return nql;
595
642
  }
@@ -28,6 +28,8 @@ function findTraversalTargets(node) {
28
28
  }
29
29
  return inTargets;
30
30
  }
31
+ case "function":
32
+ return node.args;
31
33
  case "deduplication":
32
34
  return node.expressions;
33
35
  case "select": {
@@ -214,6 +214,8 @@ export function compileOutput(n, dsMap) {
214
214
  return compileDatasetColumnRef(n.datasetId, n.column, n.as, dsMap);
215
215
  case "lit":
216
216
  return compileLit(n.value, n.value_type, n.as, false);
217
+ case "function":
218
+ return compileFunction(n, dsMap, false);
217
219
  case "raw_ref":
218
220
  return compileRawRef(n.nql, n.as);
219
221
  case "nql":
@@ -253,6 +255,8 @@ export function compileExpression(n, dsMap) {
253
255
  return compileLike(n.value, n.pattern, n.negated, n.as, dsMap);
254
256
  case "lit":
255
257
  return compileLit(n.value, n.value_type, n.as);
258
+ case "function":
259
+ return compileFunction(n, dsMap);
256
260
  case "not":
257
261
  return compileNot(n.operand, n.as, dsMap);
258
262
  case "or":
@@ -355,6 +359,10 @@ function compilePostfixUnaryOperator(op, operand, as, dsMap) {
355
359
  function compileRawRef(nql, as) {
356
360
  return aliased(nql, as);
357
361
  }
362
+ function compileFunction(n, dsMap, parens) {
363
+ const args = n.args.map((arg) => compileExpression(arg, dsMap)).join(", ");
364
+ return aliased(`${n.name}(${args})`, n.as, parens);
365
+ }
358
366
  export function compilePlaceholder(n) {
359
367
  switch (n.expectedType) {
360
368
  case "literal":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@narrative.io/data-collaboration-sdk-ts",
3
- "version": "3.4.0",
3
+ "version": "3.5.1",
4
4
  "main": "build/index.js",
5
5
  "repository": "github:narrative-io/data-collaboration-sdk-ts",
6
6
  "source": "src/index.ts",
@@ -43,7 +43,7 @@
43
43
  },
44
44
  "dependencies": {
45
45
  "zod": "4.4.3",
46
- "bignumber.js": "11.1.4"
46
+ "bignumber.js": "11.1.5"
47
47
  },
48
48
  "overrides": {
49
49
  "semver@<7.5.2": "7.5.2"