Skip to main content
Glama
store_collection.ts5.37 kB
import { Ajv } from 'ajv'; import type { ActorStoreList } from 'apify-client'; import { z } from 'zod'; import zodToJsonSchema from 'zod-to-json-schema'; import { ApifyClient } from '../apify-client.js'; import { ACTOR_SEARCH_ABOVE_LIMIT, HelperTools } from '../const.js'; import type { ActorPricingModel, ExtendedActorStoreList, HelperTool, ToolEntry } from '../types.js'; import { formatActorsListToActorCard } from '../utils/actor-card.js'; export async function searchActorsByKeywords( search: string, apifyToken: string, limit: number | undefined = undefined, offset: number | undefined = undefined, ): Promise<ExtendedActorStoreList[]> { const client = new ApifyClient({ token: apifyToken }); const results = await client.store().list({ search, limit, offset }); return results.items; } const ajv = new Ajv({ coerceTypes: 'array', strict: false }); export const searchActorsArgsSchema = z.object({ limit: z.number() .int() .min(1) .max(100) .default(10) .describe('The maximum number of Actors to return. The default value is 10.'), offset: z.number() .int() .min(0) .default(0) .describe('The number of elements to skip at the start. The default value is 0.'), search: z.string() .default('') .describe(`A string to search for in the Actor's title, name, description, username, and readme. Use simple space-separated keywords, such as "web scraping", "data extraction", or "playwright browser mcp". Do not use complex queries, AND/OR operators, or other advanced syntax, as this tool uses full-text search only.`), category: z.string() .default('') .describe('Filter the results by the specified category.'), }); /** * Filters out actors with the 'FLAT_PRICE_PER_MONTH' pricing model (rental actors), * unless the actor's ID is present in the user's rented actor IDs list. * * This is necessary because the Store list API does not support filtering by multiple pricing models at once. * * @param actors - Array of ActorStorePruned objects to filter. * @param userRentedActorIds - Array of Actor IDs that the user has rented. * @returns Array of Actors excluding those with 'FLAT_PRICE_PER_MONTH' pricing model (= rental Actors), * except for Actors that the user has rented (whose IDs are in userRentedActorIds). */ function filterRentalActors( actors: ActorStoreList[], userRentedActorIds: string[], ): ActorStoreList[] { // Store list API does not support filtering by two pricing models at once, // so we filter the results manually after fetching them. return actors.filter((actor) => ( actor.currentPricingInfo.pricingModel as ActorPricingModel) !== 'FLAT_PRICE_PER_MONTH' || userRentedActorIds.includes(actor.id), ); } /** * https://docs.apify.com/api/v2/store-get */ export const searchActors: ToolEntry = { type: 'internal', tool: { name: HelperTools.STORE_SEARCH, description: `Search for Actors or Model Context Protocol (MCP) servers in the Apify Store using keywords.\n` + `This tool returns a list of Actors with title, description, pricing model, usage statistics, and user ratings.\n` + `Use simple space-separated keywords for best results, such as "web scraping", "data extraction", or "playwright mcp".\n` + `You may need to use this tool several times to find the right Actor.\n` + `Limit the number of results returned, but ensure that relevant results are included.\n` + `Always present the results in a user-friendly format as an Actor cards.\n\n` + `USAGE:\n` + `- Use when user wants to find Actors for a specific task or technology\n` + `- Use when user asks about available Actors in the Apify Store\n` + `- Use when user needs to discover MCP servers or automation tools\n` + `EXAMPLES:\n` + `- user_input: Find Actors for web scraping\n` + `- user_input: Search for MCP servers\n` + `- user_input: What Actors are available for data extraction\n` + `- user_input: Show me Actors that use Playwright`, inputSchema: zodToJsonSchema(searchActorsArgsSchema), ajvValidate: ajv.compile(zodToJsonSchema(searchActorsArgsSchema)), call: async (toolArgs) => { const { args, apifyToken, userRentedActorIds } = toolArgs; const parsed = searchActorsArgsSchema.parse(args); let actors = await searchActorsByKeywords( parsed.search, apifyToken, parsed.limit + ACTOR_SEARCH_ABOVE_LIMIT, parsed.offset, ); actors = filterRentalActors(actors || [], userRentedActorIds || []).slice(0, parsed.limit); const actorCards = formatActorsListToActorCard(actors); return { content: [ { type: 'text', text: `**Search query:** ${parsed.search}\n\n` + `**Number of Actors found:** ${actorCards.length}\n\n` + `**Actor cards:**\n${actorCards.join('\n\n')}`, }, ], }; }, } as HelperTool, };

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/jirispilka/actors-mcp-server'

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