@@ -23,173 +23,176 @@ import {z} from 'zod';
2323 process . exit ( 0 ) ;
2424 }
2525
26- await startServer ( 's2' , readPackageVersion ( import . meta. url ) , ( server : McpServer ) => {
27- server . registerTool (
28- 'search_s2_icons' ,
29- {
30- title : 'Search S2 icons' ,
31- description :
32- 'Searches the S2 workflow icon set by one or more terms; returns matching icon names.' ,
33- inputSchema : { terms : z . union ( [ z . string ( ) , z . array ( z . string ( ) ) ] ) } ,
34- annotations : { readOnlyHint : true , openWorldHint : true }
35- } ,
36- async ( { terms} ) => {
37- const allNames = listIconNames ( ) ;
38- const nameSet = new Set ( allNames ) ;
39- const aliases = await loadIconAliases ( ) ;
40- const rawTerms = Array . isArray ( terms ) ? terms : [ terms ] ;
41- const normalized = Array . from (
42- new Set (
43- rawTerms
44- . map ( t =>
45- String ( t ?? '' )
46- . trim ( )
47- . toLowerCase ( )
48- )
49- . filter ( Boolean )
50- )
51- ) ;
52- if ( normalized . length === 0 ) {
53- throw new Error ( 'Provide at least one non-empty search term.' ) ;
54- }
55- // direct name matches
56- const results = new Set (
57- allNames . filter ( name => {
58- const nameLower = name . toLowerCase ( ) ;
59- return normalized . some ( term => nameLower . includes ( term ) ) ;
60- } )
61- ) ;
62- // alias matches
63- for ( const [ aliasKey , targets ] of Object . entries ( aliases ) ) {
64- if ( ! targets || targets . length === 0 ) {
65- continue ;
26+ await startServer ( 's2' , readPackageVersion ( import . meta. url ) , {
27+ additionalLibraries : [ 'react-aria' ] ,
28+ registerAdditionalTools : ( server : McpServer ) => {
29+ server . registerTool (
30+ 'search_s2_icons' ,
31+ {
32+ title : 'Search S2 icons' ,
33+ description :
34+ 'Searches the S2 workflow icon set by one or more terms; returns matching icon names.' ,
35+ inputSchema : { terms : z . union ( [ z . string ( ) , z . array ( z . string ( ) ) ] ) } ,
36+ annotations : { readOnlyHint : true , openWorldHint : true }
37+ } ,
38+ async ( { terms} ) => {
39+ const allNames = listIconNames ( ) ;
40+ const nameSet = new Set ( allNames ) ;
41+ const aliases = await loadIconAliases ( ) ;
42+ const rawTerms = Array . isArray ( terms ) ? terms : [ terms ] ;
43+ const normalized = Array . from (
44+ new Set (
45+ rawTerms
46+ . map ( t =>
47+ String ( t ?? '' )
48+ . trim ( )
49+ . toLowerCase ( )
50+ )
51+ . filter ( Boolean )
52+ )
53+ ) ;
54+ if ( normalized . length === 0 ) {
55+ throw new Error ( 'Provide at least one non-empty search term.' ) ;
6656 }
67- const aliasLower = aliasKey . toLowerCase ( ) ;
68- if ( normalized . some ( term => aliasLower . includes ( term ) || term . includes ( aliasLower ) ) ) {
69- for ( const t of targets ) {
70- const n = String ( t ) ;
71- if ( nameSet . has ( n ) ) {
72- results . add ( n ) ;
57+ // direct name matches
58+ const results = new Set (
59+ allNames . filter ( name => {
60+ const nameLower = name . toLowerCase ( ) ;
61+ return normalized . some ( term => nameLower . includes ( term ) ) ;
62+ } )
63+ ) ;
64+ // alias matches
65+ for ( const [ aliasKey , targets ] of Object . entries ( aliases ) ) {
66+ if ( ! targets || targets . length === 0 ) {
67+ continue ;
68+ }
69+ const aliasLower = aliasKey . toLowerCase ( ) ;
70+ if ( normalized . some ( term => aliasLower . includes ( term ) || term . includes ( aliasLower ) ) ) {
71+ for ( const t of targets ) {
72+ const n = String ( t ) ;
73+ if ( nameSet . has ( n ) ) {
74+ results . add ( n ) ;
75+ }
7376 }
7477 }
7578 }
79+ return {
80+ content : [
81+ {
82+ type : 'text' ,
83+ text : JSON . stringify (
84+ Array . from ( results ) . sort ( ( a , b ) => a . localeCompare ( b ) ) ,
85+ null ,
86+ 2
87+ )
88+ }
89+ ]
90+ } ;
7691 }
77- return {
78- content : [
79- {
80- type : 'text' ,
81- text : JSON . stringify (
82- Array . from ( results ) . sort ( ( a , b ) => a . localeCompare ( b ) ) ,
83- null ,
84- 2
85- )
86- }
87- ]
88- } ;
89- }
90- ) ;
92+ ) ;
9193
92- server . registerTool (
93- 'search_s2_illustrations' ,
94- {
95- title : 'Search S2 illustrations' ,
96- description :
97- 'Searches the S2 illustrations set by one or more terms; returns matching illustration names.' ,
98- inputSchema : { terms : z . union ( [ z . string ( ) , z . array ( z . string ( ) ) ] ) } ,
99- annotations : { readOnlyHint : true , openWorldHint : true }
100- } ,
101- async ( { terms} ) => {
102- const allNames = listIllustrationNames ( ) ;
103- const nameSet = new Set ( allNames ) ;
104- const aliases = await loadIllustrationAliases ( ) ;
105- const rawTerms = Array . isArray ( terms ) ? terms : [ terms ] ;
106- const normalized = Array . from (
107- new Set (
108- rawTerms
109- . map ( t =>
110- String ( t ?? '' )
111- . trim ( )
112- . toLowerCase ( )
113- )
114- . filter ( Boolean )
115- )
116- ) ;
117- if ( normalized . length === 0 ) {
118- throw new Error ( 'Provide at least one non-empty search term.' ) ;
119- }
120- // direct name matches
121- const results = new Set (
122- allNames . filter ( name => {
123- const nameLower = name . toLowerCase ( ) ;
124- return normalized . some ( term => nameLower . includes ( term ) ) ;
125- } )
126- ) ;
127- // alias matches
128- for ( const [ aliasKey , targets ] of Object . entries ( aliases ) ) {
129- if ( ! targets || targets . length === 0 ) {
130- continue ;
94+ server . registerTool (
95+ 'search_s2_illustrations' ,
96+ {
97+ title : 'Search S2 illustrations' ,
98+ description :
99+ 'Searches the S2 illustrations set by one or more terms; returns matching illustration names.' ,
100+ inputSchema : { terms : z . union ( [ z . string ( ) , z . array ( z . string ( ) ) ] ) } ,
101+ annotations : { readOnlyHint : true , openWorldHint : true }
102+ } ,
103+ async ( { terms} ) => {
104+ const allNames = listIllustrationNames ( ) ;
105+ const nameSet = new Set ( allNames ) ;
106+ const aliases = await loadIllustrationAliases ( ) ;
107+ const rawTerms = Array . isArray ( terms ) ? terms : [ terms ] ;
108+ const normalized = Array . from (
109+ new Set (
110+ rawTerms
111+ . map ( t =>
112+ String ( t ?? '' )
113+ . trim ( )
114+ . toLowerCase ( )
115+ )
116+ . filter ( Boolean )
117+ )
118+ ) ;
119+ if ( normalized . length === 0 ) {
120+ throw new Error ( 'Provide at least one non-empty search term.' ) ;
131121 }
132- const aliasLower = aliasKey . toLowerCase ( ) ;
133- if ( normalized . some ( term => aliasLower . includes ( term ) || term . includes ( aliasLower ) ) ) {
134- for ( const t of targets ) {
135- const n = String ( t ) ;
136- if ( nameSet . has ( n ) ) {
137- results . add ( n ) ;
122+ // direct name matches
123+ const results = new Set (
124+ allNames . filter ( name => {
125+ const nameLower = name . toLowerCase ( ) ;
126+ return normalized . some ( term => nameLower . includes ( term ) ) ;
127+ } )
128+ ) ;
129+ // alias matches
130+ for ( const [ aliasKey , targets ] of Object . entries ( aliases ) ) {
131+ if ( ! targets || targets . length === 0 ) {
132+ continue ;
133+ }
134+ const aliasLower = aliasKey . toLowerCase ( ) ;
135+ if ( normalized . some ( term => aliasLower . includes ( term ) || term . includes ( aliasLower ) ) ) {
136+ for ( const t of targets ) {
137+ const n = String ( t ) ;
138+ if ( nameSet . has ( n ) ) {
139+ results . add ( n ) ;
140+ }
138141 }
139142 }
140143 }
144+ return {
145+ content : [
146+ {
147+ type : 'text' ,
148+ text : JSON . stringify (
149+ Array . from ( results ) . sort ( ( a , b ) => a . localeCompare ( b ) ) ,
150+ null ,
151+ 2
152+ )
153+ }
154+ ]
155+ } ;
141156 }
142- return {
143- content : [
144- {
145- type : 'text' ,
146- text : JSON . stringify (
147- Array . from ( results ) . sort ( ( a , b ) => a . localeCompare ( b ) ) ,
148- null ,
149- 2
150- )
151- }
152- ]
153- } ;
154- }
155- ) ;
157+ ) ;
156158
157- server . registerTool (
158- 'get_style_macro_property_values' ,
159- {
160- title : 'Get style macro property values' ,
161- description :
162- 'Returns the allowed values for a given S2 style macro property (including expanded color/spacing value lists where applicable).' ,
163- inputSchema : { propertyName : z . string ( ) } ,
164- annotations : { readOnlyHint : true , openWorldHint : true }
165- } ,
166- async ( { propertyName} ) => {
167- const name = String ( propertyName ?? '' ) . trim ( ) ;
168- if ( ! name ) {
169- throw new Error ( 'Provide a non-empty propertyName.' ) ;
170- }
159+ server . registerTool (
160+ 'get_style_macro_property_values' ,
161+ {
162+ title : 'Get style macro property values' ,
163+ description :
164+ 'Returns the allowed values for a given S2 style macro property (including expanded color/spacing value lists where applicable).' ,
165+ inputSchema : { propertyName : z . string ( ) } ,
166+ annotations : { readOnlyHint : true , openWorldHint : true }
167+ } ,
168+ async ( { propertyName} ) => {
169+ const name = String ( propertyName ?? '' ) . trim ( ) ;
170+ if ( ! name ) {
171+ throw new Error ( 'Provide a non-empty propertyName.' ) ;
172+ }
171173
172- const all = loadStyleMacroPropertyValues ( ) ;
173- let def = all [ name ] ;
174- if ( ! def ) {
175- // fallback to case-insensitive lookup
176- const lower = name . toLowerCase ( ) ;
177- const matchKey = Object . keys ( all ) . find ( k => k . toLowerCase ( ) === lower ) ;
178- if ( matchKey ) {
179- def = all [ matchKey ] ;
174+ const all = loadStyleMacroPropertyValues ( ) ;
175+ let def = all [ name ] ;
176+ if ( ! def ) {
177+ // fallback to case-insensitive lookup
178+ const lower = name . toLowerCase ( ) ;
179+ const matchKey = Object . keys ( all ) . find ( k => k . toLowerCase ( ) === lower ) ;
180+ if ( matchKey ) {
181+ def = all [ matchKey ] ;
182+ }
180183 }
181- }
182184
183- if ( ! def ) {
184- const available = Object . keys ( all ) . sort ( ( a , b ) => a . localeCompare ( b ) ) ;
185- throw new Error (
186- `Unknown style macro property '${ name } '. Available properties: ${ available . join ( ', ' ) } `
187- ) ;
188- }
185+ if ( ! def ) {
186+ const available = Object . keys ( all ) . sort ( ( a , b ) => a . localeCompare ( b ) ) ;
187+ throw new Error (
188+ `Unknown style macro property '${ name } '. Available properties: ${ available . join ( ', ' ) } `
189+ ) ;
190+ }
189191
190- return { content : [ { type : 'text' , text : JSON . stringify ( def , null , 2 ) } ] } ;
191- }
192- ) ;
192+ return { content : [ { type : 'text' , text : JSON . stringify ( def , null , 2 ) } ] } ;
193+ }
194+ ) ;
195+ }
193196 } ) ;
194197 } catch ( err ) {
195198 console . error ( errorToString ( err ) ) ;
0 commit comments