kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
pull/715/head
rodzic
6a00a49de0
commit
d6252451f3
|
@ -18,6 +18,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@agentic/platform": "workspace:*",
|
"@agentic/platform": "workspace:*",
|
||||||
|
"@agentic/platform-core": "workspace:*",
|
||||||
"@agentic/serper": "^7.6.7",
|
"@agentic/serper": "^7.6.7",
|
||||||
"@hono/mcp": "^0.1.0",
|
"@hono/mcp": "^0.1.0",
|
||||||
"@modelcontextprotocol/sdk": "catalog:",
|
"@modelcontextprotocol/sdk": "catalog:",
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
|
|
||||||
export const envSchema = z.object({
|
export const envSchema = z.object({
|
||||||
SERPER_API_KEY: z.string().nonempty()
|
SERPER_API_KEY: z.string().nonempty(),
|
||||||
|
AGENTIC_PROXY_SECRET: z.string().nonempty()
|
||||||
})
|
})
|
||||||
|
|
||||||
export type Env = z.infer<typeof envSchema>
|
export type Env = z.infer<typeof envSchema>
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { timingSafeEqual } from 'node:crypto'
|
||||||
|
|
||||||
|
export function timingSafeCompare(a: string, b: string): boolean {
|
||||||
|
if (typeof a !== 'string' || typeof b !== 'string') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.length !== b.length) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return timingSafeEqual(Buffer.from(a), Buffer.from(b))
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { assert } from '@agentic/platform-core'
|
||||||
import { SerperClient } from '@agentic/serper'
|
import { SerperClient } from '@agentic/serper'
|
||||||
import { StreamableHTTPTransport } from '@hono/mcp'
|
import { StreamableHTTPTransport } from '@hono/mcp'
|
||||||
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
||||||
|
@ -5,6 +6,9 @@ import { Hono } from 'hono'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
|
|
||||||
import { type Env, parseEnv } from './env'
|
import { type Env, parseEnv } from './env'
|
||||||
|
import { timingSafeCompare } from './utils'
|
||||||
|
|
||||||
|
let serper: SerperClient
|
||||||
|
|
||||||
const app = new Hono()
|
const app = new Hono()
|
||||||
|
|
||||||
|
@ -13,50 +17,6 @@ const mcpServer = new McpServer({
|
||||||
version: '1.0.0'
|
version: '1.0.0'
|
||||||
})
|
})
|
||||||
|
|
||||||
let serper: SerperClient
|
|
||||||
|
|
||||||
mcpServer.registerTool(
|
|
||||||
'search',
|
|
||||||
{
|
|
||||||
description:
|
|
||||||
'Uses Google Search to return the most relevant web pages for a given query. Useful for finding up-to-date news and information about any topic.',
|
|
||||||
inputSchema: z.object({
|
|
||||||
query: z.string().describe('Search query'),
|
|
||||||
num: z
|
|
||||||
.number()
|
|
||||||
.int()
|
|
||||||
.default(5)
|
|
||||||
.optional()
|
|
||||||
.describe('Number of results to return'),
|
|
||||||
type: z
|
|
||||||
.enum(['search', 'images', 'videos', 'places', 'news', 'shopping'])
|
|
||||||
.default('search')
|
|
||||||
.optional()
|
|
||||||
.describe('Type of Google search to perform')
|
|
||||||
}).shape,
|
|
||||||
outputSchema: z.object({}).passthrough().shape
|
|
||||||
},
|
|
||||||
async (args, { _meta }) => {
|
|
||||||
// (_meta.agentic as any).agenticProxySecret
|
|
||||||
|
|
||||||
const result: any = await serper!.search({
|
|
||||||
q: args.query,
|
|
||||||
num: args.num,
|
|
||||||
type: args.type
|
|
||||||
})
|
|
||||||
|
|
||||||
delete result.topStories
|
|
||||||
delete result.peopleAlsoAsk
|
|
||||||
delete result.searchParameters
|
|
||||||
delete result.credits
|
|
||||||
|
|
||||||
return {
|
|
||||||
content: [],
|
|
||||||
structuredContent: result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
app.all('/mcp', async (c) => {
|
app.all('/mcp', async (c) => {
|
||||||
const transport = new StreamableHTTPTransport()
|
const transport = new StreamableHTTPTransport()
|
||||||
await mcpServer.connect(transport)
|
await mcpServer.connect(transport)
|
||||||
|
@ -73,6 +33,66 @@ export default {
|
||||||
|
|
||||||
if (!serper) {
|
if (!serper) {
|
||||||
serper = new SerperClient({ apiKey: parsedEnv.SERPER_API_KEY })
|
serper = new SerperClient({ apiKey: parsedEnv.SERPER_API_KEY })
|
||||||
|
|
||||||
|
mcpServer.registerTool(
|
||||||
|
'search',
|
||||||
|
{
|
||||||
|
description:
|
||||||
|
'Uses Google Search to return the most relevant web pages for a given query. Useful for finding up-to-date news and information about any topic.',
|
||||||
|
inputSchema: z.object({
|
||||||
|
query: z.string().describe('Search query'),
|
||||||
|
num: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.default(5)
|
||||||
|
.optional()
|
||||||
|
.describe('Number of results to return'),
|
||||||
|
type: z
|
||||||
|
.enum([
|
||||||
|
'search',
|
||||||
|
'images',
|
||||||
|
'videos',
|
||||||
|
'places',
|
||||||
|
'news',
|
||||||
|
'shopping'
|
||||||
|
])
|
||||||
|
.default('search')
|
||||||
|
.optional()
|
||||||
|
.describe('Type of Google search to perform')
|
||||||
|
}).shape,
|
||||||
|
outputSchema: z.object({}).passthrough().shape
|
||||||
|
},
|
||||||
|
async (args, { _meta }) => {
|
||||||
|
// Make sure the request is coming from Agentic
|
||||||
|
assert(
|
||||||
|
timingSafeCompare(
|
||||||
|
(_meta?.agentic as any)?.agenticProxySecret,
|
||||||
|
parsedEnv.AGENTIC_PROXY_SECRET
|
||||||
|
),
|
||||||
|
400,
|
||||||
|
'Invalid request'
|
||||||
|
)
|
||||||
|
|
||||||
|
const result: any = await serper!.search({
|
||||||
|
q: args.query,
|
||||||
|
num: args.num,
|
||||||
|
type: args.type
|
||||||
|
})
|
||||||
|
|
||||||
|
// Simplify search results to optimize for LLM usage
|
||||||
|
result.results = result.organic
|
||||||
|
delete result.organic
|
||||||
|
delete result.topStories
|
||||||
|
delete result.peopleAlsoAsk
|
||||||
|
delete result.searchParameters
|
||||||
|
delete result.credits
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: [],
|
||||||
|
structuredContent: result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return app.fetch(request, parsedEnv, ctx)
|
return app.fetch(request, parsedEnv, ctx)
|
||||||
|
|
|
@ -675,6 +675,9 @@ importers:
|
||||||
'@agentic/platform':
|
'@agentic/platform':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../packages/platform
|
version: link:../../packages/platform
|
||||||
|
'@agentic/platform-core':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../packages/core
|
||||||
'@agentic/serper':
|
'@agentic/serper':
|
||||||
specifier: ^7.6.7
|
specifier: ^7.6.7
|
||||||
version: 7.6.7(zod@3.25.67)
|
version: 7.6.7(zod@3.25.67)
|
||||||
|
|
Ładowanie…
Reference in New Issue