/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { z } from 'zod';
import { generateRequestId } from '../utils.js';
import type { ServerConfig } from '../server.js';
import {
validateSearchDocumentationInput,
createErrorResponse,
processSearchResponse
} from '../security/index.js';
import { generateDocumentationPathsDescription, DOCUMENTATION_PATH_NAMES } from '../constants.js';
export function createSearchDocumentationTool(config: ServerConfig) {
return {
name: 'search-documentation',
description: `Search through documentation using a natural language query.
This tool provides semantic search capabilities across the entire API documentation corpus.
It analyzes your query and returns the most relevant documentation chunks based on semantic similarity.
The results include document snippets with relevance scores and document references that can be used with the read-documentation tool to fetch complete documents.
Document snippets can also contain documentReference:<path-to-another-doc> and if the referenced document is relevant to the user's query, you should use read_documentation tool with <path-to-another-doc> as documentReference.
DOCUMENTATION PATH SELECTION:
Select the most specific documentation path that matches your query intent to filter the search radius:
${generateDocumentationPathsDescription()}
Helpful Hints:
1. If you are searching for code generation/building applications → API Swagger Model
2. If you are searching for specific API → Use that API documentation path to narrow down the search radius
3. If your query matches more than one documentation path, you can search across all the matching documentation paths
4. If you are looking for guidance/examples → Guides and FAQs
5. If uncertain → Use the most relevant API documentation path based on the core functionality described
6. If nothing is relevant, search across all the documentation paths. DO NOT provide any documentation path
HIGHLY RECOMMENDED: Start by reading the following documents (using read-documentation tool) when interacting with this server:
1. Getting started guide : "Guides and FAQs/amazon-business-apis-getting-started-guide.md"
2. To understand authentication requirements: "OAuth/oauth-instructions.md"
Common use cases:
- Finding API details by describing functionality ("how to search products")
- Searching for specific parameters, field definitions or data models
- Locating code examples and implementation guidance
- Discovering endpoint requirements and constraints
Note:
When you are implementing or building application, ALWAYS search for the swagger model, code snippets and any specific instructions given for generating code.
Verify that Amazon Business API OAuth is implemented before implementing any APIs.
It is recommended to understand and avoid common pitfalls, by using read-documentation tool with reference "Guides and FAQs/common-pitfalls.md"`,
paramSchema: {
query: z
.string()
.describe(
'Natural language search query to find relevant documentation (e.g., "How to search for products", "What are the parameters for the searchProducts endpoint")',
),
maxResults: z.number().optional().default(5).describe('Maximum number of results to return (default: 5)'),
documentationPath: z.array(z.string()).optional().describe(`List of documentation paths to search (e.g. ${DOCUMENTATION_PATH_NAMES.slice(0, 4).join(', ')})`),
},
cb: async ({ query, maxResults, documentationPath}) => {
try {
// Input Validation
const validationResult = validateSearchDocumentationInput({
query,
maxResults,
documentationPath,
});
if (!validationResult.isValid) {
return createErrorResponse(
`Input validation failed: ${validationResult.errors.join(', ')}`
);
}
// Extract validated values
const validatedParams = validationResult.sanitizedValue as {
query: string;
maxResults: number;
documentationPath?: string[];
};
// Prepare API request
const endpoint = 'https://drn57tdwuknf9.cloudfront.net/search_documentation';
const requestBody: { query: string; categories?: string[] } = {
query: validatedParams.query
};
if (validatedParams.documentationPath && validatedParams.documentationPath.length > 0) {
requestBody.categories = validatedParams.documentationPath;
}
// Make API request with error handling
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-amzn-requestid': generateRequestId(),
'x-amzn-dev-account-id': config.devAccountId,
},
body: JSON.stringify(requestBody),
});
if (!response.ok) {
throw new Error(`API request failed with status ${response.status}`);
}
// Parse and validate API response
const searchResponse = await response.json();
// Process the response
const processedResponse = processSearchResponse(searchResponse);
// Apply maxResults limit to results array
const results =
processedResponse && typeof processedResponse === 'object' &&
'results' in processedResponse && Array.isArray(processedResponse.results) ?
processedResponse.results.slice(0, validatedParams.maxResults) :
[];
// Format output
let resultText = '';
results.forEach((result: any, index: number) => {
if (result && typeof result === 'object') {
resultText += `RESULT ${index + 1}\n`;
resultText += `${result.text || 'No text available'}\n\n`;
resultText += `Score: ${result.score || 'N/A'}\n`;
resultText += `DocumentReference: ${result.documentReference || 'N/A'}\n\n`;
}
});
return {
content: [
{
type: 'text',
text: resultText || 'No results found for your query.',
},
],
};
} catch (error) {
return createErrorResponse(error);
}
},
};
}