From 870af98333a644563d8759a48886ab87980f4464 Mon Sep 17 00:00:00 2001 From: Jonathan Norris Date: Sat, 30 Aug 2025 22:43:36 -0400 Subject: [PATCH 1/2] feat: remove list_environments / get_sdk_keys / list_projects tools as the select_project tool returns that data --- src/mcp/server.test.ts | 21 +++++--- src/mcp/tools/environmentTools.ts | 80 ++++++++++++++++--------------- src/mcp/tools/projectTools.ts | 40 ++++++++-------- 3 files changed, 78 insertions(+), 63 deletions(-) diff --git a/src/mcp/server.test.ts b/src/mcp/server.test.ts index 629390293..e1974350b 100644 --- a/src/mcp/server.test.ts +++ b/src/mcp/server.test.ts @@ -121,8 +121,10 @@ describe('DevCycleMCPServer', () => { // Check that we have tools from all categories expect(registeredToolNames).to.include('list_features') expect(registeredToolNames).to.include('list_variables') - expect(registeredToolNames).to.include('list_environments') - expect(registeredToolNames).to.include('list_projects') + // expect(registeredToolNames).to.include('list_environments') + // expect(registeredToolNames).to.include('list_projects') + // list_environments and list_projects are disabled; using select_project instead + expect(registeredToolNames).to.include('select_project') expect(registeredToolNames).to.include('get_current_project') expect(registeredToolNames).to.include( 'get_self_targeting_identity', @@ -144,13 +146,20 @@ describe('DevCycleMCPServer', () => { it('should register tools with input schemas', () => { const registerToolStub = server.registerTool as sinon.SinonStub + // Old expectation (tool disabled): + // const listProjectsCall = registerToolStub + // .getCalls() + // .find((call) => call.args[0] === 'list_projects') + // expect(listProjectsCall).to.exist + // expect(listProjectsCall!.args[1]).to.have.property('inputSchema') + // Find a tool that should have an input schema - const listProjectsCall = registerToolStub + const listFeaturesCall = registerToolStub .getCalls() - .find((call) => call.args[0] === 'list_projects') + .find((call) => call.args[0] === 'list_features') - expect(listProjectsCall).to.exist - expect(listProjectsCall!.args[1]).to.have.property('inputSchema') + expect(listFeaturesCall).to.exist + expect(listFeaturesCall!.args[1]).to.have.property('inputSchema') }) }) diff --git a/src/mcp/tools/environmentTools.ts b/src/mcp/tools/environmentTools.ts index 5306d2e6e..97c8e63af 100644 --- a/src/mcp/tools/environmentTools.ts +++ b/src/mcp/tools/environmentTools.ts @@ -131,46 +131,50 @@ export async function updateEnvironmentHandler( * Register environment tools with the MCP server using the new direct registration pattern */ export function registerEnvironmentTools( - serverInstance: DevCycleMCPServerInstance, - apiClient: IDevCycleApiClient, + _serverInstance: DevCycleMCPServerInstance, + _apiClient: IDevCycleApiClient, ): void { - serverInstance.registerToolWithErrorHandling( - 'list_environments', - { - description: [ - 'List environments in the current project.', - 'Include dashboard link in the response.', - ].join('\n'), - annotations: { - title: 'List Environments', - readOnlyHint: true, - }, - inputSchema: ListEnvironmentsArgsSchema.shape, - }, - async (args: any) => { - const validatedArgs = ListEnvironmentsArgsSchema.parse(args) - return await listEnvironmentsHandler(validatedArgs, apiClient) - }, - ) + void _serverInstance + void _apiClient + // DISABLED: list_environments tool (data available from select_project) + // serverInstance.registerToolWithErrorHandling( + // 'list_environments', + // { + // description: [ + // 'List environments in the current project.', + // 'Include dashboard link in the response.', + // ].join('\n'), + // annotations: { + // title: 'List Environments', + // readOnlyHint: true, + // }, + // inputSchema: ListEnvironmentsArgsSchema.shape, + // }, + // async (args: any) => { + // const validatedArgs = ListEnvironmentsArgsSchema.parse(args) + // return await listEnvironmentsHandler(validatedArgs, apiClient) + // }, + // ) - serverInstance.registerToolWithErrorHandling( - 'get_sdk_keys', - { - description: [ - 'Get SDK keys for an environment.', - 'Include dashboard link in the response.', - ].join('\n'), - annotations: { - title: 'Get SDK Keys', - readOnlyHint: true, - }, - inputSchema: GetSdkKeysArgsSchema.shape, - }, - async (args: any) => { - const validatedArgs = GetSdkKeysArgsSchema.parse(args) - return await getSdkKeysHandler(validatedArgs, apiClient) - }, - ) + // DISABLED: get_sdk_keys tool (data available from select_project) + // serverInstance.registerToolWithErrorHandling( + // 'get_sdk_keys', + // { + // description: [ + // 'Get SDK keys for an environment.', + // 'Include dashboard link in the response.', + // ].join('\n'), + // annotations: { + // title: 'Get SDK Keys', + // readOnlyHint: true, + // }, + // inputSchema: GetSdkKeysArgsSchema.shape, + // }, + // async (args: any) => { + // const validatedArgs = GetSdkKeysArgsSchema.parse(args) + // return await getSdkKeysHandler(validatedArgs, apiClient) + // }, + // ) // DISABLED: Environment creation/update tools // serverInstance.registerToolWithErrorHandling( diff --git a/src/mcp/tools/projectTools.ts b/src/mcp/tools/projectTools.ts index aaa7ed0e6..764bab4e7 100644 --- a/src/mcp/tools/projectTools.ts +++ b/src/mcp/tools/projectTools.ts @@ -113,25 +113,27 @@ export function registerProjectTools( serverInstance: DevCycleMCPServerInstance, apiClient: IDevCycleApiClient, ): void { - serverInstance.registerToolWithErrorHandling( - 'list_projects', - { - description: [ - 'List all projects in the current organization.', - 'Can be called before "select_project"', - ].join('\n'), - annotations: { - title: 'List Projects', - readOnlyHint: true, - }, - inputSchema: ListProjectsArgsSchema.shape, - }, - async (args: unknown) => { - const validatedArgs = ListProjectsArgsSchema.parse(args) - - return await listProjectsHandler(validatedArgs, apiClient) - }, - ) + // DISABLED: list_projects tool (data available from select_project) + // serverInstance.registerToolWithErrorHandling( + // 'list_projects', + // { + // description: [ + // 'List all projects in the current organization.', + // 'Can be called before "select_project"', + // 'Include dashboard link in the response.', + // ].join('\n'), + // annotations: { + // title: 'List Projects', + // readOnlyHint: true, + // }, + // inputSchema: ListProjectsArgsSchema.shape, + // }, + // async (args: unknown) => { + // const validatedArgs = ListProjectsArgsSchema.parse(args) + // + // return await listProjectsHandler(validatedArgs, apiClient) + // }, + // ) serverInstance.registerToolWithErrorHandling( 'get_current_project', From d872e8f05348d8e3ea4d54dffcfaaf8056b81c48 Mon Sep 17 00:00:00 2001 From: Jonathan Norris Date: Sat, 30 Aug 2025 22:52:40 -0400 Subject: [PATCH 2/2] chore: disable redundant MCP tools, update tests, prioritize select_project and install_sdk --- mcp-worker/src/index.ts | 6 +++--- src/mcp/tools/index.ts | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/mcp-worker/src/index.ts b/mcp-worker/src/index.ts index 70d176f01..d1c99675c 100644 --- a/mcp-worker/src/index.ts +++ b/mcp-worker/src/index.ts @@ -128,12 +128,12 @@ export class DevCycleMCP extends McpAgent { }, } + // Register worker-specific project selection tools first so it appears at the front + registerProjectSelectionTools(serverAdapter, this.apiClient) + // Register all CLI tools using the centralized registration function registerAllToolsWithServer(serverAdapter, this.apiClient) - // Register worker-specific project selection tools using the modern pattern - registerProjectSelectionTools(serverAdapter, this.apiClient) - console.log('✅ DevCycle MCP Worker initialization completed') } diff --git a/src/mcp/tools/index.ts b/src/mcp/tools/index.ts index c17762def..823c617a8 100644 --- a/src/mcp/tools/index.ts +++ b/src/mcp/tools/index.ts @@ -20,6 +20,14 @@ export function registerAllToolsWithServer( serverInstance: DevCycleMCPServerInstance, apiClient: IDevCycleApiClient, ): void { + // Register local project selection tools first for local MCP + // We detect local MCP by checking if the apiClient is an instance of DevCycleApiClient + if (apiClient instanceof DevCycleApiClient) { + const auth = apiClient.getAuth() + registerLocalProjectTools(serverInstance, apiClient, auth) + } + + registerInstallTools(serverInstance) registerProjectTools(serverInstance, apiClient) // registerCustomPropertiesTools(serverInstance, apiClient) // DISABLED: Custom properties tools registerEnvironmentTools(serverInstance, apiClient) @@ -27,14 +35,6 @@ export function registerAllToolsWithServer( registerResultsTools(serverInstance, apiClient) registerSelfTargetingTools(serverInstance, apiClient) registerVariableTools(serverInstance, apiClient) - registerInstallTools(serverInstance) - - // Register local project selection tools only for local MCP (not worker) - // We detect local MCP by checking if the apiClient is an instance of DevCycleApiClient - if (apiClient instanceof DevCycleApiClient) { - const auth = apiClient.getAuth() - registerLocalProjectTools(serverInstance, apiClient, auth) - } } export type { IDevCycleApiClient } from '../api/interface'