feat: add @agentic/google-custom-search package

pull/701/head
Travis Fischer 2025-03-25 22:22:50 +08:00
rodzic 583a2fc2a7
commit 834300ec52
14 zmienionych plików z 419 dodań i 56 usunięć

Wyświetl plik

@ -69,6 +69,7 @@
"tools/firecrawl",
"tools/hacker-news",
"tools/gravatar",
"tools/google-custom-search",
"tools/hunter",
"tools/jina",
"tools/leadmagic",

Wyświetl plik

@ -0,0 +1,38 @@
---
title: Google Custom Search
description: Google Custom Search for online trends, news, current events, real-time information, or research topics.
---
- package: `@agentic/google-custom-search`
- exports: `class GoogleCustomSearchClient`, `namespace googleCustomSearch`
- env vars: `GOOGLE_API_KEY`, `GOOGLE_CSE_ID`
- [source](https://github.com/transitive-bullshit/agentic/blob/main/packages/google-custom-search/src/google-custom-search-client.ts)
- [google custom search docs](https://developers.google.com/custom-search/v1/overview)
## Install
<CodeGroup>
```bash npm
npm install @agentic/google-custom-search
```
```bash yarn
yarn add @agentic/google-custom-search
```
```bash pnpm
pnpm add @agentic/google-custom-search
```
</CodeGroup>
## Usage
```ts
import { GoogleCustomSearchClient } from '@agentic/google-custom-search'
const googleCustomSearch = new GoogleCustomSearchClient()
const results = await googleCustomSearch.search('latest news about openai')
```
Make sure to set the `GOOGLE_API_KEY` and `GOOGLE_CSE_ID` environment variables or pass them to the constructor using the `apiKey` and `cseId` options.

Wyświetl plik

@ -0,0 +1,47 @@
{
"name": "@agentic/google-custom-search",
"version": "0.1.0",
"description": "Agentic client for the Google Custom Search API.",
"author": "Travis Fischer <travis@transitivebullsh.it>",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/transitive-bullshit/agentic.git",
"directory": "packages/google-custom-search"
},
"type": "module",
"source": "./src/index.ts",
"types": "./dist/index.d.ts",
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"default": "./dist/index.js"
}
},
"files": [
"dist"
],
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"clean": "del dist",
"test": "run-s test:*",
"test:lint": "eslint .",
"test:typecheck": "tsc --noEmit"
},
"dependencies": {
"@agentic/core": "workspace:*",
"@googleapis/customsearch": "catalog:"
},
"peerDependencies": {
"zod": "catalog:"
},
"devDependencies": {
"@agentic/tsconfig": "workspace:*"
},
"publishConfig": {
"access": "public"
}
}

Wyświetl plik

@ -0,0 +1,24 @@
<p align="center">
<a href="https://agentic.so">
<img alt="Agentic" src="https://raw.githubusercontent.com/transitive-bullshit/agentic/main/docs/media/agentic-header.jpg" width="308">
</a>
</p>
<p align="center">
<em>AI agent stdlib that works with any LLM and TypeScript AI SDK.</em>
</p>
<p align="center">
<a href="https://github.com/transitive-bullshit/agentic/actions/workflows/main.yml"><img alt="Build Status" src="https://github.com/transitive-bullshit/agentic/actions/workflows/main.yml/badge.svg" /></a>
<a href="https://www.npmjs.com/package/@agentic/stdlib"><img alt="NPM" src="https://img.shields.io/npm/v/@agentic/stdlib.svg" /></a>
<a href="https://github.com/transitive-bullshit/agentic/blob/main/license"><img alt="MIT License" src="https://img.shields.io/badge/license-MIT-blue" /></a>
<a href="https://prettier.io"><img alt="Prettier Code Formatting" src="https://img.shields.io/badge/code_style-prettier-brightgreen.svg" /></a>
</p>
# Agentic
**See the [github repo](https://github.com/transitive-bullshit/agentic) or [docs](https://agentic.so) for more info.**
## License
MIT © [Travis Fischer](https://x.com/transitive_bs)

Wyświetl plik

@ -0,0 +1,99 @@
import { aiFunction, AIFunctionsProvider, assert, getEnv } from '@agentic/core'
import { customsearch_v1 as GoogleSearchAPI } from '@googleapis/customsearch'
import { z } from 'zod'
import { paginate } from './paginate'
export namespace googleCustomSearch {
export const SearchParamsSchema = z.object({
query: z.string().min(1).max(2048).describe('Search query'),
maxResults: z.number().optional(),
safeSearch: z
.union([z.literal('active'), z.literal('off')])
.optional()
.describe('Search safety level. Defaults to "active".')
})
export type SearchParams = z.infer<typeof SearchParamsSchema>
}
/**
* Agentic client for the official Google Custom Search API.
*
* @see https://developers.google.com/custom-search/v1/overview
*/
export class GoogleCustomSearchClient extends AIFunctionsProvider {
protected readonly apiKey: string
readonly cseId: string
readonly client: GoogleSearchAPI.Customsearch
constructor({
apiKey = getEnv('GOOGLE_API_KEY'),
cseId = getEnv('GOOGLE_CSE_ID')
}: {
/** Google API key */
apiKey?: string
/** Google Custom Search Engine ID */
cseId?: string
} = {}) {
assert(
apiKey,
'GoogleCustomSearchClient missing required "apiKey" (defaults to "GOOGLE_API_KEY")'
)
assert(
cseId,
'GoogleCustomSearchClient missing required "cseId" (defaults to "GOOGLE_CSE_ID")'
)
super()
this.apiKey = apiKey
this.cseId = cseId
this.client = new GoogleSearchAPI.Customsearch({
auth: this.apiKey
})
}
/**
* Google Custom Search for online trends, news, current events, real-time information, or research topics.
*/
@aiFunction({
name: 'google_custom_search',
description: `Google Custom Search for online trends, news, current events, real-time information, or research topics.`,
inputSchema: googleCustomSearch.SearchParamsSchema
})
async search(
queryOrParams: string | googleCustomSearch.SearchParams
): Promise<any> {
const params =
typeof queryOrParams === 'string'
? { query: queryOrParams }
: queryOrParams
const results = await paginate({
size: params.maxResults ?? 10,
handler: async ({ cursor = 0, limit }) => {
const maxChunkSize = 10
const {
data: { items = [] }
} = await this.client.cse.list({
cx: this.cseId,
q: params.query,
start: cursor,
num: Math.min(limit, maxChunkSize),
safe: params.safeSearch ?? 'active'
})
return {
data: items,
nextCursor:
items.length < maxChunkSize ? undefined : cursor + items.length
}
}
})
return results
}
}

Wyświetl plik

@ -0,0 +1 @@
export * from './google-custom-search-client'

Wyświetl plik

@ -0,0 +1,34 @@
export interface PaginateInput<T, C> {
size: number
handler: (data: {
cursor?: C
limit: number
}) => Promise<{ data: T[]; nextCursor?: C }>
}
export async function paginate<T, C = number>(
input: PaginateInput<T, C>
): Promise<T[]> {
const acc: T[] = []
let cursor: C | undefined
while (acc.length < input.size) {
const { data, nextCursor } = await input.handler({
cursor,
limit: input.size - acc.length
})
acc.push(...data)
if (nextCursor === undefined || data.length === 0) {
break
}
cursor = nextCursor
}
if (acc.length > input.size) {
acc.length = input.size
}
return acc
}

Wyświetl plik

@ -0,0 +1,5 @@
{
"extends": "@agentic/tsconfig/base.json",
"include": ["src"],
"exclude": ["node_modules", "dist"]
}

Wyświetl plik

@ -3,15 +3,14 @@
*/
import {
aiFunction,
AIFunctionsProvider,
aiFunction,
assert,
getEnv,
pick,
sanitizeSearchParams
} from '@agentic/core'
import defaultKy, { type KyInstance } from 'ky'
import { notion } from './notion'
/**
@ -74,7 +73,7 @@ export class NotionClient extends AIFunctionsProvider {
})
async getUser(params: notion.GetUserParams): Promise<notion.GetUserResponse> {
return this.ky
.get(`/users/${params.user_id}`)
.get(`/users/${params['user_id']}`)
.json<notion.GetUserResponse>()
}
@ -126,7 +125,7 @@ export class NotionClient extends AIFunctionsProvider {
})
async getPage(params: notion.GetPageParams): Promise<notion.GetPageResponse> {
return this.ky
.get(`/pages/${params.page_id}`, {
.get(`/pages/${params['page_id']}`, {
searchParams: sanitizeSearchParams(pick(params, 'filter_properties'))
})
.json<notion.GetPageResponse>()
@ -144,7 +143,7 @@ export class NotionClient extends AIFunctionsProvider {
params: notion.UpdatePageParams
): Promise<notion.UpdatePageResponse> {
return this.ky
.patch(`/pages/${params.page_id}`, {
.patch(`/pages/${params['page_id']}`, {
json: pick(params, 'properties', 'archived')
})
.json<notion.UpdatePageResponse>()
@ -162,7 +161,7 @@ export class NotionClient extends AIFunctionsProvider {
params: notion.GetPagePropertyParams
): Promise<notion.GetPagePropertyResponse> {
return this.ky
.get(`/pages/${params.page_id}/properties/${params.property_id}`, {
.get(`/pages/${params['page_id']}/properties/${params['property_id']}`, {
searchParams: sanitizeSearchParams(
pick(params, 'start_cursor', 'page_size')
)
@ -182,7 +181,7 @@ export class NotionClient extends AIFunctionsProvider {
params: notion.GetBlockParams
): Promise<notion.GetBlockResponse> {
return this.ky
.get(`/blocks/${params.block_id}`)
.get(`/blocks/${params['block_id']}`)
.json<notion.GetBlockResponse>()
}
@ -198,7 +197,7 @@ export class NotionClient extends AIFunctionsProvider {
params: notion.DeleteBlockParams
): Promise<notion.DeleteBlockResponse> {
return this.ky
.delete(`/blocks/${params.block_id}`)
.delete(`/blocks/${params['block_id']}`)
.json<notion.DeleteBlockResponse>()
}
@ -214,7 +213,7 @@ export class NotionClient extends AIFunctionsProvider {
params: notion.UpdateBlockParams
): Promise<notion.UpdateBlockResponse> {
return this.ky
.patch(`/blocks/${params.block_id}`, {
.patch(`/blocks/${params['block_id']}`, {
json: pick(
params,
'paragraph',
@ -259,7 +258,7 @@ export class NotionClient extends AIFunctionsProvider {
params: notion.ListBlockChildrenParams
): Promise<notion.ListBlockChildrenResponse> {
return this.ky
.get(`/blocks/${params.block_id}/children`, {
.get(`/blocks/${params['block_id']}/children`, {
searchParams: sanitizeSearchParams(
pick(params, 'start_cursor', 'page_size')
)
@ -279,7 +278,7 @@ export class NotionClient extends AIFunctionsProvider {
params: notion.AppendBlockChildrenParams
): Promise<notion.AppendBlockChildrenResponse> {
return this.ky
.patch(`/blocks/${params.block_id}/children`, {
.patch(`/blocks/${params['block_id']}/children`, {
json: pick(params, 'children')
})
.json<notion.AppendBlockChildrenResponse>()
@ -297,7 +296,7 @@ export class NotionClient extends AIFunctionsProvider {
params: notion.GetDatabaseParams
): Promise<notion.GetDatabaseResponse> {
return this.ky
.get(`/databases/${params.database_id}`)
.get(`/databases/${params['database_id']}`)
.json<notion.GetDatabaseResponse>()
}
@ -313,7 +312,7 @@ export class NotionClient extends AIFunctionsProvider {
params: notion.UpdateDatabaseParams
): Promise<notion.UpdateDatabaseResponse> {
return this.ky
.patch(`/databases/${params.database_id}`, {
.patch(`/databases/${params['database_id']}`, {
json: pick(
params,
'title',
@ -340,7 +339,7 @@ export class NotionClient extends AIFunctionsProvider {
params: notion.QueryDatabaseParams
): Promise<notion.QueryDatabaseResponse> {
return this.ky
.post(`/databases/${params.database_id}/query`, {
.post(`/databases/${params['database_id']}/query`, {
searchParams: sanitizeSearchParams(pick(params, 'filter_properties')),
json: pick(
params,

Wyświetl plik

@ -45,6 +45,7 @@
"@agentic/exa": "workspace:*",
"@agentic/firecrawl": "workspace:*",
"@agentic/github": "workspace:*",
"@agentic/google-custom-search": "workspace:*",
"@agentic/gravatar": "workspace:*",
"@agentic/hacker-news": "workspace:*",
"@agentic/hunter": "workspace:*",

Wyświetl plik

@ -11,6 +11,7 @@ export * from '@agentic/e2b'
export * from '@agentic/exa'
export * from '@agentic/firecrawl'
export * from '@agentic/github'
export * from '@agentic/google-custom-search'
export * from '@agentic/gravatar'
export * from '@agentic/hacker-news'
export * from '@agentic/hunter'

Wyświetl plik

@ -21,6 +21,9 @@ catalogs:
'@fisch0920/eslint-config':
specifier: ^1.4.0
version: 1.4.0
'@googleapis/customsearch':
specifier: ^3.2.0
version: 3.2.0
'@langchain/core':
specifier: ^0.3.43
version: 0.3.43
@ -779,6 +782,22 @@ importers:
specifier: workspace:*
version: link:../tsconfig
packages/google-custom-search:
dependencies:
'@agentic/core':
specifier: workspace:*
version: link:../core
'@googleapis/customsearch':
specifier: 'catalog:'
version: 3.2.0(encoding@0.1.13)
zod:
specifier: 'catalog:'
version: 3.24.2
devDependencies:
'@agentic/tsconfig':
specifier: workspace:*
version: link:../tsconfig
packages/gravatar:
dependencies:
'@agentic/core':
@ -1271,6 +1290,9 @@ importers:
'@agentic/github':
specifier: workspace:*
version: link:../github
'@agentic/google-custom-search':
specifier: workspace:*
version: link:../google-custom-search
'@agentic/gravatar':
specifier: workspace:*
version: link:../gravatar
@ -2010,6 +2032,10 @@ packages:
'@genkit-ai/core@1.3.0':
resolution: {integrity: sha512-vwoHaiKkA8VBgGbp7a2oDzBFCicCFOihwCup48PvOY8kA9VYCws/qdUkFnrDh5NqzL7urXmjKE2wNf/59UmU9g==}
'@googleapis/customsearch@3.2.0':
resolution: {integrity: sha512-NTS4S3YepwYTqvk3aYKzm6g7oXjE7rQjswoXtw4leX7ykpyu2GWwflW6haaqctZHj6IJbJ1k0xgbgLTn1BByYQ==}
engines: {node: '>=12.0.0'}
'@grpc/grpc-js@1.13.1':
resolution: {integrity: sha512-z5nNuIs75S73ZULjPDe5QCNTiCv7FyBZXEVWOyAHtcebnuJf0g1SuueI3U1/z/KK39XyAQRUC+C9ZQJOtgHynA==}
engines: {node: '>=12.10.0'}
@ -3683,6 +3709,9 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
buffer-equal-constant-time@1.0.1:
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
buffer@6.0.3:
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
@ -4087,6 +4116,9 @@ packages:
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
ecdsa-sig-formatter@1.0.11:
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
@ -4668,10 +4700,18 @@ packages:
resolution: {integrity: sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==}
engines: {node: '>=18'}
google-auth-library@9.15.1:
resolution: {integrity: sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==}
engines: {node: '>=14'}
google-logging-utils@0.0.2:
resolution: {integrity: sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==}
engines: {node: '>=14'}
googleapis-common@7.2.0:
resolution: {integrity: sha512-/fhDZEJZvOV3X5jmD+fKxMqma5q2Q9nZNSF3kn1F18tpxmA86BcTxAGBQdM0N89Z3bEaIs+HVznSmFJEAmMTjA==}
engines: {node: '>=14.0.0'}
gopd@1.2.0:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'}
@ -4685,6 +4725,10 @@ packages:
graphemer@1.4.0:
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
gtoken@7.1.0:
resolution: {integrity: sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==}
engines: {node: '>=14.0.0'}
handlebars@4.7.8:
resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==}
engines: {node: '>=0.4.7'}
@ -5084,6 +5128,12 @@ packages:
resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
engines: {node: '>=4.0'}
jwa@2.0.0:
resolution: {integrity: sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==}
jws@4.0.0:
resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==}
keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
@ -6621,6 +6671,9 @@ packages:
url-join@4.0.1:
resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==}
url-template@2.0.8:
resolution: {integrity: sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==}
use-sync-external-store@1.4.0:
resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==}
peerDependencies:
@ -7664,6 +7717,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@googleapis/customsearch@3.2.0(encoding@0.1.13)':
dependencies:
googleapis-common: 7.2.0(encoding@0.1.13)
transitivePeerDependencies:
- encoding
- supports-color
'@grpc/grpc-js@1.13.1':
dependencies:
'@grpc/proto-loader': 0.7.13
@ -9879,6 +9939,8 @@ snapshots:
node-releases: 2.0.18
update-browserslist-db: 1.1.1(browserslist@4.24.2)
buffer-equal-constant-time@1.0.1: {}
buffer@6.0.3:
dependencies:
base64-js: 1.5.1
@ -10271,6 +10333,10 @@ snapshots:
eastasianwidth@0.2.0: {}
ecdsa-sig-formatter@1.0.11:
dependencies:
safe-buffer: 5.2.1
ee-first@1.1.1: {}
electron-to-chromium@1.5.53: {}
@ -11121,8 +11187,32 @@ snapshots:
slash: 5.1.0
unicorn-magic: 0.1.0
google-auth-library@9.15.1(encoding@0.1.13):
dependencies:
base64-js: 1.5.1
ecdsa-sig-formatter: 1.0.11
gaxios: 6.7.1(encoding@0.1.13)
gcp-metadata: 6.1.1(encoding@0.1.13)
gtoken: 7.1.0(encoding@0.1.13)
jws: 4.0.0
transitivePeerDependencies:
- encoding
- supports-color
google-logging-utils@0.0.2: {}
googleapis-common@7.2.0(encoding@0.1.13):
dependencies:
extend: 3.0.2
gaxios: 6.7.1(encoding@0.1.13)
google-auth-library: 9.15.1(encoding@0.1.13)
qs: 6.14.0
url-template: 2.0.8
uuid: 9.0.1
transitivePeerDependencies:
- encoding
- supports-color
gopd@1.2.0: {}
gpt-tokenizer@2.8.1: {}
@ -11131,6 +11221,14 @@ snapshots:
graphemer@1.4.0: {}
gtoken@7.1.0(encoding@0.1.13):
dependencies:
gaxios: 6.7.1(encoding@0.1.13)
jws: 4.0.0
transitivePeerDependencies:
- encoding
- supports-color
handlebars@4.7.8:
dependencies:
minimist: 1.2.8
@ -11481,6 +11579,17 @@ snapshots:
object.assign: 4.1.5
object.values: 1.2.0
jwa@2.0.0:
dependencies:
buffer-equal-constant-time: 1.0.1
ecdsa-sig-formatter: 1.0.11
safe-buffer: 5.2.1
jws@4.0.0:
dependencies:
jwa: 2.0.0
safe-buffer: 5.2.1
keyv@4.5.4:
dependencies:
json-buffer: 3.0.1
@ -13035,6 +13144,8 @@ snapshots:
url-join@4.0.1: {}
url-template@2.0.8: {}
use-sync-external-store@1.4.0(react@18.3.1):
dependencies:
react: 18.3.1

Wyświetl plik

@ -36,6 +36,7 @@ catalog:
fast-xml-parser: ^5.0.9
genkit: ^1.3.0
genkitx-openai: ^0.20.2
'@googleapis/customsearch': ^3.2.0
json-schema-to-zod: ^2.6.0
jsonrepair: ^3.12.0
jsrsasign: ^10.9.0

Wyświetl plik

@ -179,7 +179,7 @@ Full docs are available at [agentic.so](https://agentic.so).
## Tools
| Service / Tool | Package | Docs | Description |
| ------------------------------------------------------------------------ | --------------------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ------------------------------------------------------------------------------- | ------------------------------- | ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Apollo](https://docs.apollo.io) | `@agentic/apollo` | [docs](https://agentic.so/tools/apollo) | B2B person and company enrichment API. |
| [ArXiv](https://arxiv.org) | `@agentic/arxiv` | [docs](https://agentic.so/tools/arxiv) | Search for research articles. |
| [Bing](https://www.microsoft.com/en-us/bing/apis/bing-web-search-api) | `@agentic/bing` | [docs](https://agentic.so/tools/bing) | Bing web search. |
@ -192,6 +192,7 @@ Full docs are available at [agentic.so](https://agentic.so).
| [E2B](https://e2b.dev) | `@agentic/e2b` | [docs](https://agentic.so/tools/e2b) | Hosted Python code interpreter sandbox which is really useful for data analysis, flexible code execution, and advanced reasoning on-the-fly. |
| [Exa](https://docs.exa.ai) | `@agentic/exa` | [docs](https://agentic.so/tools/exa) | Web search tailored for LLMs. |
| [Firecrawl](https://www.firecrawl.dev) | `@agentic/firecrawl` | [docs](https://agentic.so/tools/firecrawl) | Website scraping and structured data extraction. |
| [Google Custom Search](https://developers.google.com/custom-search/v1/overview) | `@agentic/google-custom-search` | [docs](https://agentic.so/tools/google-custom-search) | Official Google Custom Search API. |
| [Gravatar](https://docs.gravatar.com/api/profiles/rest-api/) | `@agentic/gravatar` | [docs](https://agentic.so/tools/gravatar) | Gravatar profile API. |
| [HackerNews](https://github.com/HackerNews/API) | `@agentic/hacker-news` | [docs](https://agentic.so/tools/hacker-news) | Official HackerNews API. |
| [Hunter](https://hunter.io) | `@agentic/hunter` | [docs](https://agentic.so/tools/hunter) | Email finder, verifier, and enrichment. |