66
77import fs from 'node:fs' ;
88
9- import { Client } from '@modelcontextprotocol/sdk/client/index.js' ;
10- import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js' ;
119import type { Tool } from '@modelcontextprotocol/sdk/types.js' ;
1210
1311import { cliOptions } from '../build/src/cli.js' ;
1412import { ToolCategory , labels } from '../build/src/tools/categories.js' ;
13+ import { tools } from '../build/src/tools/tools.js' ;
1514
16- const MCP_SERVER_PATH = 'build/src/index.js' ;
1715const OUTPUT_PATH = './docs/tool-reference.md' ;
1816const README_PATH = './README.md' ;
1917
@@ -25,6 +23,33 @@ interface ToolWithAnnotations extends Tool {
2523 } ;
2624}
2725
26+ interface ZodCheck {
27+ kind : string ;
28+ }
29+
30+ interface ZodDef {
31+ typeName : string ;
32+ checks ?: ZodCheck [ ] ;
33+ values ?: string [ ] ;
34+ type ?: ZodSchema ;
35+ innerType ?: ZodSchema ;
36+ schema ?: ZodSchema ;
37+ defaultValue ?: ( ) => unknown ;
38+ }
39+
40+ interface ZodSchema {
41+ _def : ZodDef ;
42+ description ?: string ;
43+ }
44+
45+ interface TypeInfo {
46+ type : string ;
47+ enum ?: string [ ] ;
48+ items ?: TypeInfo ;
49+ description ?: string ;
50+ default ?: unknown ;
51+ }
52+
2853function escapeHtmlTags ( text : string ) : string {
2954 return text
3055 . replace ( / & (? ! [ a - z A - Z ] + ; ) / g, '&' )
@@ -162,34 +187,102 @@ function updateReadmeWithOptionsMarkdown(optionsMarkdown: string): void {
162187 console . log ( 'Updated README.md with options markdown' ) ;
163188}
164189
165- async function generateToolDocumentation ( ) : Promise < void > {
166- console . log ( 'Starting MCP server to query tool definitions...' ) ;
167-
168- // Create MCP client with stdio transport pointing to the built server
169- const transport = new StdioClientTransport ( {
170- command : 'node' ,
171- args : [ MCP_SERVER_PATH , '--channel' , 'canary' ] ,
172- } ) ;
173-
174- const client = new Client (
175- {
176- name : 'docs-generator' ,
177- version : '1.0.0' ,
178- } ,
179- {
180- capabilities : { } ,
181- } ,
182- ) ;
190+ // Helper to convert Zod schema to JSON schema-like object for docs
191+ function getZodTypeInfo ( schema : ZodSchema ) : TypeInfo {
192+ let description = schema . description ;
193+ let def = schema . _def ;
194+ let defaultValue : unknown ;
195+
196+ // Unwrap optional/default/effects
197+ while (
198+ def . typeName === 'ZodOptional' ||
199+ def . typeName === 'ZodDefault' ||
200+ def . typeName === 'ZodEffects'
201+ ) {
202+ if ( def . typeName === 'ZodDefault' && def . defaultValue ) {
203+ defaultValue = def . defaultValue ( ) ;
204+ }
205+ const next = def . innerType || def . schema ;
206+ if ( ! next ) break ;
207+ schema = next ;
208+ def = schema . _def ;
209+ if ( ! description && schema . description ) description = schema . description ;
210+ }
183211
212+ const result : TypeInfo = { type : 'unknown' } ;
213+ if ( description ) result . description = description ;
214+ if ( defaultValue !== undefined ) result . default = defaultValue ;
215+
216+ switch ( def . typeName ) {
217+ case 'ZodString' :
218+ result . type = 'string' ;
219+ break ;
220+ case 'ZodNumber' :
221+ result . type = def . checks ?. some ( ( c : ZodCheck ) => c . kind === 'int' )
222+ ? 'integer'
223+ : 'number' ;
224+ break ;
225+ case 'ZodBoolean' :
226+ result . type = 'boolean' ;
227+ break ;
228+ case 'ZodEnum' :
229+ result . type = 'string' ;
230+ result . enum = def . values ;
231+ break ;
232+ case 'ZodArray' :
233+ result . type = 'array' ;
234+ if ( def . type ) {
235+ result . items = getZodTypeInfo ( def . type ) ;
236+ }
237+ break ;
238+ default :
239+ result . type = 'unknown' ;
240+ }
241+ return result ;
242+ }
243+
244+ function isRequired ( schema : ZodSchema ) : boolean {
245+ let def = schema . _def ;
246+ while ( def . typeName === 'ZodEffects' ) {
247+ if ( ! def . schema ) break ;
248+ schema = def . schema ;
249+ def = schema . _def ;
250+ }
251+ return def . typeName !== 'ZodOptional' && def . typeName !== 'ZodDefault' ;
252+ }
253+
254+ async function generateToolDocumentation ( ) : Promise < void > {
184255 try {
185- // Connect to the server
186- await client . connect ( transport ) ;
187- console . log ( 'Connected to MCP server' ) ;
256+ console . log ( 'Generating tool documentation from definitions...' ) ;
257+
258+ // Convert ToolDefinitions to ToolWithAnnotations
259+ const toolsWithAnnotations : ToolWithAnnotations [ ] = tools . map ( tool => {
260+ const properties : Record < string , TypeInfo > = { } ;
261+ const required : string [ ] = [ ] ;
262+
263+ for ( const [ key , schema ] of Object . entries (
264+ tool . schema as unknown as Record < string , ZodSchema > ,
265+ ) ) {
266+ const info = getZodTypeInfo ( schema ) ;
267+ properties [ key ] = info ;
268+ if ( isRequired ( schema ) ) {
269+ required . push ( key ) ;
270+ }
271+ }
272+
273+ return {
274+ name : tool . name ,
275+ description : tool . description ,
276+ inputSchema : {
277+ type : 'object' ,
278+ properties,
279+ required,
280+ } ,
281+ annotations : tool . annotations ,
282+ } ;
283+ } ) ;
188284
189- // List all available tools
190- const { tools} = await client . listTools ( ) ;
191- const toolsWithAnnotations = tools as ToolWithAnnotations [ ] ;
192- console . log ( `Found ${ tools . length } tools` ) ;
285+ console . log ( `Found ${ toolsWithAnnotations . length } tools` ) ;
193286
194287 // Generate markdown documentation
195288 let markdown = `<!-- AUTO GENERATED DO NOT EDIT - run 'npm run docs' to update-->
@@ -274,8 +367,7 @@ async function generateToolDocumentation(): Promise<void> {
274367
275368 const propertyNames = Object . keys ( properties ) . sort ( ) ;
276369 for ( const propName of propertyNames ) {
277- // eslint-disable-next-line @typescript-eslint/no-explicit-any
278- const prop = properties [ propName ] as any ;
370+ const prop = properties [ propName ] as TypeInfo ;
279371 const isRequired = required . includes ( propName ) ;
280372 const requiredText = isRequired
281373 ? ' **(required)**'
@@ -322,8 +414,6 @@ async function generateToolDocumentation(): Promise<void> {
322414 // Generate and update configuration options
323415 const optionsMarkdown = generateConfigOptionsMarkdown ( ) ;
324416 updateReadmeWithOptionsMarkdown ( optionsMarkdown ) ;
325- // Clean up
326- await client . close ( ) ;
327417 process . exit ( 0 ) ;
328418 } catch ( error ) {
329419 console . error ( 'Error generating documentation:' , error ) ;
0 commit comments