Skip to content

Commit 5a2f643

Browse files
feat(linkup): add fetch operation to the Linkup block
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 90e3b91 commit 5a2f643

12 files changed

Lines changed: 407 additions & 15 deletions

File tree

‎apps/docs/content/docs/integrations/linkup.mdx‎

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ To implement Linkup in your agent, simply add the tool to your agent's configura
2626

2727
## Usage Instructions
2828

29-
Integrate Linkup into the workflow. Can search the web.
29+
Integrate Linkup into the workflow. Can search the web and fetch the contents of a webpage as markdown.
3030

3131

3232

@@ -59,4 +59,29 @@ Search the web for information using Linkup
5959
| `answer` | string | The sourced answer to the search query |
6060
| `sources` | array | Array of sources used to compile the answer, each containing name, url, and snippet |
6161

62+
### Linkup Fetch
63+
64+
Fetch a single webpage using Linkup and return its content as clean markdown, with optional raw HTML and extracted images.
65+
66+
#### Input
67+
68+
| Parameter | Type | Required | Description |
69+
| --------- | ---- | -------- | ----------- |
70+
| `url` | string | Yes | The URL of the webpage to fetch \(e.g., "https://docs.linkup.so") |
71+
| `apiKey` | string | Yes | Enter your Linkup API key |
72+
| `renderJs` | boolean | No | Render the JavaScript of the webpage before extracting its content |
73+
| `includeRawHtml` | boolean | No | Include the raw HTML of the webpage in the response |
74+
| `extractImages` | boolean | No | Extract the images from the webpage |
75+
76+
#### Output
77+
78+
| Parameter | Type | Description |
79+
| --------- | ---- | ----------- |
80+
| `markdown` | string | The clean markdown content of the webpage |
81+
| `rawHtml` | string | The raw HTML of the webpage, when includeRawHtml is enabled |
82+
| `images` | array | Images extracted from the webpage, when extractImages is enabled |
83+
| ↳ `alt` | string | The alt text of the image |
84+
| ↳ `url` | string | The URL of the image |
85+
| `favicon` | string | The URL of the website favicon |
86+
6287

‎apps/docs/content/docs/platform/costs.mdx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ When workflows use tool blocks with Sim's hosted API keys, costs are charged per
110110
|-----------|------|
111111
| Standard search | ~$0.006 |
112112
| Deep search | ~$0.055 |
113+
| Fetch | $0.001 |
114+
| Fetch (with JavaScript rendering) | $0.005 |
113115
</Tab>
114116

115117
<Tab>

‎apps/sim/blocks/blocks/linkup.ts‎

Lines changed: 104 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
import { LinkupIcon } from '@/components/icons'
22
import { AuthMode, type BlockConfig, type BlockMeta, IntegrationType } from '@/blocks/types'
3-
import type { LinkupSearchToolResponse } from '@/tools/linkup/types'
3+
import type { LinkupResponse } from '@/tools/linkup/types'
44

5-
export const LinkupBlock: BlockConfig<LinkupSearchToolResponse> = {
5+
/**
6+
* Blocks saved before the operation dropdown existed have no stored
7+
* `operation`, so search fields are gated on "not fetch" rather than on
8+
* `linkup_search` to keep those blocks rendering and serializing as before.
9+
*/
10+
const SEARCH_CONDITION = { field: 'operation', value: 'linkup_fetch', not: true }
11+
const FETCH_CONDITION = { field: 'operation', value: 'linkup_fetch' }
12+
13+
export const LinkupBlock: BlockConfig<LinkupResponse> = {
614
type: 'linkup',
715
name: 'Linkup',
816
description: 'Search the web with Linkup',
917
authMode: AuthMode.ApiKey,
10-
longDescription: 'Integrate Linkup into the workflow. Can search the web.',
18+
longDescription:
19+
'Integrate Linkup into the workflow. Can search the web and fetch the contents of a webpage as markdown.',
1120
docsLink: 'https://docs.sim.ai/integrations/linkup',
1221
category: 'tools',
1322
integrationType: IntegrationType.Search,
@@ -16,20 +25,42 @@ export const LinkupBlock: BlockConfig<LinkupSearchToolResponse> = {
1625
canvasPresentation: {
1726
defaultTitle: 'Linkup',
1827
sentences: {
28+
/** Painted for blocks saved before the operation dropdown existed. */
1929
default: [
20-
{ text: 'Search the web for', field: 'q', core: true },
30+
'Search the web',
31+
{ text: 'for', field: 'q' },
2132
{ text: ', within', field: 'includeDomains' },
2233
{ text: ', since', field: 'fromDate' },
2334
],
35+
byOperation: {
36+
linkup_search: [
37+
{ text: 'Search the web for', field: 'q', core: true },
38+
{ text: ', within', field: 'includeDomains' },
39+
{ text: ', since', field: 'fromDate' },
40+
],
41+
linkup_fetch: [{ text: 'Fetch', field: 'url', after: 'as markdown', core: true }],
42+
},
2443
},
2544
},
2645

2746
subBlocks: [
47+
{
48+
id: 'operation',
49+
title: 'Operation',
50+
type: 'dropdown',
51+
options: [
52+
{ label: 'Search', id: 'linkup_search' },
53+
{ label: 'Fetch', id: 'linkup_fetch' },
54+
],
55+
value: () => 'linkup_search',
56+
},
57+
// Search operation inputs
2858
{
2959
id: 'q',
3060
title: 'Search Query',
3161
type: 'long-input',
3262
placeholder: 'Enter your search query',
63+
condition: SEARCH_CONDITION,
3364
required: true,
3465
},
3566
{
@@ -41,6 +72,7 @@ export const LinkupBlock: BlockConfig<LinkupSearchToolResponse> = {
4172
{ label: 'Search', id: 'searchResults' },
4273
],
4374
value: () => 'sourcedAnswer',
75+
condition: SEARCH_CONDITION,
4476
},
4577
{
4678
id: 'depth',
@@ -51,30 +83,35 @@ export const LinkupBlock: BlockConfig<LinkupSearchToolResponse> = {
5183
{ label: 'Deep', id: 'deep' },
5284
],
5385
value: () => 'standard',
86+
condition: SEARCH_CONDITION,
5487
},
5588
{
5689
id: 'includeImages',
5790
title: 'Include Images',
5891
type: 'switch',
92+
condition: SEARCH_CONDITION,
5993
mode: 'advanced',
6094
},
6195
{
6296
id: 'includeInlineCitations',
6397
title: 'Include Inline Citations',
6498
type: 'switch',
99+
condition: SEARCH_CONDITION,
65100
mode: 'advanced',
66101
},
67102
{
68103
id: 'includeSources',
69104
title: 'Include Sources',
70105
type: 'switch',
106+
condition: SEARCH_CONDITION,
71107
mode: 'advanced',
72108
},
73109
{
74110
id: 'fromDate',
75111
title: 'From Date',
76112
type: 'short-input',
77113
placeholder: 'YYYY-MM-DD',
114+
condition: SEARCH_CONDITION,
78115
mode: 'advanced',
79116
wandConfig: {
80117
enabled: true,
@@ -95,6 +132,7 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
95132
title: 'To Date',
96133
type: 'short-input',
97134
placeholder: 'YYYY-MM-DD',
135+
condition: SEARCH_CONDITION,
98136
mode: 'advanced',
99137
wandConfig: {
100138
enabled: true,
@@ -115,13 +153,44 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
115153
title: 'Include Domains',
116154
type: 'long-input',
117155
placeholder: 'example.com, another.com (comma-separated)',
156+
condition: SEARCH_CONDITION,
118157
mode: 'advanced',
119158
},
120159
{
121160
id: 'excludeDomains',
122161
title: 'Exclude Domains',
123162
type: 'long-input',
124163
placeholder: 'example.com, another.com (comma-separated)',
164+
condition: SEARCH_CONDITION,
165+
mode: 'advanced',
166+
},
167+
// Fetch operation inputs
168+
{
169+
id: 'url',
170+
title: 'URL',
171+
type: 'short-input',
172+
placeholder: 'https://example.com',
173+
condition: FETCH_CONDITION,
174+
required: true,
175+
},
176+
{
177+
id: 'renderJs',
178+
title: 'Render JavaScript',
179+
type: 'switch',
180+
condition: FETCH_CONDITION,
181+
},
182+
{
183+
id: 'includeRawHtml',
184+
title: 'Include Raw HTML',
185+
type: 'switch',
186+
condition: FETCH_CONDITION,
187+
mode: 'advanced',
188+
},
189+
{
190+
id: 'extractImages',
191+
title: 'Extract Images',
192+
type: 'switch',
193+
condition: FETCH_CONDITION,
125194
mode: 'advanced',
126195
},
127196
{
@@ -136,12 +205,24 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
136205
],
137206

138207
tools: {
139-
access: ['linkup_search'],
208+
access: ['linkup_search', 'linkup_fetch'],
209+
config: {
210+
tool: (params) => {
211+
switch (params.operation) {
212+
case 'linkup_fetch':
213+
return 'linkup_fetch'
214+
default:
215+
return 'linkup_search'
216+
}
217+
},
218+
},
140219
},
141220

142221
inputs: {
143-
q: { type: 'string', description: 'Search query' },
222+
operation: { type: 'string', description: 'Operation to perform' },
144223
apiKey: { type: 'string', description: 'Linkup API key' },
224+
// Search operation
225+
q: { type: 'string', description: 'Search query' },
145226
depth: { type: 'string', description: 'Search depth level' },
146227
outputType: { type: 'string', description: 'Output format type' },
147228
includeImages: { type: 'boolean', description: 'Include images in results' },
@@ -157,11 +238,22 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
157238
type: 'string',
158239
description: 'Domains to exclude from search (comma-separated)',
159240
},
241+
// Fetch operation
242+
url: { type: 'string', description: 'URL of the webpage to fetch' },
243+
renderJs: { type: 'boolean', description: 'Render JavaScript before extracting content' },
244+
includeRawHtml: { type: 'boolean', description: 'Include the raw HTML in the response' },
245+
extractImages: { type: 'boolean', description: 'Extract images from the webpage' },
160246
},
161247

162248
outputs: {
249+
// Search output
163250
answer: { type: 'string', description: 'Generated answer' },
164251
sources: { type: 'json', description: 'Source references' },
252+
// Fetch output
253+
markdown: { type: 'string', description: 'Webpage content as markdown' },
254+
rawHtml: { type: 'string', description: 'Raw HTML of the webpage, when requested' },
255+
images: { type: 'json', description: '[{alt, url}] — images extracted from the webpage' },
256+
favicon: { type: 'string', description: 'URL of the website favicon' },
165257
},
166258
}
167259

@@ -258,5 +350,11 @@ export const LinkupBlockMeta = {
258350
content:
259351
'# 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.\n2. Use the Search output type and set the from date to the start of the window you want to cover.\n3. Optionally restrict to news or specific domains.\n4. Filter the results to genuinely new or relevant mentions and summarize each.\n\n## Output\nA list of new mentions with source URL, date, and a one-line summary of each.',
260352
},
353+
{
354+
name: 'fetch-page-markdown',
355+
description: 'Use Linkup Fetch to turn a webpage into clean markdown for downstream steps.',
356+
content:
357+
'# Fetch Page Markdown\n\nRead the contents of a specific webpage with Linkup.\n\n## Steps\n1. Use the Fetch operation with the full URL of the page.\n2. Enable Render JavaScript for single-page apps or pages that load their content client-side.\n3. Enable Extract Images when the page images matter, and Include Raw HTML only when a later step needs the original markup.\n4. Pass the markdown to a summarizer, extractor, or knowledge base.\n\n## Output\nThe page content as markdown, plus any extracted images and the raw HTML when requested.',
358+
},
261359
],
262360
} as const satisfies BlockMeta

‎apps/sim/tools/generated/tool-ids.ts‎

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

‎apps/sim/tools/generated/tool-metadata.ts‎

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

‎apps/sim/tools/generated/tool-outputs.ts‎

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)