33import { Tool } from '@modelcontextprotocol/sdk/types.js' ;
44import { Metadata , McpRequestContext , asTextContentResult } from './types' ;
55import { getLogger } from './logger' ;
6+ import type { LocalDocsSearch } from './local-docs-search' ;
67
78export const metadata : Metadata = {
89 resource : 'all' ,
@@ -43,20 +44,49 @@ export const tool: Tool = {
4344const docsSearchURL =
4445 process . env [ 'DOCS_SEARCH_URL' ] || 'https://api.stainless.com/api/projects/brand.dev/docs/search' ;
4546
46- export const handler = async ( {
47- reqContext,
48- args,
49- } : {
50- reqContext : McpRequestContext ;
51- args : Record < string , unknown > | undefined ;
52- } ) => {
47+ let _localSearch : LocalDocsSearch | undefined ;
48+
49+ export function setLocalSearch ( search : LocalDocsSearch ) : void {
50+ _localSearch = search ;
51+ }
52+
53+ const SUPPORTED_LANGUAGES = new Set ( [ 'http' , 'typescript' , 'javascript' ] ) ;
54+
55+ async function searchLocal ( args : Record < string , unknown > ) : Promise < unknown > {
56+ if ( ! _localSearch ) {
57+ throw new Error ( 'Local search not initialized' ) ;
58+ }
59+
60+ const query = ( args [ 'query' ] as string ) ?? '' ;
61+ const language = ( args [ 'language' ] as string ) ?? 'typescript' ;
62+ const detail = ( args [ 'detail' ] as string ) ?? 'verbose' ;
63+
64+ if ( ! SUPPORTED_LANGUAGES . has ( language ) ) {
65+ throw new Error (
66+ `Local docs search only supports HTTP, TypeScript, and JavaScript. Got language="${ language } ". ` +
67+ `Use --docs-search-mode stainless-api for other languages, or set language to "http", "typescript", or "javascript".` ,
68+ ) ;
69+ }
70+
71+ return _localSearch . search ( {
72+ query,
73+ language,
74+ detail,
75+ maxResults : 10 ,
76+ } ) . results ;
77+ }
78+
79+ async function searchRemote (
80+ args : Record < string , unknown > ,
81+ stainlessApiKey : string | undefined ,
82+ ) : Promise < unknown > {
5383 const body = args as any ;
5484 const query = new URLSearchParams ( body ) . toString ( ) ;
5585
5686 const startTime = Date . now ( ) ;
5787 const result = await fetch ( `${ docsSearchURL } ?${ query } ` , {
5888 headers : {
59- ...( reqContext . stainlessApiKey && { Authorization : reqContext . stainlessApiKey } ) ,
89+ ...( stainlessApiKey && { Authorization : stainlessApiKey } ) ,
6090 } ,
6191 } ) ;
6292
@@ -75,7 +105,7 @@ export const handler = async ({
75105 'Got error response from docs search tool' ,
76106 ) ;
77107
78- if ( result . status === 404 && ! reqContext . stainlessApiKey ) {
108+ if ( result . status === 404 && ! stainlessApiKey ) {
79109 throw new Error (
80110 'Could not find docs for this project. You may need to provide a Stainless API key via the STAINLESS_API_KEY environment variable, the --stainless-api-key flag, or the x-stainless-api-key HTTP header.' ,
81111 ) ;
@@ -94,7 +124,23 @@ export const handler = async ({
94124 } ,
95125 'Got docs search result' ,
96126 ) ;
97- return asTextContentResult ( resultBody ) ;
127+ return resultBody ;
128+ }
129+
130+ export const handler = async ( {
131+ reqContext,
132+ args,
133+ } : {
134+ reqContext : McpRequestContext ;
135+ args : Record < string , unknown > | undefined ;
136+ } ) => {
137+ const body = args ?? { } ;
138+
139+ if ( _localSearch ) {
140+ return asTextContentResult ( await searchLocal ( body ) ) ;
141+ }
142+
143+ return asTextContentResult ( await searchRemote ( body , reqContext . stainlessApiKey ) ) ;
98144} ;
99145
100146export default { metadata, tool, handler } ;
0 commit comments