omnikey-cli 1.0.39 → 1.0.41

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/dist/index.js CHANGED
@@ -12,6 +12,7 @@ const showConfig_1 = require("./showConfig");
12
12
  const setConfig_1 = require("./setConfig");
13
13
  const grantBrowserAccess_1 = require("./grantBrowserAccess");
14
14
  const scheduleJob_1 = require("./scheduleJob");
15
+ const mcpServer_1 = require("./mcpServer");
15
16
  const program = new commander_1.Command();
16
17
  program
17
18
  .name('omnikey')
@@ -95,23 +96,62 @@ program
95
96
  .action(async () => {
96
97
  await (0, grantBrowserAccess_1.reopenBrowserDebugProfile)();
97
98
  });
98
- const scheduleCmd = program
99
- .command('schedule')
100
- .description('Manage scheduled prompt jobs');
99
+ const scheduleCmd = program.command('schedule').description('Manage scheduled prompt jobs');
101
100
  scheduleCmd
102
101
  .command('add')
103
102
  .description('Add a new scheduled job')
104
- .action(async () => { await (0, scheduleJob_1.scheduleAdd)(); });
103
+ .action(async () => {
104
+ await (0, scheduleJob_1.scheduleAdd)();
105
+ });
105
106
  scheduleCmd
106
107
  .command('list')
107
108
  .description('List all scheduled jobs')
108
- .action(async () => { await (0, scheduleJob_1.scheduleList)(); });
109
+ .action(async () => {
110
+ await (0, scheduleJob_1.scheduleList)();
111
+ });
109
112
  scheduleCmd
110
113
  .command('remove')
111
114
  .description('Remove a scheduled job')
112
- .action(async () => { await (0, scheduleJob_1.scheduleRemove)(); });
115
+ .action(async () => {
116
+ await (0, scheduleJob_1.scheduleRemove)();
117
+ });
113
118
  scheduleCmd
114
119
  .command('run-now <id>')
115
120
  .description('Immediately run a scheduled job by ID')
116
- .action(async (id) => { await (0, scheduleJob_1.scheduleRunNow)(id); });
121
+ .action(async (id) => {
122
+ await (0, scheduleJob_1.scheduleRunNow)(id);
123
+ });
124
+ const mcpCmd = program
125
+ .command('mcp')
126
+ .description('Manage MCP (Model Context Protocol) servers available to the agent');
127
+ mcpCmd
128
+ .command('add')
129
+ .description('Install a new MCP server')
130
+ .action(async () => {
131
+ await (0, mcpServer_1.mcpAdd)();
132
+ });
133
+ mcpCmd
134
+ .command('list')
135
+ .description('List installed MCP servers')
136
+ .action(async () => {
137
+ await (0, mcpServer_1.mcpList)();
138
+ });
139
+ mcpCmd
140
+ .command('remove')
141
+ .description('Remove an installed MCP server')
142
+ .action(async () => {
143
+ await (0, mcpServer_1.mcpRemove)();
144
+ });
145
+ mcpCmd
146
+ .command('toggle <id>')
147
+ .description('Toggle an MCP server enabled/disabled by ID')
148
+ .action(async (id) => {
149
+ await (0, mcpServer_1.mcpToggle)(id);
150
+ });
151
+ mcpCmd
152
+ .command('update <id>')
153
+ .description('Update an existing MCP server by ID')
154
+ .action(async (id) => {
155
+ await (0, mcpServer_1.mcpUpdate)(id);
156
+ });
117
157
  program.parseAsync(process.argv);
@@ -0,0 +1,334 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.mcpAdd = mcpAdd;
7
+ exports.mcpList = mcpList;
8
+ exports.mcpRemove = mcpRemove;
9
+ exports.mcpToggle = mcpToggle;
10
+ exports.mcpUpdate = mcpUpdate;
11
+ const axios_1 = __importDefault(require("axios"));
12
+ const inquirer_1 = __importDefault(require("inquirer"));
13
+ const utils_1 = require("./utils");
14
+ async function getJwt() {
15
+ const config = (0, utils_1.readConfig)();
16
+ const port = (0, utils_1.getPort)();
17
+ const licenseKey = config.OMNIKEY_LICENSE_KEY || '';
18
+ const baseUrl = `http://localhost:${port}`;
19
+ const res = await axios_1.default.post(`${baseUrl}/api/subscription/activate`, { licenseKey }, { timeout: 10000 });
20
+ return res.data.token;
21
+ }
22
+ function getBaseUrl() {
23
+ return `http://localhost:${(0, utils_1.getPort)()}`;
24
+ }
25
+ async function authHeaders() {
26
+ let token;
27
+ try {
28
+ token = await getJwt();
29
+ }
30
+ catch (err) {
31
+ throw new Error(`Authentication failed — make sure the OmniKey backend is running and your license key is configured.\nCause: ${err?.message ?? String(err)}`);
32
+ }
33
+ return { Authorization: `Bearer ${token}` };
34
+ }
35
+ function parseLines(input) {
36
+ return input
37
+ .split('\n')
38
+ .map((l) => l.trim())
39
+ .filter((l) => l.length > 0);
40
+ }
41
+ function parseKeyValueLines(input) {
42
+ const out = {};
43
+ for (const line of parseLines(input)) {
44
+ const idx = line.indexOf('=');
45
+ if (idx <= 0)
46
+ continue;
47
+ const key = line.slice(0, idx).trim();
48
+ const value = line.slice(idx + 1).trim();
49
+ if (key)
50
+ out[key] = value;
51
+ }
52
+ return out;
53
+ }
54
+ async function promptTransportFields(transport, defaults) {
55
+ if (transport === 'stdio') {
56
+ const ans = await inquirer_1.default.prompt([
57
+ {
58
+ type: 'input',
59
+ name: 'command',
60
+ message: 'Command (executable path or name):',
61
+ default: defaults?.command ?? '',
62
+ validate: (v) => v.trim().length > 0 || 'Command is required for stdio transport',
63
+ },
64
+ {
65
+ type: 'editor',
66
+ name: 'args',
67
+ message: 'Args (one per line):',
68
+ default: (defaults?.args ?? []).join('\n'),
69
+ },
70
+ {
71
+ type: 'editor',
72
+ name: 'env',
73
+ message: 'Environment variables (one KEY=VALUE per line):',
74
+ default: formatKVForEditor(defaults?.env),
75
+ },
76
+ ]);
77
+ return {
78
+ command: ans.command.trim(),
79
+ args: parseLines(ans.args),
80
+ env: parseKeyValueLines(ans.env),
81
+ url: null,
82
+ headers: {},
83
+ };
84
+ }
85
+ const ans = await inquirer_1.default.prompt([
86
+ {
87
+ type: 'input',
88
+ name: 'url',
89
+ message: `URL for ${transport} transport:`,
90
+ default: defaults?.url ?? '',
91
+ validate: (v) => v.trim().length > 0 || 'URL is required',
92
+ },
93
+ {
94
+ type: 'editor',
95
+ name: 'headers',
96
+ message: 'Headers (one KEY=VALUE per line):',
97
+ default: formatKVForEditor(defaults?.headers),
98
+ },
99
+ ]);
100
+ return {
101
+ url: ans.url.trim(),
102
+ headers: parseKeyValueLines(ans.headers),
103
+ command: null,
104
+ args: [],
105
+ env: {},
106
+ };
107
+ }
108
+ function formatKVForEditor(dict) {
109
+ if (!dict)
110
+ return '';
111
+ return Object.entries(dict)
112
+ .map(([k, v]) => `${k}=${v}`)
113
+ .join('\n');
114
+ }
115
+ async function mcpAdd() {
116
+ const baseAnswers = await inquirer_1.default.prompt([
117
+ {
118
+ type: 'input',
119
+ name: 'name',
120
+ message: 'Name (unique, e.g. "github"):',
121
+ validate: (v) => v.trim().length > 0 || 'Name is required',
122
+ },
123
+ {
124
+ type: 'input',
125
+ name: 'description',
126
+ message: 'Description (optional):',
127
+ },
128
+ {
129
+ type: 'list',
130
+ name: 'transport',
131
+ message: 'Transport:',
132
+ choices: [
133
+ { name: 'stdio (local process)', value: 'stdio' },
134
+ { name: 'http', value: 'http' },
135
+ { name: 'sse', value: 'sse' },
136
+ ],
137
+ default: 'stdio',
138
+ },
139
+ {
140
+ type: 'confirm',
141
+ name: 'isEnabled',
142
+ message: 'Enabled?',
143
+ default: true,
144
+ },
145
+ ]);
146
+ const transportFields = await promptTransportFields(baseAnswers.transport);
147
+ try {
148
+ const headers = await authHeaders();
149
+ const res = await axios_1.default.post(`${getBaseUrl()}/api/mcp-servers`, {
150
+ name: baseAnswers.name.trim(),
151
+ description: baseAnswers.description.trim() || null,
152
+ transport: baseAnswers.transport,
153
+ isEnabled: baseAnswers.isEnabled,
154
+ ...transportFields,
155
+ }, { headers, timeout: 15000 });
156
+ console.log('\nMCP server created:');
157
+ printServer(res.data);
158
+ }
159
+ catch (err) {
160
+ const msg = err.response?.data?.error ?? err.message;
161
+ console.error(`Error creating MCP server: ${msg}`);
162
+ }
163
+ }
164
+ async function mcpList() {
165
+ try {
166
+ const headers = await authHeaders();
167
+ const res = await axios_1.default.get(`${getBaseUrl()}/api/mcp-servers`, {
168
+ headers,
169
+ timeout: 10000,
170
+ });
171
+ const { servers } = res.data;
172
+ if (servers.length === 0) {
173
+ console.log('No MCP servers installed.');
174
+ return;
175
+ }
176
+ console.log('\nMCP Servers:');
177
+ console.log('─'.repeat(90));
178
+ console.log(padRight('ID', 28) +
179
+ padRight('Name', 22) +
180
+ padRight('Transport', 12) +
181
+ padRight('Enabled', 10) +
182
+ 'Endpoint');
183
+ console.log('─'.repeat(90));
184
+ for (const s of servers) {
185
+ const endpoint = s.transport === 'stdio' ? (s.command ?? '—') : (s.url ?? '—');
186
+ console.log(padRight(s.id.slice(0, 26), 28) +
187
+ padRight(s.name.slice(0, 20), 22) +
188
+ padRight(s.transport, 12) +
189
+ padRight(s.isEnabled ? 'yes' : 'no', 10) +
190
+ endpoint.slice(0, 40));
191
+ }
192
+ console.log('─'.repeat(90));
193
+ }
194
+ catch (err) {
195
+ const msg = err.response?.data?.error ?? err.message;
196
+ console.error(`Error fetching MCP servers: ${msg}`);
197
+ }
198
+ }
199
+ async function pickServer(action) {
200
+ const headers = await authHeaders();
201
+ const res = await axios_1.default.get(`${getBaseUrl()}/api/mcp-servers`, {
202
+ headers,
203
+ timeout: 10000,
204
+ });
205
+ const { servers } = res.data;
206
+ if (servers.length === 0) {
207
+ console.log(`No MCP servers to ${action}.`);
208
+ return null;
209
+ }
210
+ const { id } = await inquirer_1.default.prompt([
211
+ {
212
+ type: 'list',
213
+ name: 'id',
214
+ message: `Select an MCP server to ${action}:`,
215
+ choices: servers.map((s) => ({
216
+ name: `${s.name} [${s.transport}] ${s.isEnabled ? '✓' : '✗'}`,
217
+ value: s.id,
218
+ })),
219
+ },
220
+ ]);
221
+ return servers.find((s) => s.id === id) ?? null;
222
+ }
223
+ async function mcpRemove() {
224
+ try {
225
+ const server = await pickServer('remove');
226
+ if (!server)
227
+ return;
228
+ const { confirm } = await inquirer_1.default.prompt([
229
+ {
230
+ type: 'confirm',
231
+ name: 'confirm',
232
+ message: `Delete MCP server "${server.name}"?`,
233
+ default: false,
234
+ },
235
+ ]);
236
+ if (!confirm) {
237
+ console.log('Aborted.');
238
+ return;
239
+ }
240
+ const headers = await authHeaders();
241
+ await axios_1.default.delete(`${getBaseUrl()}/api/mcp-servers/${server.id}`, {
242
+ headers,
243
+ timeout: 10000,
244
+ });
245
+ console.log('MCP server removed.');
246
+ }
247
+ catch (err) {
248
+ const msg = err.response?.data?.error ?? err.message;
249
+ console.error(`Error removing MCP server: ${msg}`);
250
+ }
251
+ }
252
+ async function mcpToggle(id) {
253
+ try {
254
+ const headers = await authHeaders();
255
+ const current = await axios_1.default.get(`${getBaseUrl()}/api/mcp-servers/${id}`, {
256
+ headers,
257
+ timeout: 10000,
258
+ });
259
+ const newState = !current.data.isEnabled;
260
+ const res = await axios_1.default.patch(`${getBaseUrl()}/api/mcp-servers/${id}`, { isEnabled: newState }, { headers, timeout: 10000 });
261
+ console.log(`MCP server ${res.data.name} is now ${res.data.isEnabled ? 'enabled' : 'disabled'}.`);
262
+ }
263
+ catch (err) {
264
+ const msg = err.response?.data?.error ?? err.message;
265
+ console.error(`Error toggling MCP server: ${msg}`);
266
+ }
267
+ }
268
+ async function mcpUpdate(id) {
269
+ try {
270
+ const headers = await authHeaders();
271
+ const current = (await axios_1.default.get(`${getBaseUrl()}/api/mcp-servers/${id}`, {
272
+ headers,
273
+ timeout: 10000,
274
+ })).data;
275
+ const baseAnswers = await inquirer_1.default.prompt([
276
+ {
277
+ type: 'input',
278
+ name: 'name',
279
+ message: 'Name:',
280
+ default: current.name,
281
+ },
282
+ {
283
+ type: 'input',
284
+ name: 'description',
285
+ message: 'Description:',
286
+ default: current.description ?? '',
287
+ },
288
+ {
289
+ type: 'list',
290
+ name: 'transport',
291
+ message: 'Transport:',
292
+ choices: ['stdio', 'http', 'sse'],
293
+ default: current.transport,
294
+ },
295
+ {
296
+ type: 'confirm',
297
+ name: 'isEnabled',
298
+ message: 'Enabled?',
299
+ default: current.isEnabled,
300
+ },
301
+ ]);
302
+ const transportFields = await promptTransportFields(baseAnswers.transport, current);
303
+ const res = await axios_1.default.patch(`${getBaseUrl()}/api/mcp-servers/${id}`, {
304
+ name: baseAnswers.name.trim(),
305
+ description: baseAnswers.description.trim() || null,
306
+ transport: baseAnswers.transport,
307
+ isEnabled: baseAnswers.isEnabled,
308
+ ...transportFields,
309
+ }, { headers, timeout: 15000 });
310
+ console.log('\nMCP server updated:');
311
+ printServer(res.data);
312
+ }
313
+ catch (err) {
314
+ const msg = err.response?.data?.error ?? err.message;
315
+ console.error(`Error updating MCP server: ${msg}`);
316
+ }
317
+ }
318
+ function printServer(s) {
319
+ console.log(` ID: ${s.id}`);
320
+ console.log(` Name: ${s.name}`);
321
+ console.log(` Transport: ${s.transport}`);
322
+ console.log(` Enabled: ${s.isEnabled}`);
323
+ if (s.transport === 'stdio') {
324
+ console.log(` Command: ${s.command ?? '—'}`);
325
+ if (s.args.length > 0)
326
+ console.log(` Args: ${s.args.join(' ')}`);
327
+ }
328
+ else {
329
+ console.log(` URL: ${s.url ?? '—'}`);
330
+ }
331
+ }
332
+ function padRight(str, width) {
333
+ return str.length >= width ? str.slice(0, width) : str + ' '.repeat(width - str.length);
334
+ }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public",
5
5
  "registry": "https://registry.npmjs.org/"
6
6
  },
7
- "version": "1.0.39",
7
+ "version": "1.0.41",
8
8
  "description": "CLI for onboarding users to Omnikey AI and configuring OPENAI_API_KEY. Use Yarn for install/build.",
9
9
  "engines": {
10
10
  "node": ">=14.0.0",
@@ -30,6 +30,7 @@
30
30
  "@anthropic-ai/sdk": "^0.96.0",
31
31
  "@google-cloud/storage": "^7.19.0",
32
32
  "@google/genai": "^1.46.0",
33
+ "@modelcontextprotocol/sdk": "^1.29.0",
33
34
  "axios": "^1.13.5",
34
35
  "commander": "^11.0.0",
35
36
  "cors": "^2.8.5",
package/src/index.ts CHANGED
@@ -11,6 +11,7 @@ import { showConfig } from './showConfig';
11
11
  import { setConfig } from './setConfig';
12
12
  import { grantBrowserAccess, reopenBrowserDebugProfile } from './grantBrowserAccess';
13
13
  import { scheduleAdd, scheduleList, scheduleRemove, scheduleRunNow } from './scheduleJob';
14
+ import { mcpAdd, mcpList, mcpRemove, mcpToggle, mcpUpdate } from './mcpServer';
14
15
 
15
16
  const program = new Command();
16
17
 
@@ -110,28 +111,73 @@ program
110
111
  await reopenBrowserDebugProfile();
111
112
  });
112
113
 
113
- const scheduleCmd = program
114
- .command('schedule')
115
- .description('Manage scheduled prompt jobs');
114
+ const scheduleCmd = program.command('schedule').description('Manage scheduled prompt jobs');
116
115
 
117
116
  scheduleCmd
118
117
  .command('add')
119
118
  .description('Add a new scheduled job')
120
- .action(async () => { await scheduleAdd(); });
119
+ .action(async () => {
120
+ await scheduleAdd();
121
+ });
121
122
 
122
123
  scheduleCmd
123
124
  .command('list')
124
125
  .description('List all scheduled jobs')
125
- .action(async () => { await scheduleList(); });
126
+ .action(async () => {
127
+ await scheduleList();
128
+ });
126
129
 
127
130
  scheduleCmd
128
131
  .command('remove')
129
132
  .description('Remove a scheduled job')
130
- .action(async () => { await scheduleRemove(); });
133
+ .action(async () => {
134
+ await scheduleRemove();
135
+ });
131
136
 
132
137
  scheduleCmd
133
138
  .command('run-now <id>')
134
139
  .description('Immediately run a scheduled job by ID')
135
- .action(async (id: string) => { await scheduleRunNow(id); });
140
+ .action(async (id: string) => {
141
+ await scheduleRunNow(id);
142
+ });
143
+
144
+ const mcpCmd = program
145
+ .command('mcp')
146
+ .description('Manage MCP (Model Context Protocol) servers available to the agent');
147
+
148
+ mcpCmd
149
+ .command('add')
150
+ .description('Install a new MCP server')
151
+ .action(async () => {
152
+ await mcpAdd();
153
+ });
154
+
155
+ mcpCmd
156
+ .command('list')
157
+ .description('List installed MCP servers')
158
+ .action(async () => {
159
+ await mcpList();
160
+ });
161
+
162
+ mcpCmd
163
+ .command('remove')
164
+ .description('Remove an installed MCP server')
165
+ .action(async () => {
166
+ await mcpRemove();
167
+ });
168
+
169
+ mcpCmd
170
+ .command('toggle <id>')
171
+ .description('Toggle an MCP server enabled/disabled by ID')
172
+ .action(async (id: string) => {
173
+ await mcpToggle(id);
174
+ });
175
+
176
+ mcpCmd
177
+ .command('update <id>')
178
+ .description('Update an existing MCP server by ID')
179
+ .action(async (id: string) => {
180
+ await mcpUpdate(id);
181
+ });
136
182
 
137
183
  program.parseAsync(process.argv);