@hypertabai/mcp 0.2.0 → 1.0.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.
Files changed (75) hide show
  1. package/README.md +264 -0
  2. package/dist/bridge/api-client.d.ts +157 -0
  3. package/dist/bridge/api-client.d.ts.map +1 -0
  4. package/dist/bridge/api-client.js +140 -0
  5. package/dist/bridge/api-client.js.map +1 -0
  6. package/dist/bridge/artifacts.d.ts +11 -0
  7. package/dist/bridge/artifacts.d.ts.map +1 -0
  8. package/dist/bridge/artifacts.js +45 -0
  9. package/dist/bridge/artifacts.js.map +1 -0
  10. package/dist/bridge/command-contract.d.ts +14 -0
  11. package/dist/bridge/command-contract.d.ts.map +1 -0
  12. package/dist/bridge/command-contract.js +152 -0
  13. package/dist/bridge/command-contract.js.map +1 -0
  14. package/dist/bridge/index.d.ts +248 -0
  15. package/dist/bridge/index.d.ts.map +1 -0
  16. package/dist/bridge/index.js +875 -0
  17. package/dist/bridge/index.js.map +1 -0
  18. package/dist/bridge/spool.d.ts +30 -0
  19. package/dist/bridge/spool.d.ts.map +1 -0
  20. package/dist/bridge/spool.js +108 -0
  21. package/dist/bridge/spool.js.map +1 -0
  22. package/dist/connect.js +1 -1
  23. package/dist/idempotency.d.ts +11 -0
  24. package/dist/idempotency.d.ts.map +1 -0
  25. package/dist/idempotency.js +63 -0
  26. package/dist/idempotency.js.map +1 -0
  27. package/dist/index.js +5 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/install/skill-writer.d.ts +1 -1
  30. package/dist/install/skill-writer.d.ts.map +1 -1
  31. package/dist/install/skill-writer.js +21 -1
  32. package/dist/install/skill-writer.js.map +1 -1
  33. package/dist/surfaces/build.d.ts +22 -0
  34. package/dist/surfaces/build.d.ts.map +1 -0
  35. package/dist/surfaces/build.js +122 -0
  36. package/dist/surfaces/build.js.map +1 -0
  37. package/dist/surfaces/detect.d.ts +20 -0
  38. package/dist/surfaces/detect.d.ts.map +1 -0
  39. package/dist/surfaces/detect.js +130 -0
  40. package/dist/surfaces/detect.js.map +1 -0
  41. package/dist/surfaces/index.d.ts +35 -0
  42. package/dist/surfaces/index.d.ts.map +1 -0
  43. package/dist/surfaces/index.js +117 -0
  44. package/dist/surfaces/index.js.map +1 -0
  45. package/dist/surfaces/package.d.ts +48 -0
  46. package/dist/surfaces/package.d.ts.map +1 -0
  47. package/dist/surfaces/package.js +86 -0
  48. package/dist/surfaces/package.js.map +1 -0
  49. package/dist/surfaces/publish-client.d.ts +40 -0
  50. package/dist/surfaces/publish-client.d.ts.map +1 -0
  51. package/dist/surfaces/publish-client.js +111 -0
  52. package/dist/surfaces/publish-client.js.map +1 -0
  53. package/package.json +8 -3
  54. package/templates/bridge-wrapper.mjs +138 -0
  55. package/templates/bridge-wrapper.sh +78 -0
  56. package/dist/__tests__/config-writer.test.d.ts +0 -2
  57. package/dist/__tests__/config-writer.test.d.ts.map +0 -1
  58. package/dist/__tests__/config-writer.test.js +0 -186
  59. package/dist/__tests__/config-writer.test.js.map +0 -1
  60. package/dist/__tests__/index.test.d.ts +0 -2
  61. package/dist/__tests__/index.test.d.ts.map +0 -1
  62. package/dist/__tests__/index.test.js +0 -113
  63. package/dist/__tests__/index.test.js.map +0 -1
  64. package/dist/__tests__/install.test.d.ts +0 -2
  65. package/dist/__tests__/install.test.d.ts.map +0 -1
  66. package/dist/__tests__/install.test.js +0 -211
  67. package/dist/__tests__/install.test.js.map +0 -1
  68. package/dist/__tests__/platforms.test.d.ts +0 -2
  69. package/dist/__tests__/platforms.test.d.ts.map +0 -1
  70. package/dist/__tests__/platforms.test.js +0 -109
  71. package/dist/__tests__/platforms.test.js.map +0 -1
  72. package/dist/__tests__/uninstall.test.d.ts +0 -2
  73. package/dist/__tests__/uninstall.test.d.ts.map +0 -1
  74. package/dist/__tests__/uninstall.test.js +0 -117
  75. package/dist/__tests__/uninstall.test.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hypertabai/mcp",
3
- "version": "0.2.0",
3
+ "version": "1.0.0",
4
4
  "description": "One-command Hypertab MCP installer and client for Claude Code, Cursor, Windsurf, and more.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,7 +9,8 @@
9
9
  },
10
10
  "files": [
11
11
  "dist",
12
- "README.md"
12
+ "README.md",
13
+ "templates"
13
14
  ],
14
15
  "scripts": {
15
16
  "build": "tsc",
@@ -28,9 +29,13 @@
28
29
  ],
29
30
  "dependencies": {
30
31
  "@modelcontextprotocol/sdk": "^1.0.0",
31
- "commander": "latest"
32
+ "commander": "latest",
33
+ "fast-glob": "^3.3.3",
34
+ "fflate": "^0.8.3",
35
+ "mime-types": "^3.0.2"
32
36
  },
33
37
  "devDependencies": {
38
+ "@types/mime-types": "^3.0.1",
34
39
  "@types/node": "^20.0.0",
35
40
  "typescript": "~5.9.0",
36
41
  "vitest": "^3.2.4"
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env node
2
+ import { mkdtemp, writeFile } from 'node:fs/promises'
3
+ import { tmpdir } from 'node:os'
4
+ import { join } from 'node:path'
5
+ import { spawn } from 'node:child_process'
6
+
7
+ const bridgeBin = process.env.BRIDGE_BIN ?? 'npx'
8
+ const bridgeArgsPrefix = process.env.BRIDGE_BIN ? [] : ['@hypertabai/mcp']
9
+ const clientName = process.env.HYPERTAB_BRIDGE_CLIENT_NAME ?? `${process.env.USER ?? 'local'}-codex`
10
+ const clientType = process.env.HYPERTAB_BRIDGE_CLIENT_TYPE ?? 'codex'
11
+ const objective = process.argv.slice(2).join(' ') || 'Report local bridge wrapper health.'
12
+
13
+ if (!process.env.HYPERTAB_API_KEY) {
14
+ console.error('Set HYPERTAB_API_KEY before running this wrapper.')
15
+ process.exit(2)
16
+ }
17
+
18
+ const workDir = await mkdtemp(join(tmpdir(), 'hypertab-bridge-'))
19
+ const cycle = await bridge([
20
+ 'bridge',
21
+ 'start',
22
+ '--client-name',
23
+ clientName,
24
+ '--client-type',
25
+ clientType,
26
+ '--objective',
27
+ objective,
28
+ '--once',
29
+ '--json',
30
+ ])
31
+ const records = parseJsonLines(cycle.stdout)
32
+ const status = records.find((record) => record.type === 'bridge_status')
33
+ const runId = typeof status?.run?.id === 'string' ? status.run.id : null
34
+
35
+ if (runId) {
36
+ const eventFile = join(workDir, 'events.json')
37
+ await writeFile(
38
+ eventFile,
39
+ JSON.stringify(
40
+ {
41
+ events: [
42
+ {
43
+ run_id: runId,
44
+ type: 'status',
45
+ message: 'Node bridge wrapper cycle completed safely.',
46
+ data: { wrapper: 'bridge-wrapper.mjs', client_name: clientName },
47
+ idempotency_key: `starter-node-cycle:${runId}`,
48
+ },
49
+ ],
50
+ },
51
+ null,
52
+ 2,
53
+ ),
54
+ )
55
+ await bridge(['bridge', 'event', 'batch', '--file', eventFile, '--json'])
56
+ }
57
+
58
+ for (const record of records.filter((item) => item.type === 'bridge_command')) {
59
+ await handleCommand(record, runId)
60
+ }
61
+
62
+ async function handleCommand(record, runId) {
63
+ const command = record.command ?? {}
64
+ const contract = record.contract ?? {}
65
+ const commandId = command.id
66
+ if (typeof commandId !== 'string') return
67
+
68
+ if (!contract.allowed) {
69
+ await result(commandId, 'failed', contract.message ?? 'Command is blocked by bridge contract.')
70
+ return
71
+ }
72
+
73
+ if (command.command_type === 'agent.status_request') {
74
+ if (runId) await appendStatusEvent(runId, 'Starter wrapper handled agent.status_request.')
75
+ await result(commandId, 'completed', 'Starter wrapper handled safe status request.')
76
+ return
77
+ }
78
+
79
+ if (command.command_type === 'bridge.health_request') {
80
+ await bridge(['bridge', 'status', '--json'])
81
+ await result(commandId, 'completed', 'Starter wrapper read bridge health.')
82
+ return
83
+ }
84
+
85
+ await result(commandId, 'cancelled', `Starter wrapper requires local policy for ${command.command_type}.`)
86
+ }
87
+
88
+ async function appendStatusEvent(runId, message) {
89
+ const eventFile = join(workDir, `event-${Date.now()}.json`)
90
+ await writeFile(
91
+ eventFile,
92
+ JSON.stringify({
93
+ events: [{ run_id: runId, type: 'status', message, idempotency_key: `${runId}:${Date.now()}` }],
94
+ }),
95
+ )
96
+ await bridge(['bridge', 'event', 'batch', '--file', eventFile, '--json'])
97
+ }
98
+
99
+ function result(commandId, status, message) {
100
+ return bridge(['bridge', 'result', '--command-id', commandId, '--status', status, '--error-message', message, '--json'])
101
+ }
102
+
103
+ async function bridge(args) {
104
+ const command = bridgeBin
105
+ const allArgs = [...bridgeArgsPrefix, ...args]
106
+ const child = spawn(command, allArgs, { env: process.env, stdio: ['ignore', 'pipe', 'pipe'] })
107
+ const [stdout, stderr, code] = await Promise.all([readStream(child.stdout), readStream(child.stderr), exitCode(child)])
108
+ if (code !== 0) {
109
+ console.error(stderr || stdout)
110
+ process.exit(code)
111
+ }
112
+ return { stdout, stderr }
113
+ }
114
+
115
+ function parseJsonLines(text) {
116
+ return text
117
+ .split('\n')
118
+ .map((line) => line.trim())
119
+ .filter(Boolean)
120
+ .map((line) => JSON.parse(line))
121
+ }
122
+
123
+ function readStream(stream) {
124
+ return new Promise((resolve) => {
125
+ let output = ''
126
+ stream.setEncoding('utf8')
127
+ stream.on('data', (chunk) => {
128
+ output += chunk
129
+ })
130
+ stream.on('end', () => resolve(output))
131
+ })
132
+ }
133
+
134
+ function exitCode(child) {
135
+ return new Promise((resolve) => {
136
+ child.on('close', (code) => resolve(code ?? 1))
137
+ })
138
+ }
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Minimal safe local bridge wrapper.
5
+ # Requires HYPERTAB_API_KEY. Override BRIDGE_BIN for local development.
6
+
7
+ BRIDGE_BIN="${BRIDGE_BIN:-npx @hypertabai/mcp}"
8
+ CLIENT_NAME="${HYPERTAB_BRIDGE_CLIENT_NAME:-$(hostname)-codex}"
9
+ CLIENT_TYPE="${HYPERTAB_BRIDGE_CLIENT_TYPE:-codex}"
10
+ OBJECTIVE="${1:-Report local bridge wrapper health.}"
11
+ WORK_DIR="${HYPERTAB_BRIDGE_WORK_DIR:-.hypertab}"
12
+ CYCLE_FILE="$WORK_DIR/bridge-cycle.jsonl"
13
+ EVENT_FILE="$WORK_DIR/events.json"
14
+
15
+ mkdir -p "$WORK_DIR"
16
+
17
+ $BRIDGE_BIN bridge start \
18
+ --client-name "$CLIENT_NAME" \
19
+ --client-type "$CLIENT_TYPE" \
20
+ --objective "$OBJECTIVE" \
21
+ --once \
22
+ --json | tee "$CYCLE_FILE"
23
+
24
+ RUN_ID="$(
25
+ node - "$CYCLE_FILE" <<'NODE'
26
+ const fs = require('node:fs')
27
+ const file = process.argv[2]
28
+ for (const line of fs.readFileSync(file, 'utf8').trim().split('\n')) {
29
+ if (!line) continue
30
+ const record = JSON.parse(line)
31
+ if (record.type === 'bridge_status' && record.run?.id) {
32
+ process.stdout.write(record.run.id)
33
+ process.exit(0)
34
+ }
35
+ }
36
+ NODE
37
+ )"
38
+
39
+ if [[ -n "${RUN_ID:-}" ]]; then
40
+ node - "$RUN_ID" "$EVENT_FILE" <<'NODE'
41
+ const fs = require('node:fs')
42
+ const [runId, file] = process.argv.slice(2)
43
+ fs.writeFileSync(file, JSON.stringify({
44
+ events: [{
45
+ run_id: runId,
46
+ type: 'status',
47
+ message: 'Bridge wrapper cycle completed safely.',
48
+ data: { wrapper: 'bridge-wrapper.sh' },
49
+ idempotency_key: `starter-shell-cycle:${runId}`,
50
+ }],
51
+ }, null, 2))
52
+ NODE
53
+ $BRIDGE_BIN bridge event batch --file "$EVENT_FILE" --json
54
+ fi
55
+
56
+ node - "$CYCLE_FILE" <<'NODE' | while IFS=$'\t' read -r command_id status message; do
57
+ const fs = require('node:fs')
58
+ const file = process.argv[2]
59
+ for (const line of fs.readFileSync(file, 'utf8').trim().split('\n')) {
60
+ if (!line) continue
61
+ const record = JSON.parse(line)
62
+ if (record.type !== 'bridge_command') continue
63
+ const command = record.command ?? {}
64
+ const contract = record.contract ?? {}
65
+ if (!contract.allowed) {
66
+ console.log([command.id, 'failed', contract.message ?? 'Command is blocked by bridge contract.'].join('\t'))
67
+ continue
68
+ }
69
+ if (command.command_type === 'agent.status_request' || command.command_type === 'bridge.health_request') {
70
+ console.log([command.id, 'completed', 'Starter wrapper handled safe status request.'].join('\t'))
71
+ continue
72
+ }
73
+ console.log([command.id, 'cancelled', `Starter wrapper requires local policy for ${command.command_type}.`].join('\t'))
74
+ }
75
+ NODE
76
+ [[ -z "$command_id" ]] && continue
77
+ $BRIDGE_BIN bridge result --command-id "$command_id" --status "$status" --error-message "$message" --json
78
+ done
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=config-writer.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config-writer.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/config-writer.test.ts"],"names":[],"mappings":""}
@@ -1,186 +0,0 @@
1
- import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import { join } from 'node:path';
4
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
5
- import { removePlatformConfig, stripJsonComments, writePlatformConfig } from '../install/config-writer.js';
6
- function tmpPlatform(configFile) {
7
- return {
8
- id: 'test',
9
- name: 'Test',
10
- description: 'Test',
11
- supportsUserScope: true,
12
- supportsProjectScope: true,
13
- configPath: (_scope) => configFile,
14
- serversKey: 'mcpServers',
15
- serverName: 'hypertab',
16
- buildServerEntry: (url, apiKey) => ({
17
- type: 'http',
18
- url,
19
- headers: { Authorization: `Bearer ${apiKey}` },
20
- }),
21
- supportsSkill: false,
22
- restartInstruction: '',
23
- };
24
- }
25
- const ENTRY = {
26
- type: 'http',
27
- url: 'https://api.hypertab.ai/mcp',
28
- headers: { Authorization: 'Bearer ht_sk_test' },
29
- };
30
- let dir;
31
- beforeEach(() => {
32
- dir = mkdtempSync(join(tmpdir(), 'hypertab-cfg-'));
33
- });
34
- afterEach(() => {
35
- rmSync(dir, { recursive: true, force: true });
36
- });
37
- describe('writePlatformConfig', () => {
38
- it('creates a new config file when none exists', () => {
39
- const file = join(dir, 'mcp.json');
40
- const result = writePlatformConfig({ platform: tmpPlatform(file), scope: 'user', entry: ENTRY });
41
- expect(result.action).toBe('created');
42
- expect(result.backupCreated).toBe(false);
43
- expect(existsSync(file)).toBe(true);
44
- const written = JSON.parse(readFileSync(file, 'utf8'));
45
- expect(written).toEqual({ mcpServers: { hypertab: ENTRY } });
46
- });
47
- it('merges into an existing file without touching other servers', () => {
48
- const file = join(dir, 'mcp.json');
49
- const existing = {
50
- mcpServers: {
51
- other: { command: 'some-server', args: [] },
52
- another: { type: 'http', url: 'https://x' },
53
- },
54
- };
55
- writeFileSync(file, JSON.stringify(existing, null, 2));
56
- const result = writePlatformConfig({ platform: tmpPlatform(file), scope: 'user', entry: ENTRY });
57
- expect(result.action).toBe('updated');
58
- expect(result.backupCreated).toBe(true);
59
- expect(existsSync(`${file}.bak`)).toBe(true);
60
- const written = JSON.parse(readFileSync(file, 'utf8'));
61
- expect(written.mcpServers.other).toEqual(existing.mcpServers.other);
62
- expect(written.mcpServers.another).toEqual(existing.mcpServers.another);
63
- expect(written.mcpServers.hypertab).toEqual(ENTRY);
64
- });
65
- it('returns no-change when the entry already matches', () => {
66
- const file = join(dir, 'mcp.json');
67
- writeFileSync(file, JSON.stringify({ mcpServers: { hypertab: ENTRY } }, null, 2));
68
- const result = writePlatformConfig({ platform: tmpPlatform(file), scope: 'user', entry: ENTRY });
69
- expect(result.action).toBe('no-change');
70
- expect(result.backupCreated).toBe(false);
71
- expect(existsSync(`${file}.bak`)).toBe(false);
72
- });
73
- it('updates in place when the hypertab entry changed (e.g. new key)', () => {
74
- const file = join(dir, 'mcp.json');
75
- const oldEntry = {
76
- type: 'http',
77
- url: 'https://api.hypertab.ai/mcp',
78
- headers: { Authorization: 'Bearer ht_sk_old' },
79
- };
80
- writeFileSync(file, JSON.stringify({ mcpServers: { hypertab: oldEntry } }, null, 2));
81
- const result = writePlatformConfig({ platform: tmpPlatform(file), scope: 'user', entry: ENTRY });
82
- expect(result.action).toBe('updated');
83
- const written = JSON.parse(readFileSync(file, 'utf8'));
84
- expect(written.mcpServers.hypertab.headers.Authorization).toBe('Bearer ht_sk_test');
85
- });
86
- it('only creates the backup on first touch (not on subsequent updates)', () => {
87
- const file = join(dir, 'mcp.json');
88
- writeFileSync(file, JSON.stringify({ mcpServers: {} }, null, 2));
89
- const first = writePlatformConfig({ platform: tmpPlatform(file), scope: 'user', entry: ENTRY });
90
- expect(first.backupCreated).toBe(true);
91
- const second = writePlatformConfig({
92
- platform: tmpPlatform(file),
93
- scope: 'user',
94
- entry: { ...ENTRY, url: 'https://api.hypertab.ai/mcp/v2' },
95
- });
96
- expect(second.action).toBe('updated');
97
- expect(second.backupCreated).toBe(false);
98
- });
99
- it('creates parent directories if missing', () => {
100
- const file = join(dir, 'nested', 'deep', 'mcp.json');
101
- const result = writePlatformConfig({ platform: tmpPlatform(file), scope: 'user', entry: ENTRY });
102
- expect(result.action).toBe('created');
103
- expect(existsSync(file)).toBe(true);
104
- });
105
- it('preserves other top-level keys in the config file', () => {
106
- const file = join(dir, 'mcp.json');
107
- writeFileSync(file, JSON.stringify({ theme: 'dark', version: 2, mcpServers: { other: { command: 'x' } } }, null, 2));
108
- writePlatformConfig({ platform: tmpPlatform(file), scope: 'user', entry: ENTRY });
109
- const written = JSON.parse(readFileSync(file, 'utf8'));
110
- expect(written.theme).toBe('dark');
111
- expect(written.version).toBe(2);
112
- expect(written.mcpServers.other).toEqual({ command: 'x' });
113
- });
114
- it('throws a helpful error on malformed JSON', () => {
115
- const file = join(dir, 'mcp.json');
116
- writeFileSync(file, '{ "mcpServers": { "other": }');
117
- expect(() => writePlatformConfig({ platform: tmpPlatform(file), scope: 'user', entry: ENTRY })).toThrow(/Failed to parse existing config/);
118
- });
119
- });
120
- describe('removePlatformConfig', () => {
121
- it('removes the hypertab entry while preserving other servers', () => {
122
- const file = join(dir, 'mcp.json');
123
- writeFileSync(file, JSON.stringify({
124
- mcpServers: {
125
- other: { command: 'other-server' },
126
- hypertab: ENTRY,
127
- more: { type: 'http', url: 'https://x' },
128
- },
129
- }, null, 2));
130
- const result = removePlatformConfig({ platform: tmpPlatform(file), scope: 'user' });
131
- expect(result.action).toBe('removed');
132
- expect(result.backupCreated).toBe(true);
133
- const written = JSON.parse(readFileSync(file, 'utf8'));
134
- expect(written.mcpServers.hypertab).toBeUndefined();
135
- expect(written.mcpServers.other).toEqual({ command: 'other-server' });
136
- expect(written.mcpServers.more).toEqual({ type: 'http', url: 'https://x' });
137
- });
138
- it('returns not-installed when hypertab was never added', () => {
139
- const file = join(dir, 'mcp.json');
140
- writeFileSync(file, JSON.stringify({ mcpServers: { other: { command: 'x' } } }, null, 2));
141
- const result = removePlatformConfig({ platform: tmpPlatform(file), scope: 'user' });
142
- expect(result.action).toBe('not-installed');
143
- expect(result.backupCreated).toBe(false);
144
- // File should be untouched
145
- const written = JSON.parse(readFileSync(file, 'utf8'));
146
- expect(written.mcpServers.other).toEqual({ command: 'x' });
147
- });
148
- it('returns file-missing when the config file does not exist', () => {
149
- const file = join(dir, 'nonexistent.json');
150
- const result = removePlatformConfig({ platform: tmpPlatform(file), scope: 'user' });
151
- expect(result.action).toBe('file-missing');
152
- expect(existsSync(file)).toBe(false);
153
- });
154
- it('leaves an empty mcpServers map rather than deleting the key', () => {
155
- const file = join(dir, 'mcp.json');
156
- writeFileSync(file, JSON.stringify({ mcpServers: { hypertab: ENTRY } }, null, 2));
157
- removePlatformConfig({ platform: tmpPlatform(file), scope: 'user' });
158
- const written = JSON.parse(readFileSync(file, 'utf8'));
159
- expect(written.mcpServers).toEqual({});
160
- });
161
- it('preserves other top-level keys in the config file', () => {
162
- const file = join(dir, 'mcp.json');
163
- writeFileSync(file, JSON.stringify({ theme: 'dark', version: 2, mcpServers: { hypertab: ENTRY, other: { command: 'x' } } }, null, 2));
164
- removePlatformConfig({ platform: tmpPlatform(file), scope: 'user' });
165
- const written = JSON.parse(readFileSync(file, 'utf8'));
166
- expect(written.theme).toBe('dark');
167
- expect(written.version).toBe(2);
168
- expect(written.mcpServers.other).toEqual({ command: 'x' });
169
- expect(written.mcpServers.hypertab).toBeUndefined();
170
- });
171
- });
172
- describe('stripJsonComments', () => {
173
- it('removes single-line // comments', () => {
174
- expect(stripJsonComments('{ "a": 1 // trailing\n, "b": 2 }')).toBe('{ "a": 1 \n, "b": 2 }');
175
- });
176
- it('removes block /* */ comments', () => {
177
- expect(stripJsonComments('{ /* header */ "a": 1 }')).toBe('{ "a": 1 }');
178
- });
179
- it('does not strip // inside strings', () => {
180
- expect(stripJsonComments('{ "url": "http://x.com" }')).toBe('{ "url": "http://x.com" }');
181
- });
182
- it('handles escaped quotes inside strings', () => {
183
- expect(stripJsonComments('{ "msg": "a \\"b\\" //not a comment" }')).toBe('{ "msg": "a \\"b\\" //not a comment" }');
184
- });
185
- });
186
- //# sourceMappingURL=config-writer.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config-writer.test.js","sourceRoot":"","sources":["../../src/__tests__/config-writer.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=index.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/index.test.ts"],"names":[],"mappings":""}
@@ -1,113 +0,0 @@
1
- import { Command } from 'commander';
2
- import { describe, expect, it } from 'vitest';
3
- import { PLATFORM_IDS } from '../install/platforms.js';
4
- const DEFAULT_URL = 'https://api.hypertab.ai/mcp';
5
- /**
6
- * Mirrors the subcommand structure in src/index.ts so we can parse args
7
- * synchronously and assert on the opts without running any side effects.
8
- */
9
- function buildProgram() {
10
- const program = new Command().name('hypertab').version('0.2.0').exitOverride().configureOutput({
11
- writeOut: () => { },
12
- writeErr: () => { },
13
- });
14
- program
15
- .command('install')
16
- .requiredOption('-c, --client <id>', `Target client: ${PLATFORM_IDS.join(' | ')}`)
17
- .option('-k, --api-key <key>', 'Hypertab API key')
18
- .option('-s, --scope <scope>', 'Config scope', 'user')
19
- .option('-u, --url <url>', 'MCP server URL', DEFAULT_URL)
20
- .action(() => { });
21
- program
22
- .command('connect')
23
- .requiredOption('-k, --api-key <key>', 'Hypertab API key')
24
- .option('-u, --url <url>', 'MCP server URL', DEFAULT_URL)
25
- .option('-t, --transport <type>', 'Transport type', 'streamable-http')
26
- .action(() => { });
27
- return program;
28
- }
29
- function parseInstall(args) {
30
- const program = buildProgram();
31
- program.parse(['node', 'hypertab', 'install', ...args]);
32
- const installCmd = program.commands.find((c) => c.name() === 'install');
33
- if (!installCmd)
34
- throw new Error('install command missing');
35
- return installCmd.opts();
36
- }
37
- function parseConnect(args) {
38
- const program = buildProgram();
39
- program.parse(['node', 'hypertab', 'connect', ...args]);
40
- const connectCmd = program.commands.find((c) => c.name() === 'connect');
41
- if (!connectCmd)
42
- throw new Error('connect command missing');
43
- return connectCmd.opts();
44
- }
45
- describe('CLI structure', () => {
46
- it('exposes install and connect subcommands', () => {
47
- const program = buildProgram();
48
- const names = program.commands.map((c) => c.name());
49
- expect(names).toContain('install');
50
- expect(names).toContain('connect');
51
- });
52
- });
53
- describe('install subcommand parsing', () => {
54
- it('requires --client', () => {
55
- const program = buildProgram();
56
- expect(() => program.parse(['node', 'hypertab', 'install'])).toThrow();
57
- });
58
- it('parses --client and --api-key', () => {
59
- const opts = parseInstall(['-c', 'claude-code', '-k', 'ht_sk_test']);
60
- expect(opts.client).toBe('claude-code');
61
- expect(opts.apiKey).toBe('ht_sk_test');
62
- });
63
- it('defaults scope to user', () => {
64
- const opts = parseInstall(['-c', 'cursor']);
65
- expect(opts.scope).toBe('user');
66
- });
67
- it('accepts --scope project', () => {
68
- const opts = parseInstall(['-c', 'claude-code', '--scope', 'project']);
69
- expect(opts.scope).toBe('project');
70
- });
71
- it('defaults --url to the hosted MCP endpoint', () => {
72
- const opts = parseInstall(['-c', 'claude-code']);
73
- expect(opts.url).toBe(DEFAULT_URL);
74
- });
75
- it('api-key is optional (env var or prompt can provide)', () => {
76
- // Does not throw when --api-key is omitted.
77
- expect(() => parseInstall(['-c', 'claude-code'])).not.toThrow();
78
- });
79
- it('accepts every valid client id', () => {
80
- for (const id of PLATFORM_IDS) {
81
- const opts = parseInstall(['-c', id]);
82
- expect(opts.client).toBe(id);
83
- }
84
- });
85
- });
86
- describe('connect subcommand parsing', () => {
87
- it('requires --api-key', () => {
88
- const program = buildProgram();
89
- expect(() => program.parse(['node', 'hypertab', 'connect'])).toThrow();
90
- });
91
- it('parses --api-key, --url, --transport', () => {
92
- const opts = parseConnect(['-k', 'ht_sk_test', '-u', 'https://local/mcp', '-t', 'sse']);
93
- expect(opts.apiKey).toBe('ht_sk_test');
94
- expect(opts.url).toBe('https://local/mcp');
95
- expect(opts.transport).toBe('sse');
96
- });
97
- it('defaults transport to streamable-http', () => {
98
- const opts = parseConnect(['-k', 'ht_sk_test']);
99
- expect(opts.transport).toBe('streamable-http');
100
- });
101
- });
102
- describe('API key format validation (unit)', () => {
103
- it('rejects keys without ht_sk_ prefix', () => {
104
- expect('sk_bad'.startsWith('ht_sk_')).toBe(false);
105
- });
106
- it('accepts valid hypertab keys', () => {
107
- expect('ht_sk_valid'.startsWith('ht_sk_')).toBe(true);
108
- });
109
- it('rejects empty strings', () => {
110
- expect(''.startsWith('ht_sk_')).toBe(false);
111
- });
112
- });
113
- //# sourceMappingURL=index.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../src/__tests__/index.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=install.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"install.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/install.test.ts"],"names":[],"mappings":""}