agentstudio 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.
Files changed (115) hide show
  1. package/.env +15 -0
  2. package/README.md +85 -0
  3. package/dist/bin/agentstudio.d.ts +3 -0
  4. package/dist/bin/agentstudio.d.ts.map +1 -0
  5. package/dist/bin/agentstudio.js +141 -0
  6. package/dist/bin/agentstudio.js.map +1 -0
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +87 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/middleware/auth.d.ts +7 -0
  12. package/dist/middleware/auth.d.ts.map +1 -0
  13. package/dist/middleware/auth.js +21 -0
  14. package/dist/middleware/auth.js.map +1 -0
  15. package/dist/routes/agents.d.ts +4 -0
  16. package/dist/routes/agents.d.ts.map +1 -0
  17. package/dist/routes/agents.js +804 -0
  18. package/dist/routes/agents.js.map +1 -0
  19. package/dist/routes/auth.d.ts +4 -0
  20. package/dist/routes/auth.d.ts.map +1 -0
  21. package/dist/routes/auth.js +60 -0
  22. package/dist/routes/auth.js.map +1 -0
  23. package/dist/routes/files.d.ts +4 -0
  24. package/dist/routes/files.d.ts.map +1 -0
  25. package/dist/routes/files.js +301 -0
  26. package/dist/routes/files.js.map +1 -0
  27. package/dist/routes/mcp.d.ts +4 -0
  28. package/dist/routes/mcp.d.ts.map +1 -0
  29. package/dist/routes/mcp.js +652 -0
  30. package/dist/routes/mcp.js.map +1 -0
  31. package/dist/routes/media.d.ts +5 -0
  32. package/dist/routes/media.d.ts.map +1 -0
  33. package/dist/routes/media.js +117 -0
  34. package/dist/routes/media.js.map +1 -0
  35. package/dist/routes/slides.d.ts +4 -0
  36. package/dist/routes/slides.d.ts.map +1 -0
  37. package/dist/routes/slides.js +146 -0
  38. package/dist/routes/slides.js.map +1 -0
  39. package/dist/services/claudeSession.d.ts +83 -0
  40. package/dist/services/claudeSession.d.ts.map +1 -0
  41. package/dist/services/claudeSession.js +255 -0
  42. package/dist/services/claudeSession.js.map +1 -0
  43. package/dist/services/messageQueue.d.ts +31 -0
  44. package/dist/services/messageQueue.d.ts.map +1 -0
  45. package/dist/services/messageQueue.js +67 -0
  46. package/dist/services/messageQueue.js.map +1 -0
  47. package/dist/services/sessionManager.d.ts +132 -0
  48. package/dist/services/sessionManager.d.ts.map +1 -0
  49. package/dist/services/sessionManager.js +439 -0
  50. package/dist/services/sessionManager.js.map +1 -0
  51. package/dist/types/claude-history.d.ts +48 -0
  52. package/dist/types/claude-history.d.ts.map +1 -0
  53. package/dist/types/claude-history.js +2 -0
  54. package/dist/types/claude-history.js.map +1 -0
  55. package/dist/types/claude-versions.d.ts +31 -0
  56. package/dist/types/claude-versions.d.ts.map +1 -0
  57. package/dist/types/claude-versions.js +2 -0
  58. package/dist/types/claude-versions.js.map +1 -0
  59. package/dist/types/commands.d.ts +32 -0
  60. package/dist/types/commands.d.ts.map +1 -0
  61. package/dist/types/commands.js +2 -0
  62. package/dist/types/commands.js.map +1 -0
  63. package/dist/types/index.d.ts +81 -0
  64. package/dist/types/index.d.ts.map +1 -0
  65. package/dist/types/index.js +150 -0
  66. package/dist/types/index.js.map +1 -0
  67. package/dist/types/subagents.d.ts +88 -0
  68. package/dist/types/subagents.d.ts.map +1 -0
  69. package/dist/types/subagents.js +2 -0
  70. package/dist/types/subagents.js.map +1 -0
  71. package/dist/utils/agentStorage.d.ts +19 -0
  72. package/dist/utils/agentStorage.d.ts.map +1 -0
  73. package/dist/utils/agentStorage.js +110 -0
  74. package/dist/utils/agentStorage.js.map +1 -0
  75. package/dist/utils/claudeVersionStorage.d.ts +33 -0
  76. package/dist/utils/claudeVersionStorage.d.ts.map +1 -0
  77. package/dist/utils/claudeVersionStorage.js +168 -0
  78. package/dist/utils/claudeVersionStorage.js.map +1 -0
  79. package/dist/utils/jwt.d.ts +15 -0
  80. package/dist/utils/jwt.d.ts.map +1 -0
  81. package/dist/utils/jwt.js +28 -0
  82. package/dist/utils/jwt.js.map +1 -0
  83. package/dist/utils/projectMetadataStorage.d.ts +21 -0
  84. package/dist/utils/projectMetadataStorage.d.ts.map +1 -0
  85. package/dist/utils/projectMetadataStorage.js +68 -0
  86. package/dist/utils/projectMetadataStorage.js.map +1 -0
  87. package/frontend/dist/index.html +86 -0
  88. package/package.json +66 -0
  89. package/src/bin/agentstudio.ts +161 -0
  90. package/src/index.ts +100 -0
  91. package/src/middleware/auth.ts +26 -0
  92. package/src/routes/agents.ts +885 -0
  93. package/src/routes/auth.ts +73 -0
  94. package/src/routes/commands.ts.bak +441 -0
  95. package/src/routes/files.ts +352 -0
  96. package/src/routes/mcp.ts +751 -0
  97. package/src/routes/media.ts +140 -0
  98. package/src/routes/projects.ts.bak +601 -0
  99. package/src/routes/sessions.ts.bak +809 -0
  100. package/src/routes/settings.ts.bak +718 -0
  101. package/src/routes/slides.ts +170 -0
  102. package/src/routes/subagents.ts.bak +364 -0
  103. package/src/services/claudeSession.ts +293 -0
  104. package/src/services/messageQueue.ts +71 -0
  105. package/src/services/sessionManager.ts +532 -0
  106. package/src/types/claude-history.ts +50 -0
  107. package/src/types/claude-versions.ts +33 -0
  108. package/src/types/commands.ts +35 -0
  109. package/src/types/index.ts +248 -0
  110. package/src/types/subagents.ts +106 -0
  111. package/src/utils/agentStorage.ts +126 -0
  112. package/src/utils/claudeVersionStorage.ts +199 -0
  113. package/src/utils/jwt.ts +36 -0
  114. package/src/utils/projectMetadataStorage.ts +86 -0
  115. package/tsconfig.json +26 -0
@@ -0,0 +1,652 @@
1
+ import express from 'express';
2
+ import * as path from 'path';
3
+ import * as fs from 'fs';
4
+ import * as os from 'os';
5
+ import { spawn, exec } from 'child_process';
6
+ import { promisify } from 'util';
7
+ const router = express.Router();
8
+ const execAsync = promisify(exec);
9
+ // Helper function to get MCP config file path
10
+ const getMcpConfigPath = () => {
11
+ return path.join(os.homedir(), '.claude-agent', 'mcp-server.json');
12
+ };
13
+ // Helper function to ensure config directory exists
14
+ const ensureConfigDirectory = () => {
15
+ const configDir = path.dirname(getMcpConfigPath());
16
+ if (!fs.existsSync(configDir)) {
17
+ fs.mkdirSync(configDir, { recursive: true });
18
+ }
19
+ };
20
+ // Helper function to read MCP config
21
+ const readMcpConfig = () => {
22
+ const configPath = getMcpConfigPath();
23
+ if (!fs.existsSync(configPath)) {
24
+ return { mcpServers: {} };
25
+ }
26
+ try {
27
+ const content = fs.readFileSync(configPath, 'utf-8');
28
+ return JSON.parse(content);
29
+ }
30
+ catch (error) {
31
+ console.error('Failed to read MCP config:', error);
32
+ return { mcpServers: {} };
33
+ }
34
+ };
35
+ // Helper function to write MCP config
36
+ const writeMcpConfig = (config) => {
37
+ ensureConfigDirectory();
38
+ const configPath = getMcpConfigPath();
39
+ try {
40
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
41
+ }
42
+ catch (error) {
43
+ console.error('Failed to write MCP config:', error);
44
+ throw error;
45
+ }
46
+ };
47
+ // Get all MCP configurations
48
+ router.get('/', (req, res) => {
49
+ try {
50
+ const config = readMcpConfig();
51
+ const servers = Object.entries(config.mcpServers).map(([name, serverConfig]) => ({
52
+ name,
53
+ ...serverConfig
54
+ }));
55
+ res.json({ servers });
56
+ }
57
+ catch (error) {
58
+ console.error('Failed to get MCP configs:', error);
59
+ res.status(500).json({ error: 'Failed to retrieve MCP configurations' });
60
+ }
61
+ });
62
+ // Add or update MCP configuration
63
+ router.post('/', (req, res) => {
64
+ try {
65
+ const { name, type, command, args, url, timeout, autoApprove } = req.body;
66
+ if (!name || !type) {
67
+ return res.status(400).json({ error: 'Missing required fields: name, type' });
68
+ }
69
+ // Validate based on type
70
+ if (type === 'stdio') {
71
+ if (!command || !Array.isArray(args)) {
72
+ return res.status(400).json({ error: 'For stdio type: command and args are required' });
73
+ }
74
+ }
75
+ else if (type === 'http') {
76
+ if (!url) {
77
+ return res.status(400).json({ error: 'For http type: url is required' });
78
+ }
79
+ }
80
+ else {
81
+ return res.status(400).json({ error: 'Invalid type. Must be "stdio" or "http"' });
82
+ }
83
+ const config = readMcpConfig();
84
+ // Create server config without name field
85
+ const serverConfig = {
86
+ type,
87
+ ...(command && { command }),
88
+ ...(args && { args }),
89
+ ...(url && { url }),
90
+ ...(timeout && { timeout }),
91
+ ...(autoApprove && Array.isArray(autoApprove) && { autoApprove })
92
+ };
93
+ config.mcpServers[name] = serverConfig;
94
+ writeMcpConfig(config);
95
+ const responseServer = { name, ...serverConfig };
96
+ res.json({ server: responseServer, message: 'MCP configuration saved successfully' });
97
+ }
98
+ catch (error) {
99
+ console.error('Failed to save MCP config:', error);
100
+ res.status(500).json({ error: 'Failed to save MCP configuration' });
101
+ }
102
+ });
103
+ // Update MCP configuration
104
+ router.put('/:name', (req, res) => {
105
+ try {
106
+ const { name } = req.params;
107
+ const { type, command, args, url, timeout, autoApprove } = req.body;
108
+ if (!type) {
109
+ return res.status(400).json({ error: 'Missing required field: type' });
110
+ }
111
+ // Validate based on type
112
+ if (type === 'stdio') {
113
+ if (!command || !Array.isArray(args)) {
114
+ return res.status(400).json({ error: 'For stdio type: command and args are required' });
115
+ }
116
+ }
117
+ else if (type === 'http') {
118
+ if (!url) {
119
+ return res.status(400).json({ error: 'For http type: url is required' });
120
+ }
121
+ }
122
+ else {
123
+ return res.status(400).json({ error: 'Invalid type. Must be "stdio" or "http"' });
124
+ }
125
+ const config = readMcpConfig();
126
+ if (!config.mcpServers[name]) {
127
+ return res.status(404).json({ error: 'MCP configuration not found' });
128
+ }
129
+ // Update server config
130
+ const serverConfig = {
131
+ type,
132
+ ...(command && { command }),
133
+ ...(args && { args }),
134
+ ...(url && { url }),
135
+ ...(timeout && { timeout }),
136
+ ...(autoApprove && Array.isArray(autoApprove) && { autoApprove })
137
+ };
138
+ config.mcpServers[name] = serverConfig;
139
+ writeMcpConfig(config);
140
+ const responseServer = { name, ...serverConfig };
141
+ res.json({ server: responseServer, message: 'MCP configuration updated successfully' });
142
+ }
143
+ catch (error) {
144
+ console.error('Failed to update MCP config:', error);
145
+ res.status(500).json({ error: 'Failed to update MCP configuration' });
146
+ }
147
+ });
148
+ // Delete MCP configuration
149
+ router.delete('/:name', (req, res) => {
150
+ try {
151
+ const { name } = req.params;
152
+ const config = readMcpConfig();
153
+ if (!config.mcpServers[name]) {
154
+ return res.status(404).json({ error: 'MCP configuration not found' });
155
+ }
156
+ delete config.mcpServers[name];
157
+ writeMcpConfig(config);
158
+ res.json({ success: true, message: 'MCP configuration deleted successfully' });
159
+ }
160
+ catch (error) {
161
+ console.error('Failed to delete MCP config:', error);
162
+ res.status(500).json({ error: 'Failed to delete MCP configuration' });
163
+ }
164
+ });
165
+ // Validate MCP server by testing connection and getting tools
166
+ router.post('/:name/validate', async (req, res) => {
167
+ try {
168
+ const { name } = req.params;
169
+ const config = readMcpConfig();
170
+ if (!config.mcpServers[name]) {
171
+ return res.status(404).json({ error: 'MCP configuration not found' });
172
+ }
173
+ const serverConfig = config.mcpServers[name];
174
+ if (serverConfig.type === 'http') {
175
+ // Validate HTTP MCP server
176
+ await validateHttpMcpServer(name, serverConfig, config, res);
177
+ }
178
+ else if (serverConfig.type === 'stdio') {
179
+ // Validate stdio MCP server
180
+ await validateStdioMcpServer(name, serverConfig, config, res);
181
+ }
182
+ else {
183
+ res.status(400).json({ error: 'Invalid MCP server type' });
184
+ }
185
+ }
186
+ catch (error) {
187
+ console.error('Failed to validate MCP server:', error);
188
+ res.status(500).json({
189
+ error: 'Failed to validate MCP server',
190
+ details: error instanceof Error ? error.message : String(error)
191
+ });
192
+ }
193
+ });
194
+ // Get MCP configurations from Claude Code CLI
195
+ router.get('/claude-code', async (req, res) => {
196
+ try {
197
+ // Try to get MCP servers from Claude Code CLI
198
+ const { stdout } = await execAsync('claude mcp list');
199
+ // Parse the output to extract MCP server information
200
+ const lines = stdout.split('\n').filter(line => line.trim());
201
+ const servers = [];
202
+ for (const line of lines) {
203
+ // Look for lines that contain MCP server information
204
+ // Format: "server-name: url (TYPE) - ✓ Connected" or "server-name: url (TYPE) - ✗ Error"
205
+ const match = line.match(/^(.+?):\s+(.+?)\s+\((\w+)\)\s+-\s+(✓|✗)\s+(.+)$/);
206
+ if (match) {
207
+ const [, name, urlOrCommand, type, status, statusText] = match;
208
+ const server = {
209
+ name: name.trim(),
210
+ type: type.toLowerCase() === 'http' ? 'http' : 'stdio',
211
+ status: status === '✓' ? 'active' : 'error'
212
+ };
213
+ if (server.type === 'http') {
214
+ server.url = urlOrCommand.trim();
215
+ }
216
+ else {
217
+ // For stdio, we might need to parse command and args
218
+ // This is a simplified approach
219
+ const parts = urlOrCommand.trim().split(' ');
220
+ server.command = parts[0];
221
+ server.args = parts.slice(1);
222
+ }
223
+ if (status === '✗') {
224
+ server.error = statusText;
225
+ }
226
+ servers.push(server);
227
+ }
228
+ }
229
+ res.json({ servers });
230
+ }
231
+ catch (error) {
232
+ console.error('Failed to get Claude Code MCP configurations:', error);
233
+ res.status(500).json({
234
+ error: 'Failed to get Claude Code MCP configurations',
235
+ details: error instanceof Error ? error.message : String(error)
236
+ });
237
+ }
238
+ });
239
+ export default router;
240
+ // Validate HTTP MCP server
241
+ async function validateHttpMcpServer(name, serverConfig, config, res) {
242
+ try {
243
+ console.log('Validating HTTP MCP server:', serverConfig.url);
244
+ // Test HTTP connection to MCP server
245
+ const response = await fetch(serverConfig.url, {
246
+ method: 'POST',
247
+ headers: {
248
+ 'Content-Type': 'application/json',
249
+ 'Accept': 'application/json, text/event-stream',
250
+ },
251
+ body: JSON.stringify({
252
+ jsonrpc: '2.0',
253
+ id: 1,
254
+ method: 'initialize',
255
+ params: {
256
+ protocolVersion: '2024-11-05',
257
+ capabilities: {
258
+ tools: {}
259
+ },
260
+ clientInfo: {
261
+ name: 'claude-code-studio',
262
+ version: '1.0.0'
263
+ }
264
+ }
265
+ })
266
+ });
267
+ if (!response.ok) {
268
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
269
+ }
270
+ // Parse SSE response
271
+ const responseText = await response.text();
272
+ console.log('HTTP MCP initialize response text:', responseText);
273
+ // Extract JSON from SSE format
274
+ let initResult = null;
275
+ const lines = responseText.split('\n');
276
+ for (const line of lines) {
277
+ if (line.startsWith('data: ')) {
278
+ try {
279
+ initResult = JSON.parse(line.substring(6));
280
+ break;
281
+ }
282
+ catch (e) {
283
+ console.warn('Failed to parse SSE data line:', line);
284
+ }
285
+ }
286
+ }
287
+ if (!initResult) {
288
+ throw new Error('Failed to parse HTTP MCP response');
289
+ }
290
+ console.log('HTTP MCP initialize parsed result:', initResult);
291
+ // Get tools from HTTP MCP server using proper session management
292
+ let tools = [];
293
+ console.log('Getting tools from HTTP MCP server...');
294
+ try {
295
+ tools = await getHttpMcpTools(serverConfig.url);
296
+ console.log('Successfully retrieved tools from HTTP MCP:', tools);
297
+ }
298
+ catch (error) {
299
+ console.warn('Failed to get tools from HTTP MCP server:', error);
300
+ // Don't fail validation just because tools retrieval failed
301
+ }
302
+ // Update server config with successful validation
303
+ const currentTime = new Date().toISOString();
304
+ if (config.mcpServers[name]) {
305
+ config.mcpServers[name] = {
306
+ ...config.mcpServers[name],
307
+ status: 'active',
308
+ tools,
309
+ lastValidated: currentTime,
310
+ error: undefined
311
+ };
312
+ writeMcpConfig(config);
313
+ }
314
+ res.json({
315
+ success: true,
316
+ tools,
317
+ message: `HTTP MCP server validated successfully. Found ${tools.length} tools.`
318
+ });
319
+ }
320
+ catch (error) {
321
+ console.error('HTTP MCP server validation failed:', error);
322
+ // Update server config with error status
323
+ const currentTime = new Date().toISOString();
324
+ if (config.mcpServers[name]) {
325
+ config.mcpServers[name] = {
326
+ ...config.mcpServers[name],
327
+ status: 'error',
328
+ error: error instanceof Error ? error.message : String(error),
329
+ tools: undefined,
330
+ lastValidated: currentTime
331
+ };
332
+ writeMcpConfig(config);
333
+ }
334
+ res.status(400).json({
335
+ error: 'HTTP MCP server validation failed',
336
+ details: error instanceof Error ? error.message : String(error)
337
+ });
338
+ }
339
+ }
340
+ // Validate stdio MCP server
341
+ async function validateStdioMcpServer(name, serverConfig, config, res) {
342
+ if (!serverConfig.command || !serverConfig.args) {
343
+ return res.status(400).json({ error: 'Missing command or args for stdio MCP server' });
344
+ }
345
+ // Start MCP server process to test connection
346
+ console.log('Starting stdio MCP server:', serverConfig.command, serverConfig.args);
347
+ const child = spawn(serverConfig.command, serverConfig.args, {
348
+ stdio: ['pipe', 'pipe', 'pipe'],
349
+ timeout: serverConfig.timeout || 15000
350
+ });
351
+ let stdout = '';
352
+ let stderr = '';
353
+ let tools = [];
354
+ child.stderr?.on('data', (data) => {
355
+ const errorStr = data.toString();
356
+ console.log('MCP stderr:', errorStr);
357
+ stderr += errorStr;
358
+ });
359
+ let initializeDone = false;
360
+ // Send initialize request to MCP server
361
+ const initializeRequest = {
362
+ jsonrpc: '2.0',
363
+ id: 1,
364
+ method: 'initialize',
365
+ params: {
366
+ protocolVersion: '2024-11-05',
367
+ capabilities: {
368
+ tools: {}
369
+ },
370
+ clientInfo: {
371
+ name: 'claude-code-studio',
372
+ version: '1.0.0'
373
+ }
374
+ }
375
+ };
376
+ child.stdin?.write(JSON.stringify(initializeRequest) + '\n');
377
+ let buffer = '';
378
+ // Listen for stdout and parse responses in real-time
379
+ child.stdout?.on('data', (data) => {
380
+ const dataStr = data.toString();
381
+ stdout += dataStr;
382
+ buffer += dataStr;
383
+ // Try to parse complete JSON messages
384
+ const lines = buffer.split('\n');
385
+ // Keep the last incomplete line in the buffer
386
+ buffer = lines.pop() || '';
387
+ for (const line of lines) {
388
+ if (!line.trim())
389
+ continue;
390
+ try {
391
+ const response = JSON.parse(line);
392
+ console.log('MCP response:', response);
393
+ // Check if initialization was successful
394
+ if (response.id === 1 && response.result && !initializeDone) {
395
+ console.log('Initialize successful, sending initialized notification');
396
+ initializeDone = true;
397
+ // Send initialized notification
398
+ const initializedNotification = {
399
+ jsonrpc: '2.0',
400
+ method: 'notifications/initialized'
401
+ };
402
+ child.stdin?.write(JSON.stringify(initializedNotification) + '\n');
403
+ // Send tools/list request
404
+ setTimeout(() => {
405
+ console.log('Sending tools/list request');
406
+ const toolsRequest = {
407
+ jsonrpc: '2.0',
408
+ id: 2,
409
+ method: 'tools/list',
410
+ params: {}
411
+ };
412
+ child.stdin?.write(JSON.stringify(toolsRequest) + '\n');
413
+ }, 500);
414
+ }
415
+ // Handle tools/list response
416
+ if (response.id === 2 && response.result) {
417
+ console.log('Tools/list response received:', response.result);
418
+ if (response.result.tools) {
419
+ tools = response.result.tools.map((tool) => tool.name);
420
+ console.log('Found tools:', tools);
421
+ }
422
+ // Close stdin after getting tools response (even if empty)
423
+ setTimeout(() => child.stdin?.end(), 100);
424
+ }
425
+ }
426
+ catch (parseError) {
427
+ // This might be a partial JSON, continue buffering
428
+ console.log('Parse error for line:', line.substring(0, 100) + '...');
429
+ }
430
+ }
431
+ // Also try to parse the buffer as a complete JSON in case it's all one response
432
+ if (buffer.trim()) {
433
+ try {
434
+ const response = JSON.parse(buffer);
435
+ console.log('MCP buffered response:', response);
436
+ // Handle tools/list response from buffer
437
+ if (response.id === 2 && response.result) {
438
+ console.log('Tools/list response received from buffer:', response.result);
439
+ if (response.result.tools) {
440
+ tools = response.result.tools.map((tool) => tool.name);
441
+ console.log('Found tools from buffer:', tools);
442
+ }
443
+ // Clear buffer and close stdin
444
+ buffer = '';
445
+ setTimeout(() => child.stdin?.end(), 100);
446
+ }
447
+ }
448
+ catch (parseError) {
449
+ // Buffer is not complete JSON yet, continue
450
+ }
451
+ }
452
+ });
453
+ const timeoutId = setTimeout(() => {
454
+ child.kill('SIGTERM');
455
+ }, serverConfig.timeout || 10000);
456
+ child.on('close', (code) => {
457
+ clearTimeout(timeoutId);
458
+ try {
459
+ const config = readMcpConfig();
460
+ const currentTime = new Date().toISOString();
461
+ if (code === 0 || tools.length > 0) {
462
+ // Update server config with successful validation
463
+ if (config.mcpServers[name]) {
464
+ config.mcpServers[name] = {
465
+ ...config.mcpServers[name],
466
+ status: 'active',
467
+ tools,
468
+ lastValidated: currentTime,
469
+ error: undefined
470
+ };
471
+ writeMcpConfig(config);
472
+ }
473
+ res.json({
474
+ success: true,
475
+ tools,
476
+ message: `MCP server validated successfully. Found ${tools.length} tools.`
477
+ });
478
+ }
479
+ else {
480
+ // Update server config with error status
481
+ const errorMessage = `MCP server validation failed with exit code ${code}`;
482
+ if (config.mcpServers[name]) {
483
+ config.mcpServers[name] = {
484
+ ...config.mcpServers[name],
485
+ status: 'error',
486
+ error: errorMessage,
487
+ tools: undefined,
488
+ lastValidated: currentTime
489
+ };
490
+ writeMcpConfig(config);
491
+ }
492
+ res.status(400).json({
493
+ error: errorMessage,
494
+ details: stderr || 'No error details available'
495
+ });
496
+ }
497
+ }
498
+ catch (configError) {
499
+ console.error('Failed to update config with validation result:', configError);
500
+ // Still return the validation result even if config update fails
501
+ if (code === 0 || tools.length > 0) {
502
+ res.json({
503
+ success: true,
504
+ tools,
505
+ message: `MCP server validated successfully. Found ${tools.length} tools.`
506
+ });
507
+ }
508
+ else {
509
+ res.status(400).json({
510
+ error: `MCP server validation failed with exit code ${code}`,
511
+ details: stderr || 'No error details available'
512
+ });
513
+ }
514
+ }
515
+ });
516
+ child.on('error', (error) => {
517
+ clearTimeout(timeoutId);
518
+ try {
519
+ const config = readMcpConfig();
520
+ if (config.mcpServers[name]) {
521
+ config.mcpServers[name] = {
522
+ ...config.mcpServers[name],
523
+ status: 'error',
524
+ error: `Failed to start MCP server: ${error.message}`,
525
+ tools: undefined,
526
+ lastValidated: new Date().toISOString()
527
+ };
528
+ writeMcpConfig(config);
529
+ }
530
+ }
531
+ catch (configError) {
532
+ console.error('Failed to update config with error status:', configError);
533
+ }
534
+ res.status(500).json({
535
+ error: 'Failed to start MCP server',
536
+ details: error.message
537
+ });
538
+ });
539
+ }
540
+ /**
541
+ * Get tools from HTTP MCP server using proper session management
542
+ * HTTP MCP requires maintaining session state between initialize and tools/list calls
543
+ */
544
+ async function getHttpMcpTools(url) {
545
+ console.log('Starting HTTP MCP tools discovery for:', url);
546
+ // Step 1: Initialize the session
547
+ const initResponse = await fetch(url, {
548
+ method: 'POST',
549
+ headers: {
550
+ 'Content-Type': 'application/json',
551
+ 'Accept': 'application/json, text/event-stream',
552
+ },
553
+ body: JSON.stringify({
554
+ jsonrpc: '2.0',
555
+ id: 1,
556
+ method: 'initialize',
557
+ params: {
558
+ protocolVersion: '2024-11-05',
559
+ capabilities: {
560
+ tools: {}
561
+ },
562
+ clientInfo: {
563
+ name: 'agentstudio-validator',
564
+ version: '1.0.0'
565
+ }
566
+ }
567
+ })
568
+ });
569
+ if (!initResponse.ok) {
570
+ throw new Error(`HTTP MCP initialize failed: ${initResponse.status} ${initResponse.statusText}`);
571
+ }
572
+ const initResponseText = await initResponse.text();
573
+ console.log('HTTP MCP initialize response:', initResponseText);
574
+ // Parse initialize response
575
+ let initResult = null;
576
+ const initLines = initResponseText.split('\n');
577
+ for (const line of initLines) {
578
+ if (line.startsWith('data: ')) {
579
+ try {
580
+ initResult = JSON.parse(line.substring(6));
581
+ break;
582
+ }
583
+ catch (e) {
584
+ console.warn('Failed to parse init SSE data line:', line);
585
+ }
586
+ }
587
+ }
588
+ if (!initResult || !initResult.result) {
589
+ throw new Error('Invalid initialize response from HTTP MCP server');
590
+ }
591
+ // Extract session ID from response headers
592
+ const sessionId = initResponse.headers.get('mcp-session-id');
593
+ console.log('HTTP MCP session ID:', sessionId);
594
+ // Step 2: Get tools list using the session ID
595
+ // Wait a bit to ensure the session is properly established
596
+ await new Promise(resolve => setTimeout(resolve, 100));
597
+ const toolsHeaders = {
598
+ 'Content-Type': 'application/json',
599
+ 'Accept': 'application/json, text/event-stream',
600
+ };
601
+ // Add session ID if available
602
+ if (sessionId) {
603
+ toolsHeaders['mcp-session-id'] = sessionId;
604
+ }
605
+ // Copy any session cookies from the init response
606
+ if (initResponse.headers.get('set-cookie')) {
607
+ toolsHeaders['Cookie'] = initResponse.headers.get('set-cookie');
608
+ }
609
+ const toolsResponse = await fetch(url, {
610
+ method: 'POST',
611
+ headers: toolsHeaders,
612
+ body: JSON.stringify({
613
+ jsonrpc: '2.0',
614
+ id: 2,
615
+ method: 'tools/list',
616
+ params: {}
617
+ })
618
+ });
619
+ if (!toolsResponse.ok) {
620
+ throw new Error(`HTTP MCP tools/list failed: ${toolsResponse.status} ${toolsResponse.statusText}`);
621
+ }
622
+ const toolsResponseText = await toolsResponse.text();
623
+ console.log('HTTP MCP tools/list response:', toolsResponseText);
624
+ // Parse tools response
625
+ let toolsResult = null;
626
+ const toolsLines = toolsResponseText.split('\n');
627
+ for (const line of toolsLines) {
628
+ if (line.startsWith('data: ')) {
629
+ try {
630
+ toolsResult = JSON.parse(line.substring(6));
631
+ break;
632
+ }
633
+ catch (e) {
634
+ console.warn('Failed to parse tools SSE data line:', line);
635
+ }
636
+ }
637
+ }
638
+ if (!toolsResult) {
639
+ throw new Error('Invalid tools/list response from HTTP MCP server');
640
+ }
641
+ if (toolsResult.error) {
642
+ throw new Error(`HTTP MCP tools/list error: ${toolsResult.error.message}`);
643
+ }
644
+ if (!toolsResult.result || !toolsResult.result.tools) {
645
+ console.log('HTTP MCP server returned no tools');
646
+ return [];
647
+ }
648
+ const tools = toolsResult.result.tools.map((tool) => tool.name);
649
+ console.log('Extracted tools from HTTP MCP:', tools);
650
+ return tools;
651
+ }
652
+ //# sourceMappingURL=mcp.js.map