@audienti/cli 0.1.31 → 0.1.33
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/.mcp.json +13 -0
- package/CHANGELOG.md +13 -0
- package/README.md +88 -5
- package/bin/audienti-mcp.js +5 -0
- package/package.json +4 -2
- package/skills/audienti/SKILL.md +49 -7
- package/src/api-client.js +25 -0
- package/src/cli.js +291 -5
- package/src/mcp.js +103 -0
package/.mcp.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"audienti": {
|
|
4
|
+
"title": "Audienti",
|
|
5
|
+
"description": "Bridge MCP clients to Audienti's app-hosted /mcp endpoint for accounts, plays, prospects, lists, analytics, and operator workflows.",
|
|
6
|
+
"cwd": ".",
|
|
7
|
+
"command": "node",
|
|
8
|
+
"args": [
|
|
9
|
+
"./bin/audienti-mcp.js"
|
|
10
|
+
]
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,19 @@ All notable changes to the Audienti CLI are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.1.33] - 2026-08-13
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Add `audienti motions quick-start --url <company_url>` for URL-generated quick-start drafts, with optional `--wait --confirm` motion setup and discovery launch.
|
|
12
|
+
|
|
13
|
+
## [0.1.32] - 2026-08-11
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Add `audienti auth login` for browser-based local authentication through a loopback callback.
|
|
18
|
+
- Add the `audienti-mcp` stdio bridge and Codex MCP manifest for Audienti's app-hosted MCP endpoint, including stage analytics and cohort list creation.
|
|
19
|
+
|
|
7
20
|
## [0.1.31] - 2026-08-10
|
|
8
21
|
|
|
9
22
|
### Added
|
package/README.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# Audienti CLI
|
|
2
2
|
|
|
3
3
|
Audienti CLI is the agent-first command-line client for the Audienti production
|
|
4
|
-
API
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
API and the local bridge for Audienti's app-hosted MCP endpoint. It lets local
|
|
5
|
+
coding agents and operators inspect accounts,
|
|
6
|
+
create and manage plays, import prospects, build lists, manage task reminders,
|
|
7
|
+
and work supported operator flows.
|
|
7
8
|
|
|
8
9
|
## Install
|
|
9
10
|
|
|
@@ -25,16 +26,26 @@ For one-off use, run `npx @audienti/cli --help`.
|
|
|
25
26
|
|
|
26
27
|
## Authenticate
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
Authenticate through the browser, then configure the default account context:
|
|
29
30
|
|
|
30
31
|
```bash
|
|
31
|
-
audienti auth
|
|
32
|
+
audienti auth login
|
|
32
33
|
audienti accounts list --json
|
|
33
34
|
audienti accounts select <acct_id>
|
|
34
35
|
audienti users list
|
|
35
36
|
audienti users select <account_user_id|email|name|me>
|
|
36
37
|
```
|
|
37
38
|
|
|
39
|
+
`audienti auth login` opens Audienti in your browser, creates an API token from
|
|
40
|
+
your signed-in web session, sends it back to a temporary `127.0.0.1` callback,
|
|
41
|
+
and stores it in the local CLI config after validating it.
|
|
42
|
+
|
|
43
|
+
If you already have a token, you can still configure it directly:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
audienti auth token <token>
|
|
47
|
+
```
|
|
48
|
+
|
|
38
49
|
The CLI writes its local configuration to `~/.config/audienti/config.json` with
|
|
39
50
|
owner-only permissions. Do not place a production token in an agent prompt,
|
|
40
51
|
repository file, issue, or CI secret.
|
|
@@ -55,6 +66,71 @@ Use `--json` whenever another program or agent will consume the result. Inspect
|
|
|
55
66
|
the target state before mutations, and use the command-specific help before
|
|
56
67
|
creating, changing, or deleting data.
|
|
57
68
|
|
|
69
|
+
## MCP
|
|
70
|
+
|
|
71
|
+
Audienti serves MCP from the app at `/mcp`. The package ships a small stdio
|
|
72
|
+
bridge for MCP hosts that need a local command:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
audienti-mcp
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Use it this way:
|
|
79
|
+
|
|
80
|
+
1. Install the package.
|
|
81
|
+
2. Run `audienti auth login`.
|
|
82
|
+
3. Run `audienti accounts select <acct_id>`.
|
|
83
|
+
4. Configure your MCP host with the packaged `.mcp.json`, or point it at the
|
|
84
|
+
`audienti-mcp` command.
|
|
85
|
+
5. In the MCP host, list tools and call the account-scoped tool you need.
|
|
86
|
+
|
|
87
|
+
The bridge reads the same `~/.config/audienti/config.json` as the CLI, forwards
|
|
88
|
+
MCP JSON-RPC messages to the app, and injects the selected account when a tool
|
|
89
|
+
call does not include `account_id`. If you want a tool to run against a
|
|
90
|
+
different account, pass `account_id` in that tool call.
|
|
91
|
+
|
|
92
|
+
MCP clients that can call HTTP directly may use the hosted endpoint instead of
|
|
93
|
+
the stdio bridge:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
curl https://app.audienti.com/mcp \
|
|
97
|
+
-H "Authorization: Bearer $AUDIENTI_API_TOKEN" \
|
|
98
|
+
-H "Content-Type: application/json" \
|
|
99
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Example tool call:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"jsonrpc": "2.0",
|
|
107
|
+
"id": 2,
|
|
108
|
+
"method": "tools/call",
|
|
109
|
+
"params": {
|
|
110
|
+
"name": "analytics.stages",
|
|
111
|
+
"arguments": {
|
|
112
|
+
"account_id": "acct_...",
|
|
113
|
+
"query": {
|
|
114
|
+
"interval": "weekly",
|
|
115
|
+
"cohort_start_date": "2026-07-01",
|
|
116
|
+
"cohort_end_date": "2026-07-31"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Useful MCP tools to start with:
|
|
124
|
+
|
|
125
|
+
- `auth.me` confirms the token identity.
|
|
126
|
+
- `accounts.list` shows accessible accounts.
|
|
127
|
+
- `setup.play_preflight` returns LinkedIn connected-account readiness and setup
|
|
128
|
+
or mapping URLs.
|
|
129
|
+
- `offers.create`, `icps.create`, and `motions.create` set up the offer, ICP,
|
|
130
|
+
and play.
|
|
131
|
+
- `analytics.stages` returns stage conversion cohorts and stage aging.
|
|
132
|
+
- `analytics.cohort_lists.create` materializes event cohorts as reusable lists.
|
|
133
|
+
|
|
58
134
|
Common inspection commands:
|
|
59
135
|
|
|
60
136
|
```bash
|
|
@@ -63,6 +139,7 @@ audienti operator next --plan
|
|
|
63
139
|
audienti writer test-run <prsp_id>
|
|
64
140
|
audienti motions analytics <motn_id>
|
|
65
141
|
audienti motions run-discovery <motn_id>
|
|
142
|
+
audienti motions quick-start --url https://example.com --wait --confirm
|
|
66
143
|
audienti motions update <motn_id> --status paused
|
|
67
144
|
audienti motions update <motn_id> --own-post-engagement true
|
|
68
145
|
audienti motions activate <motn_id>
|
|
@@ -172,6 +249,12 @@ used by the operator surface:
|
|
|
172
249
|
audienti motions run-discovery <motn_id>
|
|
173
250
|
```
|
|
174
251
|
|
|
252
|
+
To create and launch a quick-start motion from a company URL:
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
audienti motions quick-start --url https://example.com --wait --confirm
|
|
256
|
+
```
|
|
257
|
+
|
|
175
258
|
To audit one account user's outbound actions, optionally narrowed to one motion
|
|
176
259
|
and one AccountProspect.created_at cohort:
|
|
177
260
|
|
package/package.json
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@audienti/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.33",
|
|
4
4
|
"description": "Agent-first command-line client for Audienti.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"audienti": "./bin/audienti.js"
|
|
7
|
+
"audienti": "./bin/audienti.js",
|
|
8
|
+
"audienti-mcp": "./bin/audienti-mcp.js"
|
|
8
9
|
},
|
|
9
10
|
"files": [
|
|
10
11
|
"bin/",
|
|
11
12
|
"src/",
|
|
12
13
|
"skills/",
|
|
14
|
+
".mcp.json",
|
|
13
15
|
"install",
|
|
14
16
|
"README.md",
|
|
15
17
|
"LICENSE",
|
package/skills/audienti/SKILL.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: audienti
|
|
3
|
-
description: Use when the user wants to operate Audienti through the production CLI, including account selection, plays, prospect imports, lists, message previews, or supported operator outcomes.
|
|
3
|
+
description: Use when the user wants to operate Audienti through the production CLI or app-hosted MCP endpoint, including account selection, plays, prospect imports, lists, message previews, analytics, or supported operator outcomes.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Audienti CLI
|
|
6
|
+
# Audienti CLI and MCP
|
|
7
7
|
|
|
8
|
-
Use the installed `audienti` command
|
|
9
|
-
|
|
8
|
+
Use the installed `audienti` command or Audienti's app-hosted MCP endpoint as
|
|
9
|
+
the production contract. The packaged `audienti-mcp` command is only a local
|
|
10
|
+
stdio bridge for MCP hosts. Do not build a parallel wrapper or call
|
|
11
|
+
undocumented API endpoints.
|
|
10
12
|
|
|
11
13
|
## Setup
|
|
12
14
|
|
|
@@ -23,9 +25,15 @@ curl -fsSL https://cli.audienti.com/install | bash
|
|
|
23
25
|
```
|
|
24
26
|
|
|
25
27
|
3. Authentication is explicit and per machine. Do not ask a user to paste a
|
|
26
|
-
production token into chat, a repository file, an issue, or a CI secret.
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
production token into chat, a repository file, an issue, or a CI secret. Prefer
|
|
29
|
+
browser login:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
audienti auth login
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Use `audienti auth token` only after the user supplies a token through an
|
|
36
|
+
approved secure channel.
|
|
29
37
|
|
|
30
38
|
4. Start with discovery, not mutation:
|
|
31
39
|
|
|
@@ -35,6 +43,39 @@ audienti accounts list --json
|
|
|
35
43
|
audienti help agent-workflows
|
|
36
44
|
```
|
|
37
45
|
|
|
46
|
+
For MCP hosts, configure the packaged `.mcp.json` or run:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
audienti-mcp
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The bridge reads the same local config as the CLI and forwards MCP JSON-RPC to
|
|
53
|
+
the app-hosted `/mcp` endpoint. Before starting the bridge, select account
|
|
54
|
+
context:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
audienti auth login
|
|
58
|
+
audienti accounts list --json
|
|
59
|
+
audienti accounts select <acct_id>
|
|
60
|
+
audienti users select me
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
If the MCP host can call HTTP directly, use `POST /mcp` with the same bearer
|
|
64
|
+
token as the API. Call `tools/list` first, then call named tools with
|
|
65
|
+
`tools/call`. Account-scoped tools accept `account_id`; the local bridge injects
|
|
66
|
+
the selected CLI account when it is omitted.
|
|
67
|
+
|
|
68
|
+
Useful MCP tools:
|
|
69
|
+
|
|
70
|
+
- `auth.me` confirms the token identity.
|
|
71
|
+
- `accounts.list` shows accessible accounts.
|
|
72
|
+
- `setup.play_preflight` returns connected-account readiness and setup or
|
|
73
|
+
mapping URLs.
|
|
74
|
+
- `offers.create`, `icps.create`, and `motions.create` set up the offer, ICP,
|
|
75
|
+
and play.
|
|
76
|
+
- `analytics.stages` returns stage conversion cohorts and stage aging.
|
|
77
|
+
- `analytics.cohort_lists.create` materializes event cohorts as reusable lists.
|
|
78
|
+
|
|
38
79
|
## Operating Rules
|
|
39
80
|
|
|
40
81
|
- Use `--json` whenever another agent or tool will consume the response.
|
|
@@ -59,6 +100,7 @@ audienti prospects unlock <prsp_id> --json
|
|
|
59
100
|
audienti users activity me --window 7d --json
|
|
60
101
|
audienti prospects import-batch --file prospects.csv --motion <motn_id> --assigned-user me --json
|
|
61
102
|
audienti lists create --name "Target list" --json
|
|
103
|
+
audienti motions quick-start --url https://example.com --wait --confirm --json
|
|
62
104
|
audienti motions update <motn_id> --status paused --json
|
|
63
105
|
audienti motions activate <motn_id> --json
|
|
64
106
|
audienti motions delete <motn_id> --confirm yes --json
|
package/src/api-client.js
CHANGED
|
@@ -40,6 +40,13 @@ export class AudientiClient {
|
|
|
40
40
|
return this.requestJson("/api/v1/accounts.json");
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
mcp(message) {
|
|
44
|
+
return this.requestJson("/mcp", {
|
|
45
|
+
method: "POST",
|
|
46
|
+
body: message
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
43
50
|
users(accountId) {
|
|
44
51
|
return this.requestJson(accountPath(accountId, ["users"]));
|
|
45
52
|
}
|
|
@@ -368,6 +375,24 @@ export class AudientiClient {
|
|
|
368
375
|
});
|
|
369
376
|
}
|
|
370
377
|
|
|
378
|
+
createQuickStart(accountId, body) {
|
|
379
|
+
return this.requestJson(accountPath(accountId, ["quick_start"]), {
|
|
380
|
+
method: "POST",
|
|
381
|
+
body
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
quickStart(accountId, draftId) {
|
|
386
|
+
return this.requestJson(accountPath(accountId, ["quick_start", draftId]));
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
confirmQuickStart(accountId, draftId, body = {}) {
|
|
390
|
+
return this.requestJson(accountPath(accountId, ["quick_start", draftId, "confirm"]), {
|
|
391
|
+
method: "POST",
|
|
392
|
+
body
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
371
396
|
motionProspects(accountId, motionId, query = {}) {
|
|
372
397
|
return this.requestJson(accountPath(accountId, ["motions", motionId, "prospects"], query));
|
|
373
398
|
}
|
package/src/cli.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { parseArgs } from "node:util";
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import { randomBytes } from "node:crypto";
|
|
2
5
|
import { readFile } from "node:fs/promises";
|
|
3
6
|
import { basename, join } from "node:path";
|
|
4
7
|
import { ApiError, AudientiClient, DEFAULT_HOST, normalizeHost } from "./api-client.js";
|
|
@@ -17,6 +20,7 @@ const DEFAULT_LIST_LIMIT = 20;
|
|
|
17
20
|
const API_MAX_LIST_LIMIT = 100;
|
|
18
21
|
const DEFAULT_LOOKUP_TIMEOUT_SECONDS = 60;
|
|
19
22
|
const DEFAULT_LOOKUP_POLL_INTERVAL_SECONDS = 2;
|
|
23
|
+
const DEFAULT_AUTH_LOGIN_TIMEOUT_SECONDS = 180;
|
|
20
24
|
const DEFAULT_WRITER_TEST_RUN_TIMEOUT_SECONDS = 180;
|
|
21
25
|
const DEFAULT_WRITER_TEST_RUN_POLL_INTERVAL_SECONDS = 2;
|
|
22
26
|
const PACKAGE_NAME = "@audienti/cli";
|
|
@@ -85,6 +89,7 @@ const MOTIONS_DELETE_USAGE = "Usage: audienti motions delete <motn_id> --confirm
|
|
|
85
89
|
const MOTIONS_CLONE_USAGE = "Usage: audienti motions clone <motn_id> --name <text> [--json] [--account <acct_id>]";
|
|
86
90
|
const MOTIONS_MOVE_PROSPECTS_USAGE = "Usage: audienti motions move-prospects <source_motn_id> --target <target_motn_id> <prsp_id> [prsp_id...] [--json] [--account <acct_id>]";
|
|
87
91
|
const MOTIONS_RUN_DISCOVERY_USAGE = "Usage: audienti motions run-discovery <motn_id> [--target-count <n>] [--json] [--account <acct_id>]";
|
|
92
|
+
const MOTIONS_QUICK_START_USAGE = "Usage: audienti motions quick-start --url <company_url> [--principal <account_user_id|me>] [--feedback <text>] [--offer-type <type>] [--force] [--confirm] [--wait] [--timeout-seconds <n>] [--poll-interval-seconds <n>] [--json] [--account <acct_id>]";
|
|
88
93
|
const ICPS_SHOW_USAGE = "Usage: audienti icps show <icp_id> [--json] [--account <acct_id>]";
|
|
89
94
|
const ICPS_UPDATE_USAGE = "Usage: audienti icps update <icp_id> [--name <text>] [--notes <text>] [--discovery-keyword <text>] [--tags <tag[,tag...]>] [--json] [--account <acct_id>]";
|
|
90
95
|
const ICPS_ADD_TAG_USAGE = "Usage: audienti icps add-tag <icp_id> <tag> [--json] [--account <acct_id>]";
|
|
@@ -182,6 +187,7 @@ async function dispatch(argv, context) {
|
|
|
182
187
|
const normalizedResource = normalizeResource(resource);
|
|
183
188
|
|
|
184
189
|
if (normalizedResource === "auth" && action === "token") return authToken(rest, context);
|
|
190
|
+
if (normalizedResource === "auth" && action === "login") return authLogin(rest, context);
|
|
185
191
|
if (normalizedResource === "auth" && action === "status") return authStatus(rest, context, { accountOverride });
|
|
186
192
|
if (normalizedResource === "auth" && action === "logout") return authLogout(rest, context);
|
|
187
193
|
if (normalizedResource === "config" && action === "list") return configList(rest, context);
|
|
@@ -243,6 +249,7 @@ async function dispatch(argv, context) {
|
|
|
243
249
|
if (normalizedResource === "motions" && action === "clone") return motionsClone(rest, context, { accountOverride });
|
|
244
250
|
if (normalizedResource === "motions" && action === "move-prospects") return motionsMoveProspects(rest, context, { accountOverride });
|
|
245
251
|
if (normalizedResource === "motions" && action === "run-discovery") return motionsRunDiscovery(rest, context, { accountOverride });
|
|
252
|
+
if (normalizedResource === "motions" && action === "quick-start") return motionsQuickStart(rest, context, { accountOverride });
|
|
246
253
|
if (normalizedResource === "content" && action === "programs") return contentPrograms(rest, context, { accountOverride });
|
|
247
254
|
if (normalizedResource === "content" && action === "plan") return contentPlan(rest, context, { accountOverride });
|
|
248
255
|
if (normalizedResource === "content" && action === "show") return contentShow(rest, context, { accountOverride });
|
|
@@ -406,12 +413,148 @@ async function authToken(args, context) {
|
|
|
406
413
|
writeLine(context.stdout, "Run `audienti accounts list` to choose an account.");
|
|
407
414
|
}
|
|
408
415
|
|
|
416
|
+
async function authLogin(args, context) {
|
|
417
|
+
const { values, positionals } = parseCommandArgs(args, {
|
|
418
|
+
...jsonOptions(),
|
|
419
|
+
host: { type: "string" },
|
|
420
|
+
"timeout-seconds": { type: "string" },
|
|
421
|
+
"no-open": { type: "boolean" }
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
if (positionals.length > 0) {
|
|
425
|
+
throw new CommandError("Usage: audienti auth login [--host https://app.audienti.com] [--timeout-seconds <n>] [--no-open] [--json]");
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const host = normalizeHost(values.host || DEFAULT_HOST);
|
|
429
|
+
const timeoutSeconds = normalizeOptionalPositiveInteger(values["timeout-seconds"], "--timeout-seconds") || DEFAULT_AUTH_LOGIN_TIMEOUT_SECONDS;
|
|
430
|
+
const state = randomBytes(24).toString("hex");
|
|
431
|
+
const callback = await createAuthCallbackServer({ state, context });
|
|
432
|
+
const authUrl = new URL("/cli/auth", host);
|
|
433
|
+
authUrl.searchParams.set("redirect_uri", callback.redirectUri);
|
|
434
|
+
authUrl.searchParams.set("state", state);
|
|
435
|
+
|
|
436
|
+
if (values.json) {
|
|
437
|
+
writeJson(context.stdout, {
|
|
438
|
+
kind: "auth_login_start",
|
|
439
|
+
auth_url: authUrl.toString(),
|
|
440
|
+
callback_url: callback.redirectUri,
|
|
441
|
+
timeout_seconds: timeoutSeconds
|
|
442
|
+
});
|
|
443
|
+
} else {
|
|
444
|
+
writeLine(context.stdout, "Open this URL to authenticate Audienti CLI:");
|
|
445
|
+
writeLine(context.stdout, authUrl.toString());
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (!values["no-open"]) openBrowser(authUrl.toString(), context);
|
|
449
|
+
|
|
450
|
+
let payload;
|
|
451
|
+
try {
|
|
452
|
+
payload = await callback.wait(timeoutSeconds);
|
|
453
|
+
} finally {
|
|
454
|
+
await callback.close();
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const client = new AudientiClient({ host: payload.host || host, token: payload.token, fetchImpl: context.fetchImpl });
|
|
458
|
+
const user = await client.me();
|
|
459
|
+
await writeConfig({
|
|
460
|
+
host: client.host,
|
|
461
|
+
token: payload.token,
|
|
462
|
+
accountId: payload.account_id || undefined,
|
|
463
|
+
accountName: payload.account_name || undefined
|
|
464
|
+
}, { env: context.env });
|
|
465
|
+
|
|
466
|
+
const result = {
|
|
467
|
+
kind: "auth_login_complete",
|
|
468
|
+
host: client.host,
|
|
469
|
+
user: user?.name || payload.user_name || payload.user_email || user?.email || null,
|
|
470
|
+
account_id: payload.account_id || null,
|
|
471
|
+
account_name: payload.account_name || null
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
if (values.json) return writeJson(context.stdout, result);
|
|
475
|
+
|
|
476
|
+
writeLine(context.stdout, `Authenticated to ${client.host} as ${result.user || "current user"}.`);
|
|
477
|
+
if (result.account_id) writeLine(context.stdout, `Selected account ${result.account_name || result.account_id} (${result.account_id}).`);
|
|
478
|
+
if (!result.account_id) writeLine(context.stdout, "Run `audienti accounts list` to choose an account.");
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
async function createAuthCallbackServer({ state, context }) {
|
|
482
|
+
let resolvePayload;
|
|
483
|
+
let rejectPayload;
|
|
484
|
+
const waitPromise = new Promise((resolve, reject) => {
|
|
485
|
+
resolvePayload = resolve;
|
|
486
|
+
rejectPayload = reject;
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
const server = createServer((request, response) => {
|
|
490
|
+
const url = new URL(request.url, "http://127.0.0.1");
|
|
491
|
+
if (url.pathname !== "/callback") {
|
|
492
|
+
response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
|
|
493
|
+
response.end("Not found.");
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const payload = Object.fromEntries(url.searchParams.entries());
|
|
498
|
+
if (payload.state !== state) {
|
|
499
|
+
response.writeHead(400, { "Content-Type": "text/plain; charset=utf-8" });
|
|
500
|
+
response.end("Audienti CLI authentication failed: state mismatch.");
|
|
501
|
+
rejectPayload(new CommandError("Browser authentication state mismatch."));
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (!payload.token) {
|
|
506
|
+
response.writeHead(400, { "Content-Type": "text/plain; charset=utf-8" });
|
|
507
|
+
response.end("Audienti CLI authentication failed: missing token.");
|
|
508
|
+
rejectPayload(new CommandError("Browser authentication callback did not include a token."));
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
513
|
+
response.end("<!doctype html><title>Audienti CLI authenticated</title><p>Audienti CLI is authenticated. You can close this window.</p>");
|
|
514
|
+
resolvePayload(payload);
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
await new Promise((resolve, reject) => {
|
|
518
|
+
server.once("error", reject);
|
|
519
|
+
server.listen(0, "127.0.0.1", resolve);
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
const { port } = server.address();
|
|
523
|
+
|
|
524
|
+
return {
|
|
525
|
+
redirectUri: `http://127.0.0.1:${port}/callback`,
|
|
526
|
+
wait(timeoutSeconds) {
|
|
527
|
+
let timeoutId;
|
|
528
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
529
|
+
timeoutId = setTimeout(() => reject(new CommandError(`Timed out waiting for browser authentication after ${timeoutSeconds} seconds.`)), timeoutSeconds * 1000);
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
return Promise.race([waitPromise, timeoutPromise]).finally(() => clearTimeout(timeoutId));
|
|
533
|
+
},
|
|
534
|
+
close() {
|
|
535
|
+
return new Promise((resolve) => server.close(() => resolve()));
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function openBrowser(url, context) {
|
|
541
|
+
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
542
|
+
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
543
|
+
|
|
544
|
+
try {
|
|
545
|
+
const child = spawn(command, args, { detached: true, stdio: "ignore" });
|
|
546
|
+
child.unref();
|
|
547
|
+
} catch (error) {
|
|
548
|
+
writeLine(context.stderr, `Could not open browser automatically: ${error.message}`);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
409
552
|
async function authStatus(args, context, { accountOverride } = {}) {
|
|
410
553
|
assertNoPositionals(args, "Usage: audienti auth status [--account <acct_id>]");
|
|
411
554
|
|
|
412
555
|
const config = await readConfig({ env: context.env });
|
|
413
556
|
if (!config.token) {
|
|
414
|
-
writeLine(context.stdout, "Not authenticated. Run `audienti auth
|
|
557
|
+
writeLine(context.stdout, "Not authenticated. Run `audienti auth login`.");
|
|
415
558
|
return 1;
|
|
416
559
|
}
|
|
417
560
|
|
|
@@ -1402,6 +1545,55 @@ async function motionsRunDiscovery(args, context, { accountOverride } = {}) {
|
|
|
1402
1545
|
renderMotionDiscoveryRun(payload, context);
|
|
1403
1546
|
}
|
|
1404
1547
|
|
|
1548
|
+
async function motionsQuickStart(args, context, { accountOverride } = {}) {
|
|
1549
|
+
const { values, positionals } = parseCommandArgs(args, {
|
|
1550
|
+
...jsonOptions(),
|
|
1551
|
+
url: { type: "string" },
|
|
1552
|
+
principal: { type: "string" },
|
|
1553
|
+
feedback: { type: "string" },
|
|
1554
|
+
"offer-type": { type: "string" },
|
|
1555
|
+
force: { type: "boolean" },
|
|
1556
|
+
confirm: { type: "boolean" },
|
|
1557
|
+
wait: { type: "boolean" },
|
|
1558
|
+
"timeout-seconds": { type: "string" },
|
|
1559
|
+
"poll-interval-seconds": { type: "string" }
|
|
1560
|
+
});
|
|
1561
|
+
if (positionals.length > 0 || !values.url) throw new CommandError(MOTIONS_QUICK_START_USAGE);
|
|
1562
|
+
|
|
1563
|
+
const timeoutSeconds = normalizeOptionalPositiveInteger(values["timeout-seconds"], "--timeout-seconds") || DEFAULT_LOOKUP_TIMEOUT_SECONDS;
|
|
1564
|
+
const pollIntervalSeconds = normalizeOptionalPositiveInteger(values["poll-interval-seconds"], "--poll-interval-seconds") || DEFAULT_LOOKUP_POLL_INTERVAL_SECONDS;
|
|
1565
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
1566
|
+
const requestBody = {
|
|
1567
|
+
quick_start: compactObject({
|
|
1568
|
+
company_url: values.url,
|
|
1569
|
+
principal_account_user_id: values.principal,
|
|
1570
|
+
feedback: values.feedback,
|
|
1571
|
+
offer_type: values["offer-type"],
|
|
1572
|
+
force: values.force || undefined
|
|
1573
|
+
})
|
|
1574
|
+
};
|
|
1575
|
+
|
|
1576
|
+
let draft = await client.createQuickStart(accountId, requestBody);
|
|
1577
|
+
if (values.wait && !quickStartReady(draft)) {
|
|
1578
|
+
draft = await waitForQuickStartDraft(client, accountId, draft, context, { timeoutSeconds, pollIntervalSeconds });
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
if (values.confirm) {
|
|
1582
|
+
if (!quickStartReady(draft)) {
|
|
1583
|
+
throw new CommandError(`Quick-start draft ${display(draft?.id)} is ${display(draft?.status)}. Re-run with --wait or confirm after it is ready.`);
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
const payload = await client.confirmQuickStart(accountId, draft.id, {});
|
|
1587
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
1588
|
+
|
|
1589
|
+
renderQuickStartConfirmation(payload, context);
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
if (values.json) return writeJson(context.stdout, draft);
|
|
1594
|
+
renderQuickStartDraft(draft, context);
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1405
1597
|
async function motionsAnalytics(args, context, { accountOverride } = {}) {
|
|
1406
1598
|
const { values, positionals } = parseCommandArgs(args, {
|
|
1407
1599
|
...jsonOptions(),
|
|
@@ -2833,7 +3025,7 @@ function assertNoPositionals(args, usageText) {
|
|
|
2833
3025
|
async function requireAuthenticatedConfig(context) {
|
|
2834
3026
|
const config = await readConfig({ env: context.env });
|
|
2835
3027
|
if (!config.token) {
|
|
2836
|
-
throw new CommandError("Not authenticated. Run `audienti auth token <token>`.");
|
|
3028
|
+
throw new CommandError("Not authenticated. Run `audienti auth login` or `audienti auth token <token>`.");
|
|
2837
3029
|
}
|
|
2838
3030
|
|
|
2839
3031
|
return config;
|
|
@@ -5183,6 +5375,45 @@ function renderMotionDiscoveryRun(payload, context) {
|
|
|
5183
5375
|
writeLine(context.stdout, `Target count: ${display(payload?.target_count)}`);
|
|
5184
5376
|
}
|
|
5185
5377
|
|
|
5378
|
+
function renderQuickStartDraft(draft, context) {
|
|
5379
|
+
writeLine(context.stdout, `Quick-start draft ${display(draft?.id)} is ${display(draft?.status)}.`);
|
|
5380
|
+
writeLine(context.stdout, `URL: ${display(draft?.normalized_url)}`);
|
|
5381
|
+
if (draft?.principal_account_user) writeLine(context.stdout, `Principal: ${accountUserLabel(draft.principal_account_user)}`);
|
|
5382
|
+
if (draft?.expires_at) writeLine(context.stdout, `Expires: ${draft.expires_at}`);
|
|
5383
|
+
if (draft?.error) writeLine(context.stdout, `Error: ${draft.error}`);
|
|
5384
|
+
if (quickStartReady(draft)) writeLine(context.stdout, `Confirm: audienti motions quick-start --url ${draft.normalized_url} --confirm`);
|
|
5385
|
+
}
|
|
5386
|
+
|
|
5387
|
+
function renderQuickStartConfirmation(payload, context) {
|
|
5388
|
+
const motion = payload?.motion || {};
|
|
5389
|
+
writeLine(context.stdout, `Quick-start motion ${display(entityLabel(motion) || motion.prefix_id)} ${payload?.reused ? "reused" : "created"}.`);
|
|
5390
|
+
writeLine(context.stdout, `Motion status: ${display(motion.status)}`);
|
|
5391
|
+
writeLine(context.stdout, `Reservation: ${display(payload?.reservation?.status)} / ${display(payload?.reservation?.launch_status)}`);
|
|
5392
|
+
if (payload?.reservation?.launch_reason) writeLine(context.stdout, `Launch reason: ${payload.reservation.launch_reason}`);
|
|
5393
|
+
}
|
|
5394
|
+
|
|
5395
|
+
function quickStartReady(draft) {
|
|
5396
|
+
return draft?.ready === true || draft?.status === "ready";
|
|
5397
|
+
}
|
|
5398
|
+
|
|
5399
|
+
function quickStartTerminal(draft) {
|
|
5400
|
+
return quickStartReady(draft) || draft?.status === "failed";
|
|
5401
|
+
}
|
|
5402
|
+
|
|
5403
|
+
async function waitForQuickStartDraft(client, accountId, initialDraft, context, { timeoutSeconds, pollIntervalSeconds }) {
|
|
5404
|
+
let draft = initialDraft;
|
|
5405
|
+
const deadline = context.now().getTime() + timeoutSeconds * 1000;
|
|
5406
|
+
|
|
5407
|
+
while (!quickStartTerminal(draft)) {
|
|
5408
|
+
if (context.now().getTime() >= deadline) return draft;
|
|
5409
|
+
|
|
5410
|
+
await context.sleep(pollIntervalSeconds * 1000);
|
|
5411
|
+
draft = await client.quickStart(accountId, draft.id);
|
|
5412
|
+
}
|
|
5413
|
+
|
|
5414
|
+
return draft;
|
|
5415
|
+
}
|
|
5416
|
+
|
|
5186
5417
|
function renderAnalyticsDashboard(payload, context) {
|
|
5187
5418
|
writeLine(context.stdout, `Dashboard analytics (${display(payload?.cohort?.label, "selected cohort")})`);
|
|
5188
5419
|
if (payload?.cohort) {
|
|
@@ -5982,6 +6213,7 @@ const HELP_TOPICS = new Map([
|
|
|
5982
6213
|
" audienti <command> [options]",
|
|
5983
6214
|
"",
|
|
5984
6215
|
"Start:",
|
|
6216
|
+
" audienti auth login Sign in through the browser",
|
|
5985
6217
|
" audienti auth token <token> Save an API token",
|
|
5986
6218
|
" audienti accounts list See accounts available to this token",
|
|
5987
6219
|
" audienti accounts select <acct_id> Use one account by default",
|
|
@@ -6003,6 +6235,7 @@ const HELP_TOPICS = new Map([
|
|
|
6003
6235
|
" audienti motions show <motn_id>",
|
|
6004
6236
|
" audienti motions analytics <motn_id>",
|
|
6005
6237
|
" audienti motions run-discovery <motn_id>",
|
|
6238
|
+
" audienti motions quick-start --url <company_url> --confirm --wait",
|
|
6006
6239
|
" audienti motions prospects <motn_id>",
|
|
6007
6240
|
" audienti motions create --payload <file.json>",
|
|
6008
6241
|
" audienti motions update <motn_id> [--status <state>] [--tags <tag[,tag...]>] [--own-post-engagement <true|false>]",
|
|
@@ -6121,6 +6354,7 @@ const HELP_TOPICS = new Map([
|
|
|
6121
6354
|
|
|
6122
6355
|
["auth", [
|
|
6123
6356
|
"Usage:",
|
|
6357
|
+
" audienti auth login [--host <url>] [--timeout-seconds <n>] [--no-open] [--json]",
|
|
6124
6358
|
" audienti auth token <token> [--host <url>]",
|
|
6125
6359
|
" audienti auth status",
|
|
6126
6360
|
" audienti auth logout",
|
|
@@ -6128,11 +6362,32 @@ const HELP_TOPICS = new Map([
|
|
|
6128
6362
|
"Status: implemented",
|
|
6129
6363
|
"",
|
|
6130
6364
|
"Commands:",
|
|
6365
|
+
" audienti auth login Open Audienti in a browser and save the returned token",
|
|
6131
6366
|
" audienti auth token <token> Validate and save a bearer API token",
|
|
6132
6367
|
" audienti auth status Check live auth and show selected account",
|
|
6133
6368
|
" audienti auth logout Delete local CLI auth config",
|
|
6134
6369
|
"",
|
|
6135
|
-
"Run `audienti auth
|
|
6370
|
+
"Run `audienti auth login help` for browser authentication."
|
|
6371
|
+
].join("\n")],
|
|
6372
|
+
|
|
6373
|
+
["auth login", [
|
|
6374
|
+
"Usage:",
|
|
6375
|
+
" audienti auth login [--host <url>] [--timeout-seconds <n>] [--no-open] [--json]",
|
|
6376
|
+
"",
|
|
6377
|
+
"Status: implemented",
|
|
6378
|
+
"",
|
|
6379
|
+
"Options:",
|
|
6380
|
+
" --host <url> Audienti host. Default: https://app.audienti.com",
|
|
6381
|
+
" --timeout-seconds <n> Wait budget before the command fails. Default: 180",
|
|
6382
|
+
" --no-open Print the URL without opening a browser",
|
|
6383
|
+
" --json Print machine-readable start and completion payloads",
|
|
6384
|
+
"",
|
|
6385
|
+
"Flow:",
|
|
6386
|
+
" Starts a temporary 127.0.0.1 callback server, opens /cli/auth in the browser,",
|
|
6387
|
+
" and saves the returned API token to ~/.config/audienti/config.json after validation.",
|
|
6388
|
+
"",
|
|
6389
|
+
"Example:",
|
|
6390
|
+
" audienti auth login --host https://app.audienti.com"
|
|
6136
6391
|
].join("\n")],
|
|
6137
6392
|
|
|
6138
6393
|
["auth token", [
|
|
@@ -7229,6 +7484,7 @@ const HELP_TOPICS = new Map([
|
|
|
7229
7484
|
" audienti motions show <motn_id> [--json]",
|
|
7230
7485
|
" audienti motions status <motn_id> [--json]",
|
|
7231
7486
|
" audienti motions run-discovery <motn_id> [--target-count <n>] [--json]",
|
|
7487
|
+
" audienti motions quick-start --url <company_url> [--confirm] [--wait] [--json]",
|
|
7232
7488
|
" audienti motions analytics <motn_id> [--window 30d] [--json]",
|
|
7233
7489
|
" audienti motions prospects <motn_id> [--json]",
|
|
7234
7490
|
" audienti motions add-prospects <motn_id> <prsp_id> [prsp_id...] [--json]",
|
|
@@ -7243,7 +7499,7 @@ const HELP_TOPICS = new Map([
|
|
|
7243
7499
|
" audienti motions clone <motn_id> --name <text> [--json]",
|
|
7244
7500
|
" audienti motions move-prospects <source_motn_id> --target <target_motn_id> <prsp_id> [prsp_id...] [--json]",
|
|
7245
7501
|
"",
|
|
7246
|
-
"Status: read, create, status update, delete, clone, status, discovery launch, and prospect attachment commands implemented",
|
|
7502
|
+
"Status: read, create, quick-start, status update, delete, clone, status, discovery launch, and prospect attachment commands implemented",
|
|
7247
7503
|
"",
|
|
7248
7504
|
"CLI synonym:",
|
|
7249
7505
|
" `plays` is accepted anywhere `motions` is accepted",
|
|
@@ -7381,6 +7637,36 @@ const HELP_TOPICS = new Map([
|
|
|
7381
7637
|
" POST /api/v1/accounts/:account_id/motions/:id/run_discovery.json"
|
|
7382
7638
|
].join("\n")],
|
|
7383
7639
|
|
|
7640
|
+
["motions quick-start", [
|
|
7641
|
+
"Usage:",
|
|
7642
|
+
` ${MOTIONS_QUICK_START_USAGE.slice("Usage: ".length)}`,
|
|
7643
|
+
"",
|
|
7644
|
+
"Status: implemented",
|
|
7645
|
+
"",
|
|
7646
|
+
"Purpose:",
|
|
7647
|
+
" Draft a quick-start motion from a company URL, then optionally confirm it into a launched quick-start motion.",
|
|
7648
|
+
"",
|
|
7649
|
+
"Options:",
|
|
7650
|
+
" --url <company_url> Public HTTP(S) URL used by the quick-start drafter.",
|
|
7651
|
+
" --principal <user> Account user id or me. Defaults to the authenticated token user.",
|
|
7652
|
+
" --feedback <text> Optional operator guidance for the draft.",
|
|
7653
|
+
" --offer-type <type> Optional supported offer type hint.",
|
|
7654
|
+
" --force Request a fresh draft when inputs should replace an existing ready draft.",
|
|
7655
|
+
" --confirm Confirm the ready draft into motion setup and discovery launch.",
|
|
7656
|
+
" --wait Poll until the generated draft is ready before returning or confirming.",
|
|
7657
|
+
" --timeout-seconds <n> Maximum wait time. Defaults to 60.",
|
|
7658
|
+
" --poll-interval-seconds <n> Poll cadence. Defaults to 2.",
|
|
7659
|
+
"",
|
|
7660
|
+
"Pipeline:",
|
|
7661
|
+
" POST creates or reuses a QuickStartDraft and enqueues GenerateQuickStartDraftJob when needed.",
|
|
7662
|
+
" POST confirm calls QuickStartOrchestrator, creating ICP, offer, signals, motion, reservation, and discovery launch.",
|
|
7663
|
+
"",
|
|
7664
|
+
"API:",
|
|
7665
|
+
" POST /api/v1/accounts/:account_id/quick_start.json",
|
|
7666
|
+
" GET /api/v1/accounts/:account_id/quick_start/:draft_id.json",
|
|
7667
|
+
" POST /api/v1/accounts/:account_id/quick_start/:draft_id/confirm.json"
|
|
7668
|
+
].join("\n")],
|
|
7669
|
+
|
|
7384
7670
|
["motions prospects", [
|
|
7385
7671
|
"Usage:",
|
|
7386
7672
|
" audienti motions prospects <motn_id> [--json] [--account <acct_id>]",
|
|
@@ -8911,7 +9197,7 @@ const HELP_TOPICS = new Map([
|
|
|
8911
9197
|
" Give a local coding agent the shortest safe path through the common Audienti production workflows.",
|
|
8912
9198
|
"",
|
|
8913
9199
|
"1. Authenticate and select an account",
|
|
8914
|
-
" audienti auth
|
|
9200
|
+
" audienti auth login",
|
|
8915
9201
|
" audienti accounts list",
|
|
8916
9202
|
" audienti accounts select <acct_id>",
|
|
8917
9203
|
" audienti users list",
|
package/src/mcp.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { ApiError, AudientiClient, DEFAULT_HOST } from "./api-client.js";
|
|
2
|
+
import { readConfig } from "./config.js";
|
|
3
|
+
|
|
4
|
+
export async function runMcpServer({
|
|
5
|
+
input = process.stdin,
|
|
6
|
+
output = process.stdout,
|
|
7
|
+
env = process.env,
|
|
8
|
+
fetchImpl = globalThis.fetch
|
|
9
|
+
} = {}) {
|
|
10
|
+
input.setEncoding("utf8");
|
|
11
|
+
|
|
12
|
+
let buffer = "";
|
|
13
|
+
for await (const chunk of input) {
|
|
14
|
+
buffer += chunk;
|
|
15
|
+
const lines = buffer.split(/\r?\n/);
|
|
16
|
+
buffer = lines.pop() || "";
|
|
17
|
+
|
|
18
|
+
for (const line of lines) {
|
|
19
|
+
if (!line.trim()) continue;
|
|
20
|
+
const response = await handleRawMessage(line, { env, fetchImpl });
|
|
21
|
+
if (response) output.write(`${JSON.stringify(response)}\n`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function handleRawMessage(line, options = {}) {
|
|
27
|
+
try {
|
|
28
|
+
return handleMcpRequest(JSON.parse(line), options);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
return errorResponse(null, -32700, error.message);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function handleMcpRequest(message, { env = process.env, fetchImpl = globalThis.fetch } = {}) {
|
|
35
|
+
if (!message || message.jsonrpc !== "2.0") {
|
|
36
|
+
return errorResponse(message?.id ?? null, -32600, "Invalid JSON-RPC request.");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (message.id === undefined || message.id === null) return null;
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
return await remoteMcpResponse(message, { env, fetchImpl });
|
|
43
|
+
} catch (error) {
|
|
44
|
+
return errorResponse(message.id, -32603, toolErrorMessage(error), toolErrorPayload(error));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function remoteMcpResponse(message, { env = process.env, fetchImpl = globalThis.fetch } = {}) {
|
|
49
|
+
const config = await readConfig({ env });
|
|
50
|
+
if (!config.token) throw new Error("Not authenticated. Run `audienti auth login` or `audienti auth token <token>`.");
|
|
51
|
+
|
|
52
|
+
const client = new AudientiClient({
|
|
53
|
+
host: config.host || DEFAULT_HOST,
|
|
54
|
+
token: config.token,
|
|
55
|
+
fetchImpl
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return client.mcp(withSelectedAccount(message, config));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function withSelectedAccount(message, config) {
|
|
62
|
+
if (message.method !== "tools/call" || !config.accountId) return message;
|
|
63
|
+
|
|
64
|
+
const params = message.params || {};
|
|
65
|
+
const args = params.arguments || {};
|
|
66
|
+
if (args.account_id) return message;
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
...message,
|
|
70
|
+
params: {
|
|
71
|
+
...params,
|
|
72
|
+
arguments: {
|
|
73
|
+
...args,
|
|
74
|
+
account_id: config.accountId
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function errorResponse(id, code, message, data) {
|
|
81
|
+
return {
|
|
82
|
+
jsonrpc: "2.0",
|
|
83
|
+
id,
|
|
84
|
+
error: {
|
|
85
|
+
code,
|
|
86
|
+
message,
|
|
87
|
+
...(data === undefined ? {} : { data })
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function toolErrorMessage(error) {
|
|
93
|
+
if (error instanceof ApiError && error.status) return `${error.message} (HTTP ${error.status})`;
|
|
94
|
+
return error.message;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function toolErrorPayload(error) {
|
|
98
|
+
return {
|
|
99
|
+
error: error.message,
|
|
100
|
+
status: error instanceof ApiError ? error.status || null : null,
|
|
101
|
+
body: error instanceof ApiError ? error.body || null : null
|
|
102
|
+
};
|
|
103
|
+
}
|