Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/mcp/tools/featureTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ import { IDevCycleApiClient } from '../api/interface'
import { DevCycleMCPServerInstance } from '../server'
import { handleZodiosValidationErrors } from '../utils/api'
import { dashboardLinks } from '../utils/dashboardLinks'
import { fetchAiPromptsAndRules } from '../utils/github'
import { CleanupFeatureArgsSchema } from '../types'

// Individual handler functions
export async function listFeaturesHandler(
Expand Down Expand Up @@ -678,4 +680,29 @@ export function registerFeatureTools(
)
},
)

// Cleanup Feature Prompt tool: fetches the cleanup prompt from GitHub
serverInstance.registerToolWithErrorHandling(
'cleanup_feature',
{
description: [
'Fetch the DevCycle Feature Cleanup prompt and return its markdown content.',
'Use this to guide safe cleanup of a completed feature and its variables in codebases.',
'Includes steps to analyze production state, complete the feature, and remove variables.',
].join('\n'),
annotations: {
title: 'Cleanup Feature Prompt',
readOnlyHint: true,
},
inputSchema: CleanupFeatureArgsSchema.shape,
},
async (args: unknown) => {
// validate args
CleanupFeatureArgsSchema.parse(args)
return await fetchAiPromptsAndRules(
'clean-up-prompts/clean-up.md',
'Cleanup prompt not found at clean-up-prompts/clean-up.md.',
)
},
)
}
27 changes: 5 additions & 22 deletions src/mcp/tools/installTools.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import axios from 'axios'
import { z } from 'zod'
import type { IDevCycleApiClient } from '../api/interface'
import type { DevCycleMCPServerInstance } from '../server'
import { INSTALL_GUIDES } from './installGuides.generated'
import { fetchAiPromptsAndRules } from '../utils/github'

const InstallGuideArgsSchema = z.object({
guide: z.enum(INSTALL_GUIDES),
Expand All @@ -16,26 +15,10 @@ async function fetchInstallGuideHandler(args: InstallGuideArgs) {
? trimmedGuide
: `${trimmedGuide}.md`
const repoPath = `install-prompts/${fileName}`
const sourceUrl = `https://raw.githubusercontent.com/DevCycleHQ/AI-Prompts-And-Rules/main/${repoPath}`

try {
const response = await axios.get<string>(sourceUrl, {
responseType: 'text',
})
return response.data as string
} catch (error: unknown) {
const status = axios.isAxiosError(error)
? error.response?.status
: undefined
if (status === 404) {
throw new Error(
`Install guide "${fileName}" not found in install-prompts/. Check the filename (with or without .md).`,
)
}
throw new Error(
'Unable to fetch install guide from GitHub. Please retry.',
)
}
return await fetchAiPromptsAndRules(
repoPath,
`Install guide "${fileName}" not found in install-prompts/. Check the filename (with or without .md).`,
)
}

export function registerInstallTools(
Expand Down
5 changes: 5 additions & 0 deletions src/mcp/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,11 @@ export const GetFeatureAuditLogHistoryArgsSchema = z.object({
.describe('Action type to filter audit entries by'),
})

// Cleanup feature prompt tool args
export const CleanupFeatureArgsSchema = z.object({
featureKey: z.string(),
})

// Zod schema for DevCycle Audit Log Entity - matches the actual swagger specification
export const AuditLogEntitySchema = z.object({
date: z
Expand Down
27 changes: 27 additions & 0 deletions src/mcp/utils/github.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import axios from 'axios'

export async function fetchAiPromptsAndRules(
relativePath: string,
notFoundMessage?: string,
): Promise<string> {
const trimmedPath = relativePath.trim().replace(/^\/+|\/+$/g, '')
const sourceUrl = `https://raw.githubusercontent.com/DevCycleHQ/AI-Prompts-And-Rules/main/${trimmedPath}`

try {
const response = await axios.get<string>(sourceUrl, {
responseType: 'text',
})
return response.data as string
} catch (error: unknown) {
const status = axios.isAxiosError(error)
? error.response?.status
: undefined
if (status === 404) {
throw new Error(
notFoundMessage ||
`Resource not found in AI-Prompts-And-Rules: "${trimmedPath}"`,
)
}
throw new Error('Unable to fetch resource from GitHub. Please retry.')
}
}