/** * Classify an MCP tool as a search/read operation for UI collapsing. * Returns { isSearch: false, isRead: false } for tools that should not * collapse (e.g., send_message, create_*, update_*). * * Uses explicit per-tool allowlists for the most common MCP servers. * Tool names are stable across installs (even when the server name varies, * e.g., "slack" vs "claude_ai_Slack"), so matching is keyed on the tool * name alone after normalizing camelCase/kebab-case to snake_case. * Unknown tool names don't collapse (conservative). */ // prettier-ignore const SEARCH_TOOLS = new Set([ // Slack (hosted + @modelcontextprotocol/server-slack) 'slack_search_public', 'slack_search_public_and_private', 'slack_search_channels', 'slack_search_users', // GitHub (github/github-mcp-server) 'search_code', 'search_repositories', 'search_issues', 'search_pull_requests', 'search_orgs', 'search_users', // Linear (mcp.linear.app) 'search_documentation', // Datadog (mcp.datadoghq.com) 'search_logs', 'search_spans', 'search_rum_events', 'search_audit_logs', 'search_monitors', 'search_monitor_groups', 'find_slow_spans', 'find_monitors_matching_pattern', // Sentry (getsentry/sentry-mcp) 'search_docs', 'search_events', 'search_issue_events', 'find_organizations', 'find_teams', 'find_projects', 'find_releases', 'find_dsns', // Notion (mcp.notion.com — kebab-case, normalized) 'search', // Gmail (claude.ai hosted) 'gmail_search_messages', // Google Drive (claude.ai hosted + @modelcontextprotocol/server-gdrive) 'google_drive_search', // Google Calendar (claude.ai hosted) 'gcal_find_my_free_time', 'gcal_find_meeting_times', 'gcal_find_user_emails', // Atlassian/Jira (mcp.atlassian.com — camelCase, normalized) 'search_jira_issues_using_jql', 'search_confluence_using_cql', 'lookup_jira_account_id', // Community Atlassian (sooperset/mcp-atlassian) 'confluence_search', 'jira_search', 'jira_search_fields', // Asana (mcp.asana.com) 'asana_search_tasks', 'asana_typeahead_search', // Filesystem (@modelcontextprotocol/server-filesystem) 'search_files', // Memory (@modelcontextprotocol/server-memory) 'search_nodes', // Brave Search 'brave_web_search', 'brave_local_search', // Git (mcp-server-git) // (git has no search verbs) // Grafana (grafana/mcp-grafana) 'search_dashboards', 'search_folders', // PagerDuty // (pagerduty reads all use get_/list_, no search verbs) // Supabase 'search_docs', // Stripe 'search_stripe_resources', 'search_stripe_documentation', // PubMed (claude.ai hosted + community) 'search_articles', 'find_related_articles', 'lookup_article_by_citation', 'search_papers', 'search_pubmed', 'search_pubmed_key_words', 'search_pubmed_advanced', 'pubmed_search', 'pubmed_mesh_lookup', // Firecrawl 'firecrawl_search', // Exa 'web_search_exa', 'web_search_advanced_exa', 'people_search_exa', 'linkedin_search_exa', 'deep_search_exa', // Perplexity 'perplexity_search', 'perplexity_search_web', // Tavily 'tavily_search', // Obsidian (MarkusPfundstein) 'obsidian_simple_search', 'obsidian_complex_search', // MongoDB 'find', 'search_knowledge', // Neo4j 'search_memories', 'find_memories_by_name', // Airtable 'search_records', // Todoist (Doist — kebab-case, normalized) 'find_tasks', 'find_tasks_by_date', 'find_completed_tasks', 'find_projects', 'find_sections', 'find_comments', 'find_project_collaborators', 'find_activity', 'find_labels', 'find_filters', // AWS 'search_documentation', 'search_catalog', // Terraform 'search_modules', 'search_providers', 'search_policies', ]) // prettier-ignore const READ_TOOLS = new Set([ // Slack (hosted + @modelcontextprotocol/server-slack) 'slack_read_channel', 'slack_read_thread', 'slack_read_canvas', 'slack_read_user_profile', 'slack_list_channels', 'slack_get_channel_history', 'slack_get_thread_replies', 'slack_get_users', 'slack_get_user_profile', // GitHub (github/github-mcp-server) 'get_me', 'get_team_members', 'get_teams', 'get_commit', 'get_file_contents', 'get_repository_tree', 'list_branches', 'list_commits', 'list_releases', 'list_tags', 'get_latest_release', 'get_release_by_tag', 'get_tag', 'list_issues', 'issue_read', 'list_issue_types', 'get_label', 'list_label', 'pull_request_read', 'get_gist', 'list_gists', 'list_notifications', 'get_notification_details', 'projects_list', 'projects_get', 'actions_get', 'actions_list', 'get_job_logs', 'get_code_scanning_alert', 'list_code_scanning_alerts', 'get_dependabot_alert', 'list_dependabot_alerts', 'get_secret_scanning_alert', 'list_secret_scanning_alerts', 'get_global_security_advisory', 'list_global_security_advisories', 'list_org_repository_security_advisories', 'list_repository_security_advisories', 'get_discussion', 'get_discussion_comments', 'list_discussion_categories', 'list_discussions', 'list_starred_repositories', 'get_issue', 'get_pull_request', 'list_pull_requests', 'get_pull_request_files', 'get_pull_request_status', 'get_pull_request_comments', 'get_pull_request_reviews', // Linear (mcp.linear.app) 'list_comments', 'list_cycles', 'get_document', 'list_documents', 'list_issue_statuses', 'get_issue_status', 'list_my_issues', 'list_issue_labels', 'list_projects', 'get_project', 'list_project_labels', 'list_teams', 'get_team', 'list_users', 'get_user', // Datadog (mcp.datadoghq.com) 'aggregate_logs', 'list_spans', 'aggregate_spans', 'analyze_trace', 'trace_critical_path', 'query_metrics', 'aggregate_rum_events', 'list_rum_metrics', 'get_rum_metric', 'list_monitors', 'get_monitor', 'check_can_delete_monitor', 'validate_monitor', 'validate_existing_monitor', 'list_dashboards', 'get_dashboard', 'query_dashboard_widget', 'list_notebooks', 'get_notebook', 'query_notebook_cell', 'get_profiling_metrics', 'compare_profiling_metrics', // Sentry (getsentry/sentry-mcp) 'whoami', 'get_issue_details', 'get_issue_tag_values', 'get_trace_details', 'get_event_attachment', 'get_doc', 'get_sentry_resource', 'list_events', 'list_issue_events', 'get_sentry_issue', // Notion (mcp.notion.com — kebab-case, normalized) 'fetch', 'get_comments', 'get_users', 'get_self', // Gmail (claude.ai hosted) 'gmail_get_profile', 'gmail_read_message', 'gmail_read_thread', 'gmail_list_drafts', 'gmail_list_labels', // Google Drive (claude.ai hosted + @modelcontextprotocol/server-gdrive) 'google_drive_fetch', 'google_drive_export', // Google Calendar (claude.ai hosted) 'gcal_list_calendars', 'gcal_list_events', 'gcal_get_event', // Atlassian/Jira (mcp.atlassian.com — camelCase, normalized) 'atlassian_user_info', 'get_accessible_atlassian_resources', 'get_visible_jira_projects', 'get_jira_project_issue_types_metadata', 'get_jira_issue', 'get_transitions_for_jira_issue', 'get_jira_issue_remote_issue_links', 'get_confluence_spaces', 'get_confluence_page', 'get_pages_in_confluence_space', 'get_confluence_page_ancestors', 'get_confluence_page_descendants', 'get_confluence_page_footer_comments', 'get_confluence_page_inline_comments', // Community Atlassian (sooperset/mcp-atlassian) 'confluence_get_page', 'confluence_get_page_children', 'confluence_get_comments', 'confluence_get_labels', 'jira_get_issue', 'jira_get_transitions', 'jira_get_worklog', 'jira_get_agile_boards', 'jira_get_board_issues', 'jira_get_sprints_from_board', 'jira_get_sprint_issues', 'jira_get_link_types', 'jira_download_attachments', 'jira_batch_get_changelogs', 'jira_get_user_profile', 'jira_get_project_issues', 'jira_get_project_versions', // Asana (mcp.asana.com) 'asana_get_attachment', 'asana_get_attachments_for_object', 'asana_get_goal', 'asana_get_goals', 'asana_get_parent_goals_for_goal', 'asana_get_portfolio', 'asana_get_portfolios', 'asana_get_items_for_portfolio', 'asana_get_project', 'asana_get_projects', 'asana_get_project_sections', 'asana_get_project_status', 'asana_get_project_statuses', 'asana_get_project_task_counts', 'asana_get_projects_for_team', 'asana_get_projects_for_workspace', 'asana_get_task', 'asana_get_tasks', 'asana_get_stories_for_task', 'asana_get_teams_for_workspace', 'asana_get_teams_for_user', 'asana_get_team_users', 'asana_get_time_period', 'asana_get_time_periods', 'asana_get_user', 'asana_get_workspace_users', 'asana_list_workspaces', // Filesystem (@modelcontextprotocol/server-filesystem) 'read_file', 'read_text_file', 'read_media_file', 'read_multiple_files', 'list_directory', 'list_directory_with_sizes', 'directory_tree', 'get_file_info', 'list_allowed_directories', // Memory (@modelcontextprotocol/server-memory) 'read_graph', 'open_nodes', // Postgres (@modelcontextprotocol/server-postgres) 'query', // SQLite (@modelcontextprotocol/server-sqlite) 'read_query', 'list_tables', 'describe_table', // Git (mcp-server-git) 'git_status', 'git_diff', 'git_diff_unstaged', 'git_diff_staged', 'git_log', 'git_show', 'git_branch', // Grafana (grafana/mcp-grafana) 'list_teams', 'list_users_by_org', 'get_dashboard_by_uid', 'get_dashboard_summary', 'get_dashboard_property', 'get_dashboard_panel_queries', 'run_panel_query', 'list_datasources', 'get_datasource', 'get_query_examples', 'query_prometheus', 'query_prometheus_histogram', 'list_prometheus_metric_metadata', 'list_prometheus_metric_names', 'list_prometheus_label_names', 'list_prometheus_label_values', 'query_loki_logs', 'query_loki_stats', 'query_loki_patterns', 'list_loki_label_names', 'list_loki_label_values', 'list_incidents', 'get_incident', 'list_sift_investigations', 'get_sift_investigation', 'get_sift_analysis', 'list_oncall_schedules', 'get_oncall_shift', 'get_current_oncall_users', 'list_oncall_teams', 'list_oncall_users', 'list_alert_groups', 'get_alert_group', 'get_annotations', 'get_annotation_tags', 'get_panel_image', // PagerDuty (PagerDuty/pagerduty-mcp-server) 'list_incidents', 'get_incident', 'get_outlier_incident', 'get_past_incidents', 'get_related_incidents', 'list_incident_notes', 'list_incident_workflows', 'get_incident_workflow', 'list_services', 'get_service', 'list_team_members', 'get_user_data', 'list_schedules', 'get_schedule', 'list_schedule_users', 'list_oncalls', 'list_log_entries', 'get_log_entry', 'list_escalation_policies', 'get_escalation_policy', 'list_event_orchestrations', 'get_event_orchestration', 'list_status_pages', 'get_status_page_post', 'list_alerts_from_incident', 'get_alert_from_incident', 'list_change_events', 'get_change_event', // Supabase (supabase-community/supabase-mcp) 'list_organizations', 'get_organization', 'get_cost', 'list_extensions', 'list_migrations', 'get_logs', 'get_advisors', 'get_project_url', 'get_publishable_keys', 'generate_typescript_types', 'list_edge_functions', 'get_edge_function', 'list_storage_buckets', 'get_storage_config', // Stripe (stripe/agent-toolkit) 'get_stripe_account_info', 'retrieve_balance', 'list_customers', 'list_products', 'list_prices', 'list_invoices', 'list_payment_intents', 'list_subscriptions', 'list_coupons', 'list_disputes', 'fetch_stripe_resources', // PubMed (claude.ai hosted + community) 'get_article_metadata', 'get_full_text_article', 'convert_article_ids', 'get_copyright_status', 'download_paper', 'list_papers', 'read_paper', 'get_paper_fulltext', 'get_pubmed_article_metadata', 'download_pubmed_pdf', 'pubmed_fetch', 'pubmed_pmc_fetch', 'pubmed_spell', 'pubmed_cite', 'pubmed_related', // BigQuery (claude.ai hosted + community) 'bigquery_query', 'bigquery_schema', 'list_dataset_ids', 'list_table_ids', 'get_dataset_info', 'get_table_info', // Firecrawl 'firecrawl_scrape', 'firecrawl_map', 'firecrawl_crawl', 'firecrawl_check_crawl_status', 'firecrawl_extract', // Exa 'get_code_context_exa', 'company_research_exa', 'crawling_exa', 'deep_researcher_check', // Perplexity 'perplexity_ask', 'perplexity_research', 'perplexity_reason', // Tavily 'tavily_extract', 'tavily_crawl', 'tavily_map', 'tavily_research', // Obsidian (MarkusPfundstein) 'obsidian_list_files_in_vault', 'obsidian_list_files_in_dir', 'obsidian_get_file_contents', 'obsidian_batch_get_file_contents', 'obsidian_get_periodic_note', 'obsidian_get_recent_periodic_notes', 'obsidian_get_recent_changes', // Figma (GLips/Figma-Context-MCP) 'get_figma_data', 'download_figma_images', // Playwright (microsoft/playwright-mcp) 'browser_console_messages', 'browser_network_requests', 'browser_take_screenshot', 'browser_snapshot', 'browser_get_config', 'browser_route_list', 'browser_cookie_list', 'browser_cookie_get', 'browser_localstorage_list', 'browser_localstorage_get', 'browser_sessionstorage_list', 'browser_sessionstorage_get', 'browser_storage_state', // Puppeteer (@modelcontextprotocol/server-puppeteer) 'puppeteer_screenshot', // MongoDB 'list_databases', 'list_collections', 'collection_indexes', 'collection_schema', 'collection_storage_size', 'db_stats', 'explain', 'mongodb_logs', 'aggregate', 'count', 'export', // Neo4j 'get_neo4j_schema', 'read_neo4j_cypher', 'list_instances', 'get_instance_details', 'get_instance_by_name', // Elasticsearch (elastic) 'list_indices', 'get_mappings', 'esql', 'get_shards', // Airtable 'list_records', 'list_bases', 'get_record', // Todoist (Doist — kebab-case, normalized) 'get_productivity_stats', 'get_overview', 'fetch_object', 'user_info', 'list_workspaces', 'view_attachment', // AWS (awslabs/mcp) 'get_available_services', 'read_documentation', 'read_sections', 'recommend', 'analyze_log_group', 'analyze_metric', 'describe_log_groups', 'get_active_alarms', 'get_alarm_history', 'get_metric_data', 'get_metric_metadata', // Kubernetes 'kubectl_get', 'kubectl_describe', 'kubectl_logs', 'kubectl_context', 'explain_resource', 'list_api_resources', 'namespaces_list', 'nodes_log', 'nodes_top', 'pods_get', 'pods_list', 'pods_list_in_namespace', 'pods_log', 'pods_top', 'resources_get', 'resources_list', ]) function normalize(name: string): string { return name .replace(/([a-z])([A-Z])/g, '$1_$2') .replace(/-/g, '_') .toLowerCase() } export function classifyMcpToolForCollapse( _serverName: string, toolName: string, ): { isSearch: boolean; isRead: boolean } { const normalized = normalize(toolName) return { isSearch: SEARCH_TOOLS.has(normalized), isRead: READ_TOOLS.has(normalized), } }