Skip to main content
Glama
aws-powertools

Powertools MCP Search Server

tool.ts3.88 kB
import { isNullOrUndefined } from '@aws-lambda-powertools/commons/typeutils'; import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; import lunr from 'lunr'; import { POWERTOOLS_BASE_URL, SEARCH_CONFIDENCE_THRESHOLD, } from '../../constants.ts'; import { logger } from '../../logger.ts'; import { buildResponse } from '../shared/buildResponse.ts'; import { fetchWithCache } from '../shared/fetchWithCache.ts'; import type { ToolProps } from './types.ts'; /** * Search for documentation based on the provided parameters. * * This tool fetches a search index from the Powertools for AWS documentation, * hydrates it into a Lunr index, and performs a search based on the provided query. * * The search index is expected to be in a specific format, and the results * are filtered based on a confidence threshold to ensure relevance. This threshold * can be configured via the `SEARCH_CONFIDENCE_THRESHOLD` environment variable. * * This tool is designed to work with the Powertools for AWS documentation * for various runtimes, including Python and TypeScript, and supports versioning. * * Search indexes are fetched from a remote source and cached locally * using the {@link fetchWithCache | `fetchWithCache`} utility to improve performance and reduce network calls. * * @param props - options for searching documentation * @param props.search - the search query to use * @param props.runtime - the runtime to search in (e.g., 'python', 'typescript') * @param props.version - the version of the runtime to search in (e.g., 'latest', '1.0.0') */ const tool = async (props: ToolProps): Promise<CallToolResult> => { const { search, runtime, version } = props; logger.appendKeys({ tool: 'searchDocs' }); logger.appendKeys({ search, runtime, version }); const urlParts = runtime === 'python' || runtime === 'typescript' || runtime === 'java' ? [runtime, version] : [runtime]; const baseUrl = `${POWERTOOLS_BASE_URL}/${urlParts.join('/')}`; const url = new URL(baseUrl); const urlSuffix = '/search/search_index.json'; url.pathname = `${url.pathname}${urlSuffix}`; let searchIndexContent: { docs: { location: string; title: string; text: string }[]; }; try { const content = await fetchWithCache({ url, contentType: 'application/json', }); searchIndexContent = JSON.parse(content); if ( isNullOrUndefined(searchIndexContent.docs) || !Array.isArray(searchIndexContent.docs) ) { throw new Error( `Invalid search index format for ${runtime} ${version}: missing 'docs' property` ); } } catch (error) { logger.error('Failed to fetch search index', { error: (error as Error).message, }); return buildResponse({ content: `Failed to fetch search index for ${runtime} ${version}: ${(error as Error).message}`, isError: true, }); } // TODO: consume built/exported search index - #79 const index = lunr(function () { this.ref('location'); this.field('title', { boost: 1000 }); this.field('text', { boost: 1 }); this.field('tags', { boost: 1000000 }); for (const doc of searchIndexContent.docs) { if (!doc.location || !doc.title || !doc.text) continue; this.add({ location: doc.location, title: doc.title, text: doc.text, }); } }); const results = []; for (const result of index.search(search)) { if (result.score < SEARCH_CONFIDENCE_THRESHOLD) break; // Results are sorted by score, so we can stop early results.push({ title: result.ref, url: `${baseUrl}/${result.ref}`, score: result.score, }); } logger.debug( `Search results with confidence >= ${SEARCH_CONFIDENCE_THRESHOLD} found: ${results.length}` ); return buildResponse({ content: results, }); }; export { tool };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aws-powertools/powertools-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server