/ tools / MCPTool / classifyForCollapse.ts
classifyForCollapse.ts
  1  /**
  2   * Classify an MCP tool as a search/read operation for UI collapsing.
  3   * Returns { isSearch: false, isRead: false } for tools that should not
  4   * collapse (e.g., send_message, create_*, update_*).
  5   *
  6   * Uses explicit per-tool allowlists for the most common MCP servers.
  7   * Tool names are stable across installs (even when the server name varies,
  8   * e.g., "slack" vs "claude_ai_Slack"), so matching is keyed on the tool
  9   * name alone after normalizing camelCase/kebab-case to snake_case.
 10   * Unknown tool names don't collapse (conservative).
 11   */
 12  
 13  // prettier-ignore
 14  const SEARCH_TOOLS = new Set([
 15    // Slack (hosted + @modelcontextprotocol/server-slack)
 16    'slack_search_public',
 17    'slack_search_public_and_private',
 18    'slack_search_channels',
 19    'slack_search_users',
 20    // GitHub (github/github-mcp-server)
 21    'search_code',
 22    'search_repositories',
 23    'search_issues',
 24    'search_pull_requests',
 25    'search_orgs',
 26    'search_users',
 27    // Linear (mcp.linear.app)
 28    'search_documentation',
 29    // Datadog (mcp.datadoghq.com)
 30    'search_logs',
 31    'search_spans',
 32    'search_rum_events',
 33    'search_audit_logs',
 34    'search_monitors',
 35    'search_monitor_groups',
 36    'find_slow_spans',
 37    'find_monitors_matching_pattern',
 38    // Sentry (getsentry/sentry-mcp)
 39    'search_docs',
 40    'search_events',
 41    'search_issue_events',
 42    'find_organizations',
 43    'find_teams',
 44    'find_projects',
 45    'find_releases',
 46    'find_dsns',
 47    // Notion (mcp.notion.com — kebab-case, normalized)
 48    'search',
 49    // Gmail (claude.ai hosted)
 50    'gmail_search_messages',
 51    // Google Drive (claude.ai hosted + @modelcontextprotocol/server-gdrive)
 52    'google_drive_search',
 53    // Google Calendar (claude.ai hosted)
 54    'gcal_find_my_free_time',
 55    'gcal_find_meeting_times',
 56    'gcal_find_user_emails',
 57    // Atlassian/Jira (mcp.atlassian.com — camelCase, normalized)
 58    'search_jira_issues_using_jql',
 59    'search_confluence_using_cql',
 60    'lookup_jira_account_id',
 61    // Community Atlassian (sooperset/mcp-atlassian)
 62    'confluence_search',
 63    'jira_search',
 64    'jira_search_fields',
 65    // Asana (mcp.asana.com)
 66    'asana_search_tasks',
 67    'asana_typeahead_search',
 68    // Filesystem (@modelcontextprotocol/server-filesystem)
 69    'search_files',
 70    // Memory (@modelcontextprotocol/server-memory)
 71    'search_nodes',
 72    // Brave Search
 73    'brave_web_search',
 74    'brave_local_search',
 75    // Git (mcp-server-git)
 76    // (git has no search verbs)
 77    // Grafana (grafana/mcp-grafana)
 78    'search_dashboards',
 79    'search_folders',
 80    // PagerDuty
 81    // (pagerduty reads all use get_/list_, no search verbs)
 82    // Supabase
 83    'search_docs',
 84    // Stripe
 85    'search_stripe_resources',
 86    'search_stripe_documentation',
 87    // PubMed (claude.ai hosted + community)
 88    'search_articles',
 89    'find_related_articles',
 90    'lookup_article_by_citation',
 91    'search_papers',
 92    'search_pubmed',
 93    'search_pubmed_key_words',
 94    'search_pubmed_advanced',
 95    'pubmed_search',
 96    'pubmed_mesh_lookup',
 97    // Firecrawl
 98    'firecrawl_search',
 99    // Exa
100    'web_search_exa',
101    'web_search_advanced_exa',
102    'people_search_exa',
103    'linkedin_search_exa',
104    'deep_search_exa',
105    // Perplexity
106    'perplexity_search',
107    'perplexity_search_web',
108    // Tavily
109    'tavily_search',
110    // Obsidian (MarkusPfundstein)
111    'obsidian_simple_search',
112    'obsidian_complex_search',
113    // MongoDB
114    'find',
115    'search_knowledge',
116    // Neo4j
117    'search_memories',
118    'find_memories_by_name',
119    // Airtable
120    'search_records',
121    // Todoist (Doist — kebab-case, normalized)
122    'find_tasks',
123    'find_tasks_by_date',
124    'find_completed_tasks',
125    'find_projects',
126    'find_sections',
127    'find_comments',
128    'find_project_collaborators',
129    'find_activity',
130    'find_labels',
131    'find_filters',
132    // AWS
133    'search_documentation',
134    'search_catalog',
135    // Terraform
136    'search_modules',
137    'search_providers',
138    'search_policies',
139  ])
140  
141  // prettier-ignore
142  const READ_TOOLS = new Set([
143    // Slack (hosted + @modelcontextprotocol/server-slack)
144    'slack_read_channel',
145    'slack_read_thread',
146    'slack_read_canvas',
147    'slack_read_user_profile',
148    'slack_list_channels',
149    'slack_get_channel_history',
150    'slack_get_thread_replies',
151    'slack_get_users',
152    'slack_get_user_profile',
153    // GitHub (github/github-mcp-server)
154    'get_me',
155    'get_team_members',
156    'get_teams',
157    'get_commit',
158    'get_file_contents',
159    'get_repository_tree',
160    'list_branches',
161    'list_commits',
162    'list_releases',
163    'list_tags',
164    'get_latest_release',
165    'get_release_by_tag',
166    'get_tag',
167    'list_issues',
168    'issue_read',
169    'list_issue_types',
170    'get_label',
171    'list_label',
172    'pull_request_read',
173    'get_gist',
174    'list_gists',
175    'list_notifications',
176    'get_notification_details',
177    'projects_list',
178    'projects_get',
179    'actions_get',
180    'actions_list',
181    'get_job_logs',
182    'get_code_scanning_alert',
183    'list_code_scanning_alerts',
184    'get_dependabot_alert',
185    'list_dependabot_alerts',
186    'get_secret_scanning_alert',
187    'list_secret_scanning_alerts',
188    'get_global_security_advisory',
189    'list_global_security_advisories',
190    'list_org_repository_security_advisories',
191    'list_repository_security_advisories',
192    'get_discussion',
193    'get_discussion_comments',
194    'list_discussion_categories',
195    'list_discussions',
196    'list_starred_repositories',
197    'get_issue',
198    'get_pull_request',
199    'list_pull_requests',
200    'get_pull_request_files',
201    'get_pull_request_status',
202    'get_pull_request_comments',
203    'get_pull_request_reviews',
204    // Linear (mcp.linear.app)
205    'list_comments',
206    'list_cycles',
207    'get_document',
208    'list_documents',
209    'list_issue_statuses',
210    'get_issue_status',
211    'list_my_issues',
212    'list_issue_labels',
213    'list_projects',
214    'get_project',
215    'list_project_labels',
216    'list_teams',
217    'get_team',
218    'list_users',
219    'get_user',
220    // Datadog (mcp.datadoghq.com)
221    'aggregate_logs',
222    'list_spans',
223    'aggregate_spans',
224    'analyze_trace',
225    'trace_critical_path',
226    'query_metrics',
227    'aggregate_rum_events',
228    'list_rum_metrics',
229    'get_rum_metric',
230    'list_monitors',
231    'get_monitor',
232    'check_can_delete_monitor',
233    'validate_monitor',
234    'validate_existing_monitor',
235    'list_dashboards',
236    'get_dashboard',
237    'query_dashboard_widget',
238    'list_notebooks',
239    'get_notebook',
240    'query_notebook_cell',
241    'get_profiling_metrics',
242    'compare_profiling_metrics',
243    // Sentry (getsentry/sentry-mcp)
244    'whoami',
245    'get_issue_details',
246    'get_issue_tag_values',
247    'get_trace_details',
248    'get_event_attachment',
249    'get_doc',
250    'get_sentry_resource',
251    'list_events',
252    'list_issue_events',
253    'get_sentry_issue',
254    // Notion (mcp.notion.com — kebab-case, normalized)
255    'fetch',
256    'get_comments',
257    'get_users',
258    'get_self',
259    // Gmail (claude.ai hosted)
260    'gmail_get_profile',
261    'gmail_read_message',
262    'gmail_read_thread',
263    'gmail_list_drafts',
264    'gmail_list_labels',
265    // Google Drive (claude.ai hosted + @modelcontextprotocol/server-gdrive)
266    'google_drive_fetch',
267    'google_drive_export',
268    // Google Calendar (claude.ai hosted)
269    'gcal_list_calendars',
270    'gcal_list_events',
271    'gcal_get_event',
272    // Atlassian/Jira (mcp.atlassian.com — camelCase, normalized)
273    'atlassian_user_info',
274    'get_accessible_atlassian_resources',
275    'get_visible_jira_projects',
276    'get_jira_project_issue_types_metadata',
277    'get_jira_issue',
278    'get_transitions_for_jira_issue',
279    'get_jira_issue_remote_issue_links',
280    'get_confluence_spaces',
281    'get_confluence_page',
282    'get_pages_in_confluence_space',
283    'get_confluence_page_ancestors',
284    'get_confluence_page_descendants',
285    'get_confluence_page_footer_comments',
286    'get_confluence_page_inline_comments',
287    // Community Atlassian (sooperset/mcp-atlassian)
288    'confluence_get_page',
289    'confluence_get_page_children',
290    'confluence_get_comments',
291    'confluence_get_labels',
292    'jira_get_issue',
293    'jira_get_transitions',
294    'jira_get_worklog',
295    'jira_get_agile_boards',
296    'jira_get_board_issues',
297    'jira_get_sprints_from_board',
298    'jira_get_sprint_issues',
299    'jira_get_link_types',
300    'jira_download_attachments',
301    'jira_batch_get_changelogs',
302    'jira_get_user_profile',
303    'jira_get_project_issues',
304    'jira_get_project_versions',
305    // Asana (mcp.asana.com)
306    'asana_get_attachment',
307    'asana_get_attachments_for_object',
308    'asana_get_goal',
309    'asana_get_goals',
310    'asana_get_parent_goals_for_goal',
311    'asana_get_portfolio',
312    'asana_get_portfolios',
313    'asana_get_items_for_portfolio',
314    'asana_get_project',
315    'asana_get_projects',
316    'asana_get_project_sections',
317    'asana_get_project_status',
318    'asana_get_project_statuses',
319    'asana_get_project_task_counts',
320    'asana_get_projects_for_team',
321    'asana_get_projects_for_workspace',
322    'asana_get_task',
323    'asana_get_tasks',
324    'asana_get_stories_for_task',
325    'asana_get_teams_for_workspace',
326    'asana_get_teams_for_user',
327    'asana_get_team_users',
328    'asana_get_time_period',
329    'asana_get_time_periods',
330    'asana_get_user',
331    'asana_get_workspace_users',
332    'asana_list_workspaces',
333    // Filesystem (@modelcontextprotocol/server-filesystem)
334    'read_file',
335    'read_text_file',
336    'read_media_file',
337    'read_multiple_files',
338    'list_directory',
339    'list_directory_with_sizes',
340    'directory_tree',
341    'get_file_info',
342    'list_allowed_directories',
343    // Memory (@modelcontextprotocol/server-memory)
344    'read_graph',
345    'open_nodes',
346    // Postgres (@modelcontextprotocol/server-postgres)
347    'query',
348    // SQLite (@modelcontextprotocol/server-sqlite)
349    'read_query',
350    'list_tables',
351    'describe_table',
352    // Git (mcp-server-git)
353    'git_status',
354    'git_diff',
355    'git_diff_unstaged',
356    'git_diff_staged',
357    'git_log',
358    'git_show',
359    'git_branch',
360    // Grafana (grafana/mcp-grafana)
361    'list_teams',
362    'list_users_by_org',
363    'get_dashboard_by_uid',
364    'get_dashboard_summary',
365    'get_dashboard_property',
366    'get_dashboard_panel_queries',
367    'run_panel_query',
368    'list_datasources',
369    'get_datasource',
370    'get_query_examples',
371    'query_prometheus',
372    'query_prometheus_histogram',
373    'list_prometheus_metric_metadata',
374    'list_prometheus_metric_names',
375    'list_prometheus_label_names',
376    'list_prometheus_label_values',
377    'query_loki_logs',
378    'query_loki_stats',
379    'query_loki_patterns',
380    'list_loki_label_names',
381    'list_loki_label_values',
382    'list_incidents',
383    'get_incident',
384    'list_sift_investigations',
385    'get_sift_investigation',
386    'get_sift_analysis',
387    'list_oncall_schedules',
388    'get_oncall_shift',
389    'get_current_oncall_users',
390    'list_oncall_teams',
391    'list_oncall_users',
392    'list_alert_groups',
393    'get_alert_group',
394    'get_annotations',
395    'get_annotation_tags',
396    'get_panel_image',
397    // PagerDuty (PagerDuty/pagerduty-mcp-server)
398    'list_incidents',
399    'get_incident',
400    'get_outlier_incident',
401    'get_past_incidents',
402    'get_related_incidents',
403    'list_incident_notes',
404    'list_incident_workflows',
405    'get_incident_workflow',
406    'list_services',
407    'get_service',
408    'list_team_members',
409    'get_user_data',
410    'list_schedules',
411    'get_schedule',
412    'list_schedule_users',
413    'list_oncalls',
414    'list_log_entries',
415    'get_log_entry',
416    'list_escalation_policies',
417    'get_escalation_policy',
418    'list_event_orchestrations',
419    'get_event_orchestration',
420    'list_status_pages',
421    'get_status_page_post',
422    'list_alerts_from_incident',
423    'get_alert_from_incident',
424    'list_change_events',
425    'get_change_event',
426    // Supabase (supabase-community/supabase-mcp)
427    'list_organizations',
428    'get_organization',
429    'get_cost',
430    'list_extensions',
431    'list_migrations',
432    'get_logs',
433    'get_advisors',
434    'get_project_url',
435    'get_publishable_keys',
436    'generate_typescript_types',
437    'list_edge_functions',
438    'get_edge_function',
439    'list_storage_buckets',
440    'get_storage_config',
441    // Stripe (stripe/agent-toolkit)
442    'get_stripe_account_info',
443    'retrieve_balance',
444    'list_customers',
445    'list_products',
446    'list_prices',
447    'list_invoices',
448    'list_payment_intents',
449    'list_subscriptions',
450    'list_coupons',
451    'list_disputes',
452    'fetch_stripe_resources',
453    // PubMed (claude.ai hosted + community)
454    'get_article_metadata',
455    'get_full_text_article',
456    'convert_article_ids',
457    'get_copyright_status',
458    'download_paper',
459    'list_papers',
460    'read_paper',
461    'get_paper_fulltext',
462    'get_pubmed_article_metadata',
463    'download_pubmed_pdf',
464    'pubmed_fetch',
465    'pubmed_pmc_fetch',
466    'pubmed_spell',
467    'pubmed_cite',
468    'pubmed_related',
469    // BigQuery (claude.ai hosted + community)
470    'bigquery_query',
471    'bigquery_schema',
472    'list_dataset_ids',
473    'list_table_ids',
474    'get_dataset_info',
475    'get_table_info',
476    // Firecrawl
477    'firecrawl_scrape',
478    'firecrawl_map',
479    'firecrawl_crawl',
480    'firecrawl_check_crawl_status',
481    'firecrawl_extract',
482    // Exa
483    'get_code_context_exa',
484    'company_research_exa',
485    'crawling_exa',
486    'deep_researcher_check',
487    // Perplexity
488    'perplexity_ask',
489    'perplexity_research',
490    'perplexity_reason',
491    // Tavily
492    'tavily_extract',
493    'tavily_crawl',
494    'tavily_map',
495    'tavily_research',
496    // Obsidian (MarkusPfundstein)
497    'obsidian_list_files_in_vault',
498    'obsidian_list_files_in_dir',
499    'obsidian_get_file_contents',
500    'obsidian_batch_get_file_contents',
501    'obsidian_get_periodic_note',
502    'obsidian_get_recent_periodic_notes',
503    'obsidian_get_recent_changes',
504    // Figma (GLips/Figma-Context-MCP)
505    'get_figma_data',
506    'download_figma_images',
507    // Playwright (microsoft/playwright-mcp)
508    'browser_console_messages',
509    'browser_network_requests',
510    'browser_take_screenshot',
511    'browser_snapshot',
512    'browser_get_config',
513    'browser_route_list',
514    'browser_cookie_list',
515    'browser_cookie_get',
516    'browser_localstorage_list',
517    'browser_localstorage_get',
518    'browser_sessionstorage_list',
519    'browser_sessionstorage_get',
520    'browser_storage_state',
521    // Puppeteer (@modelcontextprotocol/server-puppeteer)
522    'puppeteer_screenshot',
523    // MongoDB
524    'list_databases',
525    'list_collections',
526    'collection_indexes',
527    'collection_schema',
528    'collection_storage_size',
529    'db_stats',
530    'explain',
531    'mongodb_logs',
532    'aggregate',
533    'count',
534    'export',
535    // Neo4j
536    'get_neo4j_schema',
537    'read_neo4j_cypher',
538    'list_instances',
539    'get_instance_details',
540    'get_instance_by_name',
541    // Elasticsearch (elastic)
542    'list_indices',
543    'get_mappings',
544    'esql',
545    'get_shards',
546    // Airtable
547    'list_records',
548    'list_bases',
549    'get_record',
550    // Todoist (Doist — kebab-case, normalized)
551    'get_productivity_stats',
552    'get_overview',
553    'fetch_object',
554    'user_info',
555    'list_workspaces',
556    'view_attachment',
557    // AWS (awslabs/mcp)
558    'get_available_services',
559    'read_documentation',
560    'read_sections',
561    'recommend',
562    'analyze_log_group',
563    'analyze_metric',
564    'describe_log_groups',
565    'get_active_alarms',
566    'get_alarm_history',
567    'get_metric_data',
568    'get_metric_metadata',
569    // Kubernetes
570    'kubectl_get',
571    'kubectl_describe',
572    'kubectl_logs',
573    'kubectl_context',
574    'explain_resource',
575    'list_api_resources',
576    'namespaces_list',
577    'nodes_log',
578    'nodes_top',
579    'pods_get',
580    'pods_list',
581    'pods_list_in_namespace',
582    'pods_log',
583    'pods_top',
584    'resources_get',
585    'resources_list',
586  ])
587  
588  function normalize(name: string): string {
589    return name
590      .replace(/([a-z])([A-Z])/g, '$1_$2')
591      .replace(/-/g, '_')
592      .toLowerCase()
593  }
594  
595  export function classifyMcpToolForCollapse(
596    _serverName: string,
597    toolName: string,
598  ): { isSearch: boolean; isRead: boolean } {
599    const normalized = normalize(toolName)
600    return {
601      isSearch: SEARCH_TOOLS.has(normalized),
602      isRead: READ_TOOLS.has(normalized),
603    }
604  }