From 267182a708df29088b3537d7aac9c01f3977f8c1 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 2 Jul 2026 07:43:19 +0000 Subject: [PATCH] docs(crustdatamcp): add Crustdata MCP connector documentation --- ...er-setup-crustdatamcp-common-workflows.mdx | 78 + .../agent-connectors/_setup-crustdatamcp.mdx | 30 + .../templates/agent-connectors/index.ts | 2 + .../docs/agentkit/connectors/crustdatamcp.mdx | 89 + src/data/agent-connectors/catalog.ts | 5 + src/data/agent-connectors/crustdatamcp.ts | 1727 +++++++++++++++++ 6 files changed, 1931 insertions(+) create mode 100644 src/components/templates/agent-connectors/_section-after-setup-crustdatamcp-common-workflows.mdx create mode 100644 src/components/templates/agent-connectors/_setup-crustdatamcp.mdx create mode 100644 src/content/docs/agentkit/connectors/crustdatamcp.mdx create mode 100644 src/data/agent-connectors/crustdatamcp.ts diff --git a/src/components/templates/agent-connectors/_section-after-setup-crustdatamcp-common-workflows.mdx b/src/components/templates/agent-connectors/_section-after-setup-crustdatamcp-common-workflows.mdx new file mode 100644 index 000000000..af4e01626 --- /dev/null +++ b/src/components/templates/agent-connectors/_section-after-setup-crustdatamcp-common-workflows.mdx @@ -0,0 +1,78 @@ +{/* TODO: stub cloned from _section-after-setup-atlassianmcp-common-workflows.mdx for Crustdata MCP. Review and update connector-specific references (URLs, scopes, app-registration steps) before merging. */} +export const sectionTitle = 'Common workflows' + +import { Tabs, TabItem, Aside } from '@astrojs/starlight/components' + +### Get your cloud ID + +Most Crustdata MCP tools require a `cloudId` — the UUID that identifies your Atlassian cloud site. Call `crustdatamcp_getaccessibleatlassianresources` once to retrieve it, then pass the `id` field value in every subsequent tool call. + + + + + + ```typescript + // Step 1 — get the cloud ID + const resources = await actions.executeTool({ + connectionName: 'crustdatamcp', + identifier: 'user_123', + toolName: 'crustdatamcp_getaccessibleatlassianresources', + toolInput: {}, + }); + const cloudId = resources[0].id; + + // Step 2 — use cloudId in subsequent calls + const issue = await actions.executeTool({ + connectionName: 'crustdatamcp', + identifier: 'user_123', + toolName: 'crustdatamcp_getjiraissue', + toolInput: { + cloudId, + issueIdOrKey: 'KAN-1', + }, + }); + console.log(issue); + ``` + + + ```python + # Step 1 — get the cloud ID + resources = actions.execute_tool( + connection_name="crustdatamcp", + identifier="user_123", + tool_name="crustdatamcp_getaccessibleatlassianresources", + tool_input={}, + ) + cloud_id = resources[0]["id"] + + # Step 2 — use cloud_id in subsequent calls + issue = actions.execute_tool( + connection_name="crustdatamcp", + identifier="user_123", + tool_name="crustdatamcp_getjiraissue", + tool_input={ + "cloudId": cloud_id, + "issueIdOrKey": "KAN-1", + }, + ) + print(issue) + ``` + + + +The `crustdatamcp_getaccessibleatlassianresources` response looks like this: + +```json +[ + { + "id": "a4c9b3e2-1234-5678-abcd-ef0123456789", + "name": "My Company", + "url": "https://mycompany.atlassian.net", + "scopes": ["read:jira-work", "write:jira-work", "read:confluence-content.all"] + } +] +``` + +Use `id` as the `cloudId` parameter. If the user belongs to multiple Atlassian sites, the list contains one entry per site — pick the one matching the target `url`. diff --git a/src/components/templates/agent-connectors/_setup-crustdatamcp.mdx b/src/components/templates/agent-connectors/_setup-crustdatamcp.mdx new file mode 100644 index 000000000..168143eda --- /dev/null +++ b/src/components/templates/agent-connectors/_setup-crustdatamcp.mdx @@ -0,0 +1,30 @@ +import { Steps, Aside } from '@astrojs/starlight/components' + +{/* TODO: stub cloned from _setup-atlassianmcp.mdx for Crustdata MCP. Review and update connector-specific references (URLs, scopes, app-registration steps) before merging. */} + +Crustdata MCP uses OAuth 2.1 with Dynamic Client Registration (DCR) and PKCE. Scalekit handles client registration and token management automatically — no client ID or secret is needed in advance. + + +1. ### Create a connection in Scalekit + + In the [Scalekit dashboard](https://app.scalekit.com), go to **AgentKit** > **Connections** and click **Create Connection**. Find **Crustdata MCP** and click **Create**. + + {/* TODO: add screenshot — alt: "Create Connection screen with Crustdata MCP selected", src: @/assets/docs/agent-connectors/crustdatamcp/create-connection.png */} + +2. ### Authorize the connection + + After creating the connection, click **Authorize**. Scalekit registers an OAuth client with Crustdata via DCR and redirects you to Crustdata's authorization page. Sign in with your Crustdata account and grant the requested permissions. + + {/* TODO: add screenshot — alt: "Crustdata OAuth authorization screen", src: @/assets/docs/agent-connectors/crustdatamcp/authorize-connection.png */} + {/* TODO: add provider-specific steps — e.g. where to locate OAuth consent settings in the Crustdata dashboard */} + + + +3. ### Verify the connection is active + + Back in the Scalekit dashboard under **AgentKit** > **Connections**, confirm the Crustdata MCP connection shows a status of **Active**. Your AI agent can now call Crustdata tools through Scalekit. + + {/* TODO: add screenshot — alt: "Crustdata MCP connection status showing Active", src: @/assets/docs/agent-connectors/crustdatamcp/connection-active.png */} + diff --git a/src/components/templates/agent-connectors/index.ts b/src/components/templates/agent-connectors/index.ts index ded5fb340..dae0b5ceb 100644 --- a/src/components/templates/agent-connectors/index.ts +++ b/src/components/templates/agent-connectors/index.ts @@ -21,6 +21,7 @@ export { default as SetupClickupSection } from './_setup-clickup.mdx' export { default as SetupCloseSection } from './_setup-close.mdx' export { default as SetupCommonroommcpSection } from './_setup-commonroommcp.mdx' export { default as SetupConfluenceSection } from './_setup-confluence.mdx' +export { default as SetupCrustdatamcpSection } from './_setup-crustdatamcp.mdx' export { default as SetupCustomeriomcpSection } from './_setup-customeriomcp.mdx' export { default as SetupDatabricksSection } from './_setup-databricks.mdx' export { default as SetupDatadogSection } from './_setup-datadog.mdx' @@ -131,6 +132,7 @@ export { default as SectionAfterSetupClickupCommonWorkflows } from './_section-a export { default as SectionAfterSetupCloseCommonWorkflows } from './_section-after-setup-close-common-workflows.mdx' export { default as SectionAfterSetupCommonroommcpCommonWorkflows } from './_section-after-setup-commonroommcp-common-workflows.mdx' export { default as SectionAfterSetupConfluenceCommonWorkflows } from './_section-after-setup-confluence-common-workflows.mdx' +export { default as SectionAfterSetupCrustdatamcpCommonWorkflows } from './_section-after-setup-crustdatamcp-common-workflows.mdx' export { default as SectionAfterSetupCustomeriomcpCommonWorkflows } from './_section-after-setup-customeriomcp-common-workflows.mdx' export { default as SectionAfterSetupDatabricksCommonWorkflows } from './_section-after-setup-databricks-common-workflows.mdx' export { default as SectionAfterSetupDatadogCommonWorkflows } from './_section-after-setup-datadog-common-workflows.mdx' diff --git a/src/content/docs/agentkit/connectors/crustdatamcp.mdx b/src/content/docs/agentkit/connectors/crustdatamcp.mdx new file mode 100644 index 000000000..1c4adad89 --- /dev/null +++ b/src/content/docs/agentkit/connectors/crustdatamcp.mdx @@ -0,0 +1,89 @@ +--- +title: 'Crustdata MCP connector' +tableOfContents: true +description: 'Use Crustdata MCP to source candidates, prospect accounts, and enrich people and company data from your AI agent.' +sidebar: + label: 'Crustdata MCP' +overviewTitle: 'Quickstart' +connectorIcon: https://cdn.scalekit.com/sk-connect/assets/provider-icons/crustdata.svg +connectorAuthType: OAuth 2.1/DCR +connectorCategories: [CRM & Sales, Search] +head: + - tag: style + content: | + .sl-markdown-content h2 { + font-size: var(--sl-text-xl); + } + .sl-markdown-content h3 { + font-size: var(--sl-text-lg); + } +--- + +import ToolList from '@/components/ToolList.astro' +import { tools } from '@/data/agent-connectors/crustdatamcp' +import { Steps, Tabs, TabItem } from '@astrojs/starlight/components' +import { AgentKitCredentials } from '@components/templates' +import { SetupCrustdatamcpSection } from '@components/templates' +import { QuickstartGenericOauthSection } from '@components/templates' +import { SectionAfterSetupCrustdatamcpCommonWorkflows } from '@components/templates' + + + +1. ### Install the SDK + + + + ```bash frame="terminal" + npm install @scalekit-sdk/node + ``` + + + ```bash frame="terminal" + pip install scalekit + ``` + + + + Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) + +2. ### Set your credentials + + + +3. ### Set up the connector + + Register your Crustdata MCP credentials with Scalekit so it handles the token lifecycle. You do this once per environment. + +
+ Dashboard setup steps + + + +
+ +4. ### Authorize and make your first call + + + +
+ +## What you can do + +Connect this agent connector to let your agent: + +- **Company crustdata autocomplete** — Get autocomplete suggestions for CompanyDB field values +- **Filter crustdata autocomplete** — Get autocomplete suggestions for search filter values +- **Person crustdata autocomplete** — Get autocomplete suggestions for people database fields +- **Search crustdata batch job, crustdata company** — Async batch job search from the database for up to 10 companies at once +- **Enrich crustdata batch people, crustdata company, crustdata people** — Async batch contact enrichment for 1-1000 LinkedIn URLs +- **Identify crustdata company** — Identify and match companies by name, domain, LinkedIn URL, Crunchbase URL, or Crustdata company_id + +## Common workflows + + + +## Tool list + +Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you're not sure which name to use, list the tools available for the current user first. + + diff --git a/src/data/agent-connectors/catalog.ts b/src/data/agent-connectors/catalog.ts index c8951b28f..116bf75c5 100644 --- a/src/data/agent-connectors/catalog.ts +++ b/src/data/agent-connectors/catalog.ts @@ -1249,4 +1249,9 @@ export const catalog: Record = { authType: 'OAuth 2.0', categories: ['Communication'], }, + crustdatamcp: { + iconUrl: 'https://cdn.scalekit.com/sk-connect/assets/provider-icons/crustdata.svg', + authType: 'OAuth 2.1/DCR', + categories: ['CRM & Sales', 'Search'], + }, } diff --git a/src/data/agent-connectors/crustdatamcp.ts b/src/data/agent-connectors/crustdatamcp.ts new file mode 100644 index 000000000..bdc3a8d1e --- /dev/null +++ b/src/data/agent-connectors/crustdatamcp.ts @@ -0,0 +1,1727 @@ +import type { Tool } from '../../types/agent-connectors' + +export const tools: Tool[] = [ + { + name: 'crustdatamcp_crustdata_autocomplete_company', + description: `Get autocomplete suggestions for CompanyDB field values. Free — 0 credits consumed. Use to discover valid values before constructing a filter on crustdata_company_search_db (e.g. field='hq_city', query='san francisco' returns the actual stored values). Empty query is allowed — returns the most common values for the field.`, + params: [ + { + name: 'field', + type: 'string', + required: true, + description: `Field to get autocomplete suggestions for (e.g., 'company_name', 'hq_country', 'hq_city', 'linkedin_industries'). Works on any CompanyDB field.`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'limit', + type: 'integer', + required: false, + description: `Maximum number of suggestions (default: 20, max: 100).`, + }, + { + name: 'query', + type: 'string', + required: false, + description: `Prefix/query to get suggestions for. Empty string is allowed — returns the most common values for the field.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_autocomplete_filter', + description: `Get autocomplete suggestions for search filter values. Useful for building valid filters for people and company searches. Best coverage on 'region' and 'title'. 'school' returns matches for well-known institutions but may return empty for niche international schools. 'industry' is currently low-coverage upstream — prefer crustdata_autocomplete_company with field='linkedin_industries' for industry values when filtering company DB.`, + params: [ + { + name: 'filter_type', + type: 'string', + required: true, + description: `Type of filter to get suggestions for. Valid values: 'region', 'industry', 'title', 'school'.`, + }, + { + name: 'query', + type: 'string', + required: true, + description: `Search query for autocomplete suggestions (e.g., 'San' for regions, 'Tech' for industries).`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'count', + type: 'integer', + required: false, + description: `Maximum number of suggestions to return (default: 10, max: 50).`, + }, + { + name: 'start', + type: 'integer', + required: false, + description: `Starting index for pagination (offset-based).`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_autocomplete_person', + description: `Get autocomplete suggestions for people database fields. Useful for building filters or discovering valid field values before calling crustdata_people_search_db. CONTEXTUAL AUTOCOMPLETE: pass filters (same shape as PersonDB search filters) to narrow suggestions to a subset — e.g. field='current_employers.title' + filters for a specific company returns titles only for people at that company. Empty query is allowed — returns the most common values. Free of charge — no credits consumed.`, + params: [ + { + name: 'field', + type: 'string', + required: true, + description: `Field name to autocomplete. Common fields: 'name', 'first_name', 'last_name', 'region', 'headline', 'skills', 'current_employers.name', 'current_employers.title', 'education_background.institute_name'. NOTE: Use 'name' not 'person_name', use 'current_employers.name' not 'company_name'.`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'filters', + type: 'object', + required: false, + description: `Optional filter criteria to narrow autocomplete suggestions. Same shape as PersonDB search filters: single {"filter_type": "...", "type": "...", "value": ...} or compound {"op": "and"|"or", "conditions": [...]}. Power-feature for contextual autocomplete.`, + }, + { + name: 'limit', + type: 'integer', + required: false, + description: `Maximum number of suggestions (default: 20, max: 100).`, + }, + { + name: 'query', + type: 'string', + required: false, + description: `Prefix/query to get suggestions for. Empty string is allowed — returns the most common values for the field.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_batch_job_search', + description: `Async batch job search from the database for up to 10 companies at once. Submits a batch job, polls until completion (~10-30s), then returns results. Use crustdata_company_identify first to get company IDs (free). For quick single-company searches, prefer crustdata_job_search instead.`, + params: [ + { + name: 'crustdata_company_ids', + type: 'array', + required: true, + description: `List of Crustdata company IDs to search jobs for (1-10). Get these from crustdata_company_identify (free).`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'compact', + type: 'boolean', + required: false, + description: `Strip full job descriptions to reduce response size.`, + }, + { + name: 'fields', + type: 'string', + required: false, + description: `Comma-separated list of fields to return per result.`, + }, + { + name: 'format', + type: 'string', + required: false, + description: `Response format: 'markdown' (default) or 'json'.`, + }, + { + name: 'limit', + type: 'integer', + required: false, + description: `Maximum number of job listings to return per company (default: 100, max: 1000).`, + }, + { + name: 'truncate', + type: 'boolean', + required: false, + description: `If true (default), enforce a 75K-char response cap. Set to false to bypass the cap.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_batch_job_search_live', + description: `Async batch LIVE job search across multiple companies. Scrapes LinkedIn in real-time for up to 10 companies at once, up to 100 jobs per company. Submits a batch job, polls until completion (~15-60s), then returns results. Use crustdata_company_identify first to get company IDs (free). For a single company, prefer crustdata_job_search_live instead.`, + params: [ + { + name: 'crustdata_company_ids', + type: 'array', + required: true, + description: `List of Crustdata company IDs to fetch live jobs for (1-10). Get these from crustdata_company_identify (free).`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'compact', + type: 'boolean', + required: false, + description: `Strip full job descriptions to reduce response size.`, + }, + { + name: 'fields', + type: 'string', + required: false, + description: `Comma-separated list of fields to return per result.`, + }, + { + name: 'format', + type: 'string', + required: false, + description: `Response format: 'markdown' (default) or 'json'.`, + }, + { + name: 'limit', + type: 'integer', + required: false, + description: `Maximum number of job listings to return per company (default: 100, max: 100).`, + }, + { + name: 'truncate', + type: 'boolean', + required: false, + description: `If true (default), enforce a 75K-char response cap. Set to false to bypass the cap.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_batch_people_enrich', + description: `Async batch contact enrichment for 1-1000 LinkedIn URLs. Returns business email, personal email, and phone numbers per URL. Submits a batch job, polls until completion (typical wall time 60-180 seconds regardless of input size), then returns parsed results. Use this over crustdata_people_enrich when: 4+ LinkedIn URLs at once, user asks for contact info with high fill rate, or sync enrichment returned missing emails. Batch runs the full enrichment waterfall — on a 50-URL sample, batch lifted business-email fill from 32% to 82%. Access is per-user gated; on 403, user needs to ping gtm@crustdata.co.`, + params: [ + { + name: 'professional_network_profile_urls', + type: 'array', + required: true, + description: `List of LinkedIn profile URLs to enrich (1-1000). Example: ['https://www.linkedin.com/in/satyanadella', 'https://www.linkedin.com/in/sundarpichai']. Each URL costs the vendor charge per result returned.`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'fields', + type: 'array', + required: false, + description: `Dotted-path field names to return. Default: ['business_email', 'personal_contact_info.personal_emails', 'personal_contact_info.phone_numbers']. Other valid roots: 'business_email', 'personal_contact_info'. Use dots for nested paths (e.g. 'personal_contact_info.phone_numbers').`, + }, + { + name: 'poll_timeout_seconds', + type: 'integer', + required: false, + description: `Max seconds to wait for batch completion before returning a timeout response (default 300). Long-running batches return the batch_id so the caller can re-poll later.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_company_enrich', + description: `Get comprehensive company profile data. Each identifier (company_domain, company_name, company_linkedin_url, company_id) accepts a comma-separated list of up to 25 entries for batch enrichment. Omit \`fields\` for the basic firmographics bundle; pass \`fields=[...]\` with opt-in nested objects you need (headcount, funding_and_investment, web_traffic, etc.).`, + params: [ + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'company_domain', + type: 'string', + required: false, + description: `Company domain (e.g., 'anthropic.com'). Can be comma-separated for multiple (max 25).`, + }, + { + name: 'company_id', + type: 'string', + required: false, + description: `Crustdata company ID. Can be comma-separated for multiple (max 25).`, + }, + { + name: 'company_linkedin_url', + type: 'string', + required: false, + description: `LinkedIn company page URL. Can be comma-separated for multiple (max 25).`, + }, + { + name: 'company_name', + type: 'string', + required: false, + description: `Company name. Can be comma-separated for multiple (max 25).`, + }, + { + name: 'enrich_realtime', + type: 'boolean', + required: false, + description: `Force real-time enrichment (slower but more current data). Default: false (uses cached data).`, + }, + { + name: 'exact_match', + type: 'boolean', + required: false, + description: `Controls how company_name and company_domain are matched. true: exact string match. false (default): fuzzy match — may return more than one company.`, + }, + { + name: 'fields', + type: 'array', + required: false, + description: `Optional list of fields to include in the response. Omit for basic firmographics. Opt-in nested objects: all_office_addresses, competitors, cxos, decision_makers, founders, funding_and_investment, g2, gartner, glassdoor, headcount, job_openings, linkedin_followers, news_articles, producthunt, seo, taxonomy, web_traffic.`, + }, + { + name: 'truncate', + type: 'boolean', + required: false, + description: `If true (default), cap multi-company responses at ~75K chars by progressive compaction. Set to false to bypass the cap.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_company_identify', + description: `Identify and match companies by name, domain, LinkedIn URL, Crunchbase URL, or Crustdata company_id. Pass ONE identifier type per call; within that type, comma-separate up to 25 values for batch identification. Returns {companies: [...], count: N, used_identifier?, dropped_identifiers?, note?}. Use this to find the Crustdata company_id for follow-up enrichment.`, + params: [ + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'company_crunchbase_url', + type: 'string', + required: false, + description: `Crunchbase company URL. Accepts vanity form ('https://www.crunchbase.com/organization/retool') or UUID form. Comma-separated for up to 25.`, + }, + { + name: 'company_id', + type: 'string', + required: false, + description: `Crustdata company ID. Comma-separated for up to 25 IDs.`, + }, + { + name: 'company_linkedin_url', + type: 'string', + required: false, + description: `LinkedIn company page URL. Accepts vanity form ('https://linkedin.com/company/tryretool') or LinkedIn-ID form ('https://www.linkedin.com/company/11869260'). Comma-separated for up to 25.`, + }, + { + name: 'company_name', + type: 'string', + required: false, + description: `Company name to search for. Single value or comma-separated list of up to 25 names (e.g., 'Anthropic' or 'Anthropic,OpenAI,Mistral').`, + }, + { + name: 'company_website', + type: 'string', + required: false, + description: `Company website domain (e.g., 'anthropic.com'). Single value or comma-separated list of up to 25 domains.`, + }, + { + name: 'count', + type: 'integer', + required: false, + description: `Maximum number of matches to return per query (default: 10, max: 25).`, + }, + { + name: 'exact_match', + type: 'boolean', + required: false, + description: `If true, only return exact matches. Default is fuzzy matching.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_company_search', + description: `Real-time search for companies using LinkedIn Sales Navigator style filters. Find companies by headcount, industry, location, revenue, funding activity, and more. Returns up to 25 results per page (max 65 pages). Values MUST be arrays for in/not_in operators. For ANNUAL_REVENUE use type 'between' with {min, max} in millions USD plus sub_filter 'USD'.`, + params: [ + { + name: 'filters', + type: 'array', + required: true, + description: `Array of filter objects. Each has filter_type, type, value. Values MUST be arrays for 'in'/'not in'. Valid filter types: COMPANY_HEADCOUNT, REGION, INDUSTRY, NUM_OF_FOLLOWERS, FORTUNE, ACCOUNT_ACTIVITIES, JOB_OPPORTUNITIES, COMPANY_HEADCOUNT_GROWTH, ANNUAL_REVENUE, DEPARTMENT_HEADCOUNT, DEPARTMENT_HEADCOUNT_GROWTH, KEYWORD.`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'compact', + type: 'boolean', + required: false, + description: `Strip verbose nested data to reduce response size. Keeps: name, website, HQ, industries, headcount, growth, total funding. Set to false for full data.`, + }, + { + name: 'fields', + type: 'string', + required: false, + description: `Comma-separated list of fields to return per result.`, + }, + { + name: 'format', + type: 'string', + required: false, + description: `Response format: 'markdown' (default) or 'json'. Use 'json' for programmatic processing.`, + }, + { + name: 'page', + type: 'integer', + required: false, + description: `Page number for pagination (starts from 1, max 65). Each page returns up to 25 results.`, + }, + { + name: 'truncate', + type: 'boolean', + required: false, + description: `If true (default), enforce a 75K-char response cap. Set to false to bypass the cap.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_company_search_db', + description: `Search the Crustdata company database with flexible filters. Fast search against pre-indexed data. Uses 'filter_type'/'type'/'value' keys (NOT 'column'). Call crustdata_autocomplete_company first to resolve exact values for categorical fields like linkedin_industries or country. By default returns compact records with key fields only.`, + params: [ + { + name: 'filters', + type: 'object', + required: true, + description: `Search filters using 'filter_type'/'type'/'value' keys. Single: {"filter_type": "company_name", "type": "(.)", "value": "Anthropic"}. Multiple: {"op": "and", "conditions": [{"filter_type": "hq_country", "type": "=", "value": "USA"}, {"filter_type": "employee_metrics.latest_count", "type": ">", "value": 100}]}. Operators: = != in not_in > < => =< (.) [.]`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'compact', + type: 'boolean', + required: false, + description: `Strip verbose nested data to reduce response size. Keeps: name, website, HQ, industries, headcount, growth, total funding. Set to false for full data.`, + }, + { + name: 'cursor', + type: 'string', + required: false, + description: `Pagination cursor from previous response. Must be paired with the same filters and sorts — do not reuse with different parameters.`, + }, + { + name: 'fields', + type: 'string', + required: false, + description: `Comma-separated list of fields to return per result. Supports dot-notation: 'company_name,hq_country,employee_count_range'.`, + }, + { + name: 'format', + type: 'string', + required: false, + description: `Response format: 'markdown' (default) or 'json'. Markdown tables are more token-efficient.`, + }, + { + name: 'limit', + type: 'integer', + required: false, + description: `Number of results to return (default: 20, max: 1000).`, + }, + { + name: 'sorts', + type: 'array', + required: false, + description: `Sort options. Each entry: {column: , order: 'asc'|'desc'}. Common sort fields: employee_metrics.latest_count, employee_metrics.growth_12m_percent, crunchbase_total_investment_usd, last_funding_date.`, + }, + { + name: 'truncate', + type: 'boolean', + required: false, + description: `If true (default), enforce a 75K-char response cap. Set to false to bypass the cap.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_company_social_posts', + description: `DEPRECATED — call crustdata_social_posts_by_keyword instead. This name is kept as a temporary alias for backward compatibility; it forwards to crustdata_social_posts_by_keyword and will be removed in a future release. Searches LINKEDIN posts by keyword, covering BOTH company and personal posts.`, + params: [ + { + name: 'keyword', + type: 'string', + required: true, + description: `Keyword or phrase to search for in social posts. MAX 6 KEYWORDS joined by OR/AND operators (at most 5 operators in the string). Quoted phrases count as one keyword. Examples: 'AI', 'AI OR documentation'.`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'compact', + type: 'boolean', + required: false, + description: `Strip verbose engagement data and truncate post text. Set to false for full post data.`, + }, + { + name: 'content_type', + type: 'array', + required: false, + description: `Filter by content type. Allowed values: 'photos', 'videos', 'documents', 'jobs', 'collaborativeArticles', 'liveVideos'.`, + }, + { + name: 'date_posted', + type: 'string', + required: false, + description: `Date window filter. Values: 'past-24h', 'past-week', 'past-month', 'past-quarter', 'past-year'.`, + }, + { + name: 'exact_keyword_match', + type: 'boolean', + required: false, + description: `If true, only return posts containing the exact keyword phrase. REQUIRES limit (NOT page).`, + }, + { + name: 'fields', + type: 'string', + required: false, + description: `Comma-separated fields. Add 'reactors' for users who reacted, 'comments' for comments.`, + }, + { + name: 'filters', + type: 'array', + required: false, + description: `Additional post-level filters combined with AND (author / company constraints, etc.).`, + }, + { + name: 'format', + type: 'string', + required: false, + description: `Response format: 'markdown' (default) or 'json'.`, + }, + { + name: 'limit', + type: 'integer', + required: false, + description: `Number of posts to return per request (1-500). Server default is 5. NOTE: when page is also set, the server caps limit at 5.`, + }, + { + name: 'max_comments', + type: 'integer', + required: false, + description: `Max comments per post when 'comments' is in fields (0-5000). Server default is 5.`, + }, + { + name: 'max_reactors', + type: 'integer', + required: false, + description: `Max reactors per post when 'reactors' is in fields (0-5000). Server default is 5.`, + }, + { + name: 'page', + type: 'integer', + required: false, + description: `Page number for pagination (1-100). Each page returns up to 5 posts. Cannot be combined with exact_keyword_match=true.`, + }, + { + name: 'sort_by', + type: 'string', + required: false, + description: `Sort order. Recommended: 'relevance' (default). 'date_posted' is DEPRECATED; combine sort_by='relevance' with the date_posted filter for time-bounded results.`, + }, + { + name: 'truncate', + type: 'boolean', + required: false, + description: `If true (default), enforce a 75K-char response cap. Set to false to bypass the cap.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_credit_costs', + description: `Return a markdown table of how many Crustdata credits each MCP tool call costs, broken down by variation (in-DB vs realtime, reactors/comments, business email, exact keyword match, per-result vs per-100-results, etc.). Free — makes no API call and consumes no credits. Use this to answer 'how much does X cost?' or to compare costs before making expensive calls. Note: credits are only charged when a call returns results.`, + params: [ + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_credits_check', + description: `Check your remaining Crustdata API credit balance. Free — consumes no credits.`, + params: [ + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_get_skill_body', + description: `Fetch the live SKILL.md body for a centrally-managed skill. The local SKILL.md installed at ~/.claude/skills//SKILL.md is intentionally a stub that points here — call this tool to get the current playbook before executing the skill. Returns the full instructions exactly as authored in the admin UI. Throws if the caller hasn't been granted access to this skill.`, + params: [ + { + name: 'skill_name', + type: 'string', + required: true, + description: `Name of the skill (folder name), e.g. 'research-person'.`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_get_skill_file', + description: `Fetch a helper file (reference, script, README, etc.) for a centrally-managed skill. Use this whenever the live SKILL.md body (from crustdata_get_skill_body) references a relative path like \`references/foo.md\` or \`scripts/bar.py\` — the file is NOT installed locally, only on the server. For executable scripts, write the returned content to a temp path before running.`, + params: [ + { + name: 'file_path', + type: 'string', + required: true, + description: `Relative path within the skill, e.g. 'references/profile-card.md' or 'scripts/compress_pool.py'.`, + }, + { + name: 'skill_name', + type: 'string', + required: true, + description: `Name of the skill (folder name), e.g. 'source-candidates'.`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_get_twitter_posts', + description: `Find recent Twitter/X posts from a company or person by their Twitter handle. USE THIS TOOL whenever the user asks about Twitter posts, X posts, or tweets — NOT the social_posts tools (those are LinkedIn only). Returns post titles, URLs, and snippets.`, + params: [ + { + name: 'twitter_handle', + type: 'string', + required: true, + description: `Twitter/X handle of the company or person (with or without @). For example: 'crustdata' or '@crustdata'.`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'num_pages', + type: 'integer', + required: false, + description: `Number of result pages. Each page returns up to ~10 posts. Default: 1.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_healthz', + description: `Lightweight liveness probe for the Crustdata MCP server itself. Returns {status: 'ok'} when the server is reachable. Does NOT call the Crustdata API or consume credits. Use this when you need to verify the MCP connection is healthy without spending credits.`, + params: [ + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_install_skills', + description: `Install Crustdata research skills locally so they appear as native /slash-commands in Claude Code (e.g., /research-person). By default writes full skill content (SKILL.md + helper files like references/, scripts/) plus a .crustdata_version marker. Each skill becomes invokable without any further server roundtrips. Use crustdata_skill_versions periodically to check whether a re-install is needed.`, + params: [ + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'only', + type: 'array', + required: false, + description: `Optional list of skill names to install. When set, only those skills are returned (intersected with caller's grants). When omitted, returns every granted skill.`, + }, + { + name: 'stub_only', + type: 'boolean', + required: false, + description: `If true, install only thin SKILL.md stubs that fetch full content via crustdata_get_skill_body on every invocation (legacy behavior — guarantees always-fresh content but adds a tool roundtrip per use). Default: false (write full skill content + helper files locally for fast invocation).`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_job_search', + description: `Search the job listings database. Find jobs by company, title, location, category, and more. Supports filters, sorting, cursor pagination (up to 1000 results), and aggregations (counts/breakdowns). Filters use 'field'/'type'/'value' keys with nested field paths (e.g., 'job_details.title', 'company.basic_info.name'). Use aggregations with limit=0 to get counts without fetching listings.`, + params: [ + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'aggregations', + type: 'array', + required: false, + description: `Get counts and breakdowns. Examples: [{"type": "count"}] for total count, [{"type": "group_by", "field": "job_details.category", "agg": "count", "size": 10}] for top categories.`, + }, + { + name: 'compact', + type: 'boolean', + required: false, + description: `Strip full job descriptions to reduce response size. Set to false to include descriptions.`, + }, + { + name: 'cursor', + type: 'string', + required: false, + description: `Pagination cursor from a previous response's next_cursor field.`, + }, + { + name: 'fields', + type: 'string', + required: false, + description: `Comma-separated list of fields to return per result: 'title,company_name,location,url'.`, + }, + { + name: 'filters', + type: 'object', + required: false, + description: `Search filters. Each filter has 'field', 'type', 'value'. Single: {"field": "job_details.title", "type": "(.)", "value": "Software Engineer"}. Multiple: {"op": "and", "conditions": [...]}. Operators: (.) [.] = != in not_in > < => =< is_null is_not_null.`, + }, + { + name: 'format', + type: 'string', + required: false, + description: `Response format: 'markdown' (default, compact table) or 'json'. Use 'json' for structured data.`, + }, + { + name: 'limit', + type: 'integer', + required: false, + description: `Number of results to return (default: 20, max: 1000). Set to 0 with aggregations to get only counts/breakdowns.`, + }, + { + name: 'sorts', + type: 'array', + required: false, + description: `Sort options. Each entry is {"field": ..., "order": "asc"|"desc"}. Sortable fields: crustdata_job_id, metadata.date_added, metadata.date_updated, relevance, company.headcount.total, company.followers.count, company.revenue.estimated.lower_bound_usd, company.funding.last_fundraise_date, company.funding.num_funding_rounds.`, + }, + { + name: 'truncate', + type: 'boolean', + required: false, + description: `If true (default), enforce a 75K-char response cap. Set to false to bypass the cap.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_job_search_live', + description: `Fetch LIVE job listings from LinkedIn for a specific company. Scrapes LinkedIn in real-time — slower than crustdata_job_search but returns the most current data. No charge when 0 results come back. Use crustdata_company_identify first to get the crustdata_company_id (free). This tool only filters by company; use crustdata_job_search for filtering by title/location/category.`, + params: [ + { + name: 'crustdata_company_id', + type: 'integer', + required: true, + description: `Crustdata company ID to fetch live job listings for. Get this from crustdata_company_identify (free) first.`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'compact', + type: 'boolean', + required: false, + description: `Strip full job descriptions to reduce response size. Set to false to include descriptions.`, + }, + { + name: 'fields', + type: 'string', + required: false, + description: `Comma-separated list of fields to return per result: 'title,company_name,location,url'.`, + }, + { + name: 'format', + type: 'string', + required: false, + description: `Response format: 'markdown' (default) or 'json'.`, + }, + { + name: 'limit', + type: 'integer', + required: false, + description: `Maximum number of job listings to return (default: 100, max: 100).`, + }, + { + name: 'sort_order', + type: 'string', + required: false, + description: `Order of returned listings. Options: 'recent_first' (default — newest postings first), 'oldest_first', 'api_default' (upstream insertion order).`, + }, + { + name: 'truncate', + type: 'boolean', + required: false, + description: `If true (default), enforce a 75K-char response cap. Set to false to bypass the cap.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_list_my_skills', + description: `List the Crustdata skills your account has access to install. Use this BEFORE crustdata_install_skills to see what's available, or to confirm what was granted. Returns names + descriptions.`, + params: [ + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_people_enrich', + description: `Get detailed person profile data by LinkedIn URL, business email, personal email, or GitHub URL. ENRICH MULTIPLE PROFILES IN ONE CALL — DO NOT LOOP. linkedin_profile_url (and other identifiers) accept COMMA-SEPARATED values, up to 25 per call. ONE call with 25 comma-separated URLs is ~10x FASTER than 25 separate calls. Use exactly ONE identifier TYPE per call. Returns professional information including current role, company, experience history, education, and skills. Supports reverse lookup by personal email or GitHub URL.`, + params: [ + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'business_email', + type: 'string', + required: false, + description: `Business email address. Can be comma-separated for up to 25 emails. Mutually exclusive with linkedin_profile_url / personal_email / github_profile_url.`, + }, + { + name: 'enrich_realtime', + type: 'boolean', + required: false, + description: `Trigger a live LinkedIn scrape ONLY for profiles whose DB cache is stale or missing. Default: false.`, + }, + { + name: 'fields', + type: 'string', + required: false, + description: `Comma-separated field names to return. Limits the response to ONLY these fields. include_* flags are additive on top of this list. Ignored when include_all_fields=true.`, + }, + { + name: 'force_fetch', + type: 'boolean', + required: false, + description: `Bypass the DB cache entirely and scrape every requested profile live. Requires enrich_realtime=true. Use sparingly — costs realtime scrape credits per profile. Default: false.`, + }, + { + name: 'github_profile_url', + type: 'string', + required: false, + description: `GitHub profile URL (e.g., 'https://github.com/octocat'). Reverse lookup to find the person's LinkedIn profile and full professional data. Can be comma-separated for up to 25 profiles.`, + }, + { + name: 'include_all_fields', + type: 'boolean', + required: false, + description: `Return the full curated profile bundle (name, headline, summary, employers, education, skills, etc.). OVERRIDES the fields param when set. Does NOT include business_email, github_profiles, or personal_contact_info — use the dedicated include_* flags for those.`, + }, + { + name: 'include_business_email', + type: 'boolean', + required: false, + description: `Include verified business/work email. Set to true ONLY when the user explicitly asks for business email or wants to email the person for outreach.`, + }, + { + name: 'include_github_profiles', + type: 'boolean', + required: false, + description: `Append github_profiles to the returned field set. Set to true ONLY when the user explicitly asks for GitHub, git activity, repos, or developer/code profile data.`, + }, + { + name: 'include_personal_contact_info', + type: 'boolean', + required: false, + description: `Include personal emails and phone numbers. Set to true ONLY when the user explicitly asks for personal email, phone number, or mobile. May return 403 if not in user's plan.`, + }, + { + name: 'linkedin_profile_url', + type: 'string', + required: false, + description: `LinkedIn profile URL (e.g., 'https://linkedin.com/in/johndoe'). Can be comma-separated for up to 25 profiles. Mutually exclusive with business_email / personal_email / github_profile_url.`, + }, + { + name: 'personal_email', + type: 'string', + required: false, + description: `Personal email address (e.g., Gmail, Yahoo). Reverse lookup to find the person's LinkedIn profile and professional data. Can be comma-separated for up to 25 emails.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_people_search', + description: `DO NOT USE THIS TOOL UNLESS crustdata_people_search_db returned 0 results first. Slow (10-30s), expensive fallback that queries LinkedIn in real-time. Uses DIFFERENT filter format: 'filter_type'/'type'/'value' (NOT 'column'). Values MUST be arrays: ['Google'] not 'Google'. PAGINATION (pick ONE — never both): (a) limit=N for a single bulk call (sync max 25; for N>25 also set background_job=true). (b) page=N to iterate 25-at-a-time (max 100 pages). Setting both page AND limit is rejected.`, + params: [ + { + name: 'filters', + type: 'array', + required: true, + description: `Array of filter objects. Each MUST have keys: filter_type, type, value. value MUST be an array: ["Google"] NOT "Google". Valid filter_type values: CURRENT_COMPANY, PAST_COMPANY, CURRENT_TITLE, PAST_TITLE, FIRST_NAME, LAST_NAME, REGION, INDUSTRY, COMPANY_HEADQUARTERS, FUNCTION, SENIORITY_LEVEL, SCHOOL, KEYWORD, COMPANY_HEADCOUNT, RECENTLY_CHANGED_JOBS, POSTED_ON_LINKEDIN, YEARS_OF_EXPERIENCE.`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'background_job', + type: 'boolean', + required: false, + description: `Run the search asynchronously. Required when limit > 25. Returns a job_id you then poll back through this same tool by passing job_id.`, + }, + { + name: 'compact', + type: 'boolean', + required: false, + description: `Strip verbose nested data to reduce response size. Keeps: name, headline, region, skills, emails, education, current employers. Set to false for full profiles.`, + }, + { + name: 'exclude_names', + type: 'array', + required: false, + description: `List of person names to drop from results. Example: ['John Doe'].`, + }, + { + name: 'exclude_profiles', + type: 'array', + required: false, + description: `LinkedIn profile URLs to drop from results. Example: ['https://linkedin.com/in/johndoe'].`, + }, + { + name: 'fields', + type: 'string', + required: false, + description: `STRONGLY RECOMMENDED when you only need specific data. Comma-separated list of fields to return per result. Supports dot-notation.`, + }, + { + name: 'format', + type: 'string', + required: false, + description: `Response format: 'markdown' (default) or 'json'.`, + }, + { + name: 'fuzzy_match', + type: 'boolean', + required: false, + description: `When true, fuzzy_match=true is set on every CURRENT_TITLE / PAST_TITLE filter (broader matching). Cannot be used with strict_title_and_company_match.`, + }, + { + name: 'job_id', + type: 'string', + required: false, + description: `Poll a previously-started background job. When set, other params are ignored and the response carries the job's status (and profiles once it's ready).`, + }, + { + name: 'limit', + type: 'integer', + required: false, + description: `EITHER use this OR page — never both. Max results to return in a single call (1-10,000). Sync max is 25. For >25, MUST also set background_job=true.`, + }, + { + name: 'page', + type: 'integer', + required: false, + description: `EITHER use this OR limit — never both. Page number for sync iteration (1-100). Each page returns up to 25 results. Default server-side is page=1 if neither page nor limit is supplied.`, + }, + { + name: 'strict_title_and_company_match', + type: 'boolean', + required: false, + description: `Filter results so a profile's titles exactly match the CURRENT_TITLE / PAST_TITLE filters. Cannot be used with fuzzy_match.`, + }, + { + name: 'truncate', + type: 'boolean', + required: false, + description: `If true (default), enforce a 75K-char response cap. Set to false for offline data dumps.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_people_search_db', + description: `ALWAYS USE THIS TOOL FIRST for any people search. Primary search across 800M+ professional profiles. Up to 1000 per request with cursor pagination. Do NOT use crustdata_people_search unless this returns 0 results AND the user needs live LinkedIn data. FILTER SYNTAX: Each filter uses 'column'/'type'/'value' keys (NOT 'filter_type'). Combine multiple filters with {'op': 'and', 'conditions': [...]}. CATEGORICAL FIELDS: For current_employers.title, region, current_employers.seniority_level etc., call crustdata_autocomplete_person FIRST to resolve exact stored values.`, + params: [ + { + name: 'filters', + type: 'object', + required: true, + description: `Search filters. Each filter has exactly 3 keys: "column", "type", "value". Single: {"column": "name", "type": "[.]", "value": "Chris"}. Multiple (AND): {"op": "and", "conditions": [...]}. Operators: [.]=substring, (.)=fuzzy, (!)=fuzzy negation, = !=, in/not_in, >//=<, geo_distance. Key columns: name, headline, region, skills, current_employers.name, current_employers.title, current_employers.seniority_level, years_of_experience_raw.`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'compact', + type: 'boolean', + required: false, + description: `Strip verbose nested data to reduce response size. Keeps: name, headline, region, skills, emails, education, current employers. Set to false for full profiles including complete work history.`, + }, + { + name: 'cursor', + type: 'string', + required: false, + description: `Pagination cursor from previous response for fetching next page. TIED TO the original filters + sorts — reusing with different filters/sorts returns an error. Treat as opaque; don't modify.`, + }, + { + name: 'exclude_names', + type: 'array', + required: false, + description: `List of exact full-name strings to drop from results (e.g. test accounts, banned names).`, + }, + { + name: 'exclude_profiles', + type: 'array', + required: false, + description: `List of LinkedIn profile URLs to drop from results. Useful for de-duping across paginated calls. Each URL MUST match https://www.linkedin.com/in/{slug} exactly. Max 50,000 entries.`, + }, + { + name: 'fields', + type: 'string', + required: false, + description: `STRONGLY RECOMMENDED when you only need specific data. Comma-separated list of fields to return per result (e.g. 'name,linkedin_profile_url,current_employers.title,emails'). Supports dot-notation. Avoids auto-compaction and eliminates need for follow-up enrich calls.`, + }, + { + name: 'format', + type: 'string', + required: false, + description: `Response format: 'markdown' (default) or 'json'. Markdown tables are much more token-efficient. Use 'json' for programmatic processing.`, + }, + { + name: 'limit', + type: 'integer', + required: false, + description: `Number of results to return (default: 20, max: 1000).`, + }, + { + name: 'sorts', + type: 'array', + required: false, + description: `Sort options. Example: [{"column": "years_of_experience_raw", "order": "desc"}]. Sortable columns: current_employers.company_headcount_latest, name, num_of_connections, years_of_experience_raw, etc.`, + }, + { + name: 'truncate', + type: 'boolean', + required: false, + description: `If true (default), enforce a 75K-char response cap. Set to false to bypass the cap — Claude Code will spill large results to a file automatically.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_people_search_db_v2', + description: `PEOPLE SEARCH — NEW DATASET API (v2025-11-01). DO NOT USE BY DEFAULT — use crustdata_people_search_db for normal people search. Only use this v2 tool when the user EXPLICITLY asks for it (phrases like 'use the new API', 'use v2', 'use the 2025-11-01 API'). Differences from legacy: same DB, richer nested response shape, vanity LinkedIn URLs. FILTER SHAPE: leaves use {field, type, value} (NOT 'column'). Field paths are nested (e.g. basic_profile.name, experience.employment_details.current.title).`, + params: [ + { + name: 'filters', + type: 'object', + required: true, + description: `Filter tree. LEAF: {"field": "", "type": "", "value": ...}. BRANCH: {"op": "and"|"or", "conditions": [...]}. NOTE: use 'field' (not 'column' like the legacy tool). Common field paths: basic_profile.name, basic_profile.headline, basic_profile.location.country, experience.employment_details.current.company_name, experience.employment_details.current.title, experience.employment_details.current.seniority_level, education.schools.school, skills.professional_network_skills, years_of_experience_raw.`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'cursor', + type: 'string', + required: false, + description: `Pagination cursor from previous response.`, + }, + { + name: 'fields', + type: 'string', + required: false, + description: `Limit response to specific field paths (e.g. ['basic_profile.name', 'social_handles.professional_network_identifier.profile_url']). Omit for full nested payload. Comma-separated string also accepted.`, + }, + { + name: 'limit', + type: 'integer', + required: false, + description: `Results per page (1-100, default 20).`, + }, + { + name: 'sorts', + type: 'array', + required: false, + description: `Optional sort list. Example: [{"field": "years_of_experience_raw", "order": "desc"}]. Each entry: field + order (asc/desc).`, + }, + { + name: 'truncate', + type: 'boolean', + required: false, + description: `If true (default), enforce a 75K-char response cap by progressively dropping fields. Set false when dumping raw data for offline analysis.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_people_search_semantic', + description: `PEOPLE SEMANTIC SEARCH (beta) — natural-language people search on the /person/search dataset (v2025-11-01). DO NOT USE BY DEFAULT — use crustdata_people_search_db for normal people search. Only use this when the user EXPLICITLY asks for semantic / natural-language search. Ranks people by how well their whole profile matches the query text. KEY: results ALWAYS come back — read the per-profile 'fit' tier (strong/possible/weak) to judge quality. Results are relevance-ordered; sorting is not available. Cost: 0.03 credits per result.`, + params: [ + { + name: 'query', + type: 'string', + required: true, + description: `Natural-language description of the people to find — a role, persona, skill set, or pasted job description. e.g. 'founding engineers at developer-tools startups', 'backend engineers with Golang and distributed systems experience'. This text is the ranking signal.`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'cursor', + type: 'string', + required: false, + description: `Pagination cursor from a previous response.`, + }, + { + name: 'fields', + type: 'string', + required: false, + description: `Limit the response to specific field paths (list or comma-separated string). 'fit' is auto-added so the per-profile relevance tier is never dropped. Omit for the full nested payload.`, + }, + { + name: 'filters', + type: 'object', + required: false, + description: `OPTIONAL structured filter tree to constrain/seed the ranked set — same shape as crustdata_people_search_db_v2. LEAF: {"field": "", "type": "", "value": ...}. BRANCH: {"op": "and"|"or", "conditions": [...]}.`, + }, + { + name: 'limit', + type: 'integer', + required: false, + description: `Results per page (1-100, default 20).`, + }, + { + name: 'recall_mode', + type: 'string', + required: false, + description: `How explicit filters combine with the query. 'managed' (default) = filters are a ranking signal. 'exact' = filters are hard constraints and query only ranks within them. Use 'exact' when every returned profile MUST satisfy your filters.`, + }, + { + name: 'search_mode', + type: 'string', + required: false, + description: `Retrieval signal for the query. 'hybrid' (default) = keyword + vector, best general default. 'lexical' = keyword only (exact terms, acronyms, names, IDs). 'semantic' = vector only (concept match even with different wording).`, + }, + { + name: 'truncate', + type: 'boolean', + required: false, + description: `If true (default), enforce a 75K-char response cap. Set false when dumping raw data for offline analysis.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_skill_versions', + description: `Return current server-side version markers for the caller's granted skills. Each version is the ISO timestamp of the most recent update to the skill in the admin DB. Compare against the .crustdata_version file written at install time — if they differ, re-run crustdata_install_skills to refresh local content. Cheap, single DB query. Safe to call once per session.`, + params: [ + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_social_posts', + description: `Get recent LinkedIn posts authored by a specific person OR company profile, OR fetch a single post by URL. LinkedIn only — for Twitter/X posts use crustdata_get_twitter_posts. Provide exactly ONE identifier: person_linkedin_url, company_domain, company_linkedin_url, company_name, company_id, or linkedin_post_url (single-post mode). Posts are always sorted by date (most recent first). Add 'reactors' and/or 'comments' to the fields parameter for engagement details.`, + params: [ + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'compact', + type: 'boolean', + required: false, + description: `Strip verbose engagement data and truncate post text. Set to false for full post data.`, + }, + { + name: 'company_domain', + type: 'string', + required: false, + description: `Company domain to fetch posts for (e.g., 'anthropic.com').`, + }, + { + name: 'company_id', + type: 'string', + required: false, + description: `Crustdata company ID to fetch posts for.`, + }, + { + name: 'company_linkedin_url', + type: 'string', + required: false, + description: `LinkedIn company page URL (e.g., 'https://linkedin.com/company/anthropic').`, + }, + { + name: 'company_name', + type: 'string', + required: false, + description: `Company name to fetch posts for.`, + }, + { + name: 'fields', + type: 'string', + required: false, + description: `Comma-separated fields. Add 'reactors' for users who reacted, 'comments' for comments.`, + }, + { + name: 'limit', + type: 'integer', + required: false, + description: `Number of posts to return per request (1-100). Default: 20.`, + }, + { + name: 'linkedin_post_url', + type: 'string', + required: false, + description: `Fetch a single LinkedIn post by its direct URL. Returns exactly one post and disables pagination. Mutually exclusive with person_linkedin_url and the company_* identifiers.`, + }, + { + name: 'max_comments', + type: 'integer', + required: false, + description: `Max comments per post when 'comments' is in fields. Server default: 100. Valid range 1-5000; 0 means no comment data.`, + }, + { + name: 'max_reactors', + type: 'integer', + required: false, + description: `Max reactors per post when 'reactors' is in fields. Server default: 100. Valid range 1-5000; 0 means no reactor data.`, + }, + { + name: 'page', + type: 'integer', + required: false, + description: `Page number for pagination (1-20). Most recent posts on page 1.`, + }, + { + name: 'person_linkedin_url', + type: 'string', + required: false, + description: `LinkedIn profile URL of a person (e.g., 'https://linkedin.com/in/johndoe'). Mutually exclusive with the company_* identifiers and linkedin_post_url.`, + }, + { + name: 'post_types', + type: 'string', + required: false, + description: `Comma-separated post types: 'original', 'repost', or 'repost,original' (default).`, + }, + { + name: 'response_format', + type: 'string', + required: false, + description: `Response format: 'markdown' (default) or 'json'. Markdown tables are more token-efficient.`, + }, + { + name: 'truncate', + type: 'boolean', + required: false, + description: `If true (default), enforce a 75K-char response cap. Set to false to bypass the cap.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_social_posts_by_keyword', + description: `Search LINKEDIN posts by keyword (POST /screener/linkedin_posts/keyword_search/). Finds BOTH company and personal LinkedIn posts mentioning specific topics, products, or trends. Useful for market research, competitive intelligence, and trend analysis. CONSTRAINT: keyword accepts at most 6 keywords joined by OR/AND (max 5 operators). By default (compact=true), returns trimmed posts. Set compact=false for full data.`, + params: [ + { + name: 'keyword', + type: 'string', + required: true, + description: `Keyword or phrase to search for in social posts. MAX 6 KEYWORDS joined by OR/AND operators (so at most 5 operators in the string). 7+ keywords will return a 400 error. Quoted phrases count as one keyword regardless of words inside. Examples: 'AI', 'AI OR documentation', '"AI Engineer" OR "ML Engineer" OR "Data Scientist"'. To search 7+ terms, split into multiple calls and merge results.`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'compact', + type: 'boolean', + required: false, + description: `Strip verbose engagement data (full reactor/comment lists) and truncate post text. Set to false for full post data including all reactors and comments.`, + }, + { + name: 'content_type', + type: 'array', + required: false, + description: `Filter by content type. Allowed values: 'photos', 'videos', 'documents', 'jobs', 'collaborativeArticles', 'liveVideos'. Example: ['photos', 'videos'].`, + }, + { + name: 'date_posted', + type: 'string', + required: false, + description: `Date window filter (server default: 'past-month'). Values: 'past-24h', 'past-week', 'past-month', 'past-quarter', 'past-year'.`, + }, + { + name: 'exact_keyword_match', + type: 'boolean', + required: false, + description: `If true, only return posts containing the exact keyword phrase. REQUIRES limit (NOT page) — when true the API scans the first n posts (n=limit) and returns only the exact-match subset; may return fewer than limit. If no posts match, returns a 404.`, + }, + { + name: 'fields', + type: 'string', + required: false, + description: `Comma-separated fields. Add 'reactors' for users who reacted, 'comments' for comments. Affects cost: default=1/post, with reactors=5/post, with comments=5/post, with both=10/post.`, + }, + { + name: 'filters', + type: 'array', + required: false, + description: `Additional post-level filters combined with AND. Each entry is a filter dict (author / company constraints, etc.). Posts must satisfy ALL listed filters.`, + }, + { + name: 'format', + type: 'string', + required: false, + description: `Response format: 'markdown' (default) or 'json'. Use 'json' for programmatic processing.`, + }, + { + name: 'limit', + type: 'integer', + required: false, + description: `Number of posts to return per request (1-500). Server default is 5. NOTE: when page is also set, the server caps limit at 5 — for bulk retrieval use limit WITHOUT page.`, + }, + { + name: 'max_comments', + type: 'integer', + required: false, + description: `Max comments per post when 'comments' is in fields (0-5000). Server default is 5. Omit to use the server default.`, + }, + { + name: 'max_reactors', + type: 'integer', + required: false, + description: `Max reactors per post when 'reactors' is in fields (0-5000). Server default is 5. Omit to use the server default.`, + }, + { + name: 'page', + type: 'integer', + required: false, + description: `Page number for pagination (1-100). Each page returns up to 5 posts. When page is set, limit cannot exceed 5. CANNOT be combined with exact_keyword_match=true — use limit instead.`, + }, + { + name: 'sort_by', + type: 'string', + required: false, + description: `Sort order. Recommended: 'relevance' (default — sorts by top match). 'date_posted' is DEPRECATED; for time-bounded results combine sort_by='relevance' with the date_posted date-window filter.`, + }, + { + name: 'truncate', + type: 'boolean', + required: false, + description: `If true (default), enforce a 75K-char response cap by progressively compacting/dropping fields. Set to false to bypass the cap entirely.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_watcher_cancel', + description: `Permanently cancel a watcher subscription by ID. Cancellation is irreversible — the watch stops running and cannot be reactivated (use crustdata_watcher_update with status='paused' if you only want to pause it). Notification history and run records are preserved. The cancelled watch is hidden from crustdata_watcher_list unless include_cancelled=true.`, + params: [ + { + name: 'subscription_id', + type: 'integer', + required: true, + description: `ID of the watch to cancel. Must be positive. Cancellation is permanent and irreversible.`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_watcher_create', + description: `Create a watcher to monitor events. No webhook hosting required — if the user does not provide notification_endpoint, the watcher posts to a Crustdata-managed receiver and the MCP retrieves delivered payloads via crustdata_watcher_run_summary. Creating a watch is FREE; credits are only charged when the watch runs. Event types include 'job-posting-with-keyword-and-location' (new job postings), 'company-watch-linkedin-posts' (company LinkedIn activity), 'linkedin-person-post-updates' (person LinkedIn posts), 'person-discovery-via-filters' (people matching Sales Nav filters), and more. Use crustdata_watcher_simulate to dry-run first, crustdata_watcher_cancel to permanently stop a watch. The response includes an \`api_request\` field with the exact JSON body posted and a copy-pasteable curl; after creating, surface that curl to the user so they can reproduce or save it.`, + params: [ + { + name: 'event_filters', + type: 'array', + required: true, + description: `Event-specific filters. Example: [{"filter_type": "REGION", "type": "in", "value": "San Francisco"}]. For title matching use CURRENT_TITLE (tokenized) or KEYWORD (phrase-exact). For job-posting watchers include TITLE+REGION. For person-discovery include CURRENT_TITLE, REGION, SENIORITY_LEVEL, etc.`, + }, + { + name: 'event_type_slug', + type: 'string', + required: true, + description: `Type of event to monitor. Options include: 'linkedin-person-profile-updates', 'linkedin-person-post-updates', 'linkedin-person-press-mentions', 'company-watch-linkedin-posts', 'company-watch-linkedin-job-postings', 'company-watch-funding-milestones', 'company-watch-press-mentions', 'company-watch-headcount-growth', 'person-discovery-via-filters', 'linkedin-post-with-keyword', 'new-funding-announcements', 'job-posting-with-keyword-and-location', 'job-posting-with-location', 'person-starting-new-position', 'company-headcount-growth', and more.`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'account_filters', + type: 'array', + required: false, + description: `Company filters. Filter types: INDUSTRY, COMPANY_HEADCOUNT, COMPANY_HQ_COUNTRY, LAST_FUNDING_ROUND_TYPE. Required for certain event types like 'new-funding-announcements' and 'job-posting-with-keyword-and-location'.`, + }, + { + name: 'approximate_notification_time', + type: 'integer', + required: false, + description: `Hour of day (0-23, UTC) to send notifications.`, + }, + { + name: 'expiration_date', + type: 'string', + required: false, + description: `Subscription expiration date in YYYY-MM-DD format. Must be a future date.`, + }, + { + name: 'frequency', + type: 'integer', + required: false, + description: `Notification frequency in days (minimum 1 day). Default: 1`, + }, + { + name: 'lead_filters', + type: 'array', + required: false, + description: `Person filters. Filter types: CURRENT_TITLE, PAST_TITLE, CURRENT_COMPANY, PAST_COMPANY, REGION, etc.`, + }, + { + name: 'max_notifications_per_execution', + type: 'integer', + required: false, + description: `Optional cap on notifications sent per watch execution. Must be a positive multiple of 50. If omitted, all matching events are sent.`, + }, + { + name: 'notification_endpoint', + type: 'string', + required: false, + description: `HTTPS URL to receive webhook notifications when events occur. OPTIONAL — defaults to a Crustdata-managed receiver. When omitted, use crustdata_watcher_run_summary to view delivered payloads directly in chat.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_watcher_get', + description: `Get the full details of a single watcher subscription by ID (status, filters, endpoint, frequency, etc.).`, + params: [ + { + name: 'subscription_id', + type: 'integer', + required: true, + description: `ID of the watch. Must be positive.`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_watcher_list', + description: `List the caller's watcher subscriptions, most recent first. Returns id, event_type_slug, status, frequency, created_at, last_run_id, notification_endpoint, and max_notifications_per_execution. Use this at the start of any session that references a previously-created watch — users often forget the subscription_id, so match by event_type_slug + created_at to recover it.`, + params: [ + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'compact', + type: 'boolean', + required: false, + description: `When true (default), return only the headline fields per watcher and drop the bulky event_filters / account_filters / lead_filters payloads. Set to false when the user explicitly asks to inspect filter configuration.`, + }, + { + name: 'include_cancelled', + type: 'boolean', + required: false, + description: `When true, cancelled watches are returned alongside active/paused ones. Default false (cancelled watches are hidden).`, + }, + { + name: 'limit', + type: 'integer', + required: false, + description: `Maximum number of watchers to return (most recent first). Default 50. Use a smaller limit (e.g. 10) if you only need the latest few.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_watcher_run_summary', + description: `Fetch the detailed summary of a single watcher run: per-stage pipeline logs with timestamps AND the actual webhook payload(s) delivered for that run. Each entry in notifications has sent_at, http_status, and the full payload POSTed (subscription_id, event_type, timestamp, and the matching records). Use this to show the user the actual matches their watcher produced — no webhook hosting required; the MCP can render results directly in chat.`, + params: [ + { + name: 'run_id', + type: 'integer', + required: true, + description: `ID of the specific run (from crustdata_watcher_runs).`, + }, + { + name: 'subscription_id', + type: 'integer', + required: true, + description: `ID of the watch the run belongs to.`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_watcher_runs', + description: `List recent runs of a watcher. Each entry includes the run id, status (RUNNING/SUCCESS/FAILED/SKIPPED), started_at, completed_at, new_records_count, credits_deducted, and notification_http_status. Cursor-paginated, most recent first. Use this to find runs worth inspecting, then call crustdata_watcher_run_summary for the actual delivered records.`, + params: [ + { + name: 'subscription_id', + type: 'integer', + required: true, + description: `ID of the watch whose run history you want.`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'cursor', + type: 'integer', + required: false, + description: `Pagination cursor — pass the next_cursor returned by the previous call to fetch the next page. Omit for the first page.`, + }, + { + name: 'limit', + type: 'integer', + required: false, + description: `Maximum number of runs to return (most recent first). Default 20, max 100.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_watcher_simulate', + description: `Simulate a watcher subscription to test your webhook endpoint. Sends a test notification without creating a persistent subscription.`, + params: [ + { + name: 'event_filters', + type: 'array', + required: true, + description: `Event-specific filters for the simulation.`, + }, + { + name: 'event_type_slug', + type: 'string', + required: true, + description: `Type of event to simulate monitoring.`, + }, + { + name: 'notification_endpoint', + type: 'string', + required: true, + description: `HTTPS URL to receive test webhook notification. Must use HTTPS protocol.`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'account_filters', + type: 'array', + required: false, + description: `Company filters to simulate (same shape as crustdata_watcher_create). Filter types: INDUSTRY, COMPANY_HEADCOUNT, COMPANY_HQ_COUNTRY, LAST_FUNDING_ROUND_TYPE.`, + }, + { + name: 'expiration_date', + type: 'string', + required: false, + description: `Optional expiration date (YYYY-MM-DD, future) for the simulated watch.`, + }, + { + name: 'frequency', + type: 'integer', + required: false, + description: `Simulated notification frequency in days (minimum 1 day).`, + }, + { + name: 'lead_filters', + type: 'array', + required: false, + description: `Person filters to simulate (same shape as crustdata_watcher_create). Filter types: CURRENT_TITLE, PAST_TITLE, CURRENT_COMPANY, PAST_COMPANY, REGION, etc.`, + }, + { + name: 'max_notifications_per_execution', + type: 'integer', + required: false, + description: `Optional cap on notifications sent per watch execution. Must be a positive multiple of 50. If omitted, all matching events are sent.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_watcher_update', + description: `Update an existing watcher subscription. Can change status (pause/resume), update webhook endpoint, or modify filters for certain subscription types. To permanently cancel a watch use crustdata_watcher_cancel instead — the update endpoint cannot cancel.`, + params: [ + { + name: 'subscription_id', + type: 'integer', + required: true, + description: `ID of the subscription to update. Must be positive.`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'event_filters', + type: 'array', + required: false, + description: `Updated event filters (only for certain subscription types).`, + }, + { + name: 'max_notifications_per_execution', + type: 'integer', + required: false, + description: `Optional cap on notifications sent per watch execution. Must be a positive multiple of 50. If omitted, all matching events are sent. Applied from the next watch execution onward.`, + }, + { + name: 'notification_endpoint', + type: 'string', + required: false, + description: `New HTTPS webhook endpoint URL.`, + }, + { + name: 'status', + type: 'string', + required: false, + description: `New status: 'active' (resume) or 'paused'. To permanently cancel a watch, use crustdata_watcher_cancel instead — the update endpoint cannot cancel.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_web_fetch', + description: `Fetch and extract text content from up to 10 web page URLs in one request. HTML is stripped and content is capped per URL.`, + params: [ + { + name: 'urls', + type: 'array', + required: true, + description: `URLs to fetch content from (max 10).`, + }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + ], + }, + { + name: 'crustdatamcp_crustdata_web_search', + description: `Search the web for information about companies, people, or topics. Returns search results with titles, URLs, and snippets.`, + params: [ + { name: 'query', type: 'string', required: true, description: `Search query string.` }, + { + name: '_rationale', + type: 'string', + required: false, + description: `Describe the user's intent and why this tool was selected in general terms only; do not include specific field values, names, emails, or other data.`, + }, + { + name: 'end_date', + type: 'integer', + required: false, + description: `End date for results (Unix timestamp).`, + }, + { + name: 'fetch_content', + type: 'boolean', + required: false, + description: `When true, also fetch the full page content for each result (returned in a 'contents' array).`, + }, + { + name: 'geolocation', + type: 'string', + required: false, + description: `Geographic location for search results (e.g., 'US', 'GB'). ISO 3166-1 alpha-2 country code.`, + }, + { + name: 'num_pages', + type: 'integer', + required: false, + description: `Number of search result pages to return. Each page returns up to ~10 results. Max: 15. num_pages > 1 is only valid for the 'web' source.`, + }, + { + name: 'site', + type: 'string', + required: false, + description: `Limit search to a specific site (e.g., 'linkedin.com').`, + }, + { + name: 'sources', + type: 'array', + required: false, + description: `Specific sources to search. Valid values: news, web, scholar-articles, scholar-articles-enriched, scholar-author, ai, social.`, + }, + { + name: 'start_date', + type: 'integer', + required: false, + description: `Start date for results (Unix timestamp).`, + }, + ], + }, +]