mcp-detector.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. /**
  2. * MCP SERVER DETECTION UTILITY
  3. * ============================
  4. *
  5. * Centralized utility for detecting MCP server configurations.
  6. * Used across TaskMaster integration and other MCP-dependent features.
  7. */
  8. import { promises as fsPromises } from 'fs';
  9. import path from 'path';
  10. import os from 'os';
  11. /**
  12. * Check if task-master-ai MCP server is configured
  13. * @returns {Promise<Object>} MCP detection result
  14. */
  15. export async function detectTaskMasterMCPServer() {
  16. try {
  17. const homeDir = os.homedir();
  18. const configPaths = [
  19. path.join(homeDir, '.pilotdeck.json'),
  20. path.join(homeDir, '.pilotdeck', 'settings.json')
  21. ];
  22. let configData = null;
  23. let configPath = null;
  24. // Try to read from either config file
  25. for (const filepath of configPaths) {
  26. try {
  27. const fileContent = await fsPromises.readFile(filepath, 'utf8');
  28. configData = JSON.parse(fileContent);
  29. configPath = filepath;
  30. break;
  31. } catch (error) {
  32. // File doesn't exist or is not valid JSON, try next
  33. continue;
  34. }
  35. }
  36. if (!configData) {
  37. return {
  38. hasMCPServer: false,
  39. reason: 'No pilotdeck configuration file found',
  40. hasConfig: false
  41. };
  42. }
  43. // Look for task-master-ai in user-scoped MCP servers
  44. let taskMasterServer = null;
  45. if (configData.mcpServers && typeof configData.mcpServers === 'object') {
  46. const serverEntry = Object.entries(configData.mcpServers).find(([name, config]) =>
  47. name === 'task-master-ai' ||
  48. name.includes('task-master') ||
  49. (config && config.command && config.command.includes('task-master'))
  50. );
  51. if (serverEntry) {
  52. const [name, config] = serverEntry;
  53. taskMasterServer = {
  54. name,
  55. scope: 'user',
  56. config,
  57. type: config.command ? 'stdio' : (config.url ? 'http' : 'unknown')
  58. };
  59. }
  60. }
  61. // Also check project-specific MCP servers if not found globally
  62. if (!taskMasterServer && configData.projects) {
  63. for (const [projectPath, projectConfig] of Object.entries(configData.projects)) {
  64. if (projectConfig.mcpServers && typeof projectConfig.mcpServers === 'object') {
  65. const serverEntry = Object.entries(projectConfig.mcpServers).find(([name, config]) =>
  66. name === 'task-master-ai' ||
  67. name.includes('task-master') ||
  68. (config && config.command && config.command.includes('task-master'))
  69. );
  70. if (serverEntry) {
  71. const [name, config] = serverEntry;
  72. taskMasterServer = {
  73. name,
  74. scope: 'local',
  75. projectPath,
  76. config,
  77. type: config.command ? 'stdio' : (config.url ? 'http' : 'unknown')
  78. };
  79. break;
  80. }
  81. }
  82. }
  83. }
  84. if (taskMasterServer) {
  85. const isValid = !!(taskMasterServer.config &&
  86. (taskMasterServer.config.command || taskMasterServer.config.url));
  87. const hasEnvVars = !!(taskMasterServer.config &&
  88. taskMasterServer.config.env &&
  89. Object.keys(taskMasterServer.config.env).length > 0);
  90. return {
  91. hasMCPServer: true,
  92. isConfigured: isValid,
  93. hasApiKeys: hasEnvVars,
  94. scope: taskMasterServer.scope,
  95. config: {
  96. command: taskMasterServer.config?.command,
  97. args: taskMasterServer.config?.args || [],
  98. url: taskMasterServer.config?.url,
  99. envVars: hasEnvVars ? Object.keys(taskMasterServer.config.env) : [],
  100. type: taskMasterServer.type
  101. }
  102. };
  103. } else {
  104. // Get list of available servers for debugging
  105. const availableServers = [];
  106. if (configData.mcpServers) {
  107. availableServers.push(...Object.keys(configData.mcpServers));
  108. }
  109. if (configData.projects) {
  110. for (const projectConfig of Object.values(configData.projects)) {
  111. if (projectConfig.mcpServers) {
  112. availableServers.push(...Object.keys(projectConfig.mcpServers).map(name => `local:${name}`));
  113. }
  114. }
  115. }
  116. return {
  117. hasMCPServer: false,
  118. reason: 'task-master-ai not found in configured MCP servers',
  119. hasConfig: true,
  120. configPath,
  121. availableServers
  122. };
  123. }
  124. } catch (error) {
  125. console.error('Error detecting MCP server config:', error);
  126. return {
  127. hasMCPServer: false,
  128. reason: `Error checking MCP config: ${error.message}`,
  129. hasConfig: false
  130. };
  131. }
  132. }
  133. /**
  134. * Get all configured MCP servers (not just TaskMaster)
  135. * @returns {Promise<Object>} All MCP servers configuration
  136. */
  137. export async function getAllMCPServers() {
  138. try {
  139. const homeDir = os.homedir();
  140. const configPaths = [
  141. path.join(homeDir, '.pilotdeck.json'),
  142. path.join(homeDir, '.pilotdeck', 'settings.json')
  143. ];
  144. let configData = null;
  145. let configPath = null;
  146. // Try to read from either config file
  147. for (const filepath of configPaths) {
  148. try {
  149. const fileContent = await fsPromises.readFile(filepath, 'utf8');
  150. configData = JSON.parse(fileContent);
  151. configPath = filepath;
  152. break;
  153. } catch (error) {
  154. continue;
  155. }
  156. }
  157. if (!configData) {
  158. return {
  159. hasConfig: false,
  160. servers: {},
  161. projectServers: {}
  162. };
  163. }
  164. return {
  165. hasConfig: true,
  166. configPath,
  167. servers: configData.mcpServers || {},
  168. projectServers: configData.projects || {}
  169. };
  170. } catch (error) {
  171. console.error('Error getting all MCP servers:', error);
  172. return {
  173. hasConfig: false,
  174. error: error.message,
  175. servers: {},
  176. projectServers: {}
  177. };
  178. }
  179. }