From 6ca6f9d68fe0e11083bcf49ae2f15c1226ec770e Mon Sep 17 00:00:00 2001 From: Eden d'Alexis <225963065+edendalexis@users.noreply.github.com> Date: Wed, 22 Jul 2026 16:20:24 +0200 Subject: [PATCH] feat: add SERPdive search integration --- apps/docs/components/icons.tsx | 25 +++ apps/docs/components/ui/icon-mapping.ts | 2 + .../content/docs/en/integrations/meta.json | 3 +- .../content/docs/en/integrations/serpdive.mdx | 66 ++++++ apps/sim/blocks/blocks/serpdive.ts | 200 ++++++++++++++++++ apps/sim/blocks/registry-maps.ts | 5 +- apps/sim/components/icons.tsx | 25 +++ apps/sim/lib/integrations/icon-mapping.ts | 2 + apps/sim/lib/integrations/integrations.json | 21 +- apps/sim/tools/registry.ts | 4 +- apps/sim/tools/serpdive/index.ts | 3 + apps/sim/tools/serpdive/search.ts | 122 +++++++++++ apps/sim/tools/serpdive/types.ts | 49 +++++ 13 files changed, 523 insertions(+), 4 deletions(-) create mode 100644 apps/docs/content/docs/en/integrations/serpdive.mdx create mode 100644 apps/sim/blocks/blocks/serpdive.ts create mode 100644 apps/sim/tools/serpdive/index.ts create mode 100644 apps/sim/tools/serpdive/search.ts create mode 100644 apps/sim/tools/serpdive/types.ts diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index 1ebe7b408bc..b882b17d2dd 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -833,6 +833,31 @@ export function SerperIcon(props: SVGProps) { ) } +export function SerpdiveIcon(props: SVGProps) { + return ( + + + + + + + ) +} + export function TailscaleIcon(props: SVGProps) { return ( diff --git a/apps/docs/components/ui/icon-mapping.ts b/apps/docs/components/ui/icon-mapping.ts index cdab0cd4c76..c41470ee4f9 100644 --- a/apps/docs/components/ui/icon-mapping.ts +++ b/apps/docs/components/ui/icon-mapping.ts @@ -193,6 +193,7 @@ import { SendblueIcon, SendgridIcon, SentryIcon, + SerpdiveIcon, SerperIcon, ServiceNowIcon, SftpIcon, @@ -455,6 +456,7 @@ export const blockTypeToIconMap: Record = { sendblue: SendblueIcon, sendgrid: SendgridIcon, sentry: SentryIcon, + serpdive: SerpdiveIcon, serper: SerperIcon, servicenow: ServiceNowIcon, ses: SESIcon, diff --git a/apps/docs/content/docs/en/integrations/meta.json b/apps/docs/content/docs/en/integrations/meta.json index 551b8d25890..d0b87516d7d 100644 --- a/apps/docs/content/docs/en/integrations/meta.json +++ b/apps/docs/content/docs/en/integrations/meta.json @@ -213,6 +213,7 @@ "sendblue", "sendgrid", "sentry", + "serpdive", "serper", "servicenow", "ses", @@ -264,4 +265,4 @@ "zoom", "zoominfo" ] -} +} \ No newline at end of file diff --git a/apps/docs/content/docs/en/integrations/serpdive.mdx b/apps/docs/content/docs/en/integrations/serpdive.mdx new file mode 100644 index 00000000000..fe44885be00 --- /dev/null +++ b/apps/docs/content/docs/en/integrations/serpdive.mdx @@ -0,0 +1,66 @@ +--- +title: SERPdive +description: Search the web with SERPdive +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +{/* MANUAL-CONTENT-START:intro */} +[SERPdive](https://serpdive.com) is a web search API built for AI agents. Instead of a list of links or short snippets, one call returns the extracted, answer-ready content of each source page, cleaned and sized for a context window. + +SERPdive gives your agents live web access without a scraping loop to maintain: + +- **Answer-ready content**: every result carries the readable content of the page, so the agent can quote and cite facts straight from the response. +- **Two retrieval depths**: Mako returns the fact-carrying sentences of each source and is fast; Moby returns the full readable content of every page when whole-page context matters. +- **Optional written answer**: the API can also return an answer built from the sources, concise on Mako and cited on Moby. +- **Automatic localization**: the query's language decides where the search runs, so no country parameter is needed. + +Quality is measured, not asserted: on a [public, replayable 1,000-question benchmark](https://github.com/edendalexis/serpdive-benchmark) judged blind by an independent model, SERPdive runs at the same speed as Tavily, feeds the LLM **20.2% fewer tokens**, and wins **60.7% of decided quality duels** against Tavily's default search. + +Every account gets 1,000 free credits per month with no card. +{/* MANUAL-CONTENT-END */} + + +## Usage Instructions + +Integrate SERPdive into the workflow. Can search the web and return the extracted, answer-ready content of each source instead of links or snippets. + + + +## Actions + +### `serpdive_search` + +Search the web with SERPdive. Each result carries the extracted, answer-ready content of the page instead of a link or snippet, cleaned and sized for an LLM. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `query` | string | Yes | The search query, phrased as a question or topic, in any language \(e.g., "what changed in the latest Next.js release"\). Maximum 300 characters. | +| `model` | string | No | Retrieval depth: mako \(1 credit\) returns the fact-carrying sentences of each source, moby \(1.5 credits\) returns the full readable content of every page \(e.g., "mako"\) | +| `answer` | boolean | No | Also return a written answer built from the sources: concise on mako, detailed with citations on moby. No extra credits. | +| `max_results` | number | No | Hard cap on delivered results, keeping the best-ranked ones \(1-10, e.g., 5\). Omit to get everything relevant. | +| `apiKey` | string | Yes | SERPdive API Key | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `query` | string | The search query that was executed | +| `results` | array | Delivered sources, best first, each carrying the extracted content of the page | +| ↳ `url` | string | Result URL, tracking parameters stripped | +| ↳ `title` | string | Page title | +| ↳ `content` | string | Extracted, answer-ready content of the page | +| ↳ `date` | string | Publication date when known, ISO YYYY-MM-DD | +| `answer` | string | Written answer built from the sources \(if requested\) | +| `extra_info` | object | Structured direct-answer block \(weather, exchange rates, live scores...\) when the query has one | +| `model` | string | The retrieval model that answered: mako or moby | +| `response_time_ms` | number | Time taken for the search request in milliseconds | + + diff --git a/apps/sim/blocks/blocks/serpdive.ts b/apps/sim/blocks/blocks/serpdive.ts new file mode 100644 index 00000000000..4a5336f8da1 --- /dev/null +++ b/apps/sim/blocks/blocks/serpdive.ts @@ -0,0 +1,200 @@ +import { SerpdiveIcon } from '@/components/icons' +import { AuthMode, type BlockConfig, type BlockMeta, IntegrationType } from '@/blocks/types' +import type { SerpdiveSearchResponse } from '@/tools/serpdive/types' + +export const SerpdiveBlock: BlockConfig = { + type: 'serpdive', + name: 'SERPdive', + description: 'Search the web with SERPdive', + authMode: AuthMode.ApiKey, + longDescription: + 'Integrate SERPdive into the workflow. Can search the web and return the extracted, answer-ready content of each source instead of links or snippets.', + docsLink: 'https://docs.sim.ai/integrations/serpdive', + category: 'tools', + integrationType: IntegrationType.Search, + bgColor: '#0A0F13', + icon: SerpdiveIcon, + + subBlocks: [ + { + id: 'query', + title: 'Search Query', + type: 'long-input', + placeholder: 'Enter your search query', + required: true, + }, + { + id: 'model', + title: 'Retrieval Depth', + type: 'dropdown', + options: [ + { label: 'Mako (fast, key sentences)', id: 'mako' }, + { label: 'Moby (full page content)', id: 'moby' }, + ], + value: () => 'mako', + }, + { + id: 'answer', + title: 'Include Answer', + type: 'switch', + mode: 'advanced', + }, + { + id: 'max_results', + title: 'Max Results', + type: 'short-input', + placeholder: '5', + mode: 'advanced', + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + placeholder: 'Enter your SERPdive API key', + password: true, + required: true, + }, + ], + + tools: { + access: ['serpdive_search'], + }, + + inputs: { + query: { type: 'string', description: 'Search query' }, + apiKey: { type: 'string', description: 'SERPdive API key' }, + model: { type: 'string', description: 'Retrieval depth: mako or moby' }, + answer: { type: 'boolean', description: 'Also return a written answer built from the sources' }, + max_results: { type: 'number', description: 'Hard cap on delivered results (1-10)' }, + }, + + outputs: { + query: { type: 'string', description: 'Search query used' }, + results: { type: 'json', description: 'Sources with extracted page content' }, + answer: { type: 'string', description: 'Written answer built from the sources' }, + extra_info: { + type: 'json', + description: 'Structured direct-answer block, when the query has one', + }, + model: { type: 'string', description: 'Retrieval model that answered' }, + response_time_ms: { type: 'number', description: 'Request duration in milliseconds' }, + }, +} + +export const SerpdiveBlockMeta = { + tags: ['web-scraping', 'enrichment'], + url: 'https://serpdive.com', + templates: [ + { + icon: SerpdiveIcon, + title: 'SERPdive research agent', + prompt: + 'Build a research agent that takes a question, runs SERPdive searches to gather live sources, and returns a written answer with the source URLs it relied on.', + modules: ['agent', 'workflows'], + category: 'productivity', + tags: ['research'], + }, + { + icon: SerpdiveIcon, + title: 'SERPdive daily digest', + prompt: + 'Create a scheduled daily workflow that searches SERPdive for tracked topics, summarizes the extracted content from each source, and emails a digest with links.', + modules: ['scheduled', 'agent', 'workflows'], + category: 'productivity', + tags: ['research', 'reporting'], + alsoIntegrations: ['gmail'], + }, + { + icon: SerpdiveIcon, + title: 'SERPdive competitor monitor', + prompt: + 'Build a scheduled weekly workflow that searches SERPdive for competitor mentions, scores each result for relevance, and appends new findings to a competitive intel table.', + modules: ['scheduled', 'tables', 'agent', 'workflows'], + category: 'marketing', + tags: ['marketing', 'monitoring'], + }, + { + icon: SerpdiveIcon, + title: 'SERPdive lead enrichment', + prompt: + 'Create a workflow that for each company in a table runs a SERPdive search on recent news and funding, then writes a short enriched summary back to the row.', + modules: ['tables', 'agent', 'workflows'], + category: 'sales', + tags: ['sales', 'enrichment'], + }, + { + icon: SerpdiveIcon, + title: 'SERPdive fact checker', + prompt: + 'Build a workflow that takes a claim, searches SERPdive with the answer option on, and returns a verdict backed by quotes from the extracted source content.', + modules: ['agent', 'workflows'], + category: 'productivity', + tags: ['research'], + }, + { + icon: SerpdiveIcon, + title: 'SERPdive + Slack answer bot', + prompt: + 'Build a Slack bot that answers questions about current events by searching SERPdive, replying in-thread with the answer and the source links it used.', + modules: ['agent', 'workflows'], + category: 'productivity', + tags: ['research', 'communication'], + alsoIntegrations: ['slack'], + }, + { + icon: SerpdiveIcon, + title: 'SERPdive knowledge base feeder', + prompt: + 'Create a workflow that searches SERPdive on a topic with full page content, then writes the extracted content of each source into a knowledge base for later retrieval.', + modules: ['agent', 'knowledge-base', 'workflows'], + category: 'productivity', + tags: ['research'], + }, + { + icon: SerpdiveIcon, + title: 'SERPdive research report', + prompt: + 'Build a workflow that for a chosen topic runs several SERPdive searches with full page content, synthesizes the findings, and writes a cited report file.', + modules: ['agent', 'files', 'workflows'], + category: 'productivity', + tags: ['research', 'content'], + }, + ], + skills: [ + { + name: 'answer-with-sources', + description: + 'Answer a question with SERPdive and surface the source URLs the answer was built from.', + content: + '# Answer with Sources\n\nGround an answer in live web content using SERPdive.\n\n## Steps\n1. Set the search query to the user question, phrased the way someone would search it.\n2. Turn on Include Answer so the response carries a written answer built from the sources.\n3. Use Moby retrieval depth when the question needs whole-page context, otherwise leave it on Mako.\n4. Present the answer and attach the URL of every result it drew on.\n\n## Output\nA concise answer followed by the list of source URLs that support it.', + }, + { + name: 'gather-research-sources', + description: + 'Run a SERPdive search and return a ranked list of sources with their extracted page content.', + content: + '# Gather Research Sources\n\nCollect sources on a topic for downstream research.\n\n## Steps\n1. Set the search query to the research topic, narrowed with the key terms that matter.\n2. Leave Include Answer off so the response is sources only.\n3. Set Max Results to bound how many sources come back, and pick Moby when full page text is needed.\n4. Review the returned results and order them by relevance to the topic.\n\n## Output\nA ranked list of sources with title, URL, and the extracted content of each page, ready to feed a summarizer or knowledge base.', + }, + { + name: 'monitor-topic-mentions', + description: + 'Search SERPdive for recent mentions of a brand, competitor, or topic and summarize what is new.', + content: + '# Monitor Topic Mentions\n\nTrack fresh mentions of a topic or competitor.\n\n## Steps\n1. Set the search query to the brand, competitor, or topic to monitor, adding a recency word such as the current month or "latest".\n2. Leave Include Answer off and keep Mako depth so the run stays fast and lean.\n3. Compare the returned URLs against mentions already recorded and keep only the new ones.\n4. Summarize each new mention in one line, keeping its source URL.\n\n## Output\nA list of new mentions, each with source URL and a one-line summary.', + }, + { + name: 'enrich-company-record', + description: + 'Search SERPdive for recent public information about a company and write a short enrichment summary.', + content: + '# Enrich Company Record\n\nAdd fresh public context to a company record.\n\n## Steps\n1. Set the search query to the company name plus what you need, for example recent funding, product launches, or leadership changes.\n2. Set Max Results to a small number so the summary stays focused.\n3. Read the extracted content of each result and pull out only facts that are stated in the sources.\n4. Write a short summary and keep the source URL for each fact.\n\n## Output\nA few sentences of enrichment with a source URL per claim, ready to write back to the record.', + }, + { + name: 'verify-a-claim', + description: + 'Check a specific claim against live sources with SERPdive and report whether the sources support it.', + content: + '# Verify a Claim\n\nCheck a claim against what live sources actually say.\n\n## Steps\n1. Set the search query to the claim itself, phrased neutrally rather than as a leading question.\n2. Turn on Include Answer and use Moby depth so the response carries full page context.\n3. Quote the passages in the extracted content that address the claim directly.\n4. State whether the sources support, contradict, or do not address the claim, and never assert beyond what the quotes show.\n\n## Output\nA verdict with the supporting quotes and their source URLs.', + }, + ], +} as const satisfies BlockMeta diff --git a/apps/sim/blocks/registry-maps.ts b/apps/sim/blocks/registry-maps.ts index d3bc8cea720..7a2ea9d763f 100644 --- a/apps/sim/blocks/registry-maps.ts +++ b/apps/sim/blocks/registry-maps.ts @@ -269,6 +269,7 @@ import { SecretsManagerBlock, SecretsManagerBlockMeta } from '@/blocks/blocks/se import { SendblueBlock, SendblueBlockMeta } from '@/blocks/blocks/sendblue' import { SendGridBlock, SendGridBlockMeta } from '@/blocks/blocks/sendgrid' import { SentryBlock, SentryBlockMeta } from '@/blocks/blocks/sentry' +import { SerpdiveBlock, SerpdiveBlockMeta } from '@/blocks/blocks/serpdive' import { SerperBlock, SerperBlockMeta } from '@/blocks/blocks/serper' import { ServiceNowBlock, ServiceNowBlockMeta } from '@/blocks/blocks/servicenow' import { SESBlock, SESBlockMeta } from '@/blocks/blocks/ses' @@ -584,6 +585,7 @@ export const BLOCK_REGISTRY: Record = { sendblue: SendblueBlock, sendgrid: SendGridBlock, sentry: SentryBlock, + serpdive: SerpdiveBlock, serper: SerperBlock, servicenow: ServiceNowBlock, ses: SESBlock, @@ -869,6 +871,7 @@ export const BLOCK_META_REGISTRY: Record = { sendblue: SendblueBlockMeta, sendgrid: SendGridBlockMeta, sentry: SentryBlockMeta, + serpdive: SerpdiveBlockMeta, serper: SerperBlockMeta, servicenow: ServiceNowBlockMeta, ses: SESBlockMeta, @@ -919,4 +922,4 @@ export const BLOCK_META_REGISTRY: Record = { zerobounce: ZeroBounceBlockMeta, zoom: ZoomBlockMeta, zoominfo: ZoomInfoBlockMeta, -} +} \ No newline at end of file diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index a10c0acf1f9..81b2717790f 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -833,6 +833,31 @@ export function SerperIcon(props: SVGProps) { ) } +export function SerpdiveIcon(props: SVGProps) { + return ( + + + + + + + ) +} + export function TailscaleIcon(props: SVGProps) { return ( diff --git a/apps/sim/lib/integrations/icon-mapping.ts b/apps/sim/lib/integrations/icon-mapping.ts index 1ab1b49b330..7b1aab7bdd4 100644 --- a/apps/sim/lib/integrations/icon-mapping.ts +++ b/apps/sim/lib/integrations/icon-mapping.ts @@ -192,6 +192,7 @@ import { SendblueIcon, SendgridIcon, SentryIcon, + SerpdiveIcon, SerperIcon, ServiceNowIcon, SftpIcon, @@ -432,6 +433,7 @@ export const blockTypeToIconMap: Record = { sendblue: SendblueIcon, sendgrid: SendgridIcon, sentry: SentryIcon, + serpdive: SerpdiveIcon, serper: SerperIcon, servicenow: ServiceNowIcon, ses: SESIcon, diff --git a/apps/sim/lib/integrations/integrations.json b/apps/sim/lib/integrations/integrations.json index de2f74c9522..fa40ece658a 100644 --- a/apps/sim/lib/integrations/integrations.json +++ b/apps/sim/lib/integrations/integrations.json @@ -1,5 +1,5 @@ { - "updatedAt": "2026-07-16", + "updatedAt": "2026-07-22", "integrations": [ { "type": "onepassword", @@ -16688,6 +16688,25 @@ "integrationType": "observability", "tags": ["error-tracking", "monitoring"] }, + { + "type": "serpdive", + "slug": "serpdive", + "name": "SERPdive", + "description": "Search the web with SERPdive", + "longDescription": "Integrate SERPdive into the workflow. Can search the web and return the extracted, answer-ready content of each source instead of links or snippets.", + "bgColor": "#0A0F13", + "iconName": "SerpdiveIcon", + "docsUrl": "https://docs.sim.ai/integrations/serpdive", + "operations": [], + "operationCount": 0, + "triggers": [], + "triggerCount": 0, + "authType": "api-key", + "category": "tools", + "integrationType": "search", + "tags": ["web-scraping", "enrichment"] + }, + { "type": "serper", "slug": "serper", diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 296a2bae42d..b428fefc255 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -3524,6 +3524,7 @@ import { updateIssueTool, updateProjectTool, } from '@/tools/sentry' +import { serpdiveSearchTool } from '@/tools/serpdive' import { serperSearchTool } from '@/tools/serper' import { servicenowAggregateTool, @@ -5688,6 +5689,7 @@ export const tools: Record = { github_repo_info_v2: githubRepoInfoV2Tool, github_latest_commit: githubLatestCommitTool, github_latest_commit_v2: githubLatestCommitV2Tool, + serpdive_search: serpdiveSearchTool, serper_search: serperSearchTool, similarweb_website_overview: similarwebWebsiteOverviewTool, similarweb_traffic_visits: similarwebTrafficVisitsTool, @@ -9070,4 +9072,4 @@ export const tools: Record = { uptimerobot_update_psp: uptimeRobotUpdatePspTool, uptimerobot_delete_psp: uptimeRobotDeletePspTool, uptimerobot_get_account: uptimeRobotGetAccountTool, -} +} \ No newline at end of file diff --git a/apps/sim/tools/serpdive/index.ts b/apps/sim/tools/serpdive/index.ts new file mode 100644 index 00000000000..37a81a072cd --- /dev/null +++ b/apps/sim/tools/serpdive/index.ts @@ -0,0 +1,3 @@ +import { searchTool } from '@/tools/serpdive/search' + +export const serpdiveSearchTool = searchTool diff --git a/apps/sim/tools/serpdive/search.ts b/apps/sim/tools/serpdive/search.ts new file mode 100644 index 00000000000..cff22dfb1f8 --- /dev/null +++ b/apps/sim/tools/serpdive/search.ts @@ -0,0 +1,122 @@ +import type { SerpdiveSearchParams, SerpdiveSearchResponse } from '@/tools/serpdive/types' +import { SERPDIVE_SEARCH_RESULT_OUTPUT_PROPERTIES } from '@/tools/serpdive/types' +import type { ToolConfig } from '@/tools/types' + +export const searchTool: ToolConfig = { + id: 'serpdive_search', + name: 'SERPdive Search', + description: + 'Search the web with SERPdive. Each result carries the extracted, answer-ready content of the page instead of a link or snippet, cleaned and sized for an LLM.', + version: '1.0.0', + + params: { + query: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: + 'The search query, phrased as a question or topic, in any language (e.g., "what changed in the latest Next.js release"). Maximum 300 characters.', + }, + model: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Retrieval depth: mako (1 credit) returns the fact-carrying sentences of each source, moby (1.5 credits) returns the full readable content of every page (e.g., "mako")', + }, + answer: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: + 'Also return a written answer built from the sources: concise on mako, detailed with citations on moby. No extra credits.', + }, + max_results: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: + 'Hard cap on delivered results, keeping the best-ranked ones (1-10, e.g., 5). Omit to get everything relevant.', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'SERPdive API Key', + }, + }, + + request: { + url: 'https://api.serpdive.com/v1/search', + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: Record = { + query: params.query, + } + + if (params.model) body.model = params.model + if (params.answer !== undefined) body.answer = params.answer + if (params.max_results) body.max_results = Number(params.max_results) + + return body + }, + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + return { + success: true, + output: { + query: data.query, + results: (data.results ?? []).map((result: any) => ({ + url: result.url, + title: result.title ?? null, + content: result.content, + date: result.date ?? null, + })), + ...(data.model && { model: data.model }), + ...(data.response_time_ms != null && { response_time_ms: data.response_time_ms }), + ...(data.answer && { answer: data.answer }), + ...(data.extra_info && { extra_info: data.extra_info }), + }, + } + }, + + outputs: { + query: { type: 'string', description: 'The search query that was executed' }, + results: { + type: 'array', + description: 'Delivered sources, best first, each carrying the extracted content of the page', + items: { + type: 'object', + properties: SERPDIVE_SEARCH_RESULT_OUTPUT_PROPERTIES, + }, + }, + answer: { + type: 'string', + description: 'Written answer built from the sources (if requested)', + optional: true, + }, + extra_info: { + type: 'object', + description: + 'Structured direct-answer block (weather, exchange rates, live scores...) when the query has one', + optional: true, + }, + model: { + type: 'string', + description: 'The retrieval model that answered: mako or moby', + optional: true, + }, + response_time_ms: { + type: 'number', + description: 'Time taken for the search request in milliseconds', + optional: true, + }, + }, +} diff --git a/apps/sim/tools/serpdive/types.ts b/apps/sim/tools/serpdive/types.ts new file mode 100644 index 00000000000..32418ba96a1 --- /dev/null +++ b/apps/sim/tools/serpdive/types.ts @@ -0,0 +1,49 @@ +import type { OutputProperty, ToolResponse } from '@/tools/types' + +/** + * Shared output property definitions for SERPdive API responses. + * Based on the SERPdive API reference: https://serpdive.com/openapi.json + */ + +/** + * Output definition for a delivered search result + */ +export const SERPDIVE_SEARCH_RESULT_OUTPUT_PROPERTIES = { + url: { type: 'string', description: 'Result URL, tracking parameters stripped' }, + title: { type: 'string', description: 'Page title', optional: true }, + content: { + type: 'string', + description: 'Extracted, answer-ready content of the page', + }, + date: { + type: 'string', + description: 'Publication date when known, ISO YYYY-MM-DD', + optional: true, + }, +} as const satisfies Record + +export interface SerpdiveSearchParams { + query: string + apiKey: string + model?: 'mako' | 'moby' + answer?: boolean + max_results?: number +} + +export interface SerpdiveSearchResult { + url: string + title: string | null + content: string + date: string | null +} + +export interface SerpdiveSearchResponse extends ToolResponse { + output: { + query: string + results: SerpdiveSearchResult[] + model?: string + response_time_ms?: number + answer?: string + extra_info?: Record + } +}