@everstateai/mcp 1.3.1 → 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 +264 -28
  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 +297 -28
@@ -0,0 +1,329 @@
1
+ /**
2
+ * MCP Configuration Validator
3
+ *
4
+ * Validates that Claude Code and Claude Desktop have correct MCP configuration.
5
+ */
6
+
7
+ import * as fs from 'fs';
8
+ import {
9
+ Validator,
10
+ ValidationResult,
11
+ ValidationContext,
12
+ getClaudeConfigPath,
13
+ getClaudeDesktopConfigPath,
14
+ getEverstateDir,
15
+ } from '../types.js';
16
+
17
+ export const mcpConfigValidator: Validator = {
18
+ name: 'MCP Configuration',
19
+
20
+ async validate(context: ValidationContext): Promise<ValidationResult[]> {
21
+ const results: ValidationResult[] = [];
22
+
23
+ // Check Claude Code config
24
+ const claudeCodeResults = await validateClaudeCodeConfig();
25
+ results.push(...claudeCodeResults);
26
+
27
+ // Check Claude Desktop config (optional)
28
+ const desktopPath = getClaudeDesktopConfigPath();
29
+ if (desktopPath && fs.existsSync(desktopPath)) {
30
+ const desktopResults = await validateClaudeDesktopConfig(desktopPath);
31
+ results.push(...desktopResults);
32
+ } else if (desktopPath) {
33
+ results.push({
34
+ component: 'MCP Config',
35
+ check: 'Claude Desktop',
36
+ status: 'warn',
37
+ message: 'Claude Desktop not installed or not configured',
38
+ details: { path: desktopPath },
39
+ });
40
+ }
41
+
42
+ return results;
43
+ },
44
+
45
+ async repair(result: ValidationResult, context: ValidationContext): Promise<boolean> {
46
+ if (result.check === 'Claude Code config' && result.repairAction?.type === 'install') {
47
+ return await repairClaudeCodeConfig();
48
+ }
49
+ if (result.check === 'Everstate MCP entry' && result.repairAction?.type === 'configure') {
50
+ return await repairEverstateEntry();
51
+ }
52
+ return false;
53
+ },
54
+ };
55
+
56
+ async function validateClaudeCodeConfig(): Promise<ValidationResult[]> {
57
+ const results: ValidationResult[] = [];
58
+ const configPath = getClaudeConfigPath();
59
+
60
+ // Check 1: Config file exists
61
+ if (!fs.existsSync(configPath)) {
62
+ results.push({
63
+ component: 'MCP Config',
64
+ check: 'Claude Code config',
65
+ status: 'fail',
66
+ message: `Config not found at ${configPath}`,
67
+ repairAction: {
68
+ type: 'install',
69
+ description: 'Create Claude Code configuration',
70
+ automatic: true,
71
+ },
72
+ });
73
+ return results;
74
+ }
75
+
76
+ // Check 2: Valid JSON
77
+ let config: Record<string, unknown>;
78
+ try {
79
+ const content = fs.readFileSync(configPath, 'utf8');
80
+ config = JSON.parse(content);
81
+ } catch (err) {
82
+ results.push({
83
+ component: 'MCP Config',
84
+ check: 'Claude Code config',
85
+ status: 'fail',
86
+ message: `Invalid JSON: ${err instanceof Error ? err.message : 'Parse error'}`,
87
+ repairAction: {
88
+ type: 'configure',
89
+ description: 'Fix JSON syntax or restore backup',
90
+ automatic: false,
91
+ },
92
+ });
93
+ return results;
94
+ }
95
+
96
+ results.push({
97
+ component: 'MCP Config',
98
+ check: 'Claude Code config',
99
+ status: 'pass',
100
+ message: `Valid at ${configPath}`,
101
+ });
102
+
103
+ // Check 3: mcpServers exists
104
+ const mcpServers = config.mcpServers as Record<string, unknown> | undefined;
105
+ if (!mcpServers) {
106
+ results.push({
107
+ component: 'MCP Config',
108
+ check: 'mcpServers section',
109
+ status: 'fail',
110
+ message: 'No mcpServers section in config',
111
+ repairAction: {
112
+ type: 'configure',
113
+ description: 'Add mcpServers configuration',
114
+ automatic: true,
115
+ },
116
+ });
117
+ return results;
118
+ }
119
+
120
+ // Check 4: Everstate entry exists
121
+ const everstateConfig = mcpServers.everstate as Record<string, unknown> | undefined;
122
+ if (!everstateConfig) {
123
+ results.push({
124
+ component: 'MCP Config',
125
+ check: 'Everstate MCP entry',
126
+ status: 'fail',
127
+ message: 'Everstate not configured in mcpServers',
128
+ repairAction: {
129
+ type: 'configure',
130
+ description: 'Add Everstate MCP server configuration',
131
+ automatic: true,
132
+ },
133
+ });
134
+ return results;
135
+ }
136
+
137
+ results.push({
138
+ component: 'MCP Config',
139
+ check: 'Everstate MCP entry',
140
+ status: 'pass',
141
+ message: 'Everstate MCP server configured',
142
+ });
143
+
144
+ // Check 5: Command is valid
145
+ const command = everstateConfig.command as string | undefined;
146
+ const args = everstateConfig.args as string[] | undefined;
147
+
148
+ if (command === 'npx' && args?.includes('@everstateai/mcp')) {
149
+ results.push({
150
+ component: 'MCP Config',
151
+ check: 'MCP command',
152
+ status: 'pass',
153
+ message: 'Using npx @everstateai/mcp (recommended)',
154
+ });
155
+ } else if (command === 'node') {
156
+ results.push({
157
+ component: 'MCP Config',
158
+ check: 'MCP command',
159
+ status: 'warn',
160
+ message: 'Using local node path - consider using npx for auto-updates',
161
+ details: { command, args },
162
+ });
163
+ } else {
164
+ results.push({
165
+ component: 'MCP Config',
166
+ check: 'MCP command',
167
+ status: 'warn',
168
+ message: `Non-standard command: ${command}`,
169
+ details: { command, args },
170
+ });
171
+ }
172
+
173
+ // Check 6: Environment variables
174
+ const env = everstateConfig.env as Record<string, string> | undefined;
175
+ if (!env?.EVERSTATE_API_KEY) {
176
+ // Check if API key is in file instead
177
+ const apiKeyPath = `${getEverstateDir()}/api-key`;
178
+ if (!fs.existsSync(apiKeyPath)) {
179
+ results.push({
180
+ component: 'MCP Config',
181
+ check: 'API key configuration',
182
+ status: 'fail',
183
+ message: 'No API key in MCP config or key file',
184
+ repairAction: {
185
+ type: 'configure',
186
+ description: 'Configure API key',
187
+ automatic: false,
188
+ },
189
+ });
190
+ } else {
191
+ results.push({
192
+ component: 'MCP Config',
193
+ check: 'API key configuration',
194
+ status: 'pass',
195
+ message: 'Using API key from file (hooks will read it)',
196
+ });
197
+ }
198
+ } else {
199
+ results.push({
200
+ component: 'MCP Config',
201
+ check: 'API key configuration',
202
+ status: 'pass',
203
+ message: 'API key configured in MCP env',
204
+ });
205
+ }
206
+
207
+ return results;
208
+ }
209
+
210
+ async function validateClaudeDesktopConfig(configPath: string): Promise<ValidationResult[]> {
211
+ const results: ValidationResult[] = [];
212
+
213
+ // Similar validation for Claude Desktop
214
+ let config: Record<string, unknown>;
215
+ try {
216
+ const content = fs.readFileSync(configPath, 'utf8');
217
+ config = JSON.parse(content);
218
+ } catch (err) {
219
+ results.push({
220
+ component: 'MCP Config',
221
+ check: 'Claude Desktop config',
222
+ status: 'warn',
223
+ message: `Could not parse: ${err instanceof Error ? err.message : 'Error'}`,
224
+ });
225
+ return results;
226
+ }
227
+
228
+ const mcpServers = config.mcpServers as Record<string, unknown> | undefined;
229
+ if (!mcpServers?.everstate) {
230
+ results.push({
231
+ component: 'MCP Config',
232
+ check: 'Claude Desktop Everstate',
233
+ status: 'warn',
234
+ message: 'Everstate not configured for Claude Desktop',
235
+ repairAction: {
236
+ type: 'configure',
237
+ description: 'Add Everstate to Claude Desktop',
238
+ automatic: true,
239
+ },
240
+ });
241
+ } else {
242
+ results.push({
243
+ component: 'MCP Config',
244
+ check: 'Claude Desktop Everstate',
245
+ status: 'pass',
246
+ message: 'Everstate configured for Claude Desktop',
247
+ });
248
+ }
249
+
250
+ return results;
251
+ }
252
+
253
+ async function repairClaudeCodeConfig(): Promise<boolean> {
254
+ const configPath = getClaudeConfigPath();
255
+
256
+ try {
257
+ // Create minimal config
258
+ const config = {
259
+ mcpServers: {
260
+ everstate: {
261
+ command: 'npx',
262
+ args: ['-y', '@everstateai/mcp'],
263
+ env: {},
264
+ },
265
+ },
266
+ };
267
+
268
+ // Check for API key
269
+ const apiKeyPath = `${getEverstateDir()}/api-key`;
270
+ if (fs.existsSync(apiKeyPath)) {
271
+ const apiKey = fs.readFileSync(apiKeyPath, 'utf8').trim();
272
+ if (apiKey) {
273
+ (config.mcpServers.everstate.env as Record<string, string>).EVERSTATE_API_KEY = apiKey;
274
+ }
275
+ }
276
+
277
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
278
+ return true;
279
+ } catch {
280
+ return false;
281
+ }
282
+ }
283
+
284
+ async function repairEverstateEntry(): Promise<boolean> {
285
+ const configPath = getClaudeConfigPath();
286
+
287
+ try {
288
+ let config: Record<string, unknown> = {};
289
+
290
+ if (fs.existsSync(configPath)) {
291
+ const content = fs.readFileSync(configPath, 'utf8');
292
+ config = JSON.parse(content);
293
+ }
294
+
295
+ if (!config.mcpServers) {
296
+ config.mcpServers = {};
297
+ }
298
+
299
+ const mcpServers = config.mcpServers as Record<string, unknown>;
300
+
301
+ // Add Everstate entry
302
+ const everstateConfig: Record<string, unknown> = {
303
+ command: 'npx',
304
+ args: ['-y', '@everstateai/mcp'],
305
+ env: {},
306
+ };
307
+
308
+ // Check for API key
309
+ const apiKeyPath = `${getEverstateDir()}/api-key`;
310
+ if (fs.existsSync(apiKeyPath)) {
311
+ const apiKey = fs.readFileSync(apiKeyPath, 'utf8').trim();
312
+ if (apiKey) {
313
+ (everstateConfig.env as Record<string, string>).EVERSTATE_API_KEY = apiKey;
314
+ }
315
+ }
316
+
317
+ mcpServers.everstate = everstateConfig;
318
+
319
+ // Backup existing
320
+ if (fs.existsSync(configPath)) {
321
+ fs.copyFileSync(configPath, `${configPath}.backup`);
322
+ }
323
+
324
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
325
+ return true;
326
+ } catch {
327
+ return false;
328
+ }
329
+ }
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Project Validator
3
+ *
4
+ * Validates project-level configuration (.everstate.json).
5
+ */
6
+
7
+ import * as fs from 'fs';
8
+ import * as path from 'path';
9
+ import {
10
+ Validator,
11
+ ValidationResult,
12
+ ValidationContext,
13
+ EVERSTATE_API_URL,
14
+ } from '../types.js';
15
+
16
+ export const projectValidator: Validator = {
17
+ name: 'Project',
18
+
19
+ async validate(context: ValidationContext): Promise<ValidationResult[]> {
20
+ const results: ValidationResult[] = [];
21
+
22
+ if (!context.projectDir) {
23
+ results.push({
24
+ component: 'Project',
25
+ check: 'Project directory',
26
+ status: 'warn',
27
+ message: 'No project directory specified (running globally)',
28
+ });
29
+ return results;
30
+ }
31
+
32
+ const configPath = path.join(context.projectDir, '.everstate.json');
33
+
34
+ // Check 1: Config file exists
35
+ if (!fs.existsSync(configPath)) {
36
+ results.push({
37
+ component: 'Project',
38
+ check: '.everstate.json exists',
39
+ status: 'fail',
40
+ message: `Not found at ${configPath}`,
41
+ repairAction: {
42
+ type: 'install',
43
+ description: 'Initialize project with everstate',
44
+ automatic: false, // Requires user input for project name
45
+ },
46
+ });
47
+ return results;
48
+ }
49
+
50
+ results.push({
51
+ component: 'Project',
52
+ check: '.everstate.json exists',
53
+ status: 'pass',
54
+ message: `Found at ${configPath}`,
55
+ });
56
+
57
+ // Check 2: Valid JSON
58
+ let config: Record<string, unknown>;
59
+ try {
60
+ const content = fs.readFileSync(configPath, 'utf8');
61
+ config = JSON.parse(content);
62
+ } catch (error) {
63
+ results.push({
64
+ component: 'Project',
65
+ check: '.everstate.json valid',
66
+ status: 'fail',
67
+ message: 'Invalid JSON syntax',
68
+ details: { error: String(error) },
69
+ repairAction: {
70
+ type: 'configure',
71
+ description: 'Fix JSON syntax in .everstate.json',
72
+ automatic: false,
73
+ },
74
+ });
75
+ return results;
76
+ }
77
+
78
+ results.push({
79
+ component: 'Project',
80
+ check: '.everstate.json valid',
81
+ status: 'pass',
82
+ message: 'Valid JSON',
83
+ });
84
+
85
+ // Check 3: Has projectId
86
+ const projectId = config.projectId as string | undefined;
87
+ if (!projectId) {
88
+ results.push({
89
+ component: 'Project',
90
+ check: 'Project ID',
91
+ status: 'fail',
92
+ message: 'Missing projectId field',
93
+ repairAction: {
94
+ type: 'configure',
95
+ description: 'Add projectId to .everstate.json',
96
+ automatic: false,
97
+ },
98
+ });
99
+ return results;
100
+ }
101
+
102
+ // Check 4: Valid projectId format
103
+ if (!projectId.startsWith('proj_')) {
104
+ results.push({
105
+ component: 'Project',
106
+ check: 'Project ID format',
107
+ status: 'warn',
108
+ message: `Unusual format: ${projectId} (expected proj_...)`,
109
+ details: { projectId },
110
+ });
111
+ } else {
112
+ results.push({
113
+ component: 'Project',
114
+ check: 'Project ID format',
115
+ status: 'pass',
116
+ message: `Valid format: ${projectId.substring(0, 20)}...`,
117
+ });
118
+ }
119
+
120
+ // Check 5: Verify project exists on server (if not skipping network)
121
+ if (!context.skipNetwork && context.apiKey) {
122
+ try {
123
+ const response = await fetch(`${EVERSTATE_API_URL}/api/project/${projectId}`, {
124
+ headers: {
125
+ Authorization: `Bearer ${context.apiKey}`,
126
+ },
127
+ });
128
+
129
+ if (response.ok) {
130
+ const data = (await response.json()) as { name?: string };
131
+ results.push({
132
+ component: 'Project',
133
+ check: 'Project exists on server',
134
+ status: 'pass',
135
+ message: `Project "${data.name || projectId}" found`,
136
+ });
137
+ } else if (response.status === 404) {
138
+ results.push({
139
+ component: 'Project',
140
+ check: 'Project exists on server',
141
+ status: 'warn',
142
+ message: 'Project not found on server (may need to sync)',
143
+ details: { projectId },
144
+ });
145
+ } else if (response.status === 401) {
146
+ results.push({
147
+ component: 'Project',
148
+ check: 'Project exists on server',
149
+ status: 'warn',
150
+ message: 'Cannot verify - API key may not have access',
151
+ });
152
+ } else {
153
+ results.push({
154
+ component: 'Project',
155
+ check: 'Project exists on server',
156
+ status: 'warn',
157
+ message: `Unexpected response: ${response.status}`,
158
+ });
159
+ }
160
+ } catch (error) {
161
+ results.push({
162
+ component: 'Project',
163
+ check: 'Project exists on server',
164
+ status: 'warn',
165
+ message: 'Could not verify (network error)',
166
+ details: { error: String(error) },
167
+ });
168
+ }
169
+ }
170
+
171
+ return results;
172
+ },
173
+
174
+ async repair(result: ValidationResult, context: ValidationContext): Promise<boolean> {
175
+ // Project repairs typically require user input
176
+ // The setup command handles project initialization
177
+ return false;
178
+ },
179
+ };