@everstateai/mcp 1.3.2 → 1.3.3

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 (70) hide show
  1. package/dist/index.d.ts +5 -3
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +32 -5
  4. package/dist/index.js.map +1 -1
  5. package/dist/setup/auto-update.d.ts +20 -0
  6. package/dist/setup/auto-update.d.ts.map +1 -0
  7. package/dist/setup/auto-update.js +295 -0
  8. package/dist/setup/auto-update.js.map +1 -0
  9. package/dist/setup/commands/doctor.d.ts +15 -0
  10. package/dist/setup/commands/doctor.d.ts.map +1 -0
  11. package/dist/setup/commands/doctor.js +264 -0
  12. package/dist/setup/commands/doctor.js.map +1 -0
  13. package/dist/setup/commands/repair.d.ts +14 -0
  14. package/dist/setup/commands/repair.d.ts.map +1 -0
  15. package/dist/setup/commands/repair.js +252 -0
  16. package/dist/setup/commands/repair.js.map +1 -0
  17. package/dist/setup/hooks/templates.d.ts +30 -0
  18. package/dist/setup/hooks/templates.d.ts.map +1 -0
  19. package/dist/setup/hooks/templates.js +237 -0
  20. package/dist/setup/hooks/templates.js.map +1 -0
  21. package/dist/setup/types.d.ts +120 -0
  22. package/dist/setup/types.d.ts.map +1 -0
  23. package/dist/setup/types.js +58 -0
  24. package/dist/setup/types.js.map +1 -0
  25. package/dist/setup/validators/api-key.d.ts +8 -0
  26. package/dist/setup/validators/api-key.d.ts.map +1 -0
  27. package/dist/setup/validators/api-key.js +233 -0
  28. package/dist/setup/validators/api-key.js.map +1 -0
  29. package/dist/setup/validators/connectivity.d.ts +8 -0
  30. package/dist/setup/validators/connectivity.d.ts.map +1 -0
  31. package/dist/setup/validators/connectivity.js +150 -0
  32. package/dist/setup/validators/connectivity.js.map +1 -0
  33. package/dist/setup/validators/hooks.d.ts +8 -0
  34. package/dist/setup/validators/hooks.d.ts.map +1 -0
  35. package/dist/setup/validators/hooks.js +431 -0
  36. package/dist/setup/validators/hooks.js.map +1 -0
  37. package/dist/setup/validators/index.d.ts +18 -0
  38. package/dist/setup/validators/index.d.ts.map +1 -0
  39. package/dist/setup/validators/index.js +123 -0
  40. package/dist/setup/validators/index.js.map +1 -0
  41. package/dist/setup/validators/mcp-config.d.ts +8 -0
  42. package/dist/setup/validators/mcp-config.d.ts.map +1 -0
  43. package/dist/setup/validators/mcp-config.js +333 -0
  44. package/dist/setup/validators/mcp-config.js.map +1 -0
  45. package/dist/setup/validators/project.d.ts +8 -0
  46. package/dist/setup/validators/project.d.ts.map +1 -0
  47. package/dist/setup/validators/project.js +202 -0
  48. package/dist/setup/validators/project.js.map +1 -0
  49. package/dist/setup/version.d.ts +58 -0
  50. package/dist/setup/version.d.ts.map +1 -0
  51. package/dist/setup/version.js +262 -0
  52. package/dist/setup/version.js.map +1 -0
  53. package/dist/setup.d.ts.map +1 -1
  54. package/dist/setup.js +207 -27
  55. package/dist/setup.js.map +1 -1
  56. package/package.json +1 -1
  57. package/src/index.ts +32 -5
  58. package/src/setup/auto-update.ts +328 -0
  59. package/src/setup/commands/doctor.ts +266 -0
  60. package/src/setup/commands/repair.ts +260 -0
  61. package/src/setup/hooks/templates.ts +239 -0
  62. package/src/setup/types.ts +199 -0
  63. package/src/setup/validators/api-key.ts +218 -0
  64. package/src/setup/validators/connectivity.ts +176 -0
  65. package/src/setup/validators/hooks.ts +447 -0
  66. package/src/setup/validators/index.ts +137 -0
  67. package/src/setup/validators/mcp-config.ts +329 -0
  68. package/src/setup/validators/project.ts +179 -0
  69. package/src/setup/version.ts +267 -0
  70. package/src/setup.ts +229 -27
@@ -0,0 +1,218 @@
1
+ /**
2
+ * API Key Validator
3
+ *
4
+ * Validates that the API key exists, has correct format, and authenticates.
5
+ */
6
+
7
+ import * as fs from 'fs';
8
+ import * as https from 'https';
9
+ import {
10
+ Validator,
11
+ ValidationResult,
12
+ ValidationContext,
13
+ getEverstateDir,
14
+ EVERSTATE_API_URL,
15
+ } from '../types.js';
16
+
17
+ export const apiKeyValidator: Validator = {
18
+ name: 'API Key',
19
+
20
+ async validate(context: ValidationContext): Promise<ValidationResult[]> {
21
+ const results: ValidationResult[] = [];
22
+ const apiKeyPath = `${getEverstateDir()}/api-key`;
23
+
24
+ // Check 1: API key file exists
25
+ if (!fs.existsSync(apiKeyPath)) {
26
+ results.push({
27
+ component: 'API Key',
28
+ check: 'File exists',
29
+ status: 'fail',
30
+ message: `API key file not found at ${apiKeyPath}`,
31
+ repairAction: {
32
+ type: 'install',
33
+ description: 'Run setup with your API key',
34
+ automatic: false,
35
+ },
36
+ });
37
+ return results; // Can't continue without file
38
+ }
39
+
40
+ results.push({
41
+ component: 'API Key',
42
+ check: 'File exists',
43
+ status: 'pass',
44
+ message: `Found at ${apiKeyPath}`,
45
+ });
46
+
47
+ // Check 2: File is not empty and has correct format
48
+ let apiKey: string;
49
+ try {
50
+ apiKey = fs.readFileSync(apiKeyPath, 'utf8').trim();
51
+ } catch (err) {
52
+ results.push({
53
+ component: 'API Key',
54
+ check: 'Readable',
55
+ status: 'fail',
56
+ message: `Cannot read API key file: ${err instanceof Error ? err.message : 'Unknown error'}`,
57
+ repairAction: {
58
+ type: 'chmod',
59
+ description: 'Fix file permissions',
60
+ automatic: true,
61
+ },
62
+ });
63
+ return results;
64
+ }
65
+
66
+ if (!apiKey) {
67
+ results.push({
68
+ component: 'API Key',
69
+ check: 'Not empty',
70
+ status: 'fail',
71
+ message: 'API key file is empty',
72
+ repairAction: {
73
+ type: 'configure',
74
+ description: 'Run setup with your API key',
75
+ automatic: false,
76
+ },
77
+ });
78
+ return results;
79
+ }
80
+
81
+ // Check 3: Format validation
82
+ if (!apiKey.startsWith('ck_') && !apiKey.startsWith('ckp_')) {
83
+ results.push({
84
+ component: 'API Key',
85
+ check: 'Format',
86
+ status: 'fail',
87
+ message: `Invalid format - must start with 'ck_' or 'ckp_'`,
88
+ details: { keyPrefix: apiKey.substring(0, 4) },
89
+ repairAction: {
90
+ type: 'configure',
91
+ description: 'Replace with valid API key',
92
+ automatic: false,
93
+ },
94
+ });
95
+ return results;
96
+ }
97
+
98
+ if (apiKey.length < 20) {
99
+ results.push({
100
+ component: 'API Key',
101
+ check: 'Format',
102
+ status: 'warn',
103
+ message: 'API key appears unusually short',
104
+ details: { length: apiKey.length },
105
+ });
106
+ } else {
107
+ results.push({
108
+ component: 'API Key',
109
+ check: 'Format',
110
+ status: 'pass',
111
+ message: `Valid format (${apiKey.substring(0, 7)}...)`,
112
+ });
113
+ }
114
+
115
+ // Check 4: Authentication (skip if network checks disabled)
116
+ if (context.skipNetwork) {
117
+ results.push({
118
+ component: 'API Key',
119
+ check: 'Authentication',
120
+ status: 'warn',
121
+ message: 'Skipped (network checks disabled)',
122
+ });
123
+ return results;
124
+ }
125
+
126
+ const authResult = await checkAuthentication(apiKey);
127
+ results.push(authResult);
128
+
129
+ return results;
130
+ },
131
+
132
+ async repair(result: ValidationResult, context: ValidationContext): Promise<boolean> {
133
+ if (result.check === 'Readable' && result.repairAction?.type === 'chmod') {
134
+ const apiKeyPath = `${getEverstateDir()}/api-key`;
135
+ try {
136
+ fs.chmodSync(apiKeyPath, 0o600);
137
+ return true;
138
+ } catch {
139
+ return false;
140
+ }
141
+ }
142
+ // Other repairs require user input (new API key)
143
+ return false;
144
+ },
145
+ };
146
+
147
+ async function checkAuthentication(apiKey: string): Promise<ValidationResult> {
148
+ return new Promise((resolve) => {
149
+ const url = new URL(`${EVERSTATE_API_URL}/api/projects`);
150
+
151
+ const req = https.request(
152
+ {
153
+ hostname: url.hostname,
154
+ port: 443,
155
+ path: url.pathname,
156
+ method: 'GET',
157
+ headers: {
158
+ Authorization: `Bearer ${apiKey}`,
159
+ 'Content-Type': 'application/json',
160
+ },
161
+ timeout: 10000,
162
+ },
163
+ (res) => {
164
+ if (res.statusCode === 200) {
165
+ resolve({
166
+ component: 'API Key',
167
+ check: 'Authentication',
168
+ status: 'pass',
169
+ message: 'Authenticated successfully',
170
+ });
171
+ } else if (res.statusCode === 401 || res.statusCode === 403) {
172
+ resolve({
173
+ component: 'API Key',
174
+ check: 'Authentication',
175
+ status: 'fail',
176
+ message: 'Authentication failed - invalid or expired key',
177
+ details: { statusCode: res.statusCode },
178
+ repairAction: {
179
+ type: 'configure',
180
+ description: 'Replace with valid API key',
181
+ automatic: false,
182
+ },
183
+ });
184
+ } else {
185
+ resolve({
186
+ component: 'API Key',
187
+ check: 'Authentication',
188
+ status: 'warn',
189
+ message: `Unexpected response: ${res.statusCode}`,
190
+ details: { statusCode: res.statusCode },
191
+ });
192
+ }
193
+ }
194
+ );
195
+
196
+ req.on('error', (err) => {
197
+ resolve({
198
+ component: 'API Key',
199
+ check: 'Authentication',
200
+ status: 'warn',
201
+ message: `Network error: ${err.message}`,
202
+ details: { error: err.message },
203
+ });
204
+ });
205
+
206
+ req.on('timeout', () => {
207
+ req.destroy();
208
+ resolve({
209
+ component: 'API Key',
210
+ check: 'Authentication',
211
+ status: 'warn',
212
+ message: 'Request timed out',
213
+ });
214
+ });
215
+
216
+ req.end();
217
+ });
218
+ }
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Connectivity Validator
3
+ *
4
+ * Tests API connectivity and response times.
5
+ */
6
+
7
+ import {
8
+ Validator,
9
+ ValidationResult,
10
+ ValidationContext,
11
+ EVERSTATE_API_URL,
12
+ } from '../types.js';
13
+
14
+ const SLOW_THRESHOLD_MS = 2000;
15
+ const TIMEOUT_MS = 10000;
16
+
17
+ export const connectivityValidator: Validator = {
18
+ name: 'Connectivity',
19
+
20
+ async validate(context: ValidationContext): Promise<ValidationResult[]> {
21
+ const results: ValidationResult[] = [];
22
+
23
+ if (context.skipNetwork) {
24
+ results.push({
25
+ component: 'Connectivity',
26
+ check: 'API reachable',
27
+ status: 'warn',
28
+ message: 'Skipped (--skip-network)',
29
+ });
30
+ return results;
31
+ }
32
+
33
+ // Check 1: Health endpoint
34
+ const healthResult = await checkHealth();
35
+ results.push(healthResult);
36
+
37
+ // Check 2: Authenticated endpoint (if API key available)
38
+ if (context.apiKey && healthResult.status === 'pass') {
39
+ const authResult = await checkAuthenticated(context.apiKey);
40
+ results.push(authResult);
41
+ }
42
+
43
+ return results;
44
+ },
45
+ };
46
+
47
+ async function checkHealth(): Promise<ValidationResult> {
48
+ const startTime = Date.now();
49
+
50
+ try {
51
+ const controller = new AbortController();
52
+ const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);
53
+
54
+ const response = await fetch(`${EVERSTATE_API_URL}/health`, {
55
+ signal: controller.signal,
56
+ });
57
+
58
+ clearTimeout(timeoutId);
59
+
60
+ const elapsed = Date.now() - startTime;
61
+
62
+ if (!response.ok) {
63
+ return {
64
+ component: 'Connectivity',
65
+ check: 'API reachable',
66
+ status: 'fail',
67
+ message: `Health check failed: ${response.status}`,
68
+ details: { status: response.status, elapsed },
69
+ };
70
+ }
71
+
72
+ if (elapsed > SLOW_THRESHOLD_MS) {
73
+ return {
74
+ component: 'Connectivity',
75
+ check: 'API reachable',
76
+ status: 'warn',
77
+ message: `Slow response (${elapsed}ms)`,
78
+ details: { elapsed, threshold: SLOW_THRESHOLD_MS },
79
+ };
80
+ }
81
+
82
+ return {
83
+ component: 'Connectivity',
84
+ check: 'API reachable',
85
+ status: 'pass',
86
+ message: `OK (${elapsed}ms)`,
87
+ details: { elapsed },
88
+ };
89
+ } catch (error) {
90
+ const elapsed = Date.now() - startTime;
91
+
92
+ if (error instanceof Error && error.name === 'AbortError') {
93
+ return {
94
+ component: 'Connectivity',
95
+ check: 'API reachable',
96
+ status: 'fail',
97
+ message: `Timeout after ${TIMEOUT_MS}ms`,
98
+ details: { timeout: TIMEOUT_MS },
99
+ };
100
+ }
101
+
102
+ return {
103
+ component: 'Connectivity',
104
+ check: 'API reachable',
105
+ status: 'fail',
106
+ message: 'Network error',
107
+ details: { error: String(error), elapsed },
108
+ };
109
+ }
110
+ }
111
+
112
+ async function checkAuthenticated(apiKey: string): Promise<ValidationResult> {
113
+ const startTime = Date.now();
114
+
115
+ try {
116
+ const controller = new AbortController();
117
+ const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);
118
+
119
+ const response = await fetch(`${EVERSTATE_API_URL}/api/context`, {
120
+ headers: {
121
+ Authorization: `Bearer ${apiKey}`,
122
+ },
123
+ signal: controller.signal,
124
+ });
125
+
126
+ clearTimeout(timeoutId);
127
+
128
+ const elapsed = Date.now() - startTime;
129
+
130
+ if (response.status === 401) {
131
+ return {
132
+ component: 'Connectivity',
133
+ check: 'Authenticated access',
134
+ status: 'fail',
135
+ message: 'API key rejected',
136
+ details: { elapsed },
137
+ };
138
+ }
139
+
140
+ if (!response.ok) {
141
+ return {
142
+ component: 'Connectivity',
143
+ check: 'Authenticated access',
144
+ status: 'warn',
145
+ message: `Unexpected status: ${response.status}`,
146
+ details: { status: response.status, elapsed },
147
+ };
148
+ }
149
+
150
+ if (elapsed > SLOW_THRESHOLD_MS) {
151
+ return {
152
+ component: 'Connectivity',
153
+ check: 'Authenticated access',
154
+ status: 'warn',
155
+ message: `Slow response (${elapsed}ms)`,
156
+ details: { elapsed, threshold: SLOW_THRESHOLD_MS },
157
+ };
158
+ }
159
+
160
+ return {
161
+ component: 'Connectivity',
162
+ check: 'Authenticated access',
163
+ status: 'pass',
164
+ message: `OK (${elapsed}ms)`,
165
+ details: { elapsed },
166
+ };
167
+ } catch (error) {
168
+ return {
169
+ component: 'Connectivity',
170
+ check: 'Authenticated access',
171
+ status: 'warn',
172
+ message: 'Could not verify authenticated access',
173
+ details: { error: String(error) },
174
+ };
175
+ }
176
+ }