@mikado-ai/cli 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,229 @@
1
+ # @mikado-ai/cli
2
+
3
+ Command-line interface and MCP server for [Mikado AI](https://mikadoai.app) — submit conversation transcripts, extract structured insights, and integrate with AI agents.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npx @mikado-ai/cli
9
+ ```
10
+
11
+ Or install globally:
12
+
13
+ ```bash
14
+ npm install -g @mikado-ai/cli
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```bash
20
+ # 1. Authenticate (get your API key from Mikado AI → Settings → API Keys)
21
+ mikado auth
22
+
23
+ # 2. See available campaigns
24
+ mikado campaigns
25
+
26
+ # 3. Submit a transcript
27
+ mikado submit transcript.txt --campaign abcd1234
28
+ ```
29
+
30
+ ## Authentication
31
+
32
+ ### Interactive
33
+
34
+ ```bash
35
+ mikado auth
36
+ ```
37
+
38
+ You'll be prompted for your API key and server URL.
39
+
40
+ ### Non-Interactive
41
+
42
+ ```bash
43
+ mikado auth --key mkd_your_api_key --url https://mikadoai.app
44
+ ```
45
+
46
+ ### Environment Variable
47
+
48
+ ```bash
49
+ export MIKADO_API_KEY=mkd_your_api_key
50
+ export MIKADO_URL=https://mikadoai.app
51
+ ```
52
+
53
+ Environment variables take precedence over the config file.
54
+
55
+ ### Config File
56
+
57
+ Credentials are stored in `~/.mikado/config.json`. The file is created with restricted permissions (owner-only read/write).
58
+
59
+ ## Commands
60
+
61
+ ### `mikado submit <file>`
62
+
63
+ Submit a conversation transcript for processing. By default, waits for results.
64
+
65
+ ```bash
66
+ # Submit and wait for results (default)
67
+ mikado submit transcript.txt --campaign abcd1234
68
+
69
+ # Submit without waiting
70
+ mikado submit transcript.txt --campaign abcd1234 --no-wait
71
+
72
+ # Read from stdin
73
+ cat transcript.txt | mikado submit -
74
+
75
+ # JSON output for scripting
76
+ mikado submit transcript.txt --campaign abcd1234 --json
77
+
78
+ # Custom timeout (default: 300s)
79
+ mikado submit transcript.txt --timeout 60
80
+ ```
81
+
82
+ **Options:**
83
+
84
+ | Flag | Description |
85
+ |------|-------------|
86
+ | `-c, --campaign <id>` | Campaign short ID, UUID, or name |
87
+ | `--no-wait` | Return immediately with job ID |
88
+ | `--timeout <seconds>` | Max wait time (default: 300) |
89
+ | `--json` | Output JSON to stdout |
90
+
91
+ ### `mikado campaigns`
92
+
93
+ List available campaigns in your organization.
94
+
95
+ ```bash
96
+ mikado campaigns
97
+ ```
98
+
99
+ ```
100
+ ID Name Status Templates Conversations
101
+ abcd1234 Sales Calls ACTIVE 2 142
102
+ efgh5678 Support Tickets ACTIVE 1 89
103
+ ```
104
+
105
+ ### `mikado status <job-id>`
106
+
107
+ Check the processing status of a submitted transcript.
108
+
109
+ ```bash
110
+ mikado status 3f8a2b1c-...
111
+ ```
112
+
113
+ ```
114
+ Job: 3f8a2b1c-...
115
+ Status: ✓ SUCCESS
116
+ Duration: 11.2s
117
+ Insights: 1 generated
118
+ Sentiment: positive (0.82)
119
+ ```
120
+
121
+ ### `mikado result <conversation-id>`
122
+
123
+ Retrieve full results for a processed conversation.
124
+
125
+ ```bash
126
+ mikado result 550e8400-...
127
+ ```
128
+
129
+ ### `mikado auth`
130
+
131
+ Set up or update authentication credentials.
132
+
133
+ ```bash
134
+ mikado auth
135
+ mikado auth --key mkd_... --url https://api.mikado.ai
136
+ ```
137
+
138
+ ## JSON Output
139
+
140
+ All commands support `--json` for machine-readable output. Errors go to stderr, data goes to stdout.
141
+
142
+ ```bash
143
+ # Pipe to jq
144
+ mikado submit transcript.txt --campaign abcd1234 --json | jq '.insights[0]'
145
+
146
+ # Batch processing
147
+ for f in transcripts/*.txt; do
148
+ mikado submit "$f" --campaign abcd1234 --json >> results.jsonl
149
+ done
150
+ ```
151
+
152
+ ## Exit Codes
153
+
154
+ | Code | Meaning |
155
+ |------|---------|
156
+ | `0` | Success |
157
+ | `1` | General error (auth failure, network error) |
158
+ | `2` | Invalid arguments |
159
+ | `3` | Processing failed (pipeline error) |
160
+ | `4` | Timeout (polling exceeded `--timeout`) |
161
+
162
+ ## MCP Server
163
+
164
+ Use Mikado AI as a tool in AI agents via the [Model Context Protocol](https://modelcontextprotocol.io/).
165
+
166
+ ### Configuration
167
+
168
+ **Claude Desktop** (`claude_desktop_config.json`):
169
+
170
+ ```json
171
+ {
172
+ "mcpServers": {
173
+ "mikado": {
174
+ "command": "npx",
175
+ "args": ["@mikado-ai/cli", "mcp"],
176
+ "env": {
177
+ "MIKADO_API_KEY": "mkd_your_api_key",
178
+ "MIKADO_URL": "https://mikadoai.app"
179
+ }
180
+ }
181
+ }
182
+ }
183
+ ```
184
+
185
+ **Claude Code:**
186
+
187
+ ```bash
188
+ claude mcp add mikado -- npx @mikado-ai/cli mcp
189
+ ```
190
+
191
+ ### Available Tools
192
+
193
+ | Tool | Description |
194
+ |------|-------------|
195
+ | `mikado_submit_and_wait` | Submit a transcript and wait for full results. Primary tool for AI agents. |
196
+ | `mikado_submit` | Submit a transcript, return immediately with job ID. |
197
+ | `mikado_status` | Check processing status of a job. |
198
+ | `mikado_result` | Get results for a completed conversation. |
199
+ | `mikado_campaigns` | List available campaigns. |
200
+
201
+ ### Example Agent Workflow
202
+
203
+ ```
204
+ 1. Call mikado_campaigns to see available campaigns
205
+ 2. Call mikado_submit_and_wait with transcript + campaign
206
+ 3. Use the returned insights (scores, labels, extracted data) in your response
207
+ ```
208
+
209
+ ## Local Development
210
+
211
+ ```bash
212
+ cd cli
213
+ npm install
214
+ npm run build # Build to dist/
215
+ npm run dev # Watch mode
216
+
217
+ # Test locally
218
+ node dist/index.js --help
219
+ node dist/index.js auth --key mkd_... --url http://localhost:8100
220
+ ```
221
+
222
+ ## Requirements
223
+
224
+ - Node.js 18 or later
225
+ - A [Mikado AI](https://mikadoai.app) account with an API key
226
+
227
+ ## License
228
+
229
+ MIT
@@ -0,0 +1,94 @@
1
+ // src/core/client.ts
2
+ var MikadoClient = class {
3
+ apiKey;
4
+ baseUrl;
5
+ constructor(config) {
6
+ this.apiKey = config.apiKey;
7
+ this.baseUrl = config.baseUrl.replace(/\/$/, "");
8
+ }
9
+ async request(path, options = {}) {
10
+ const url = `${this.baseUrl}${path}`;
11
+ const headers = {
12
+ "Authorization": `Bearer ${this.apiKey}`,
13
+ "Content-Type": "application/json",
14
+ ...options.headers
15
+ };
16
+ const response = await fetch(url, {
17
+ ...options,
18
+ headers
19
+ });
20
+ if (!response.ok) {
21
+ let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
22
+ try {
23
+ const errorBody = await response.json();
24
+ if (errorBody.message) {
25
+ errorMessage = errorBody.message;
26
+ } else if (errorBody.detail) {
27
+ errorMessage = errorBody.detail;
28
+ }
29
+ } catch {
30
+ }
31
+ throw new Error(errorMessage);
32
+ }
33
+ return response.json();
34
+ }
35
+ async whoami() {
36
+ return this.request("/api/cli/whoami");
37
+ }
38
+ async submit(content, options) {
39
+ const body = { content };
40
+ if (options?.campaign) {
41
+ body.campaign = options.campaign;
42
+ }
43
+ if (options?.filename) {
44
+ body.filename = options.filename;
45
+ }
46
+ return this.request("/api/cli/submit", {
47
+ method: "POST",
48
+ body: JSON.stringify(body)
49
+ });
50
+ }
51
+ async getJobStatus(jobId) {
52
+ return this.request(`/api/cli/jobs/${jobId}`);
53
+ }
54
+ async getConversation(conversationId) {
55
+ return this.request(
56
+ `/api/cli/conversations/${conversationId}`
57
+ );
58
+ }
59
+ async listCampaigns() {
60
+ return this.request("/api/cli/campaigns");
61
+ }
62
+ };
63
+
64
+ // src/core/poller.ts
65
+ async function pollJob(client, jobId, options = {}) {
66
+ const {
67
+ interval = 1e3,
68
+ timeout = 3e5,
69
+ onUpdate
70
+ } = options;
71
+ const startTime = Date.now();
72
+ let currentInterval = interval;
73
+ const maxInterval = 5e3;
74
+ while (true) {
75
+ const elapsed = Date.now() - startTime;
76
+ if (elapsed > timeout) {
77
+ throw new Error(`Job polling timed out after ${timeout}ms`);
78
+ }
79
+ const status = await client.getJobStatus(jobId);
80
+ if (onUpdate) {
81
+ onUpdate(status);
82
+ }
83
+ if (status.status === "SUCCESS" || status.status === "FAILED") {
84
+ return status;
85
+ }
86
+ await new Promise((resolve) => setTimeout(resolve, currentInterval));
87
+ currentInterval = Math.min(currentInterval * 2, maxInterval);
88
+ }
89
+ }
90
+
91
+ export {
92
+ MikadoClient,
93
+ pollJob
94
+ };
@@ -0,0 +1,94 @@
1
+ // src/core/client.ts
2
+ var MikadoClient = class {
3
+ apiKey;
4
+ baseUrl;
5
+ constructor(config) {
6
+ this.apiKey = config.apiKey;
7
+ this.baseUrl = config.baseUrl.replace(/\/$/, "");
8
+ }
9
+ async request(path, options = {}) {
10
+ const url = `${this.baseUrl}${path}`;
11
+ const headers = {
12
+ "Authorization": `Bearer ${this.apiKey}`,
13
+ "Content-Type": "application/json",
14
+ ...options.headers
15
+ };
16
+ const response = await fetch(url, {
17
+ ...options,
18
+ headers
19
+ });
20
+ if (!response.ok) {
21
+ let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
22
+ try {
23
+ const errorBody = await response.json();
24
+ if (errorBody.message) {
25
+ errorMessage = errorBody.message;
26
+ } else if (errorBody.detail) {
27
+ errorMessage = errorBody.detail;
28
+ }
29
+ } catch {
30
+ }
31
+ throw new Error(errorMessage);
32
+ }
33
+ return response.json();
34
+ }
35
+ async whoami() {
36
+ return this.request("/api/cli/whoami");
37
+ }
38
+ async submit(content, options) {
39
+ const body = { content };
40
+ if (options?.campaign) {
41
+ body.campaign_id = options.campaign;
42
+ }
43
+ if (options?.filename) {
44
+ body.filename = options.filename;
45
+ }
46
+ return this.request("/api/cli/submit", {
47
+ method: "POST",
48
+ body: JSON.stringify(body)
49
+ });
50
+ }
51
+ async getJobStatus(jobId) {
52
+ return this.request(`/api/cli/jobs/${jobId}`);
53
+ }
54
+ async getConversation(conversationId) {
55
+ return this.request(
56
+ `/api/cli/conversations/${conversationId}`
57
+ );
58
+ }
59
+ async listCampaigns() {
60
+ return this.request("/api/cli/campaigns");
61
+ }
62
+ };
63
+
64
+ // src/core/poller.ts
65
+ async function pollJob(client, jobId, options = {}) {
66
+ const {
67
+ interval = 1e3,
68
+ timeout = 3e5,
69
+ onUpdate
70
+ } = options;
71
+ const startTime = Date.now();
72
+ let currentInterval = interval;
73
+ const maxInterval = 5e3;
74
+ while (true) {
75
+ const elapsed = Date.now() - startTime;
76
+ if (elapsed > timeout) {
77
+ throw new Error(`Job polling timed out after ${timeout}ms`);
78
+ }
79
+ const status = await client.getJobStatus(jobId);
80
+ if (onUpdate) {
81
+ onUpdate(status);
82
+ }
83
+ if (status.status === "COMPLETED" || status.status === "FAILED") {
84
+ return status;
85
+ }
86
+ await new Promise((resolve) => setTimeout(resolve, currentInterval));
87
+ currentInterval = Math.min(currentInterval * 2, maxInterval);
88
+ }
89
+ }
90
+
91
+ export {
92
+ MikadoClient,
93
+ pollJob
94
+ };
@@ -0,0 +1,94 @@
1
+ // src/core/client.ts
2
+ var MikadoClient = class {
3
+ apiKey;
4
+ baseUrl;
5
+ constructor(config) {
6
+ this.apiKey = config.apiKey;
7
+ this.baseUrl = config.baseUrl.replace(/\/$/, "");
8
+ }
9
+ async request(path, options = {}) {
10
+ const url = `${this.baseUrl}${path}`;
11
+ const headers = {
12
+ "Authorization": `Bearer ${this.apiKey}`,
13
+ "Content-Type": "application/json",
14
+ ...options.headers
15
+ };
16
+ const response = await fetch(url, {
17
+ ...options,
18
+ headers
19
+ });
20
+ if (!response.ok) {
21
+ let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
22
+ try {
23
+ const errorBody = await response.json();
24
+ if (errorBody.message) {
25
+ errorMessage = errorBody.message;
26
+ } else if (errorBody.detail) {
27
+ errorMessage = errorBody.detail;
28
+ }
29
+ } catch {
30
+ }
31
+ throw new Error(errorMessage);
32
+ }
33
+ return response.json();
34
+ }
35
+ async whoami() {
36
+ return this.request("/api/v1/cli/whoami");
37
+ }
38
+ async submit(content, options) {
39
+ const body = { content };
40
+ if (options?.campaign) {
41
+ body.campaign = options.campaign;
42
+ }
43
+ if (options?.filename) {
44
+ body.filename = options.filename;
45
+ }
46
+ return this.request("/api/v1/cli/submit", {
47
+ method: "POST",
48
+ body: JSON.stringify(body)
49
+ });
50
+ }
51
+ async getJobStatus(jobId) {
52
+ return this.request(`/api/v1/cli/jobs/${jobId}`);
53
+ }
54
+ async getConversation(conversationId) {
55
+ return this.request(
56
+ `/api/v1/cli/conversations/${conversationId}`
57
+ );
58
+ }
59
+ async listCampaigns() {
60
+ return this.request("/api/v1/cli/campaigns");
61
+ }
62
+ };
63
+
64
+ // src/core/poller.ts
65
+ async function pollJob(client, jobId, options = {}) {
66
+ const {
67
+ interval = 1e3,
68
+ timeout = 3e5,
69
+ onUpdate
70
+ } = options;
71
+ const startTime = Date.now();
72
+ let currentInterval = interval;
73
+ const maxInterval = 5e3;
74
+ while (true) {
75
+ const elapsed = Date.now() - startTime;
76
+ if (elapsed > timeout) {
77
+ throw new Error(`Job polling timed out after ${timeout}ms`);
78
+ }
79
+ const status = await client.getJobStatus(jobId);
80
+ if (onUpdate) {
81
+ onUpdate(status);
82
+ }
83
+ if (status.status === "SUCCESS" || status.status === "FAILED") {
84
+ return status;
85
+ }
86
+ await new Promise((resolve) => setTimeout(resolve, currentInterval));
87
+ currentInterval = Math.min(currentInterval * 2, maxInterval);
88
+ }
89
+ }
90
+
91
+ export {
92
+ MikadoClient,
93
+ pollJob
94
+ };
@@ -0,0 +1,95 @@
1
+ // src/core/client.ts
2
+ var MikadoClient = class {
3
+ apiKey;
4
+ baseUrl;
5
+ constructor(config) {
6
+ this.apiKey = config.apiKey;
7
+ this.baseUrl = config.baseUrl.replace(/\/$/, "");
8
+ }
9
+ async request(path, options = {}) {
10
+ const url = `${this.baseUrl}${path}`;
11
+ const headers = {
12
+ "Authorization": `Bearer ${this.apiKey}`,
13
+ "Content-Type": "application/json",
14
+ ...options.headers
15
+ };
16
+ const response = await fetch(url, {
17
+ ...options,
18
+ headers
19
+ });
20
+ if (!response.ok) {
21
+ let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
22
+ try {
23
+ const errorBody = await response.json();
24
+ if (errorBody.message) {
25
+ errorMessage = errorBody.message;
26
+ } else if (errorBody.detail) {
27
+ errorMessage = errorBody.detail;
28
+ }
29
+ } catch {
30
+ }
31
+ throw new Error(errorMessage);
32
+ }
33
+ return response.json();
34
+ }
35
+ // All /api/v1/* routes go via nginx directly to FastAPI (see nginx/nginx.conf)
36
+ async whoami() {
37
+ return this.request("/api/v1/cli/whoami");
38
+ }
39
+ async submit(content, options) {
40
+ const body = { content };
41
+ if (options?.campaign) {
42
+ body.campaign = options.campaign;
43
+ }
44
+ if (options?.filename) {
45
+ body.filename = options.filename;
46
+ }
47
+ return this.request("/api/v1/cli/submit", {
48
+ method: "POST",
49
+ body: JSON.stringify(body)
50
+ });
51
+ }
52
+ async getJobStatus(jobId) {
53
+ return this.request(`/api/v1/cli/jobs/${jobId}`);
54
+ }
55
+ async getConversation(conversationId) {
56
+ return this.request(
57
+ `/api/v1/cli/conversations/${conversationId}`
58
+ );
59
+ }
60
+ async listCampaigns() {
61
+ return this.request("/api/v1/cli/campaigns");
62
+ }
63
+ };
64
+
65
+ // src/core/poller.ts
66
+ async function pollJob(client, jobId, options = {}) {
67
+ const {
68
+ interval = 1e3,
69
+ timeout = 3e5,
70
+ onUpdate
71
+ } = options;
72
+ const startTime = Date.now();
73
+ let currentInterval = interval;
74
+ const maxInterval = 5e3;
75
+ while (true) {
76
+ const elapsed = Date.now() - startTime;
77
+ if (elapsed > timeout) {
78
+ throw new Error(`Job polling timed out after ${timeout}ms`);
79
+ }
80
+ const status = await client.getJobStatus(jobId);
81
+ if (onUpdate) {
82
+ onUpdate(status);
83
+ }
84
+ if (status.status === "SUCCESS" || status.status === "FAILED") {
85
+ return status;
86
+ }
87
+ await new Promise((resolve) => setTimeout(resolve, currentInterval));
88
+ currentInterval = Math.min(currentInterval * 2, maxInterval);
89
+ }
90
+ }
91
+
92
+ export {
93
+ MikadoClient,
94
+ pollJob
95
+ };
@@ -0,0 +1,94 @@
1
+ // src/core/client.ts
2
+ var MikadoClient = class {
3
+ apiKey;
4
+ baseUrl;
5
+ constructor(config) {
6
+ this.apiKey = config.apiKey;
7
+ this.baseUrl = config.baseUrl.replace(/\/$/, "");
8
+ }
9
+ async request(path, options = {}) {
10
+ const url = `${this.baseUrl}${path}`;
11
+ const headers = {
12
+ "Authorization": `Bearer ${this.apiKey}`,
13
+ "Content-Type": "application/json",
14
+ ...options.headers
15
+ };
16
+ const response = await fetch(url, {
17
+ ...options,
18
+ headers
19
+ });
20
+ if (!response.ok) {
21
+ let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
22
+ try {
23
+ const errorBody = await response.json();
24
+ if (errorBody.message) {
25
+ errorMessage = errorBody.message;
26
+ } else if (errorBody.detail) {
27
+ errorMessage = errorBody.detail;
28
+ }
29
+ } catch {
30
+ }
31
+ throw new Error(errorMessage);
32
+ }
33
+ return response.json();
34
+ }
35
+ async whoami() {
36
+ return this.request("/api/cli/whoami");
37
+ }
38
+ async submit(content, options) {
39
+ const body = { content };
40
+ if (options?.campaign) {
41
+ body.campaign = options.campaign;
42
+ }
43
+ if (options?.filename) {
44
+ body.filename = options.filename;
45
+ }
46
+ return this.request("/api/cli/submit", {
47
+ method: "POST",
48
+ body: JSON.stringify(body)
49
+ });
50
+ }
51
+ async getJobStatus(jobId) {
52
+ return this.request(`/api/cli/jobs/${jobId}`);
53
+ }
54
+ async getConversation(conversationId) {
55
+ return this.request(
56
+ `/api/cli/conversations/${conversationId}`
57
+ );
58
+ }
59
+ async listCampaigns() {
60
+ return this.request("/api/cli/campaigns");
61
+ }
62
+ };
63
+
64
+ // src/core/poller.ts
65
+ async function pollJob(client, jobId, options = {}) {
66
+ const {
67
+ interval = 1e3,
68
+ timeout = 3e5,
69
+ onUpdate
70
+ } = options;
71
+ const startTime = Date.now();
72
+ let currentInterval = interval;
73
+ const maxInterval = 5e3;
74
+ while (true) {
75
+ const elapsed = Date.now() - startTime;
76
+ if (elapsed > timeout) {
77
+ throw new Error(`Job polling timed out after ${timeout}ms`);
78
+ }
79
+ const status = await client.getJobStatus(jobId);
80
+ if (onUpdate) {
81
+ onUpdate(status);
82
+ }
83
+ if (status.status === "COMPLETED" || status.status === "FAILED") {
84
+ return status;
85
+ }
86
+ await new Promise((resolve) => setTimeout(resolve, currentInterval));
87
+ currentInterval = Math.min(currentInterval * 2, maxInterval);
88
+ }
89
+ }
90
+
91
+ export {
92
+ MikadoClient,
93
+ pollJob
94
+ };
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node