get_scripting_tips
Search and browse a knowledge base of AppleScript/JXA tips to automate macOS tasks. Use natural language queries, explore categories, or refresh the database to find pre-built scripts for common apps and system functions.
Instructions
Discover how to automate any app on your Mac with this comprehensive knowledge base of AppleScript/JXA tips and runnable scripts. This tool is essential for discovery and should be the FIRST CHOICE when aiming to automate macOS tasks, especially those involving common applications or system functions, before attempting to write scripts from scratch. It helps identify pre-built, tested solutions, effectively teaching you how to control virtually any aspect of your macOS experience.
Primary Use Cases & Parameters:
Discovering Solutions (Use
Parameter:
search_term(string, optional).Functionality: Performs a fuzzy search across all tip titles, descriptions, keywords, script content, and IDs. Ideal for natural language queries like "how to..." (e.g.,
search_term: "how do I get the current Safari URL and title?"). This is the most common way to find relevant tips.Output: Returns a list of matching tips in Markdown format.
Limiting Search Results (Use
Parameter:
limit(integer, optional, default: 10).Functionality: Specifies the maximum number of script tips to return when using
search_termor browsing a specificcategory(withoutlist_categories: true). Does not apply iflist_categoriesis true.
Browsing by Category (Use
Parameter:
category(string, optional).Functionality: Shows tips from a specific category. Combine with
limitto control result count.Example:
category: "01_intro"orcategory: "07_browsers/chrome".
Listing All Categories (Use
Parameter:
list_categories(boolean, optional).Functionality: Returns a structured list of all available categories with their descriptions. This helps you understand what automation areas are covered.
Output: Category tree in Markdown format.
Refreshing Database (Use
Parameter:
refresh_database(boolean, optional).Functionality: Forces a reload of the knowledge base if new scripts have been added. Typically not needed as the database refreshes automatically.
Best Practices:
Always start with search: Use natural language queries to find solutions (e.g., "send email from Mail app").
Browse categories when exploring: Use
list_categories: trueto see available automation areas.Use specific IDs for execution: Once you find a script, use its ID with
execute_scripttool for precise execution.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| category | No | ||
| limit | No | ||
| list_categories | No | ||
| refresh_database | No | ||
| search_term | No |
Implementation Reference
- Main handler function that implements the logic for retrieving scripting tips from the knowledge base, including searching, filtering by category, listing categories, formatting output as Markdown, and handling limits and refreshes.export async function getScriptingTipsService( input: GetScriptingTipsInput, serverInfo?: { startTime: string; mode: string; version?: string } ): Promise<string> { if (input.refresh_database) { await forceReloadKnowledgeBase(); } const kb: KnowledgeBaseIndex = await getKnowledgeBase(); let serverDetailsString = ""; if (serverInfo) { const versionInfo = serverInfo.version ? ` Version: ${serverInfo.version}` : ""; serverDetailsString = `\n\n---\nServer Started: ${serverInfo.startTime}\nExecution Mode: ${serverInfo.mode}${versionInfo}`; } // Handle listCategories separately as it overrides other filters and limit if (input.list_categories || (!input.category && !input.search_term && !input.limit)) { if (input.list_categories || (!input.category && !input.search_term)) { const listCategoriesMessage = handleListCategories(kb, serverInfo?.version); return listCategoriesMessage + serverDetailsString; } if(input.limit && !input.category && !input.search_term){ const listCategoriesMessage = handleListCategories(kb, serverInfo?.version); return `${listCategoriesMessage}\n\nNote: \`limit\` parameter is applied to search results or category browsing, not general listing.${serverDetailsString}`; } } const searchResult = performSearch(kb, input.category, input.search_term); let noticeAboutLimit = ""; const actualLimit = input.limit || DEFAULT_TIP_LIMIT; if (!input.list_categories && (input.search_term || input.category) && searchResult.tips.length > 0) { if (searchResult.tips.length > actualLimit) { noticeAboutLimit = `Showing the first ${actualLimit} of ${searchResult.tips.length} matching tips. Use the \`limit\` parameter to adjust this. (Default is 10).\n\n`; searchResult.tips = searchResult.tips.slice(0, actualLimit); } } if (searchResult.tips.length === 0 && !input.list_categories) { const noResultsMessage = generateNoResultsMessage(input.category, input.search_term); return noResultsMessage + serverDetailsString; } const categorizedTips = groupTipsByCategory(searchResult.tips, input.category); const formattingResult = formatResultsToMarkdown(categorizedTips, input.category as KnowledgeCategory | undefined); const formattedTips = formattingResult.markdownOutput; const lineLimitNotice = formattingResult.lineLimitNotice; let outputMessage: string; if (formattedTips.trim() === "") { if (input.list_categories || (!input.category && !input.search_term)) { // Avoid double no-results message if categories were shown outputMessage = ""; // Categories were already listed, or will be if no other criteria met } else { logger.warn('Formatted tips were empty despite having search results (after potential limit).',{input, searchResultTipsCount: searchResult.tips.length}); outputMessage = generateNoResultsMessage(input.category, input.search_term) + serverDetailsString; } } else { outputMessage = searchResult.notice + noticeAboutLimit + lineLimitNotice + formattedTips; } // If we reached here and outputMessage is empty (e.g. only limit was specified), default to listCategories if (outputMessage.trim() === "" && !input.list_categories && !(input.search_term || input.category) ) { const listCategoriesMessage = handleListCategories(kb, serverInfo?.version); return `${listCategoriesMessage}\n\nNote: \`limit\` parameter applies to search results or category browsing.${serverDetailsString}`; } if (input.refresh_database) { outputMessage = `Knowledge base reloaded successfully.${serverDetailsString}\n\n${outputMessage}`; } else if (!outputMessage.includes(serverDetailsString) && outputMessage.trim() !== "" && !input.list_categories) { // If not refresh, details not already in message, message not empty, and not listCategories (which handles its own details) // This is to catch normal search results that didn't go through refresh/listCategories/noResults paths for serverDetailsString outputMessage += serverDetailsString; } return outputMessage; }
- src/schemas.ts:54-66 (schema)Zod schema definition for the input parameters of the get_scripting_tips tool, including category, search_term, list_categories, refresh_database, and limit.export const GetScriptingTipsInputSchema = z.object({ category: DynamicScriptingKnowledgeCategoryEnum.optional() .describe("Specific category of tips. If omitted with no `search_term`, lists all categories."), search_term: z.string().optional() .describe("Keyword to search within tip titles, content, keywords, or IDs."), list_categories: z.boolean().optional().default(false) .describe("If true, returns only the list of available categories and their descriptions. Overrides other parameters."), refresh_database: z.boolean().optional().describe("If true, forces a reload of the knowledge base before processing the request."), limit: z.number().int().positive().optional().default(10) .describe("Maximum number of results to return. Default is 10."), }); export type GetScriptingTipsInput = z.infer<typeof GetScriptingTipsInputSchema>;
- src/server.ts:344-397 (registration)Registers the get_scripting_tips tool with the MCP server, providing a detailed description, input shape, and a handler that validates input with the schema and delegates to getScriptingTipsService for execution.server.tool( 'get_scripting_tips', `Discover how to automate any app on your Mac with this comprehensive knowledge base of AppleScript/JXA tips and runnable scripts. This tool is essential for discovery and should be the FIRST CHOICE when aiming to automate macOS tasks, especially those involving common applications or system functions, before attempting to write scripts from scratch. It helps identify pre-built, tested solutions, effectively teaching you how to control virtually any aspect of your macOS experience. **Primary Use Cases & Parameters:** * **Discovering Solutions (Use \`search_term\`):** * Parameter: \`search_term\` (string, optional). * Functionality: Performs a fuzzy search across all tip titles, descriptions, keywords, script content, and IDs. Ideal for natural language queries like "how to..." (e.g., \`search_term: "how do I get the current Safari URL and title?"\`). This is the most common way to find relevant tips. * Output: Returns a list of matching tips in Markdown format. * **Limiting Search Results (Use \`limit\`):** * Parameter: \`limit\` (integer, optional, default: 10). * Functionality: Specifies the maximum number of script tips to return when using \`search_term\` or browsing a specific \`category\` (without \`list_categories: true\`). Does not apply if \`list_categories\` is true. * **Browsing by Category (Use \`category\`):** * Parameter: \`category\` (string, optional). * Functionality: Shows tips from a specific category. Combine with \`limit\` to control result count. * Example: \`category: "01_intro"\` or \`category: "07_browsers/chrome"\`. * **Listing All Categories (Use \`list_categories: true\`):** * Parameter: \`list_categories\` (boolean, optional). * Functionality: Returns a structured list of all available categories with their descriptions. This helps you understand what automation areas are covered. * Output: Category tree in Markdown format. * **Refreshing Database (Use \`refresh_database: true\`):** * Parameter: \`refresh_database\` (boolean, optional). * Functionality: Forces a reload of the knowledge base if new scripts have been added. Typically not needed as the database refreshes automatically. **Best Practices:** 1. **Always start with search**: Use natural language queries to find solutions (e.g., "send email from Mail app"). 2. **Browse categories when exploring**: Use \`list_categories: true\` to see available automation areas. 3. **Use specific IDs for execution**: Once you find a script, use its ID with \`execute_script\` tool for precise execution.`, GetScriptingTipsInputShape, async (args: unknown) => { const input = GetScriptingTipsInputSchema.parse(args); // Call getScriptingTipsService directly with the input parameters let content = await getScriptingTipsService(input); // Append first-call info if applicable if (!IS_E2E_TESTING && !hasEmittedFirstCallInfo) { content += '\n\n' + serverInfoMessage; hasEmittedFirstCallInfo = true; } return { content: [{ type: 'text', text: content }] }; } );