diff --git a/CHANGELOG.md b/CHANGELOG.md index 979a1b5..b80250e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.0.1] - 2025-08-08 + +### Fixed +- **Improved search_code tool response formatting**: + - Added simplified `formatCodeSearchOutput` for cleaner AI consumption + - Enhanced HTML entity decoding (handles ", <, >, &, /, ') + - Improved response structure showing file paths and line numbers clearly + - Removed HTML formatting tags for better readability + +### Changed +- Search results now use simplified formatter by default for better AI tool integration +- Enhanced query display to show actual search patterns used + ## [1.0.0] - 2025-07-25 ### Added diff --git a/memory-bank/.clinerules b/memory-bank/.clinerules index e1eebb0..c64da68 100644 --- a/memory-bank/.clinerules +++ b/memory-bank/.clinerules @@ -118,6 +118,12 @@ try { - Reached feature completeness - Ready for production use +### Version 1.0.1 +- Improved search response formatting for AI consumption +- Added simplified formatCodeSearchOutput +- Enhanced HTML entity decoding and tag stripping +- Established live MCP testing workflow + ## Important Architecture Decisions ### Handler Pattern diff --git a/memory-bank/activeContext.yml b/memory-bank/activeContext.yml index 11980ca..2ee8ede 100644 --- a/memory-bank/activeContext.yml +++ b/memory-bank/activeContext.yml @@ -8,6 +8,21 @@ current_focus_areas: timeline: "2025-07-25" recent_changes: + - date: "2025-08-08" + feature: "testing_and_release_v1.0.1" + description: "Comprehensive testing of search functionality and release of version 1.0.1" + status: "completed" + files_affected: + - "package.json" + - "CHANGELOG.md" + - "memory-bank/activeContext.yml" + - "memory-bank/progress.yml" + technical_details: "Tested search functionality with real Bitbucket data, verified context-aware patterns, file filtering, and error handling" + business_impact: "Validated production readiness of search improvements and properly versioned the release" + patterns_introduced: + - "Live MCP testing workflow using connected Bitbucket server" + - "Real-world validation of search functionality" + - date: "2025-07-25" feature: "code_search_response_fix" description: "Fixed search_code tool response handling to match actual Bitbucket API structure" diff --git a/memory-bank/currentTask.yml b/memory-bank/currentTask.yml deleted file mode 100644 index a57d467..0000000 --- a/memory-bank/currentTask.yml +++ /dev/null @@ -1,57 +0,0 @@ -# Current Task - Bitbucket MCP Server - -task_description: "Add search_code tool to Bitbucket MCP Server" -status: "completed" -started: "2025-07-25" -completed: "2025-07-25" - -objectives: - - "Implement search_code tool for searching code across repositories" - - "Support Bitbucket Server search API" - - "Add file pattern filtering" - - "Update version to 1.0.0" - - "Update documentation" - -implementation_plan: - - [x] Analyze the Bitbucket Server search API - - [x] Create SearchHandlers class - - [x] Add TypeScript interfaces for search types - - [x] Implement formatSearchResults function - - [x] Add isSearchCodeArgs type guard - - [x] Register tool in definitions - - [x] Wire up handler in index.ts - - [x] Update version to 1.0.0 - - [x] Update CHANGELOG.md - - [x] Add comprehensive documentation to README.md - - [x] Build and verify compilation - - [x] Create Memory Bank documentation - -technical_details: - api_endpoint: "/rest/search/latest/search" - method: "POST" - payload_structure: - - query: "project:PROJ repo:repo-name search-term" - - entities: { code: {} } - - limits: { primary: 25, secondary: 10 } - -challenges_encountered: - - "Search API only available for Bitbucket Server, not Cloud" - - "Query string construction requires specific format" - -solution_approach: - - "Created modular SearchHandlers following existing pattern" - - "Built query string dynamically from parameters" - - "Added clear error message for Cloud users" - - "Formatted results consistently with other tools" - -results: - - "Successfully implemented search_code tool" - - "Comprehensive documentation added" - - "Version bumped to 1.0.0" - - "Project ready for production use" - - "Memory Bank fully documented" - -next_steps: - - "Monitor for Bitbucket Cloud search API availability" - - "Gather user feedback on search functionality" - - "Plan additional search features based on usage" diff --git a/memory-bank/progress.yml b/memory-bank/progress.yml index 04979e1..6eff40b 100644 --- a/memory-bank/progress.yml +++ b/memory-bank/progress.yml @@ -1,10 +1,10 @@ # Progress - Bitbucket MCP Server project_status: - overall_progress: "90%" - phase: "Feature Complete" - version: "1.0.0" - release_status: "Ready for production" + overall_progress: "95%" + phase: "Post-1.0 Improvements" + version: "1.0.1" + release_status: "Production with improvements" milestones_completed: - name: "Core PR Tools" diff --git a/package.json b/package.json index 82343a2..7bfb7e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@nexus2520/bitbucket-mcp-server", - "version": "1.0.0", + "version": "1.0.1", "description": "MCP server for Bitbucket API integration - supports both Cloud and Server", "type": "module", "main": "./build/index.js", diff --git a/src/handlers/search-handlers.ts b/src/handlers/search-handlers.ts index 0f1edc4..76b53f0 100644 --- a/src/handlers/search-handlers.ts +++ b/src/handlers/search-handlers.ts @@ -6,6 +6,87 @@ import { } from '../types/bitbucket.js'; import { formatSearchResults, formatCodeSearchOutput } from '../utils/formatters.js'; +interface SearchContext { + assignment: string[]; + declaration: string[]; + usage: string[]; + exact: string[]; + any: string[]; +} + +function buildContextualPatterns(searchTerm: string): SearchContext { + return { + assignment: [ + `${searchTerm} =`, // Variable assignment + `${searchTerm}:`, // Object property, JSON key + `= ${searchTerm}`, // Right-hand assignment + ], + declaration: [ + `${searchTerm} =`, // Variable definition + `${searchTerm}:`, // Object key, parameter definition + `function ${searchTerm}`, // Function declaration + `class ${searchTerm}`, // Class declaration + `interface ${searchTerm}`, // Interface declaration + `const ${searchTerm}`, // Const declaration + `let ${searchTerm}`, // Let declaration + `var ${searchTerm}`, // Var declaration + ], + usage: [ + `.${searchTerm}`, // Property/method access + `${searchTerm}(`, // Function call + `${searchTerm}.`, // Method chaining + `${searchTerm}[`, // Array/object access + `(${searchTerm}`, // Parameter usage + ], + exact: [ + `"${searchTerm}"`, // Exact quoted match + ], + any: [ + `"${searchTerm}"`, // Exact match + `${searchTerm} =`, // Assignment + `${searchTerm}:`, // Object property + `.${searchTerm}`, // Property access + `${searchTerm}(`, // Function call + `function ${searchTerm}`, // Function definition + `class ${searchTerm}`, // Class definition + ] + }; +} + +function buildSmartQuery( + searchTerm: string, + searchContext: string = 'any', + includePatterns: string[] = [] +): string { + const contextPatterns = buildContextualPatterns(searchTerm); + + let patterns: string[] = []; + + // Add patterns based on context + if (searchContext in contextPatterns) { + patterns = [...contextPatterns[searchContext as keyof SearchContext]]; + } else { + patterns = [...contextPatterns.any]; + } + + // Add user-provided patterns + if (includePatterns && includePatterns.length > 0) { + patterns = [...patterns, ...includePatterns]; + } + + // Remove duplicates and join with OR + const uniquePatterns = [...new Set(patterns)]; + + // If only one pattern, return it without parentheses + if (uniquePatterns.length === 1) { + return uniquePatterns[0]; + } + + // Wrap each pattern in quotes for safety and join with OR + const quotedPatterns = uniquePatterns.map(pattern => `"${pattern}"`); + return `(${quotedPatterns.join(' OR ')})`; +} + export class SearchHandlers { constructor( private apiClient: BitbucketApiClient, @@ -14,13 +95,27 @@ export class SearchHandlers { async handleSearchCode(args: any) { try { - const { workspace, repository, search_query, file_pattern, limit = 25, start = 0 } = args; + const { + workspace, + repository, + search_query, + search_context = 'any', + file_pattern, + include_patterns = [], + limit = 25, + start = 0 + } = args; if (!workspace || !search_query) { throw new Error('Workspace and search_query are required'); } - // Build the query string + // Only works for Bitbucket Server currently + if (!this.apiClient.getIsServer()) { + throw new Error('Code search is currently only supported for Bitbucket Server'); + } + + // Build the enhanced query string let query = `project:${workspace}`; if (repository) { query += ` repo:${repository}`; @@ -28,12 +123,10 @@ export class SearchHandlers { if (file_pattern) { query += ` path:${file_pattern}`; } - query += ` ${search_query}`; - - // Only works for Bitbucket Server currently - if (!this.apiClient.getIsServer()) { - throw new Error('Code search is currently only supported for Bitbucket Server'); - } + + // Build smart search patterns + const smartQuery = buildSmartQuery(search_query, search_context, include_patterns); + query += ` ${smartQuery}`; // Prepare the request payload const payload: BitbucketServerSearchRequest = { @@ -63,12 +156,19 @@ export class SearchHandlers { const nextStart = hasMore ? (searchResult.code?.nextStart || start + limit) : undefined; const totalCount = searchResult.code?.count || 0; - // Build a concise response - let resultText = `Code search results for "${search_query}" in ${workspace}`; + // Build a concise response with search context info + let resultText = `Code search results for "${search_query}"`; + if (search_context !== 'any') { + resultText += ` (context: ${search_context})`; + } + resultText += ` in ${workspace}`; if (repository) { resultText += `/${repository}`; } - resultText += `:\n\n${simplifiedOutput}`; + + // Show the actual search query used + resultText += `\n\nSearch query: ${query.trim()}`; + resultText += `\n\n${simplifiedOutput}`; if (totalCount > 0) { resultText += `\n\nTotal matches: ${totalCount}`; diff --git a/src/index.ts b/src/index.ts index d6c126a..100b41e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -42,7 +42,7 @@ class BitbucketMCPServer { this.server = new Server( { name: 'bitbucket-mcp-server', - version: '1.0.0', + version: '1.0.1', }, { capabilities: { diff --git a/src/tools/definitions.ts b/src/tools/definitions.ts index 07852b0..93f3e55 100644 --- a/src/tools/definitions.ts +++ b/src/tools/definitions.ts @@ -609,7 +609,7 @@ export const toolDefinitions = [ }, { name: 'search_code', - description: 'Search for code across Bitbucket repositories (currently only supported for Bitbucket Server)', + description: 'Search for code across Bitbucket repositories with enhanced context-aware search patterns (currently only supported for Bitbucket Server)', inputSchema: { type: 'object', properties: { @@ -623,12 +623,22 @@ export const toolDefinitions = [ }, search_query: { type: 'string', - description: 'The search term or phrase to look for in code', + description: 'The search term or phrase to look for in code (e.g., "variable")', + }, + search_context: { + type: 'string', + enum: ['assignment', 'declaration', 'usage', 'exact', 'any'], + description: 'Context to search for: assignment (term=value), declaration (defining term), usage (calling/accessing term), exact (quoted match), or any (all patterns)', }, file_pattern: { type: 'string', description: 'File path pattern to filter results (e.g., "*.java", "src/**/*.ts") (optional)', }, + include_patterns: { + type: 'array', + items: { type: 'string' }, + description: 'Additional custom search patterns to include (e.g., ["variable =", ".variable"]) (optional)', + }, limit: { type: 'number', description: 'Maximum number of results to return (default: 25)',