@pimzino/agentic-tools-mcp
Version:
A comprehensive MCP server for task management and agent memories with JSON file storage
118 lines (113 loc) • 4.68 kB
JavaScript
import { z } from 'zod';
/**
* Search memories by semantic similarity
*
* @param storage - Memory storage instance
* @returns MCP tool handler for searching memories
*/
export function createSearchMemoriesTool(storage) {
return {
name: 'search_memories',
description: 'Search memories by text content matching',
inputSchema: {
query: z.string(),
limit: z.number().min(1).max(100).optional(),
threshold: z.number().min(0).max(1).optional(),
category: z.string().optional()
},
handler: async ({ query, limit = 10, threshold, category }) => {
try {
// Validate inputs
if (!query || query.trim().length === 0) {
return {
content: [{
type: 'text',
text: 'Error: Search query is required.'
}],
isError: true
};
}
if (query.trim().length > 1000) {
return {
content: [{
type: 'text',
text: 'Error: Search query must be 1000 characters or less.'
}],
isError: true
};
}
if (limit < 1 || limit > 100) {
return {
content: [{
type: 'text',
text: 'Error: Limit must be between 1 and 100.'
}],
isError: true
};
}
if (threshold !== undefined && (threshold < 0 || threshold > 1)) {
return {
content: [{
type: 'text',
text: 'Error: Threshold must be between 0 and 1.'
}],
isError: true
};
}
const searchInput = {
query: query.trim(),
limit,
threshold, // Will use config default if undefined
category: category?.trim()
};
const results = await storage.searchMemories(searchInput);
// Get the actual threshold used (from config if not provided)
const actualThreshold = threshold ?? storage.config?.defaultThreshold ?? 0.3;
if (results.length === 0) {
return {
content: [{
type: 'text',
text: `🔍 No memories found matching your search criteria.
**Query:** "${query}"
**Threshold:** ${actualThreshold}
**Filters:** ${[
category && `Category: ${category}`
].filter(Boolean).join(', ') || 'None'}
Try adjusting your search query or using different keywords.`
}]
};
}
const resultText = results.map((result, index) => {
const memory = result.memory;
return `**${index + 1}. Memory ID:** ${memory.id}
**Relevance Score:** ${(result.score * 100).toFixed(1)}%
**Content:** ${memory.content.substring(0, 300)}${memory.content.length > 300 ? '...' : ''}
**Category:** ${memory.category || 'Not specified'}
**Created:** ${new Date(memory.createdAt).toLocaleString()}
**Metadata:** ${Object.keys(memory.metadata).length > 0 ? JSON.stringify(memory.metadata, null, 2) : 'None'}`;
}).join('\n\n---\n\n');
return {
content: [{
type: 'text',
text: `🔍 Found ${results.length} memory(ies) matching your search:
**Query:** "${query}"
**Threshold:** ${actualThreshold}
**Filters:** ${[
category && `Category: ${category}`
].filter(Boolean).join(', ') || 'None'}
${resultText}`
}]
};
}
catch (error) {
return {
content: [{
type: 'text',
text: `Error searching memories: ${error instanceof Error ? error.message : 'Unknown error'}`
}],
isError: true
};
}
}
};
}