Skip to content

feat(linkup): add fetch operation to the Linkup block - #8547

Open
shauryajain21 wants to merge 2 commits into
simstudioai:stagingfrom
shauryajain21:add-linkup-integration
Open

shauryajain21 wants to merge 2 commits into
simstudioai:stagingfrom
shauryajain21:add-linkup-integration

Conversation

@shauryajain21

Copy link
Copy Markdown

Summary

Adds a Linkup Fetch tool (linkup_fetch, POST https://api.linkup.so/v1/fetch) that returns a webpage as clean markdown, and exposes it as a second operation on the existing Linkup block. The block now has an operation dropdown (Search / Fetch) with operation-conditioned sub-blocks, following the same structure as the Exa block.

  • tools/linkup/fetch.ts: params url (required), renderJs, includeRawHtml, extractImages; outputs markdown, rawHtml, images[{alt, url}], favicon. Uses the same hosted-key / BYOK config as linkup_search (LINKUP_API_KEY, provider linkup), priced per Linkup's pricing: $0.001/call, $0.005 with renderJs.
  • Linkup block: operation dropdown defaulting to linkup_search, fetch sub-blocks, tools.access + tools.config.tool, inputs/outputs, canvas sentences, and a fetch-page-markdown skill.
  • Backwards compatible: blocks saved before this change have no stored operation. Search fields are gated on operation !== 'linkup_fetch' and the tool selector falls back to linkup_search, so existing workflows keep rendering, serializing, and running as search. The default canvas sentence still covers those blocks.
  • Registered in tools/registry.ts; regenerated tool metadata, integration docs (integrations/linkup.mdx) and integrations.json; added Fetch rows to the Linkup tab in platform/costs.mdx.

Usage: add a Linkup block, choose Fetch, set URL to https://docs.linkup.so, then reference <linkup.markdown> downstream. Agents can also call linkup_fetch as a tool.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Other: ___________

Testing

  • tools/linkup/linkup.test.ts (new, mocked): request body (url trimming, option passthrough incl. explicit false), transformResponse mapping and defaults, hosted pricing, block routing (including blocks with no stored operation), and condition visibility for legacy vs. fetch.
  • bunx vitest run tools/linkup blocks/blocks.test.ts blocks/password-masking.test.ts blocks/registry-lookup.test.ts tools/metadata.test.ts tools/index.test.ts tools/model-input-selectors.test.ts tools/param-shape.test.ts lib/workflows/blocks lib/workflows/migrations: 22 files, 573 tests passing.
  • bun run type-check (apps/sim), biome check on changed files.
  • check:canvas-sentences, tool-metadata:check, docs:check, deployment-config:check, integration-catalog:check, check:byok-providers, check:tool-param-reachability, check:tool-request-boundary, check:tool-registry-boundary, check:test-patterns, check-block-registry.ts: all pass.
  • Live check against the Linkup API with a real key: linkup_search (sourcedAnswer) and linkup_fetch (with includeRawHtml / extractImages) both return the expected outputs through the tool configs.

Worth a look: the "not fetch" condition used for backwards compatibility with blocks that predate the operation dropdown.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Screenshots/Videos

@vercel

vercel Bot commented Oct 1, 2026

Copy link
Copy Markdown

@shauryajain21 is attempting to deploy a commit to the Sim Team on Vercel.

A member of the Team first needs to authorize it.

@waleedlatif1
waleedlatif1 marked this pull request as ready for review October 2, 2026 00:34
@greptile-apps

greptile-apps Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 4/5

[Medium risk] Adds a webpage fetch operation to the Linkup integration tool.

The PR should not merge until Fetch handles string-valued Render JavaScript inputs consistently and the repository testing and typing requirements are met.

Findings

  1. P1 Render pricing misses string switches ▶
  2. P2 Optional outputs can be null ▶
  3. P2 Legacy cards lose query placeholder ▶
  4. P2 New code uses any ▶
  5. P2 Tests stop before execution ▶

Summary

The PR adds Linkup Fetch as a second block operation and agent tool, with hosted-key pricing, output metadata, tests, and documentation.

  • Legacy blocks continue to route to Search, although an empty legacy card loses its query placeholder.
  • String-valued Render JavaScript inputs need consistent request and billing conversion.
  • Fetch output declarations and new test/type conventions need attention.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Linkup block] --> B{operation}
  B -->|missing or Search| C[linkup_search]
  B -->|Fetch| D[linkup_fetch]
  D --> E[POST api.linkup.so/v1/fetch]
  E --> F[markdown and optional page assets]
Loading

Reviews (1) · Last reviewed commit: "feat(linkup): add fetch operation to the..."

Comment thread apps/sim/tools/linkup/fetch.ts Outdated
// Linkup pricing (https://docs.linkup.so/pages/documentation/platform/pricing):
// Standard: $0.001/call
// Standard + renderJs: $0.005/call
const renderJs = params.renderJs === true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Render pricing misses string switches

A workflow variable or legacy switch can pass Render JavaScript as the string 'true'. The request forwards that string, but this check charges the rendered rate only for boolean true. If Linkup accepts the string as enabling rendering, the call is billed at $0.001 instead of $0.005; if it rejects the string, the requested fetch fails. Convert the value consistently before building the request and calculating its cost.

Comment on lines +104 to +108
rawHtml: {
type: 'string',
description: 'The raw HTML of the webpage, when includeRawHtml is enabled',
optional: true,
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Optional outputs can be null

When Linkup omits rawHtml or favicon, the transform returns null for those fields, but both are declared only as optional strings. Consumers of the output schema can therefore expect a string or a missing field and receive null instead. Declare both outputs nullable, or omit them when absent.

Comment on lines +30 to +31
'Search the web',
{ text: 'for', field: 'q' },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Legacy cards lose query placeholder

Saved blocks without an operation use this default sentence. When their query is empty, the new non-core clause disappears, leaving only “Search the web” where the card previously showed a Search Query placeholder. Keeping the query clause core would preserve that cue on existing cards.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread apps/sim/tools/linkup/fetch.ts Outdated
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const body: Record<string, any> = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 New code uses any

The new request body uses Record<string, any>, and the test helper repeats that cast. The repository’s TypeScript directive prohibits any and calls for precise types or unknown with guards. Please type these values to match their actual shapes before merging.

Context Used: CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread apps/sim/tools/linkup/linkup.test.ts Outdated
Comment on lines +78 to +89
describe('linkup block', () => {
const selectTool = (params: Record<string, unknown>) =>
LinkupBlock.tools.config?.tool?.(params as never)

it('routes blocks saved before the operation dropdown to search', () => {
expect(selectTool({ q: 'latest news' })).toBe('linkup_search')
})

it('routes each operation to its tool', () => {
expect(selectTool({ operation: 'linkup_search' })).toBe('linkup_search')
expect(selectTool({ operation: 'linkup_fetch' })).toBe('linkup_fetch')
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Tests stop before execution

These tests call the block’s tool selector directly, so they cannot catch failures between a saved Linkup block and an executed Fetch request, including input conversion or hosted-key handling. The repository’s testing directive discourages tests that restate declarations and favors checks at the real boundary. Add execution-boundary coverage before merging.

Context Used: CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@shauryajain21
shauryajain21 force-pushed the add-linkup-integration branch from 5a2f643 to 2c39728 Compare October 2, 2026 13:45
Render JavaScript and the other fetch switches can arrive as strings from block inputs. Normalize them once so the request sends booleans and hosted pricing bills the same rendering decision. Declare rawHtml/favicon as nullable, type the request and response bodies, and cover the string cases plus an executor-level fetch run in tests.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant