# How the Search Tool Provider is Implemented in Craft Agents OSS

> Discover how the search tool provider is implemented in Craft Agents OSS. Explore its pluggable architecture, factory function, and resilient DuckDuckGo fallback with exponential backoff retries.

- Repository: [Craft Docs/craft-agents-oss](https://github.com/lukilabs/craft-agents-oss)
- Tags: how-to-guide
- Published: 2026-04-18

---

**The search tool provider in Craft Agents OSS uses a pluggable architecture with a generic `WebSearchProvider` interface, a factory function that creates the `web_search` AgentTool, and a resilient DuckDuckGo fallback implementation featuring multi-stage endpoints and exponential backoff retries.**

The **search tool provider** implementation in the `lukilabs/craft-agents-oss` repository demonstrates a clean separation of concerns between interface contracts, tool creation, and concrete provider logic. This architecture allows developers to swap search backends without modifying agent code, while built-in fallback mechanisms ensure reliability when primary services fail.

## The Provider Contract: WebSearchProvider Interface

Every search backend in the system implements the `WebSearchProvider` interface defined in [`/packages/pi-agent-server/src/tools/search/types.ts`](https://github.com/lukilabs/craft-agents-oss/blob/main//packages/pi-agent-server/src/tools/search/types.ts). This contract ensures uniform interaction across different search services.

The interface requires two properties:

- `name`: A string identifier for the provider instance
- `search()`: An async method accepting a query string and result count, returning a promise of `WebSearchResult[]`

```typescript
export interface WebSearchProvider {
  name: string;
  search(query: string, count: number): Promise<WebSearchResult[]>;
}

```

This abstraction allows the agent system to treat Google, DuckDuckGo, OpenAI, or ChatGPT search providers identically at the tool level.

## Factory Function: createSearchTool

The `createSearchTool()` function in [`/packages/pi-agent-server/src/tools/search/create-search-tool.ts`](https://github.com/lukilabs/craft-agents-oss/blob/main//packages/pi-agent-server/src/tools/search/create-search-tool.ts) serves as the bridge between raw search providers and the agent's tool ecosystem. It constructs an `AgentTool` named `web_search` that handles parameter validation, execution, and fallback logic.

The factory accepts:
- A primary `WebSearchProvider`
- An optional fallback provider (defaults to `DDGSearchProvider`)

The resulting tool's `execute()` method performs several operations:

1. Validates input parameters (ensuring `query` is a non-empty string and `count` is a positive number)
2. Invokes the primary provider's `search()` method
3. Formats successful results using `formatResults()` to convert `WebSearchResult[]` into agent-readable text blocks
4. Catches errors and attempts the fallback provider if configured
5. Returns structured content that the agent can process

```typescript
const tool = createSearchTool(googleProvider);
// tool.name === 'web_search'
// tool.execute() handles the full search lifecycle

```

## Concrete Implementation: DDGSearchProvider

The `DDGSearchProvider` class in [`/packages/pi-agent-server/src/tools/search/providers/ddg.ts`](https://github.com/lukilabs/craft-agents-oss/blob/main//packages/pi-agent-server/src/tools/search/providers/ddg.ts) demonstrates a production-ready implementation of the `WebSearchProvider` contract. It serves as the default fallback when primary providers fail and implements a resilient multi-stage search strategy.

### Multi-Endpoint Strategy

Rather than relying on a single API, the DuckDuckGo provider implements three distinct search strategies executed sequentially:

1. **Primary API**: Uses the `duck-duck-scrape` library for structured JSON results
2. **HTML Endpoint**: Scrapes `html.duckduckgo.com` when the API fails
3. **Lite Endpoint**: Falls back to `lite.duckduckgo.com` for minimal HTML results

Each endpoint targets different DuckDuckGo interfaces to maximize availability when rate limits or service changes occur.

### Retry and Error Handling

Every search stage wraps execution in a `withRetry()` helper that implements exponential backoff with jitter. The retry logic specifically targets transient failures:

- Network timeouts
- HTTP 429 (rate limit) responses
- Temporary service unavailability

The retry configuration uses aggressive backoff parameters suitable for real-time agent interactions while preventing infinite loops.

If all three endpoints exhaust their retries, the provider aggregates all encountered errors and throws a comprehensive failure message, allowing the `createSearchTool` factory to activate the next fallback in the chain.

## Usage Examples

Creating a search tool with Google as the primary provider and DuckDuckGo as the automatic fallback:

```typescript
import { createSearchTool } from './create-search-tool';
import { GoogleSearchProvider } from './providers/google';

const googleProvider = new GoogleSearchProvider({
  apiKey: process.env.GOOGLE_API_KEY!
});

export const webSearchTool = createSearchTool(googleProvider);

```

Using the default DuckDuckGo provider directly:

```typescript
import { createSearchTool } from './create-search-tool';
import { DDGSearchProvider } from './providers/ddg';

export const webSearchTool = createSearchTool(new DDGSearchProvider());

// Executing a search
const result = await webSearchTool.execute('toolCallId', {
  query: 'craft agents architecture',
  count: 3
});
console.log(result.content[0].text);

```

Testing a provider implementation in isolation:

```typescript
import { DDGSearchProvider } from './providers/ddg';

const provider = new DDGSearchProvider();
const results = await provider.search('open source agents', 5);
console.log(results);

```

## Summary

- The **search tool provider** architecture separates interface contracts from implementation details using the `WebSearchProvider` interface in [`/packages/pi-agent-server/src/tools/search/types.ts`](https://github.com/lukilabs/craft-agents-oss/blob/main//packages/pi-agent-server/src/tools/search/types.ts).

- The `createSearchTool()` factory in [`/packages/pi-agent-server/src/tools/search/create-search-tool.ts`](https://github.com/lukilabs/craft-agents-oss/blob/main//packages/pi-agent-server/src/tools/search/create-search-tool.ts) wraps providers into agent-ready tools with built-in fallback support and result formatting.

- The **DuckDuckGo provider** (`DDGSearchProvider`) implements a resilient three-stage search strategy with automatic retry logic, serving as the default fallback when primary providers fail.

- All providers adhere to a uniform contract, enabling seamless swapping of search backends without modifying agent logic.

## Frequently Asked Questions

### What is the WebSearchProvider interface?

The `WebSearchProvider` interface is a TypeScript contract defined in [`/packages/pi-agent-server/src/tools/search/types.ts`](https://github.com/lukilabs/craft-agents-oss/blob/main//packages/pi-agent-server/src/tools/search/types.ts) that requires every search provider to implement a `name` property and a `search(query, count)` method returning `Promise<WebSearchResult[]>`. This abstraction allows the agent system to interact with Google, DuckDuckGo, or any custom search backend identically.

### How does the search tool handle provider failures?

The `createSearchTool()` factory implements a fallback chain: if the primary provider throws an error during execution, the tool automatically attempts the fallback provider (defaulting to `DDGSearchProvider`). Each individual provider, particularly the DuckDuckGo implementation, also implements internal retry logic with exponential backoff to handle transient network errors before surfacing failures to the tool layer.

### Can I use a custom search provider instead of DuckDuckGo?

Yes. Any class implementing the `WebSearchProvider` interface can be passed to `createSearchTool()`. The repository includes example implementations for Google and OpenAI providers. To use a custom provider, instantiate your implementation and pass it as the first argument to `createSearchTool()`, optionally including a second fallback provider.

### What endpoints does the DuckDuckGo provider use?

The `DDGSearchProvider` in [`/packages/pi-agent-server/src/tools/search/providers/ddg.ts`](https://github.com/lukilabs/craft-agents-oss/blob/main//packages/pi-agent-server/src/tools/search/providers/ddg.ts) implements a three-tier fallback strategy: first the `duck-duck-scrape` library API, then the HTML endpoint at `html.duckduckgo.com`, and finally the Lite endpoint at `lite.duckduckgo.com`. Each stage is wrapped in retry logic with exponential backoff to maximize availability.