1- /**
2- * Fetches the MCP specification types from the official repository and
3- * post-processes them to add @description JSDoc tags for Zod schema generation.
4- *
5- * The spec uses multiline JSDoc comments where the first paragraph is the description.
6- * ts-to-zod requires explicit @description tags to generate .describe() calls in schemas.
7- * This script automatically converts the first paragraph to a @description tag.
8- */
91import { writeFileSync } from 'node:fs' ;
102import { dirname , join } from 'node:path' ;
113import { fileURLToPath } from 'node:url' ;
@@ -45,128 +37,6 @@ async function fetchSpecTypes(sha: string): Promise<string> {
4537 return await response . text ( ) ;
4638}
4739
48- /**
49- * Injects @description tags into JSDoc comments that don't already have them.
50- *
51- * Converts:
52- * ```
53- * /**
54- * * A progress token, used to associate progress notifications.
55- * *
56- * * @category Common Types
57- * *\/
58- * ```
59- *
60- * To:
61- * ```
62- * /**
63- * * A progress token, used to associate progress notifications.
64- * *
65- * * @description A progress token, used to associate progress notifications.
66- * * @category Common Types
67- * *\/
68- * ```
69- */
70- function injectDescriptionTags ( content : string ) : string {
71- // Match JSDoc comments: /** ... */
72- const jsdocPattern = / \/ \* \* [ \s \S ] * ?\* \/ / g;
73-
74- return content . replace ( jsdocPattern , ( comment ) => {
75- // Skip if already has @description
76- if ( / @ d e s c r i p t i o n \s / . test ( comment ) ) {
77- return comment ;
78- }
79-
80- // Skip single-line comments like /** @internal */
81- if ( ! comment . includes ( '\n' ) ) {
82- return comment ;
83- }
84-
85- // Extract the description: text after /** and before any @tag or empty line
86- // Split into lines for processing
87- const lines = comment . split ( '\n' ) ;
88-
89- // Find description lines (between /** and first @tag or blank line)
90- const descLines : string [ ] = [ ] ;
91- let foundDescContent = false ;
92- let firstTagIndex = - 1 ;
93-
94- for ( let i = 0 ; i < lines . length ; i ++ ) {
95- const line = lines [ i ] ;
96- // Clean the line: remove leading *, spaces
97- const cleaned = line . replace ( / ^ \s * \* \s ? / , '' ) . trim ( ) ;
98-
99- // Skip the opening /**
100- if ( line . includes ( '/**' ) ) {
101- // Check if there's content after /**
102- const afterOpening = line . replace ( / .* \/ \* \* \s * / , '' ) . trim ( ) ;
103- if ( afterOpening && ! afterOpening . startsWith ( '@' ) && afterOpening !== '*' ) {
104- descLines . push ( afterOpening ) ;
105- foundDescContent = true ;
106- }
107- continue ;
108- }
109-
110- // Skip the closing */
111- if ( cleaned === '/' || line . includes ( '*/' ) ) {
112- continue ;
113- }
114-
115- // Stop at first @tag
116- if ( cleaned . startsWith ( '@' ) ) {
117- firstTagIndex = i ;
118- break ;
119- }
120-
121- // Stop at empty line (paragraph break)
122- if ( cleaned === '' ) {
123- if ( foundDescContent ) {
124- firstTagIndex = i ;
125- break ;
126- }
127- continue ;
128- }
129-
130- // Accumulate description content
131- descLines . push ( cleaned ) ;
132- foundDescContent = true ;
133- }
134-
135- // No description found or no @tag to insert before
136- if ( descLines . length === 0 ) {
137- return comment ;
138- }
139-
140- // Join description into a single line (for @description tag)
141- const description = descLines . join ( ' ' ) . trim ( ) ;
142-
143- // Skip very short or empty descriptions
144- if ( description . length < 5 ) {
145- return comment ;
146- }
147-
148- // Find where to insert the @description tag
149- // Insert before the first @tag , or before the closing */
150- if ( firstTagIndex === - 1 ) {
151- // No @tag found, insert before */
152- const closingIndex = lines . findIndex ( l => l . includes ( '*/' ) ) ;
153- if ( closingIndex === - 1 ) return comment ;
154- firstTagIndex = closingIndex ;
155- }
156-
157- // Insert @description tag
158- // Get the indentation from the next line, ensuring proper " * " prefix
159- const lineMatch = lines [ firstTagIndex ] . match ( / ^ ( \s * \* ) / ) ;
160- const indent = lineMatch ? lineMatch [ 1 ] + ' ' : ' * ' ;
161- const descriptionLine = `${ indent } @description ${ description } ` ;
162-
163- // Insert the description line
164- lines . splice ( firstTagIndex , 0 , descriptionLine ) ;
165-
166- return lines . join ( '\n' ) ;
167- } ) ;
168- }
169-
17040async function main ( ) {
17141 try {
17242 // Check if SHA is provided as command line argument
@@ -183,11 +53,7 @@ async function main() {
18353
18454 console . log ( `Fetching spec.types.ts from commit: ${ latestSHA } ` ) ;
18555
186- let specContent = await fetchSpecTypes ( latestSHA ) ;
187-
188- // Inject @description tags for ts-to-zod schema generation
189- console . log ( 'Injecting @description tags for Zod schema generation...' ) ;
190- specContent = injectDescriptionTags ( specContent ) ;
56+ const specContent = await fetchSpecTypes ( latestSHA ) ;
19157
19258 // Read header template
19359 const headerTemplate = `/**
@@ -197,9 +63,6 @@ async function main() {
19763 * Pulled from: https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/main/schema/draft/schema.ts
19864 * Last updated from commit: {SHA}
19965 *
200- * Post-processed to add @description JSDoc tags for Zod schema generation.
201- * These tags are automatically extracted from the first paragraph of each JSDoc comment.
202- *
20366 * DO NOT EDIT THIS FILE MANUALLY. Changes will be overwritten by automated updates.
20467 * To update this file, run: npm run fetch:spec-types
20568 */` ;
0 commit comments