kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
feat: add MCP, arxiv, and duck-duck-go tools
rodzic
885924e993
commit
c8e7eb5ca6
|
@ -0,0 +1,39 @@
|
||||||
|
---
|
||||||
|
title: ArXiv
|
||||||
|
description: Search for research articles.
|
||||||
|
---
|
||||||
|
|
||||||
|
- package: `@agentic/arxiv`
|
||||||
|
- exports: `class ArXivClient`, `namespace arxiv`
|
||||||
|
- [source](https://github.com/transitive-bullshit/agentic/blob/main/packages/arxiv/src/arxiv-client.ts)
|
||||||
|
- [arxiv api docs](https://info.arxiv.org/help/api/index.html)
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
<CodeGroup>
|
||||||
|
```bash npm
|
||||||
|
npm install @agentic/arxiv
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash yarn
|
||||||
|
yarn add @agentic/arxiv
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash pnpm
|
||||||
|
pnpm add @agentic/arxiv
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeGroup>
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { ArXivClient } from '@agentic/arxiv'
|
||||||
|
|
||||||
|
// No API is required to use the ArXiv API
|
||||||
|
const arxiv = new ArXivClient()
|
||||||
|
const results = await arxiv.search({
|
||||||
|
query: 'machine learning',
|
||||||
|
maxResults: 10
|
||||||
|
})
|
||||||
|
```
|
|
@ -0,0 +1,38 @@
|
||||||
|
---
|
||||||
|
title: DuckDuckGo
|
||||||
|
description: Search for research articles.
|
||||||
|
---
|
||||||
|
|
||||||
|
- package: `@agentic/duck-duck-go`
|
||||||
|
- exports: `class DuckDuckGoClient`, `namespace duckduckgo`
|
||||||
|
- [source](https://github.com/transitive-bullshit/agentic/blob/main/packages/duck-duck-go/src/duck-duck-go-client.ts)
|
||||||
|
- [Duck Duck Go api docs](https://api.duckduckgo.com)
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
<CodeGroup>
|
||||||
|
```bash npm
|
||||||
|
npm install @agentic/duck-duck-go
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash yarn
|
||||||
|
yarn add @agentic/duck-duck-go
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash pnpm
|
||||||
|
pnpm add @agentic/duck-duck-go
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeGroup>
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { DuckDuckGoClient } from '@agentic/duck-duck-go'
|
||||||
|
|
||||||
|
// No API is required to use the DuckDuckGo API
|
||||||
|
const duckDuckGo = new DuckDuckGoClient()
|
||||||
|
const results = await duckDuckGo.search({
|
||||||
|
query: 'latest news about AI'
|
||||||
|
})
|
||||||
|
```
|
|
@ -0,0 +1,85 @@
|
||||||
|
---
|
||||||
|
title: MCP Tools
|
||||||
|
description: Agentic adapter for accessing tools defined by Model Context Protocol (MCP) servers.
|
||||||
|
---
|
||||||
|
|
||||||
|
- package: `@agentic/mcp`
|
||||||
|
- exports: `class McpTools`, `createMcpTools`
|
||||||
|
- [source](https://github.com/transitive-bullshit/agentic/blob/main/packages/mcp/src/mcp-tools.ts)
|
||||||
|
- [MCP docs](https://modelcontextprotocol.io)
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
<CodeGroup>
|
||||||
|
```bash npm
|
||||||
|
npm install @agentic/mcp
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash yarn
|
||||||
|
yarn add @agentic/mcp
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash pnpm
|
||||||
|
pnpm add @agentic/mcp
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeGroup>
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import 'dotenv/config'
|
||||||
|
|
||||||
|
import { createAISDKTools } from '@agentic/ai-sdk'
|
||||||
|
import { createMcpTools } from '@agentic/mcp'
|
||||||
|
import { openai } from '@ai-sdk/openai'
|
||||||
|
import { generateText } from 'ai'
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
// Create an MCP tools provider, which will start a local MCP server process
|
||||||
|
// and use the stdio transport to communicate with it.
|
||||||
|
const mcpTools = await createMcpTools({
|
||||||
|
name: 'agentic-mcp-filesystem',
|
||||||
|
serverProcess: {
|
||||||
|
command: 'npx',
|
||||||
|
args: [
|
||||||
|
'-y',
|
||||||
|
// This example uses a built-in example MCP server from Anthropic, which
|
||||||
|
// provides a set of tools to access the local filesystem.
|
||||||
|
'@modelcontextprotocol/server-filesystem',
|
||||||
|
// Allow the MCP server to access the current working directory.
|
||||||
|
process.cwd()
|
||||||
|
// Feel free to add additional directories the tool should have access to.
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await generateText({
|
||||||
|
model: openai('gpt-4o-mini'),
|
||||||
|
tools: createAISDKTools(mcpTools),
|
||||||
|
toolChoice: 'required',
|
||||||
|
temperature: 0,
|
||||||
|
system: 'You are a helpful assistant. Be as concise as possible.',
|
||||||
|
prompt: 'What files are in the current directory?'
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(result.toolResults[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
await main()
|
||||||
|
```
|
||||||
|
|
||||||
|
### createMcpTools
|
||||||
|
|
||||||
|
`createMcpTools` creates a new `McpTools` instance by starting or connecting to an MCP server.
|
||||||
|
|
||||||
|
You must provide either an existing `transport`, an existing `serverUrl`, or a
|
||||||
|
`serverProcess` to spawn.
|
||||||
|
|
||||||
|
All tools within the `McpTools` instance will be namespaced under the given `name`.
|
||||||
|
|
||||||
|
### JSON Schema
|
||||||
|
|
||||||
|
Note that `McpTools` uses JSON Schemas for toll input parameters, whereas most built-in tools use Zod schemas. This is important because some AI frameworks don't support JSON Schemas.
|
||||||
|
|
||||||
|
Currently, Mastra, Dexter, and xsAI don't support JSON Schema input parameters, so they won't work with `McpTools`.
|
|
@ -0,0 +1,44 @@
|
||||||
|
import 'dotenv/config'
|
||||||
|
|
||||||
|
import { createAISDKTools } from '@agentic/ai-sdk'
|
||||||
|
import { createMcpTools } from '@agentic/mcp'
|
||||||
|
import { openai } from '@ai-sdk/openai'
|
||||||
|
import { generateText } from 'ai'
|
||||||
|
import { gracefulExit } from 'exit-hook'
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
// Create an MCP tools provider, which will start a local MCP server process
|
||||||
|
// and use the stdio transport to communicate with it.
|
||||||
|
const mcpTools = await createMcpTools({
|
||||||
|
name: 'agentic-mcp-filesystem',
|
||||||
|
serverProcess: {
|
||||||
|
command: 'npx',
|
||||||
|
args: [
|
||||||
|
'-y',
|
||||||
|
'@modelcontextprotocol/server-filesystem',
|
||||||
|
// Allow the MCP server to access the current working directory.
|
||||||
|
process.cwd()
|
||||||
|
// Feel free to add additional directories the tool should have access to.
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await generateText({
|
||||||
|
model: openai('gpt-4o-mini'),
|
||||||
|
tools: createAISDKTools(mcpTools),
|
||||||
|
toolChoice: 'required',
|
||||||
|
temperature: 0,
|
||||||
|
system: 'You are a helpful assistant. Be as concise as possible.',
|
||||||
|
prompt: 'What files are in the current directory?'
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(result.toolResults[0]?.result || JSON.stringify(result, null, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await main()
|
||||||
|
gracefulExit(0)
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
gracefulExit(1)
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
{
|
||||||
|
"name": "@agentic/arxiv",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Agentic SDK for Arxiv.",
|
||||||
|
"author": "Travis Fischer <travis@transitivebullsh.it>",
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/transitive-bullshit/agentic.git"
|
||||||
|
},
|
||||||
|
"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:*",
|
||||||
|
"fast-xml-parser": "catalog:",
|
||||||
|
"ky": "catalog:"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"zod": "catalog:"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@agentic/tsconfig": "workspace:*"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
|
@ -0,0 +1,243 @@
|
||||||
|
import {
|
||||||
|
aiFunction,
|
||||||
|
AIFunctionsProvider,
|
||||||
|
pruneEmpty,
|
||||||
|
sanitizeSearchParams
|
||||||
|
} from '@agentic/core'
|
||||||
|
import { XMLParser } from 'fast-xml-parser'
|
||||||
|
import defaultKy, { type KyInstance } from 'ky'
|
||||||
|
import { z } from 'zod'
|
||||||
|
|
||||||
|
import { castArray, getProp } from './utils'
|
||||||
|
|
||||||
|
export namespace arxiv {
|
||||||
|
export const API_BASE_URL = 'https://export.arxiv.org/api'
|
||||||
|
|
||||||
|
export const SortType = {
|
||||||
|
RELEVANCE: 'relevance',
|
||||||
|
LAST_UPDATED_DATE: 'lastUpdatedDate',
|
||||||
|
SUBMITTED_DATE: 'submittedDate'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export const SortOrder = {
|
||||||
|
ASCENDING: 'ascending',
|
||||||
|
DESCENDING: 'descending'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export const FilterType = {
|
||||||
|
ALL: 'all',
|
||||||
|
TITLE: 'title',
|
||||||
|
AUTHOR: 'author',
|
||||||
|
ABSTRACT: 'abstract',
|
||||||
|
COMMENT: 'comment',
|
||||||
|
JOURNAL_REFERENCE: 'journal_reference',
|
||||||
|
SUBJECT_CATEGORY: 'subject_category',
|
||||||
|
REPORT_NUMBER: 'report_number'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type ValueOf<T extends NonNullable<unknown>> = T[keyof T]
|
||||||
|
export const FilterTypeMapping: Record<ValueOf<typeof FilterType>, string> = {
|
||||||
|
all: 'all',
|
||||||
|
title: 'ti',
|
||||||
|
author: 'au',
|
||||||
|
abstract: 'abs',
|
||||||
|
comment: 'co',
|
||||||
|
journal_reference: 'jr',
|
||||||
|
subject_category: 'cat',
|
||||||
|
report_number: 'rn'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Separators = {
|
||||||
|
AND: '+AND+',
|
||||||
|
OR: '+OR+',
|
||||||
|
ANDNOT: '+ANDNOT+'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export interface ArXivResponse {
|
||||||
|
totalResults: number
|
||||||
|
startIndex: number
|
||||||
|
itemsPerPage: number
|
||||||
|
entries: {
|
||||||
|
id: string
|
||||||
|
title: string
|
||||||
|
summary: string
|
||||||
|
published: string
|
||||||
|
updated: string
|
||||||
|
authors: { name: string; affiliation: string[] }[]
|
||||||
|
doi: string
|
||||||
|
comment: string
|
||||||
|
journalReference: string
|
||||||
|
primaryCategory: string
|
||||||
|
categories: string[]
|
||||||
|
links: string[]
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const extractId = (value: string) =>
|
||||||
|
value
|
||||||
|
.replace('https://arxiv.org/abs/', '')
|
||||||
|
.replace('https://arxiv.org/pdf/', '')
|
||||||
|
.replace(/v\d$/, '')
|
||||||
|
|
||||||
|
const EntrySchema = z.object({
|
||||||
|
field: z.nativeEnum(FilterType).default(FilterType.ALL),
|
||||||
|
value: z.string().min(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const SearchParamsSchema = z
|
||||||
|
.object({
|
||||||
|
ids: z.array(z.string().min(1)).optional(),
|
||||||
|
searchQuery: z
|
||||||
|
.union([
|
||||||
|
z.string(),
|
||||||
|
z.object({
|
||||||
|
include: z
|
||||||
|
.array(EntrySchema)
|
||||||
|
.nonempty()
|
||||||
|
.describe('Filters to include results.'),
|
||||||
|
exclude: z
|
||||||
|
.array(EntrySchema)
|
||||||
|
.optional()
|
||||||
|
.describe('Filters to exclude results.')
|
||||||
|
})
|
||||||
|
])
|
||||||
|
.optional(),
|
||||||
|
start: z.number().int().min(0).default(0),
|
||||||
|
maxResults: z.number().int().min(1).max(100).default(5)
|
||||||
|
})
|
||||||
|
.describe('Sorting by date is not supported.')
|
||||||
|
export type SearchParams = z.infer<typeof SearchParamsSchema>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lightweight wrapper around ArXiv for academic / scholarly research articles.
|
||||||
|
*
|
||||||
|
* @see https://arxiv.org
|
||||||
|
*/
|
||||||
|
export class ArXivClient extends AIFunctionsProvider {
|
||||||
|
protected readonly ky: KyInstance
|
||||||
|
protected readonly apiBaseUrl: string
|
||||||
|
|
||||||
|
constructor({
|
||||||
|
apiBaseUrl = arxiv.API_BASE_URL,
|
||||||
|
ky = defaultKy
|
||||||
|
}: {
|
||||||
|
apiKey?: string
|
||||||
|
apiBaseUrl?: string
|
||||||
|
ky?: KyInstance
|
||||||
|
}) {
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.apiBaseUrl = apiBaseUrl
|
||||||
|
|
||||||
|
this.ky = ky.extend({
|
||||||
|
prefixUrl: this.apiBaseUrl
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches for research articles published on arXiv.
|
||||||
|
*/
|
||||||
|
@aiFunction({
|
||||||
|
name: 'arxiv_search',
|
||||||
|
description: 'Searches for research articles published on arXiv.',
|
||||||
|
inputSchema: arxiv.SearchParamsSchema
|
||||||
|
})
|
||||||
|
async search(queryOrOpts: string | arxiv.SearchParams) {
|
||||||
|
const opts =
|
||||||
|
typeof queryOrOpts === 'string'
|
||||||
|
? ({ searchQuery: queryOrOpts } as arxiv.SearchParams)
|
||||||
|
: queryOrOpts
|
||||||
|
|
||||||
|
if (!opts.ids?.length && !opts.searchQuery) {
|
||||||
|
throw new Error(
|
||||||
|
`The 'searchQuery' property must be non-empty if the 'ids' property is not provided.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchParams = sanitizeSearchParams({
|
||||||
|
start: opts.start,
|
||||||
|
max_results: opts.maxResults,
|
||||||
|
id_list: opts.ids?.map(arxiv.extractId),
|
||||||
|
search_query: opts.searchQuery
|
||||||
|
? typeof opts.searchQuery === 'string'
|
||||||
|
? opts.searchQuery
|
||||||
|
: [
|
||||||
|
opts.searchQuery.include
|
||||||
|
.map(
|
||||||
|
(tag) => `${arxiv.FilterTypeMapping[tag.field]}:${tag.value}`
|
||||||
|
)
|
||||||
|
.join(arxiv.Separators.AND),
|
||||||
|
(opts.searchQuery.exclude ?? [])
|
||||||
|
.map(
|
||||||
|
(tag) => `${arxiv.FilterTypeMapping[tag.field]}:${tag.value}`
|
||||||
|
)
|
||||||
|
.join(arxiv.Separators.ANDNOT)
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(arxiv.Separators.ANDNOT)
|
||||||
|
: undefined,
|
||||||
|
sortBy: arxiv.SortType.RELEVANCE,
|
||||||
|
sortOrder: arxiv.SortOrder.DESCENDING
|
||||||
|
})
|
||||||
|
|
||||||
|
const responseText = await this.ky.get('query', { searchParams }).text()
|
||||||
|
|
||||||
|
const parser = new XMLParser({
|
||||||
|
allowBooleanAttributes: true,
|
||||||
|
alwaysCreateTextNode: false,
|
||||||
|
attributeNamePrefix: '@_',
|
||||||
|
attributesGroupName: false,
|
||||||
|
cdataPropName: '#cdata',
|
||||||
|
ignoreAttributes: true,
|
||||||
|
numberParseOptions: { hex: false, leadingZeros: true },
|
||||||
|
parseAttributeValue: false,
|
||||||
|
parseTagValue: true,
|
||||||
|
preserveOrder: false,
|
||||||
|
removeNSPrefix: true,
|
||||||
|
textNodeName: '#text',
|
||||||
|
trimValues: true,
|
||||||
|
ignoreDeclaration: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const parsedData = parser.parse(responseText)
|
||||||
|
|
||||||
|
let entries: Record<string, any>[] = getProp(
|
||||||
|
parsedData,
|
||||||
|
['feed', 'entry'],
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
entries = castArray(entries)
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalResults: Math.max(
|
||||||
|
getProp(parsedData, ['feed', 'totalResults'], 0),
|
||||||
|
entries.length
|
||||||
|
),
|
||||||
|
startIndex: getProp(parsedData, ['feed', 'startIndex'], 0),
|
||||||
|
itemsPerPage: getProp(parsedData, ['feed', 'itemsPerPage'], 0),
|
||||||
|
entries: entries.map((entry) =>
|
||||||
|
pruneEmpty({
|
||||||
|
id: arxiv.extractId(entry.id),
|
||||||
|
url: entry.id,
|
||||||
|
title: entry.title,
|
||||||
|
summary: entry.summary,
|
||||||
|
published: entry.published,
|
||||||
|
updated: entry.updated,
|
||||||
|
authors: castArray(entry.author)
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((author: any) => ({
|
||||||
|
name: author.name,
|
||||||
|
affiliation: castArray(author.affiliation ?? [])
|
||||||
|
})),
|
||||||
|
doi: entry.doi,
|
||||||
|
comment: entry.comment,
|
||||||
|
journalReference: entry.journal_ref,
|
||||||
|
primaryCategory: entry.primary_category,
|
||||||
|
categories: castArray(entry.category).filter(Boolean),
|
||||||
|
links: castArray(entry.link).filter(Boolean)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './arxiv-client'
|
|
@ -0,0 +1,30 @@
|
||||||
|
export function hasProp<T>(
|
||||||
|
target: T | undefined,
|
||||||
|
key: keyof T
|
||||||
|
): key is keyof T {
|
||||||
|
return Boolean(target) && Object.prototype.hasOwnProperty.call(target, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getProp(
|
||||||
|
target: unknown,
|
||||||
|
paths: readonly (keyof any)[],
|
||||||
|
defaultValue: any = undefined
|
||||||
|
) {
|
||||||
|
let value: any = target
|
||||||
|
if (!value) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key of paths) {
|
||||||
|
if (!hasProp(value, key)) {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
value = value[key]
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
export function castArray<T>(arr: T) {
|
||||||
|
const result = Array.isArray(arr) ? arr : [arr]
|
||||||
|
return result as T extends unknown[] ? T : [T]
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"extends": "@agentic/tsconfig/base.json",
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
{
|
||||||
|
"name": "@agentic/duck-duck-go",
|
||||||
|
"version": "7.5.3",
|
||||||
|
"description": "Agentic SDK for DuckDuckGo.",
|
||||||
|
"author": "Travis Fischer <travis@transitivebullsh.it>",
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/transitive-bullshit/agentic.git"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"source": "./src/index.ts",
|
||||||
|
"module": "./dist/index.js",
|
||||||
|
"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:*",
|
||||||
|
"duck-duck-scrape": "^2.2.7",
|
||||||
|
"string-strip-html": "^13.4.12"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"zod": "catalog:"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@agentic/tsconfig": "workspace:*"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
|
@ -0,0 +1,78 @@
|
||||||
|
import { aiFunction, AIFunctionsProvider } from '@agentic/core'
|
||||||
|
import { SafeSearchType, search, type SearchOptions } from 'duck-duck-scrape'
|
||||||
|
import { z } from 'zod'
|
||||||
|
|
||||||
|
import { paginate } from './paginate'
|
||||||
|
|
||||||
|
export namespace duckduckgo {
|
||||||
|
export interface DuckDuckGoSearchToolOptions {
|
||||||
|
search?: SearchOptions
|
||||||
|
maxResults: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DuckDuckGoSearchToolRunOptions {
|
||||||
|
search?: SearchOptions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* DuckDuckGo search client.
|
||||||
|
*
|
||||||
|
* @see https://duckduckgo.com
|
||||||
|
*/
|
||||||
|
export class DuckDuckGoClient extends AIFunctionsProvider {
|
||||||
|
/**
|
||||||
|
* Searches the web using DuckDuckGo for a given query.
|
||||||
|
*/
|
||||||
|
@aiFunction({
|
||||||
|
name: 'duck_duck_go_search',
|
||||||
|
description: 'Searches the web using DuckDuckGo for a given query.',
|
||||||
|
inputSchema: z.object({
|
||||||
|
query: z.string({ description: 'Search query' }).min(1).max(128),
|
||||||
|
maxResults: z.number().min(1).max(100).optional()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
async search(
|
||||||
|
queryOrOptions:
|
||||||
|
| string
|
||||||
|
| { query: string; maxResults?: number; search?: SearchOptions }
|
||||||
|
) {
|
||||||
|
const options =
|
||||||
|
typeof queryOrOptions === 'string'
|
||||||
|
? { query: queryOrOptions }
|
||||||
|
: queryOrOptions
|
||||||
|
|
||||||
|
const results = await paginate({
|
||||||
|
size: options.maxResults ?? 10,
|
||||||
|
handler: async ({ cursor = 0 }) => {
|
||||||
|
const { results: data, noResults: done } = await search(
|
||||||
|
options.query,
|
||||||
|
{
|
||||||
|
safeSearch: SafeSearchType.MODERATE,
|
||||||
|
...options.search,
|
||||||
|
offset: cursor
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri_modifier: (rawUrl: string) => {
|
||||||
|
const url = new URL(rawUrl)
|
||||||
|
url.searchParams.delete('ss_mkt')
|
||||||
|
return url.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
nextCursor: done ? undefined : cursor + data.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const { stripHtml } = await import('string-strip-html')
|
||||||
|
|
||||||
|
return results.map((result) => ({
|
||||||
|
url: result.url,
|
||||||
|
title: stripHtml(result.title).result,
|
||||||
|
description: stripHtml(result.description).result
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './duck-duck-go-client'
|
|
@ -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
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"extends": "@agentic/tsconfig/base.json",
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"name": "@agentic/mcp",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Agentic SDK wrapping an MCP client.",
|
||||||
|
"author": "Travis Fischer <travis@transitivebullsh.it>",
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/transitive-bullshit/agentic.git"
|
||||||
|
},
|
||||||
|
"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:*",
|
||||||
|
"@modelcontextprotocol/sdk": "catalog:"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"zod": "catalog:"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@agentic/tsconfig": "workspace:*"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
|
@ -0,0 +1,2 @@
|
||||||
|
export * from './mcp-tools'
|
||||||
|
export type * from './types'
|
|
@ -0,0 +1,216 @@
|
||||||
|
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'
|
||||||
|
import type {
|
||||||
|
CallToolResult,
|
||||||
|
ListToolsResult
|
||||||
|
} from '@modelcontextprotocol/sdk/types.js'
|
||||||
|
import {
|
||||||
|
AIFunctionSet,
|
||||||
|
AIFunctionsProvider,
|
||||||
|
assert,
|
||||||
|
createAIFunction,
|
||||||
|
createJsonSchema
|
||||||
|
} from '@agentic/core'
|
||||||
|
import { Client as McpClient } from '@modelcontextprotocol/sdk/client/index.js'
|
||||||
|
import { type z } from 'zod'
|
||||||
|
|
||||||
|
import type { McpClientOptions, McpToolsFilter } from './types'
|
||||||
|
import { paginate } from './paginate'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Agentic tools provider wrapping an MCP client.
|
||||||
|
*
|
||||||
|
* You likely want to use `createMcpTools` to create an instance of `McpTools`
|
||||||
|
* which enables exposing MCP server tools to the agentic ecosystem.
|
||||||
|
*
|
||||||
|
* @see https://modelcontextprotocol.io
|
||||||
|
*/
|
||||||
|
export class McpTools extends AIFunctionsProvider {
|
||||||
|
readonly name: string
|
||||||
|
readonly client: McpClient
|
||||||
|
readonly rawToolResponses: boolean
|
||||||
|
|
||||||
|
protected _toolsMap: Map<string, ListToolsResult['tools'][number]> | undefined
|
||||||
|
protected readonly _toolsFilter: McpToolsFilter | undefined
|
||||||
|
|
||||||
|
protected constructor({
|
||||||
|
name,
|
||||||
|
client,
|
||||||
|
toolsFilter,
|
||||||
|
rawToolResponses = false
|
||||||
|
}: {
|
||||||
|
client: McpClient
|
||||||
|
} & McpClientOptions) {
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.name = name
|
||||||
|
this.client = client
|
||||||
|
this.rawToolResponses = rawToolResponses
|
||||||
|
|
||||||
|
this._toolsFilter = toolsFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
override get functions(): AIFunctionSet {
|
||||||
|
assert(this._functions)
|
||||||
|
return this._functions
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the MCPTools instance by fetching all available tools from the MCP client.
|
||||||
|
* This method must be called before using this class' tools.
|
||||||
|
* It is called automatically when using `MCPTools.from()`.
|
||||||
|
*/
|
||||||
|
protected async _init() {
|
||||||
|
const capabilties = this.client.getServerCapabilities()
|
||||||
|
const initPromises: Promise<any>[] = []
|
||||||
|
|
||||||
|
if (capabilties?.tools) {
|
||||||
|
initPromises.push(this._initTools())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: handle prompts, resources, etc.
|
||||||
|
await Promise.all(initPromises)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async _initTools() {
|
||||||
|
const tools = await paginate({
|
||||||
|
size: Infinity,
|
||||||
|
handler: async ({ cursor }: { cursor?: string }) => {
|
||||||
|
const { tools, nextCursor } = await this.client.listTools({ cursor })
|
||||||
|
return { data: tools, nextCursor } as const
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const enabledTools = this._toolsFilter
|
||||||
|
? tools.filter((tool) => this._toolsFilter!(tool.name))
|
||||||
|
: tools
|
||||||
|
|
||||||
|
this._toolsMap = new Map(enabledTools.map((tool) => [tool.name, tool]))
|
||||||
|
this._updateFunctions()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _updateFunctions() {
|
||||||
|
assert(this._toolsMap)
|
||||||
|
|
||||||
|
this._functions = new AIFunctionSet(
|
||||||
|
Array.from(this._toolsMap.entries()).map(([_name, tool]) => {
|
||||||
|
return createAIFunction(
|
||||||
|
{
|
||||||
|
name: `${this.name}_${tool.name}`,
|
||||||
|
description: tool.description,
|
||||||
|
inputSchema: createJsonSchema(tool.inputSchema),
|
||||||
|
strict: true
|
||||||
|
},
|
||||||
|
async (args) => {
|
||||||
|
const result = await this.client.callTool({
|
||||||
|
name: tool.name,
|
||||||
|
arguments: args
|
||||||
|
})
|
||||||
|
|
||||||
|
if (this.rawToolResponses) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
return processToolCallResult(result as CallToolResult)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async callTool(name: string, args: z.infer<z.ZodObject<any>>) {
|
||||||
|
const tool =
|
||||||
|
this._toolsMap?.get(name) ?? this._toolsMap?.get(`${this.name}_${name}`)
|
||||||
|
assert(tool, `Tool ${name} not found`)
|
||||||
|
|
||||||
|
const result = await this.client.callTool({ name, arguments: args })
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new McpTools instance from an existing, fully initialized
|
||||||
|
* MCP client.
|
||||||
|
*
|
||||||
|
* You probably want to use `createMcpTool` instead, which makes initializing
|
||||||
|
* the MCP client and connecting to its transport easier.
|
||||||
|
*
|
||||||
|
* All tools within the `McpTools` instance will be namespaced under the given
|
||||||
|
* `name`.
|
||||||
|
*/
|
||||||
|
static async fromMcpClient(params: { client: McpClient } & McpClientOptions) {
|
||||||
|
const mcpTools = new McpTools(params)
|
||||||
|
await mcpTools._init()
|
||||||
|
return mcpTools
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new McpTools instance by connecting to an MCP server. You must
|
||||||
|
* provide either an existing `transport`, an existing `serverUrl`, or a
|
||||||
|
* `serverProcess` to spawn.
|
||||||
|
*
|
||||||
|
* All tools within the `McpTools` instance will be namespaced under the given
|
||||||
|
* `name`.
|
||||||
|
*/
|
||||||
|
export async function createMcpTools(
|
||||||
|
params: McpClientOptions
|
||||||
|
): Promise<McpTools> {
|
||||||
|
const transport = await createMcpTransport(params)
|
||||||
|
const client = new McpClient(
|
||||||
|
{ name: params.name, version: params.version || '1.0.0' },
|
||||||
|
{ capabilities: {} }
|
||||||
|
)
|
||||||
|
await client.connect(transport)
|
||||||
|
|
||||||
|
return McpTools.fromMcpClient({ client, ...params })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new MCP transport from either an existing `transport`, an existing
|
||||||
|
* `serverUrl`, or a `serverProcess` to spawn.
|
||||||
|
*/
|
||||||
|
export async function createMcpTransport(
|
||||||
|
params: McpClientOptions
|
||||||
|
): Promise<Transport> {
|
||||||
|
if (params.transport) return params.transport
|
||||||
|
|
||||||
|
if (params.serverUrl) {
|
||||||
|
const { SSEClientTransport } = await import(
|
||||||
|
'@modelcontextprotocol/sdk/client/sse.js'
|
||||||
|
)
|
||||||
|
return new SSEClientTransport(new URL(params.serverUrl))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.serverProcess) {
|
||||||
|
const { StdioClientTransport } = await import(
|
||||||
|
'@modelcontextprotocol/sdk/client/stdio.js'
|
||||||
|
)
|
||||||
|
return new StdioClientTransport(params.serverProcess)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
'Unable to create a server connection with supplied options. Must provide transport, stdio, or sseUrl.'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function toText(c: CallToolResult['content']) {
|
||||||
|
return c.map((p) => p.text || '').join('')
|
||||||
|
}
|
||||||
|
|
||||||
|
function processToolCallResult(result: CallToolResult) {
|
||||||
|
if (result.isError) return { error: toText(result.content) }
|
||||||
|
|
||||||
|
if (result.content.every((c) => !!c.text)) {
|
||||||
|
const text = toText(result.content)
|
||||||
|
if (text.trim().startsWith('{') || text.trim().startsWith('[')) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(text)
|
||||||
|
} catch {
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.content.length === 1) return result.content[0]
|
||||||
|
return result
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
import type { StdioServerParameters } from '@modelcontextprotocol/sdk/client/stdio.js'
|
||||||
|
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'
|
||||||
|
|
||||||
|
export type McpToolsFilter = (toolName: string) => boolean
|
||||||
|
|
||||||
|
export interface McpClientOptions {
|
||||||
|
/**
|
||||||
|
* Provide a name for this client which will be its namespace for all tools and prompts.
|
||||||
|
*/
|
||||||
|
name: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a version number for this client (defaults to 1.0.0).
|
||||||
|
*/
|
||||||
|
version?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If you already have an MCP transport you'd like to use, pass it here to connect to the server.
|
||||||
|
*/
|
||||||
|
transport?: Transport
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a local server process using the stdio MCP transport.
|
||||||
|
*/
|
||||||
|
serverProcess?: StdioServerParameters
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to a remote server process using the SSE MCP transport.
|
||||||
|
*/
|
||||||
|
serverUrl?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return tool responses in raw MCP form instead of processing them for Genkit compatibility.
|
||||||
|
*/
|
||||||
|
rawToolResponses?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An optional filter function to determine which tools should be enabled.
|
||||||
|
*
|
||||||
|
* By default, all tools available on the MCP server will be enabled, but you
|
||||||
|
* can use this to filter a subset of those tools.
|
||||||
|
*/
|
||||||
|
toolsFilter?: McpToolsFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// export interface McpServerOptions {
|
||||||
|
// /** The name you want to give your server for MCP inspection. */
|
||||||
|
// name: string
|
||||||
|
//
|
||||||
|
// /** The version you want the server to advertise to clients. Defaults to 1.0.0. */
|
||||||
|
// version?: string
|
||||||
|
// }
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"extends": "@agentic/tsconfig/base.json",
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
Ładowanie…
Reference in New Issue