index.ts•5.2 kB
import { ToolDefinition, ToolResponse } from '../types.js';
import { Config } from '../../config/config.js';
import axios from 'axios';
import { HttpsProxyAgent } from 'https-proxy-agent';
interface BraveSearchResult {
title: string;
url: string;
description: string;
}
interface BraveSearchResponse {
web: {
results: BraveSearchResult[];
total: number;
};
}
// 搜索管理器类
class SearchManager {
private readonly baseUrl = 'https://api.search.brave.com/res/v1/web/search';
private lastRequestTime: number = 0;
private readonly minRequestInterval: number = 1000;
constructor(private readonly config: Config) {}
private validateApiKey(): void {
if (!this.config.brave.apiKey) {
throw new Error('请先在配置文件中设置 Brave Search API 密钥');
}
}
private getRequestOptions() {
const options: any = {
headers: {
'X-Subscription-Token': this.config.brave.apiKey,
'Accept': 'application/json',
}
};
// 如果配置了代理,使用代理
if (this.config.network.proxy) {
options.httpsAgent = new HttpsProxyAgent(this.config.network.proxy);
}
return options;
}
private async enforceRateLimit(): Promise<void> {
const now = Date.now();
if (now - this.lastRequestTime < this.minRequestInterval) {
await new Promise(resolve => setTimeout(resolve, this.minRequestInterval));
}
this.lastRequestTime = now;
}
// Brave 搜索处理器
async search(args: {
query: string;
count?: number;
type?: 'web' | 'news';
country?: string;
search_lang?: string;
}): Promise<ToolResponse> {
try {
if (!args.query?.trim()) {
return {
content: [{
type: 'text',
text: '搜索关键词不能为空'
}],
isError: true
};
}
this.validateApiKey();
await this.enforceRateLimit();
const response = await axios.get(this.baseUrl, {
...this.getRequestOptions(),
params: {
q: args.query,
count: args.count || 10,
type: args.type || 'web',
country: args.country,
search_lang: args.search_lang
}
});
if (response.status === 401) {
return {
content: [{
type: 'text',
text: 'API密钥无效或已过期,请检查配置文件中的 brave.apiKey'
}],
isError: true
};
}
if (!response.status) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = response.data as BraveSearchResponse;
if (!data.web?.results?.length) {
return {
content: [{
type: 'text',
text: '未找到任何结果'
}]
};
}
// 格式化搜索结果为文本
const resultText = data.web.results.map((result, index) => {
return `${index + 1}. ${result.title}\n ${result.url}\n ${result.description}\n`;
}).join('\n');
return {
content: [{
type: 'text',
text: `找到 ${data.web.total} 个结果:\n\n${resultText}`
}]
};
} catch (error: unknown) {
if (error instanceof Error) {
if (error.message.includes('429')) {
return {
content: [{
type: 'text',
text: '已超过API调用限制,请等待一段时间后重试'
}],
isError: true
};
}
return {
content: [{
type: 'text',
text: `搜索请求失败: ${error.message}`
}],
isError: true
};
}
return {
content: [{
type: 'text',
text: '搜索过程中发生未知错误'
}],
isError: true
};
}
}
}
// 创建搜索工具
export function createSearchTools(config: Config): ToolDefinition[] {
const manager = new SearchManager(config);
return [
{
name: 'brave_search',
description: '使用Brave搜索引擎搜索网页。当需要搜索网页内容时优先使用此工具,而不是使用浏览器。支持高级搜索语法,结果更准确且无广告。',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: '搜索关键词。支持高级搜索语法,如: "site:github.com typescript"'
},
count: {
type: 'number',
description: '返回结果数量,默认10,最大20'
},
type: {
type: 'string',
description: '搜索类型: web(网页搜索) 或 news(新闻搜索)',
enum: ['web', 'news'],
default: 'web'
},
country: {
type: 'string',
description: '国家代码,如: us, cn等。默认为全球'
},
search_lang: {
type: 'string',
description: '搜索语言,如: en, zh等。默认为所有语言'
}
},
required: ['query']
},
handler: args => manager.search(args)
}
];
}