@julong/mono-rele2-utils 1.20.0 → 1.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,32 @@
2
2
 
3
3
  Use this skill to invoke text utility functions via the mono-rele2-utils CLI. Handles class name merging, case conversion, and text truncation.
4
4
 
5
+ ## MCP Server
6
+
7
+ ### Configuration
8
+
9
+ Add to your MCP client config:
10
+
11
+ ```json
12
+ {
13
+ "mcpServers": {
14
+ "@julong/mono-rele2-utils": {
15
+ "command": "npx",
16
+ "args": ["-y", "@julong/mono-rele2-utils"],
17
+ "env": {
18
+ "API_KEY": "<value>"
19
+ }
20
+ }
21
+ }
22
+ }
23
+ ```
24
+
25
+ ### Run
26
+
27
+ ```sh
28
+ npx -y @julong/mono-rele2-utils
29
+ ```
30
+
5
31
  ## CLI
6
32
 
7
33
  ### Installation
@@ -9,7 +35,7 @@ Use this skill to invoke text utility functions via the mono-rele2-utils CLI. Ha
9
35
  ```sh
10
36
  npm install -g @julong/mono-rele2-utils
11
37
  # or
12
- npx @julong/mono-rele2-utils-cli <toolName> [...args]
38
+ npx mono-rele2-utils-cli <toolName> [...args]
13
39
  ```
14
40
 
15
41
  ### Usage
@@ -24,99 +50,217 @@ Run without arguments to list all available tools:
24
50
  mono-rele2-utils-cli
25
51
  ```
26
52
 
27
- ### Tools
53
+ ## Tools API Reference
28
54
 
29
- #### `cnTool`
55
+ ### `cn(classes)`
56
+
57
+ **Signature**
58
+
59
+ ```typescript
60
+ function cn(classes: string[]): string
61
+ ```
30
62
 
31
63
  Merges class names, filtering out falsy values.
32
64
 
65
+
66
+ **Parameters**
67
+
68
+ | Name | Type | Description |
69
+ |------|------|-------------|
70
+ | `classes` | `string[]` | List of class names to merge |
71
+
72
+
73
+ **Returns**
74
+
75
+ `string` — Merged class name string with falsy values filtered out
76
+
77
+
78
+ **CLI**
79
+
33
80
  ```sh
34
81
  mono-rele2-utils-cli cnTool <classes>
35
82
  ```
36
83
 
37
- | arg | type | description |
38
- |-----|------|-------------|
39
- | `classes` | JSON string (array) | List of class names to merge |
84
+
85
+
86
+ **Examples**
40
87
 
41
88
  ```sh
42
- mono-rele2-utils-cli cnTool '["btn","active","large"]' # btn active large
89
+ mono-rele2-utils-cli cnTool '["btn","active","large"]'
90
+ # → btn active large
43
91
  ```
44
92
 
45
- #### `caseConvertTool`
93
+ ### `case_convert(input, to)`
94
+
95
+ **Signature**
96
+
97
+ ```typescript
98
+ function case_convert(input: string, to: "upper" | "lower" | "capitalize" | "camel" | "snake" | "kebab"): string
99
+ ```
46
100
 
47
101
  Converts text to the specified case format.
48
102
 
103
+
104
+ **Parameters**
105
+
106
+ | Name | Type | Description |
107
+ |------|------|-------------|
108
+ | `input` | `string` | Text to convert |
109
+ | `to` | `"upper" | "lower" | "capitalize" | "camel" | "snake" | "kebab"` | Target case format |
110
+
111
+
112
+ **Returns**
113
+
114
+ `string` — Converted text in the target case format
115
+
116
+
117
+ **CLI**
118
+
49
119
  ```sh
50
120
  mono-rele2-utils-cli caseConvertTool <input> <to>
51
121
  ```
52
122
 
53
- | arg | type | description |
54
- |-----|------|-------------|
55
- | `input` | string | Text to convert |
56
- | `to` | `upper` \| `lower` \| `capitalize` \| `camel` \| `snake` \| `kebab` | Target case format |
57
123
 
124
+
125
+ **Examples**
126
+
127
+ ```sh
128
+ mono-rele2-utils-cli caseConvertTool "hello world" camel
129
+ # → helloWorld
130
+ ```
58
131
  ```sh
59
- mono-rele2-utils-cli caseConvertTool "hello world" camel # helloWorld
60
- mono-rele2-utils-cli caseConvertTool "helloWorld" snake # hello_world
61
- mono-rele2-utils-cli caseConvertTool "hello world" kebab # hello-world
132
+ mono-rele2-utils-cli caseConvertTool "helloWorld" snake
133
+ # hello_world
62
134
  ```
135
+ ```sh
136
+ mono-rele2-utils-cli caseConvertTool "hello world" kebab
137
+ # → hello-world
138
+ ```
139
+
140
+ ### `truncate(input, maxLength, suffix)`
63
141
 
64
- #### `truncateTool`
142
+ **Signature**
143
+
144
+ ```typescript
145
+ function truncate(input: string, maxLength: number, suffix?: string): string
146
+ ```
65
147
 
66
148
  Truncates text to a maximum length and appends a suffix.
67
149
 
150
+
151
+ **Parameters**
152
+
153
+ | Name | Type | Description |
154
+ |------|------|-------------|
155
+ | `input` | `string` | Text to truncate |
156
+ | `maxLength` | `number` | Maximum character length |
157
+ | `suffix` | `string` | Suffix to append when truncated (default: `...`) |
158
+
159
+
160
+ **Returns**
161
+
162
+ `string` — Truncated text with the configured suffix appended if truncated
163
+
164
+
165
+ **CLI**
166
+
68
167
  ```sh
69
168
  mono-rele2-utils-cli truncateTool <input> <maxLength> [suffix]
70
169
  ```
71
170
 
72
- | arg | type | description |
73
- |-----|------|-------------|
74
- | `input` | string | Text to truncate |
75
- | `maxLength` | number | Maximum character length |
76
- | `suffix` | string | Suffix to append when truncated (default: `...`) |
77
171
 
172
+
173
+ **Examples**
174
+
175
+ ```sh
176
+ mono-rele2-utils-cli truncateTool "hello world long text" 10
177
+ # → hello w...
178
+ ```
78
179
  ```sh
79
- mono-rele2-utils-cli truncateTool "hello world long text" 10 # hello w...
80
- mono-rele2-utils-cli truncateTool "hello world" 8 "…" # hello w…
180
+ mono-rele2-utils-cli truncateTool "hello world" 8 "…"
181
+ # hello w…
81
182
  ```
82
183
 
83
- #### `objectFlattenTool`
184
+ ### `object_flatten(json)`
185
+
186
+ **Signature**
187
+
188
+ ```typescript
189
+ function object_flatten(json: string \| JSON object): JsonObject
190
+ ```
84
191
 
85
192
  Flattens a nested JSON object of any depth into dot-notation key-value pairs. Accepts a JSON string and recursively flattens all levels. Arrays and primitives at any level are treated as leaf values..
86
193
 
194
+
195
+ **Parameters**
196
+
197
+ | Name | Type | Description |
198
+ |------|------|-------------|
199
+ | `json` | `string \| JSON object` | JSON string or parsed object to flatten (unlimited depth) |
200
+
201
+
202
+ **Returns**
203
+
204
+ `JsonObject` — Flattened object with dot-notation keys — e.g. { "a.b.c": value }
205
+
206
+
207
+ **CLI**
208
+
87
209
  ```sh
88
210
  mono-rele2-utils-cli objectFlattenTool <json>
89
211
  ```
90
212
 
91
- | arg | type | description |
92
- |-----|------|-------------|
93
- | `json` | string \| JSON object | JSON string or parsed object to flatten (unlimited depth) |
213
+
214
+
215
+ **Examples**
94
216
 
95
217
  ```sh
96
- mono-rele2-utils-cli objectFlattenTool '{"user":{"name":"Alice","address":{"city":"Seoul","zip":"12345"}},"active":true}' # {
218
+ mono-rele2-utils-cli objectFlattenTool '{"user":{"name":"Alice","address":{"city":"Seoul","zip":"12345"}},"active":true}'
219
+ # → {
97
220
  "user.name": "Alice",
98
221
  "user.address.city": "Seoul",
99
222
  "user.address.zip": "12345",
100
223
  "active": true
101
224
  }
102
- mono-rele2-utils-cli objectFlattenTool '{"a":{"b":{"c":{"d":{"e":"deep"}}}}}' # {
225
+ ```
226
+ ```sh
227
+ mono-rele2-utils-cli objectFlattenTool '{"a":{"b":{"c":{"d":{"e":"deep"}}}}}'
228
+ # → {
103
229
  "a.b.c.d.e": "deep"
104
230
  }
105
231
  ```
106
232
 
107
- #### `getUserTool`
233
+ ### `getUser(user)`
234
+
235
+ **Signature**
236
+
237
+ ```typescript
238
+ function getUser(user: RandomUser): string
239
+ ```
108
240
 
109
241
  RandomUser API 형식의 사용자 객체를 받아 이름과 거주 도시로 구성된 한글 문장을 반환합니다. JSON 문자열 또는 파싱된 객체를 입력받습니다..
110
242
 
243
+
244
+ **Parameters**
245
+
246
+ | Name | Type | Description |
247
+ |------|------|-------------|
248
+ | `user` | `RandomUser` | RandomUser 형식의 JSON 문자열 또는 객체 — name.first / name.last / location.city 필수 |
249
+
250
+
251
+ **Returns**
252
+
253
+ `string` — "이름은 {first} {last} 이고 현재 {city} 에 살고 있습니다." 형식의 한글 문장
254
+
255
+
256
+ **CLI**
257
+
111
258
  ```sh
112
259
  mono-rele2-utils-cli getUserTool <user>
113
260
  ```
114
261
 
115
- | arg | type | description |
116
- |-----|------|-------------|
117
- | `user` | RandomUser | RandomUser 형식의 JSON 문자열 또는 객체 — name.first / name.last / location.city 필수 |
118
262
 
119
- **`user`** type definition:
263
+ **`user`** type definition
120
264
 
121
265
  ```typescript
122
266
  interface RandomUser {
@@ -164,12 +308,70 @@ interface RandomUser {
164
308
  }
165
309
  ```
166
310
 
311
+
312
+ **Examples**
313
+
167
314
  ```sh
168
- mono-rele2-utils-cli getUserTool '{"name":{"title":"Mr","first":"Alice","last":"Kim"},"location":{"city":"Seoul"},"gender":"female","email":"alice@example.com","nat":"KR"}' # 이름은 Alice Kim 이고 현재 Seoul 에 살고 있습니다.
315
+ mono-rele2-utils-cli getUserTool '{"name":{"title":"Mr","first":"Alice","last":"Kim"},"location":{"city":"Seoul"},"gender":"female","email":"alice@example.com","nat":"KR"}'
316
+ # → 이름은 Alice Kim 이고 현재 Seoul 에 살고 있습니다.
169
317
  ```
170
318
 
171
- ## MCP Server
319
+ ### `env_get(keys)`
320
+
321
+ **Signature**
322
+
323
+ ```typescript
324
+ function env_get(keys: string[]): Record<string, string>
325
+ ```
326
+
327
+ MCP 클라이언트 config의 env 필드를 통해 주입된 환경 변수 값을 조회합니다. 조회 가능한 키는 패키지에서 제공하는 환경 변수로 한정됩니다. 현재 지원: API_KEY..
328
+
329
+
330
+ **Parameters**
331
+
332
+ | Name | Type | Description |
333
+ |------|------|-------------|
334
+ | `keys` | `string[]` | 조회할 환경 변수 이름 목록 (유효 키: API_KEY) |
335
+
336
+
337
+ **Returns**
338
+
339
+ `Record<string, string>` — key: 환경 변수 이름, value: 해당 값 (설정되지 않은 변수는 결과에서 제외)
340
+
341
+
342
+ **CLI**
172
343
 
173
344
  ```sh
174
- npx -y @julong/mono-rele2-utils
345
+ mono-rele2-utils-cli envGetTool <keys>
346
+ ```
347
+
348
+
349
+ **`keys`** type definition
350
+
351
+ ```typescript
352
+ // @julong/mono-rele2-utils 환경 변수 키 목록
353
+ // "API_KEY"
354
+
355
+ // MCP client config 예시:
356
+ // {
357
+ // "mcpServers": {
358
+ // "@julong/mono-rele2-utils": {
359
+ // "command": "npx",
360
+ // "args": ["-y", "@julong/mono-rele2-utils"],
361
+ // "env": {
362
+ // "API_KEY": "<value>"
363
+ // }
364
+ // }
365
+ // }
366
+ // }
367
+ ```
368
+
369
+
370
+ **Examples**
371
+
372
+ ```sh
373
+ mono-rele2-utils-cli envGetTool '["API_KEY"]'
374
+ # → {
375
+ "API_KEY": "<value>"
376
+ }
175
377
  ```
package/dist/cli.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
- function r(e){return{content:[{type:"text",text:e}]}}import{McpServer as C}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as U}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as h}from"zod";async function T(e){let[,,t,...n]=process.argv;(!t||!(t in e))&&(t&&console.error(`Unknown skill: "${t}"
3
- `),process.exit(t?1:0));let o=e[t],s=Object.keys(o.inputSchema),a={};for(let i=0;i<s.length;i++){let m=n[i];if(m!==void 0)try{a[s[i]]=JSON.parse(m)}catch{a[s[i]]=m}}let d=h.object(o.inputSchema).parse(a),f=await o.handler(d);for(let i of f.content)i.type==="text"&&console.log(i.text)}function b(e){e instanceof h.ZodError?console.error("Validation error:",e.issues.map(t=>`${t.path.join(".")}: ${t.message}`).join(", ")):console.error("Error:",e instanceof Error?e.message:String(e)),process.exit(1)}import{z as _}from"zod";import{z as l}from"zod";function x(...e){return e.filter(Boolean).join(" ")}var c={cnTool:{name:"cn",description:"Merges class names, filtering out falsy values",inputSchema:{classes:l.array(l.string()).describe("List of class names to merge")},handler:async({classes:e})=>r(x(...e)),examples:[{args:[`'["btn","active","large"]'`],result:"btn active large"}]},caseConvertTool:{name:"case_convert",description:"Converts text to the specified case format",inputSchema:{input:l.string().describe("Text to convert"),to:l.enum(["upper","lower","capitalize","camel","snake","kebab"]).describe("Target case format")},handler:async({input:e,to:t})=>r(A(e,t)),examples:[{args:['"hello world"',"camel"],result:"helloWorld"},{args:['"helloWorld"',"snake"],result:"hello_world"},{args:['"hello world"',"kebab"],result:"hello-world"}]},truncateTool:{name:"truncate",description:"Truncates text to a maximum length and appends a suffix",inputSchema:{input:l.string().describe("Text to truncate"),maxLength:l.number().int().positive().describe("Maximum character length"),suffix:l.string().default("...").describe("Suffix to append when truncated")},handler:async({input:e,maxLength:t,suffix:n})=>{let o=e.length<=t?e:e.slice(0,t-n.length)+n;return r(o)},examples:[{args:['"hello world long text"',"10"],result:"hello w..."},{args:['"hello world"',"8",'"\u2026"'],result:"hello w\u2026"}]}},$=c.cnTool,j=c.caseConvertTool,O=c.truncateTool;function A(e,t){switch(t){case"upper":return e.toUpperCase();case"lower":return e.toLowerCase();case"capitalize":return e.charAt(0).toUpperCase()+e.slice(1).toLowerCase();case"camel":return e.replace(/[-_\s]+(.)/g,(n,o)=>o.toUpperCase()).replace(/^(.)/,n=>n.toLowerCase());case"snake":return e.replace(/([A-Z])/g,"_$1").replace(/[-\s]+/g,"_").toLowerCase().replace(/^_/,"");case"kebab":return e.replace(/([A-Z])/g,"-$1").replace(/[_\s]+/g,"-").toLowerCase().replace(/^-/,"")}}import{z as p}from"zod";function z(e){let t=typeof e=="string"?JSON.parse(e):e,n={};function o(s,a){if(s===null||typeof s!="object"||Array.isArray(s)){n[a]=s;return}for(let[d,f]of Object.entries(s)){let i=a?`${a}.${d}`:d;o(f,i)}}return o(t,""),n}var g={objectFlattenTool:{name:"object_flatten",description:"Flattens a nested JSON object of any depth into dot-notation key-value pairs. Accepts a JSON string and recursively flattens all levels. Arrays and primitives at any level are treated as leaf values.",inputSchema:{json:p.union([p.string(),p.record(p.string(),p.any())]).describe("JSON string or parsed object to flatten (unlimited depth)")},handler:async({json:e})=>{try{let n=z(e);return r(JSON.stringify(n,null,2))}catch(t){return t instanceof SyntaxError?r("Error: invalid JSON string \u2014 unable to parse input"):r(`Error: failed to flatten object \u2014 ${t.message}`)}},examples:[{args:[`'{"user":{"name":"Alice","address":{"city":"Seoul","zip":"12345"}},"active":true}'`],result:JSON.stringify({"user.name":"Alice","user.address.city":"Seoul","user.address.zip":"12345",active:!0},null,2)},{args:[`'{"a":{"b":{"c":{"d":{"e":"deep"}}}}}'`],result:JSON.stringify({"a.b.c.d.e":"deep"},null,2)}],guidelines:["Arrays and primitives at any level are always treated as leaf values \u2014 they are never traversed.","The result is always a flat JSON object with dot-notation keys.","There is no depth limit \u2014 objects of any nesting level are fully flattened.","Input is accepted as a JSON string (MCP/CLI) and parsed internally."]}},k=g.objectFlattenTool;import{z as u}from"zod";function v(e){let t=typeof e=="string"?JSON.parse(e):e,{first:n,last:o}=t.name,s=t.location.city;return`\uC774\uB984\uC740 ${n} ${o} \uC774\uACE0 \uD604\uC7AC ${s} \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4.`}var y={getUserTool:{name:"getUser",description:"RandomUser API \uD615\uC2DD\uC758 \uC0AC\uC6A9\uC790 \uAC1D\uCCB4\uB97C \uBC1B\uC544 \uC774\uB984\uACFC \uAC70\uC8FC \uB3C4\uC2DC\uB85C \uAD6C\uC131\uB41C \uD55C\uAE00 \uBB38\uC7A5\uC744 \uBC18\uD658\uD569\uB2C8\uB2E4. JSON \uBB38\uC790\uC5F4 \uB610\uB294 \uD30C\uC2F1\uB41C \uAC1D\uCCB4\uB97C \uC785\uB825\uBC1B\uC2B5\uB2C8\uB2E4.",inputSchema:{user:u.union([u.string(),u.record(u.string(),u.any())]).describe("RandomUser \uD615\uC2DD\uC758 JSON \uBB38\uC790\uC5F4 \uB610\uB294 \uAC1D\uCCB4 \u2014 name.first / name.last / location.city \uD544\uC218")},typeLabels:{user:"RandomUser"},typeDefs:{user:["interface RandomUser {",' gender: "male" | "female";'," name: {"," title: string;"," first: string;"," last: string;"," };"," location: {"," street: {"," number: number;"," name: string;"," };"," city: string;"," state: string;"," country: string;"," postcode: string | number;"," coordinates: {"," latitude: string;"," longitude: string;"," };"," timezone: {"," offset: string;"," description: string;"," };"," };"," email: string;"," login: {"," uuid: string;"," username: string;"," };"," dob: {"," date: string;"," age: number;"," };"," phone: string;"," cell: string;"," picture: {"," large: string;"," medium: string;"," thumbnail: string;"," };"," nat: string;","}"].join(`
4
- `)},handler:async({user:e})=>{try{let n=v(e);return r(n)}catch(t){return t instanceof SyntaxError?r("Error: invalid JSON string \u2014 unable to parse input"):r(`Error: failed to process user data \u2014 ${t.message}
2
+ function r(e){return{content:[{type:"text",text:e}]}}import{McpServer as _}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as I}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as S}from"zod";async function $(e){let[,,n,...t]=process.argv;(!n||!(n in e))&&(n&&console.error(`Unknown skill: "${n}"
3
+ `),process.exit(n?1:0));let o=e[n],s=Object.keys(o.inputSchema),l={};for(let i=0;i<s.length;i++){let g=t[i];if(g!==void 0)try{l[s[i]]=JSON.parse(g)}catch{l[s[i]]=g}}let f=S.object(o.inputSchema).parse(l),m=await o.handler(f);for(let i of m.content)i.type==="text"&&console.log(i.text)}function j(e){e instanceof S.ZodError?console.error("Validation error:",e.issues.map(n=>`${n.path.join(".")}: ${n.message}`).join(", ")):console.error("Error:",e instanceof Error?e.message:String(e)),process.exit(1)}import{z as G}from"zod";import{z as c}from"zod";function Z(...e){return e.filter(Boolean).join(" ")}var p={cnTool:{name:"cn",description:"Merges class names, filtering out falsy values",inputSchema:{classes:c.array(c.string()).describe("List of class names to merge")},typeLabels:{classes:"string[]"},returnType:"string",returnDescription:"Merged class name string with falsy values filtered out",handler:async({classes:e})=>r(Z(...e)),examples:[{args:[`'["btn","active","large"]'`],result:"btn active large"}]},caseConvertTool:{name:"case_convert",description:"Converts text to the specified case format",inputSchema:{input:c.string().describe("Text to convert"),to:c.enum(["upper","lower","capitalize","camel","snake","kebab"]).describe("Target case format")},typeLabels:{input:"string",to:'"upper" | "lower" | "capitalize" | "camel" | "snake" | "kebab"'},returnType:"string",returnDescription:"Converted text in the target case format",handler:async({input:e,to:n})=>r(z(e,n)),examples:[{args:['"hello world"',"camel"],result:"helloWorld"},{args:['"helloWorld"',"snake"],result:"hello_world"},{args:['"hello world"',"kebab"],result:"hello-world"}]},truncateTool:{name:"truncate",description:"Truncates text to a maximum length and appends a suffix",inputSchema:{input:c.string().describe("Text to truncate"),maxLength:c.number().int().positive().describe("Maximum character length"),suffix:c.string().default("...").describe("Suffix to append when truncated")},typeLabels:{input:"string",maxLength:"number",suffix:"string"},returnType:"string",returnDescription:"Truncated text with the configured suffix appended if truncated",handler:async({input:e,maxLength:n,suffix:t})=>{let o=e.length<=n?e:e.slice(0,n-t.length)+t;return r(o)},examples:[{args:['"hello world long text"',"10"],result:"hello w..."},{args:['"hello world"',"8",'"\u2026"'],result:"hello w\u2026"}]}},O=p.cnTool,A=p.caseConvertTool,k=p.truncateTool;function z(e,n){switch(n){case"upper":return e.toUpperCase();case"lower":return e.toLowerCase();case"capitalize":return e.charAt(0).toUpperCase()+e.slice(1).toLowerCase();case"camel":return e.replace(/[-_\s]+(.)/g,(t,o)=>o.toUpperCase()).replace(/^(.)/,t=>t.toLowerCase());case"snake":return e.replace(/([A-Z])/g,"_$1").replace(/[-\s]+/g,"_").toLowerCase().replace(/^_/,"");case"kebab":return e.replace(/([A-Z])/g,"-$1").replace(/[_\s]+/g,"-").toLowerCase().replace(/^-/,"")}}import{z as u}from"zod";function D(e){let n=typeof e=="string"?JSON.parse(e):e,t={};function o(s,l){if(s===null||typeof s!="object"||Array.isArray(s)){t[l]=s;return}for(let[f,m]of Object.entries(s)){let i=l?`${l}.${f}`:f;o(m,i)}}return o(n,""),t}var h={objectFlattenTool:{name:"object_flatten",description:"Flattens a nested JSON object of any depth into dot-notation key-value pairs. Accepts a JSON string and recursively flattens all levels. Arrays and primitives at any level are treated as leaf values.",inputSchema:{json:u.union([u.string(),u.record(u.string(),u.any())]).describe("JSON string or parsed object to flatten (unlimited depth)")},handler:async({json:e})=>{try{let t=D(e);return r(JSON.stringify(t,null,2))}catch(n){return n instanceof SyntaxError?r("Error: invalid JSON string \u2014 unable to parse input"):r(`Error: failed to flatten object \u2014 ${n.message}`)}},examples:[{args:[`'{"user":{"name":"Alice","address":{"city":"Seoul","zip":"12345"}},"active":true}'`],result:JSON.stringify({"user.name":"Alice","user.address.city":"Seoul","user.address.zip":"12345",active:!0},null,2)},{args:[`'{"a":{"b":{"c":{"d":{"e":"deep"}}}}}'`],result:JSON.stringify({"a.b.c.d.e":"deep"},null,2)}],returnType:"JsonObject",returnDescription:'Flattened object with dot-notation keys \u2014 e.g. { "a.b.c": value }',guidelines:["Arrays and primitives at any level are always treated as leaf values \u2014 they are never traversed.","The result is always a flat JSON object with dot-notation keys.","There is no depth limit \u2014 objects of any nesting level are fully flattened.","Input is accepted as a JSON string (MCP/CLI) and parsed internally."]}},R=h.objectFlattenTool;import{z as d}from"zod";function E(e){let n=typeof e=="string"?JSON.parse(e):e,{first:t,last:o}=n.name,s=n.location.city;return`\uC774\uB984\uC740 ${t} ${o} \uC774\uACE0 \uD604\uC7AC ${s} \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4.`}var b={getUserTool:{name:"getUser",description:"RandomUser API \uD615\uC2DD\uC758 \uC0AC\uC6A9\uC790 \uAC1D\uCCB4\uB97C \uBC1B\uC544 \uC774\uB984\uACFC \uAC70\uC8FC \uB3C4\uC2DC\uB85C \uAD6C\uC131\uB41C \uD55C\uAE00 \uBB38\uC7A5\uC744 \uBC18\uD658\uD569\uB2C8\uB2E4. JSON \uBB38\uC790\uC5F4 \uB610\uB294 \uD30C\uC2F1\uB41C \uAC1D\uCCB4\uB97C \uC785\uB825\uBC1B\uC2B5\uB2C8\uB2E4.",inputSchema:{user:d.union([d.string(),d.record(d.string(),d.any())]).describe("RandomUser \uD615\uC2DD\uC758 JSON \uBB38\uC790\uC5F4 \uB610\uB294 \uAC1D\uCCB4 \u2014 name.first / name.last / location.city \uD544\uC218")},typeLabels:{user:"RandomUser"},typeDefs:{user:["interface RandomUser {",' gender: "male" | "female";'," name: {"," title: string;"," first: string;"," last: string;"," };"," location: {"," street: {"," number: number;"," name: string;"," };"," city: string;"," state: string;"," country: string;"," postcode: string | number;"," coordinates: {"," latitude: string;"," longitude: string;"," };"," timezone: {"," offset: string;"," description: string;"," };"," };"," email: string;"," login: {"," uuid: string;"," username: string;"," };"," dob: {"," date: string;"," age: number;"," };"," phone: string;"," cell: string;"," picture: {"," large: string;"," medium: string;"," thumbnail: string;"," };"," nat: string;","}"].join(`
4
+ `)},returnType:"string",returnDescription:'"\uC774\uB984\uC740 {first} {last} \uC774\uACE0 \uD604\uC7AC {city} \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4." \uD615\uC2DD\uC758 \uD55C\uAE00 \uBB38\uC7A5',handler:async({user:e})=>{try{let t=E(e);return r(t)}catch(n){return n instanceof SyntaxError?r("Error: invalid JSON string \u2014 unable to parse input"):r(`Error: failed to process user data \u2014 ${n.message}
5
5
 
6
- Input must include \`name.first\`, \`name.last\`, and \`location.city\`.`)}},examples:[{args:[`'{"name":{"title":"Mr","first":"Alice","last":"Kim"},"location":{"city":"Seoul"},"gender":"female","email":"alice@example.com","nat":"KR"}'`],result:"\uC774\uB984\uC740 Alice Kim \uC774\uACE0 \uD604\uC7AC Seoul \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4."}],guidelines:["\uC785\uB825\uC740 RandomUser API \uC2A4\uD399\uC744 \uB530\uB985\uB2C8\uB2E4. \uCD5C\uC18C\uD55C name.first, name.last, location.city \uD544\uB4DC\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.","JSON \uBB38\uC790\uC5F4(string)\uACFC \uD30C\uC2F1\uB41C \uAC1D\uCCB4 \uBAA8\uB450 \uD5C8\uC6A9\uB429\uB2C8\uB2E4 (CLI / MCP \uBAA8\uB450 \uB3D9\uC791).","\uACB0\uACFC\uB294 \uD56D\uC0C1 '\uC774\uB984\uC740 {first} {last} \uC774\uACE0 \uD604\uC7AC {city} \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4.' \uD615\uC2DD\uC758 \uD55C\uAE00 \uBB38\uC7A5\uC785\uB2C8\uB2E4."]}},D=y.getUserTool;var w={...c,...g,...y};T(w).catch(b);
6
+ Input must include \`name.first\`, \`name.last\`, and \`location.city\`.`)}},examples:[{args:[`'{"name":{"title":"Mr","first":"Alice","last":"Kim"},"location":{"city":"Seoul"},"gender":"female","email":"alice@example.com","nat":"KR"}'`],result:"\uC774\uB984\uC740 Alice Kim \uC774\uACE0 \uD604\uC7AC Seoul \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4."}],guidelines:["\uC785\uB825\uC740 RandomUser API \uC2A4\uD399\uC744 \uB530\uB985\uB2C8\uB2E4. \uCD5C\uC18C\uD55C name.first, name.last, location.city \uD544\uB4DC\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.","JSON \uBB38\uC790\uC5F4(string)\uACFC \uD30C\uC2F1\uB41C \uAC1D\uCCB4 \uBAA8\uB450 \uD5C8\uC6A9\uB429\uB2C8\uB2E4 (CLI / MCP \uBAA8\uB450 \uB3D9\uC791).","\uACB0\uACFC\uB294 \uD56D\uC0C1 '\uC774\uB984\uC740 {first} {last} \uC774\uACE0 \uD604\uC7AC {city} \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4.' \uD615\uC2DD\uC758 \uD55C\uAE00 \uBB38\uC7A5\uC785\uB2C8\uB2E4."]}},N=b.getUserTool;import{z as w}from"zod";var a=["API_KEY"];function J(e){let n=e.filter(o=>!a.includes(o));if(n.length>0)throw new Error(`Invalid env key(s): ${n.join(", ")}. Allowed keys: ${a.join(", ")}`);let t={};for(let o of e){let s=process.env[o];s!==void 0&&(t[o]=s)}return t}var x={envGetTool:{name:"env_get",description:`MCP \uD074\uB77C\uC774\uC5B8\uD2B8 config\uC758 env \uD544\uB4DC\uB97C \uD1B5\uD574 \uC8FC\uC785\uB41C \uD658\uACBD \uBCC0\uC218 \uAC12\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4. \uC870\uD68C \uAC00\uB2A5\uD55C \uD0A4\uB294 \uD328\uD0A4\uC9C0\uC5D0\uC11C \uC81C\uACF5\uD558\uB294 \uD658\uACBD \uBCC0\uC218\uB85C \uD55C\uC815\uB429\uB2C8\uB2E4. \uD604\uC7AC \uC9C0\uC6D0: ${a.join(", ")}.`,inputSchema:{keys:w.array(w.string()).nonempty().describe(`\uC870\uD68C\uD560 \uD658\uACBD \uBCC0\uC218 \uC774\uB984 \uBAA9\uB85D (\uC720\uD6A8 \uD0A4: ${a.join(", ")})`)},typeLabels:{keys:"string[]"},typeDefs:{keys:["// @julong/mono-rele2-utils \uD658\uACBD \uBCC0\uC218 \uD0A4 \uBAA9\uB85D",...a.map(e=>`// "${e}"`),"","// MCP client config \uC608\uC2DC:","// {",'// "mcpServers": {','// "@julong/mono-rele2-utils": {','// "command": "npx",','// "args": ["-y", "@julong/mono-rele2-utils"],','// "env": {',...a.map(e=>`// "${e}": "<value>"`),"// }","// }","// }","// }"].join(`
7
+ `)},returnType:"Record<string, string>",returnDescription:"key: \uD658\uACBD \uBCC0\uC218 \uC774\uB984, value: \uD574\uB2F9 \uAC12 (\uC124\uC815\uB418\uC9C0 \uC54A\uC740 \uBCC0\uC218\uB294 \uACB0\uACFC\uC5D0\uC11C \uC81C\uC678)",handler:async({keys:e})=>{try{let n=J(e);return Object.keys(n).length===0?r("(no matching environment variables found)"):r(JSON.stringify(n,null,2))}catch(n){return r(`Error: ${n.message}`)}},examples:[{args:[`'["API_KEY"]'`],result:JSON.stringify({API_KEY:"<value>"},null,2)}],guidelines:[`\uC870\uD68C \uAC00\uB2A5\uD55C \uD0A4: ${a.join(", ")}`,"MCP client config.json\uC758 env \uD544\uB4DC\uC5D0 \uC704 \uD0A4\uB4E4\uB9CC \uC124\uC815\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.","\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uD0A4\uB97C \uC694\uCCAD\uD558\uBA74 \uC5D0\uB7EC \uBA54\uC2DC\uC9C0\uC5D0 \uD5C8\uC6A9\uB41C \uD0A4 \uBAA9\uB85D\uC774 \uD45C\uC2DC\uB429\uB2C8\uB2E4.","\uC124\uC815\uB418\uC9C0 \uC54A\uC740 \uD658\uACBD \uBCC0\uC218\uB294 \uACB0\uACFC \uAC1D\uCCB4\uC5D0\uC11C \uC81C\uC678\uB429\uB2C8\uB2E4."]}},C=x.envGetTool;var v={...p,...h,...b,...x};$(v).catch(j);
package/dist/index.d.ts CHANGED
@@ -18,6 +18,8 @@ type AnyToolDef = {
18
18
  guidelines?: string[];
19
19
  typeLabels?: Record<string, string>;
20
20
  typeDefs?: Record<string, string>;
21
+ returnType?: string;
22
+ returnDescription?: string;
21
23
  };
22
24
 
23
25
  type SkillTools = Record<string, AnyToolDef>;
@@ -32,6 +34,26 @@ declare function generateReadmeSkills(opts: {
32
34
  }): string;
33
35
 
34
36
  declare const tools: {
37
+ envGetTool: {
38
+ name: string;
39
+ description: string;
40
+ inputSchema: {
41
+ readonly keys: zod.ZodArray<zod.ZodString>;
42
+ };
43
+ handler: (input: {
44
+ keys: string[];
45
+ }) => Promise<ToolResult>;
46
+ examples?: ToolExample[];
47
+ guidelines?: string[];
48
+ typeLabels?: {
49
+ readonly keys?: string | undefined;
50
+ } | undefined;
51
+ typeDefs?: {
52
+ readonly keys?: string | undefined;
53
+ } | undefined;
54
+ returnType?: string;
55
+ returnDescription?: string;
56
+ };
35
57
  getUserTool: {
36
58
  name: string;
37
59
  description: string;
@@ -49,6 +71,8 @@ declare const tools: {
49
71
  typeDefs?: {
50
72
  readonly user?: string | undefined;
51
73
  } | undefined;
74
+ returnType?: string;
75
+ returnDescription?: string;
52
76
  };
53
77
  objectFlattenTool: {
54
78
  name: string;
@@ -67,6 +91,8 @@ declare const tools: {
67
91
  typeDefs?: {
68
92
  readonly json?: string | undefined;
69
93
  } | undefined;
94
+ returnType?: string;
95
+ returnDescription?: string;
70
96
  };
71
97
  cnTool: {
72
98
  name: string;
@@ -85,6 +111,8 @@ declare const tools: {
85
111
  typeDefs?: {
86
112
  readonly classes?: string | undefined;
87
113
  } | undefined;
114
+ returnType?: string;
115
+ returnDescription?: string;
88
116
  };
89
117
  caseConvertTool: {
90
118
  name: string;
@@ -114,6 +142,8 @@ declare const tools: {
114
142
  readonly input?: string | undefined;
115
143
  readonly to?: string | undefined;
116
144
  } | undefined;
145
+ returnType?: string;
146
+ returnDescription?: string;
117
147
  };
118
148
  truncateTool: {
119
149
  name: string;
@@ -140,6 +170,8 @@ declare const tools: {
140
170
  readonly maxLength?: string | undefined;
141
171
  readonly suffix?: string | undefined;
142
172
  } | undefined;
173
+ returnType?: string;
174
+ returnDescription?: string;
143
175
  };
144
176
  };
145
177
 
package/dist/index.js CHANGED
@@ -1,32 +1,32 @@
1
- function x(...t){return t.filter(Boolean).join(" ")}function c(t){return{content:[{type:"text",text:t}]}}import{McpServer as H}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as X}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as te}from"zod";import{z as i}from"zod";function k(t){let{binName:n,description:e,tools:o}=t;return`---
2
- name: ${n}
1
+ function S(...n){return n.filter(Boolean).join(" ")}function l(n){return{content:[{type:"text",text:n}]}}import{McpServer as ne}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as oe}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as ie}from"zod";import{z as i}from"zod";function R(n){let{binName:t,description:e,tools:o}=n;return`---
2
+ name: ${t}
3
3
  description: ${e}
4
4
  ---
5
5
 
6
- # ${n}
6
+ # ${t}
7
7
 
8
8
  \`\`\`sh
9
- ${n} <toolName> [...args]
9
+ ${t} <toolName> [...args]
10
10
  \`\`\`
11
11
 
12
12
  ## Skills
13
13
 
14
- ${v(o)}
14
+ ${N(o)}
15
15
 
16
16
  ## Examples
17
17
 
18
- ${R(n,o)}
18
+ ${J(t,o)}
19
19
 
20
20
  ## Guidelines
21
21
 
22
- ${J(n,o)}
23
- `}function v(t){return Object.entries(t).map(([n,e])=>{let o=Object.entries(e.inputSchema).map(([a,r])=>{let l=e.typeLabels?.[a];return`| \`${a}\` | ${D(r,l)} |`}).join(`
22
+ ${C(t,o)}
23
+ `}function N(n){return Object.entries(n).map(([t,e])=>{let o=Object.entries(e.inputSchema).map(([a,r])=>{let c=e.typeLabels?.[a];return`| \`${a}\` | ${E(r,c)} |`}).join(`
24
24
  `),s=e.typeDefs?Object.entries(e.typeDefs).map(([a,r])=>`
25
25
 
26
26
  **\`${a}\`** type definition:
27
27
  \`\`\`typescript
28
28
  ${r}
29
- \`\`\``).join(""):"";return`### ${n}
29
+ \`\`\``).join(""):"";return`### ${t}
30
30
 
31
31
  ${e.description}
32
32
 
@@ -34,33 +34,34 @@ ${e.description}
34
34
  |-----|-------------|
35
35
  ${o}${s}`}).join(`
36
36
 
37
- `)}function D(t,n){let e=t.description??"",o=t,s,a=!1;for(;o instanceof i.ZodOptional||o instanceof i.ZodDefault;){if(o instanceof i.ZodDefault){let l=o.def.defaultValue;s=typeof l=="function"?l():l}o instanceof i.ZodOptional&&(a=!0),o=o.unwrap()}let r=[];if(n&&r.push(`Type: ${n}`),e&&r.push(e),o instanceof i.ZodEnum){let l=o.options.map(m=>`\`${m}\``).join(" \\| ");r.push(l)}return s!==void 0?r.push(`default: \`${String(s)}\``):a&&r.push("optional"),r.join(" \u2014 ")}function R(t,n){let e=[];for(let[o,s]of Object.entries(n))if(s.examples)for(let a of s.examples){let r=[t,o,...a.args].join(" ");e.push(`- \`${r}\` => \`${a.result}\``)}return e.join(`
38
- `)}function J(t,n){let e=[],o=new Set,s=r=>{o.has(r)||(o.add(r),e.push(r))};s("Arguments are positional \u2014 pass them in the order listed in each skill's table");let a=Object.values(n).flatMap(r=>Object.values(r.inputSchema));a.some(r=>w(r,i.ZodNumber))&&s("Numeric args are auto-parsed \u2014 pass as plain numbers (e.g. `10`)"),a.some(r=>w(r,i.ZodArray))&&s('Array args must be valid JSON \u2014 wrap in single quotes on Unix shells (e.g. `\'["a","b"]\'`)'),a.some(r=>r instanceof i.ZodOptional||r instanceof i.ZodDefault)&&s("Optional args with defaults may be omitted");for(let r of Object.values(n))if(r.guidelines)for(let l of r.guidelines)s(l);return s(`Run \`${t}\` with no args to list all available skills`),e.map(r=>`- ${r}`).join(`
39
- `)}function w(t,n){let e=t;for(;e instanceof i.ZodOptional||e instanceof i.ZodDefault;)e=e.unwrap();return e instanceof n}function N(t){let{binName:n,tools:e}=t;return Object.entries(e).map(([o,s])=>C(n,o,s)).join(`
37
+ `)}function E(n,t){let e=n.description??"",o=n,s,a=!1;for(;o instanceof i.ZodOptional||o instanceof i.ZodDefault;){if(o instanceof i.ZodDefault){let c=o.def.defaultValue;s=typeof c=="function"?c():c}o instanceof i.ZodOptional&&(a=!0),o=o.unwrap()}let r=[];if(t&&r.push(`Type: ${t}`),e&&r.push(e),o instanceof i.ZodEnum){let c=o.options.map(g=>`\`${g}\``).join(" \\| ");r.push(c)}return s!==void 0?r.push(`default: \`${String(s)}\``):a&&r.push("optional"),r.join(" \u2014 ")}function J(n,t){let e=[];for(let[o,s]of Object.entries(t))if(s.examples)for(let a of s.examples){let r=[n,o,...a.args].join(" ");e.push(`- \`${r}\` => \`${a.result}\``)}return e.join(`
38
+ `)}function C(n,t){let e=[],o=new Set,s=r=>{o.has(r)||(o.add(r),e.push(r))};s("Arguments are positional \u2014 pass them in the order listed in each skill's table");let a=Object.values(t).flatMap(r=>Object.values(r.inputSchema));a.some(r=>O(r,i.ZodNumber))&&s("Numeric args are auto-parsed \u2014 pass as plain numbers (e.g. `10`)"),a.some(r=>O(r,i.ZodArray))&&s('Array args must be valid JSON \u2014 wrap in single quotes on Unix shells (e.g. `\'["a","b"]\'`)'),a.some(r=>r instanceof i.ZodOptional||r instanceof i.ZodDefault)&&s("Optional args with defaults may be omitted");for(let r of Object.values(t))if(r.guidelines)for(let c of r.guidelines)s(c);return s(`Run \`${n}\` with no args to list all available skills`),e.map(r=>`- ${r}`).join(`
39
+ `)}function O(n,t){let e=n;for(;e instanceof i.ZodOptional||e instanceof i.ZodDefault;)e=e.unwrap();return e instanceof t}function U(n){let{binName:t,tools:e}=n;return Object.entries(e).map(([o,s])=>L(t,o,s)).join(`
40
40
 
41
- `)}function C(t,n,e){let o=Object.entries(e.inputSchema),s=o.map(([p,u])=>u instanceof i.ZodOptional||u instanceof i.ZodDefault?`[${p}]`:`<${p}>`).join(" "),a=[t,n,s].filter(Boolean).join(" "),r=o.map(([p,u])=>{let T=e.typeLabels?.[p],d=$(u,T),b=E(u);return`| \`${p}\` | ${d} | ${b} |`}).join(`
42
- `),l=e.examples??[],m="";if(l.length>0){let p=l.map(d=>[t,n,...d.args].join(" ")),u=Math.max(...p.map(d=>d.length));m=`
41
+ `)}function L(n,t,e){let o=Object.entries(e.inputSchema),s=o.map(([p,u])=>u instanceof i.ZodOptional||u instanceof i.ZodDefault?`[${p}]`:`<${p}>`).join(" "),a=[n,t,s].filter(Boolean).join(" "),r=o.map(([p,u])=>{let h=e.typeLabels?.[p],f=A(u,h),x=M(u);return`| \`${p}\` | ${f} | ${x} |`}).join(`
42
+ `),c=e.examples??[],g="";if(c.length>0){let p=c.map(f=>[n,t,...f.args].join(" ")),u=Math.max(...p.map(f=>f.length));g=`
43
43
 
44
44
  \`\`\`sh
45
- ${l.map((d,b)=>`${p[b].padEnd(u)} # ${d.result}`).join(`
45
+ ${c.map((f,x)=>`${p[x].padEnd(u)} # ${f.result}`).join(`
46
46
  `)}
47
- \`\`\``}let A=e.typeDefs?Object.entries(e.typeDefs).map(([p,u])=>`
47
+ \`\`\``}let z=e.typeDefs?Object.entries(e.typeDefs).map(([p,u])=>`
48
48
 
49
49
  **\`${p}\`** type definition:
50
50
 
51
51
  \`\`\`typescript
52
52
  ${u}
53
- \`\`\``).join(""):"",z=r?`
53
+ \`\`\``).join(""):"",D=r?`
54
54
 
55
55
  | arg | type | description |
56
56
  |-----|------|-------------|
57
- ${r}`:"";return`#### \`${n}\`
57
+ ${r}`:"";return`#### \`${t}\`
58
58
 
59
59
  ${e.description}.
60
60
 
61
61
  \`\`\`sh
62
62
  ${a}
63
- \`\`\`${z}${A}${m}`}function $(t,n){if(n)return n;let e=t;for(;e instanceof i.ZodOptional||e instanceof i.ZodDefault;)e=e.unwrap();return e instanceof i.ZodEnum?e.options.map(o=>`\`${o}\``).join(" \\| "):e instanceof i.ZodNumber?"number":e instanceof i.ZodString?"string":e instanceof i.ZodBoolean?"boolean":e instanceof i.ZodArray?"JSON string (array)":e instanceof i.ZodUnion?e.options.map(s=>$(s)).join(" \\| "):e instanceof i.ZodRecord?"JSON object":"unknown"}function E(t){let n=t.description??"",e=t,o,s=!1;for(;e instanceof i.ZodOptional||e instanceof i.ZodDefault;){if(e instanceof i.ZodDefault){let a=e.def.defaultValue;o=typeof a=="function"?a():a}e instanceof i.ZodOptional&&(s=!0),e=e.unwrap()}return o!==void 0?`${n} (default: \`${String(o)}\`)`:s?`${n} (optional)`:n}import{z as f}from"zod";var g={cnTool:{name:"cn",description:"Merges class names, filtering out falsy values",inputSchema:{classes:f.array(f.string()).describe("List of class names to merge")},handler:async({classes:t})=>c(x(...t)),examples:[{args:[`'["btn","active","large"]'`],result:"btn active large"}]},caseConvertTool:{name:"case_convert",description:"Converts text to the specified case format",inputSchema:{input:f.string().describe("Text to convert"),to:f.enum(["upper","lower","capitalize","camel","snake","kebab"]).describe("Target case format")},handler:async({input:t,to:n})=>c(V(t,n)),examples:[{args:['"hello world"',"camel"],result:"helloWorld"},{args:['"helloWorld"',"snake"],result:"hello_world"},{args:['"hello world"',"kebab"],result:"hello-world"}]},truncateTool:{name:"truncate",description:"Truncates text to a maximum length and appends a suffix",inputSchema:{input:f.string().describe("Text to truncate"),maxLength:f.number().int().positive().describe("Maximum character length"),suffix:f.string().default("...").describe("Suffix to append when truncated")},handler:async({input:t,maxLength:n,suffix:e})=>{let o=t.length<=n?t:t.slice(0,n-e.length)+e;return c(o)},examples:[{args:['"hello world long text"',"10"],result:"hello w..."},{args:['"hello world"',"8",'"\u2026"'],result:"hello w\u2026"}]}},U=g.cnTool,M=g.caseConvertTool,L=g.truncateTool;function V(t,n){switch(n){case"upper":return t.toUpperCase();case"lower":return t.toLowerCase();case"capitalize":return t.charAt(0).toUpperCase()+t.slice(1).toLowerCase();case"camel":return t.replace(/[-_\s]+(.)/g,(e,o)=>o.toUpperCase()).replace(/^(.)/,e=>e.toLowerCase());case"snake":return t.replace(/([A-Z])/g,"_$1").replace(/[-\s]+/g,"_").toLowerCase().replace(/^_/,"");case"kebab":return t.replace(/([A-Z])/g,"-$1").replace(/[_\s]+/g,"-").toLowerCase().replace(/^-/,"")}}import{z as y}from"zod";function P(t){let n=typeof t=="string"?JSON.parse(t):t,e={};function o(s,a){if(s===null||typeof s!="object"||Array.isArray(s)){e[a]=s;return}for(let[r,l]of Object.entries(s)){let m=a?`${a}.${r}`:r;o(l,m)}}return o(n,""),e}var S={objectFlattenTool:{name:"object_flatten",description:"Flattens a nested JSON object of any depth into dot-notation key-value pairs. Accepts a JSON string and recursively flattens all levels. Arrays and primitives at any level are treated as leaf values.",inputSchema:{json:y.union([y.string(),y.record(y.string(),y.any())]).describe("JSON string or parsed object to flatten (unlimited depth)")},handler:async({json:t})=>{try{let e=P(t);return c(JSON.stringify(e,null,2))}catch(n){return n instanceof SyntaxError?c("Error: invalid JSON string \u2014 unable to parse input"):c(`Error: failed to flatten object \u2014 ${n.message}`)}},examples:[{args:[`'{"user":{"name":"Alice","address":{"city":"Seoul","zip":"12345"}},"active":true}'`],result:JSON.stringify({"user.name":"Alice","user.address.city":"Seoul","user.address.zip":"12345",active:!0},null,2)},{args:[`'{"a":{"b":{"c":{"d":{"e":"deep"}}}}}'`],result:JSON.stringify({"a.b.c.d.e":"deep"},null,2)}],guidelines:["Arrays and primitives at any level are always treated as leaf values \u2014 they are never traversed.","The result is always a flat JSON object with dot-notation keys.","There is no depth limit \u2014 objects of any nesting level are fully flattened.","Input is accepted as a JSON string (MCP/CLI) and parsed internally."]}},_=S.objectFlattenTool;import{z as h}from"zod";function I(t){let n=typeof t=="string"?JSON.parse(t):t,{first:e,last:o}=n.name,s=n.location.city;return`\uC774\uB984\uC740 ${e} ${o} \uC774\uACE0 \uD604\uC7AC ${s} \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4.`}var Z={getUserTool:{name:"getUser",description:"RandomUser API \uD615\uC2DD\uC758 \uC0AC\uC6A9\uC790 \uAC1D\uCCB4\uB97C \uBC1B\uC544 \uC774\uB984\uACFC \uAC70\uC8FC \uB3C4\uC2DC\uB85C \uAD6C\uC131\uB41C \uD55C\uAE00 \uBB38\uC7A5\uC744 \uBC18\uD658\uD569\uB2C8\uB2E4. JSON \uBB38\uC790\uC5F4 \uB610\uB294 \uD30C\uC2F1\uB41C \uAC1D\uCCB4\uB97C \uC785\uB825\uBC1B\uC2B5\uB2C8\uB2E4.",inputSchema:{user:h.union([h.string(),h.record(h.string(),h.any())]).describe("RandomUser \uD615\uC2DD\uC758 JSON \uBB38\uC790\uC5F4 \uB610\uB294 \uAC1D\uCCB4 \u2014 name.first / name.last / location.city \uD544\uC218")},typeLabels:{user:"RandomUser"},typeDefs:{user:["interface RandomUser {",' gender: "male" | "female";'," name: {"," title: string;"," first: string;"," last: string;"," };"," location: {"," street: {"," number: number;"," name: string;"," };"," city: string;"," state: string;"," country: string;"," postcode: string | number;"," coordinates: {"," latitude: string;"," longitude: string;"," };"," timezone: {"," offset: string;"," description: string;"," };"," };"," email: string;"," login: {"," uuid: string;"," username: string;"," };"," dob: {"," date: string;"," age: number;"," };"," phone: string;"," cell: string;"," picture: {"," large: string;"," medium: string;"," thumbnail: string;"," };"," nat: string;","}"].join(`
64
- `)},handler:async({user:t})=>{try{let e=I(t);return c(e)}catch(n){return n instanceof SyntaxError?c("Error: invalid JSON string \u2014 unable to parse input"):c(`Error: failed to process user data \u2014 ${n.message}
63
+ \`\`\`${D}${z}${g}`}function A(n,t){if(t)return t;let e=n;for(;e instanceof i.ZodOptional||e instanceof i.ZodDefault;)e=e.unwrap();return e instanceof i.ZodEnum?e.options.map(o=>`\`${o}\``).join(" \\| "):e instanceof i.ZodNumber?"number":e instanceof i.ZodString?"string":e instanceof i.ZodBoolean?"boolean":e instanceof i.ZodArray?"JSON string (array)":e instanceof i.ZodUnion?e.options.map(s=>A(s)).join(" \\| "):e instanceof i.ZodRecord?"JSON object":"unknown"}function M(n){let t=n.description??"",e=n,o,s=!1;for(;e instanceof i.ZodOptional||e instanceof i.ZodDefault;){if(e instanceof i.ZodDefault){let a=e.def.defaultValue;o=typeof a=="function"?a():a}e instanceof i.ZodOptional&&(s=!0),e=e.unwrap()}return o!==void 0?`${t} (default: \`${String(o)}\`)`:s?`${t} (optional)`:t}import{z as m}from"zod";var y={cnTool:{name:"cn",description:"Merges class names, filtering out falsy values",inputSchema:{classes:m.array(m.string()).describe("List of class names to merge")},typeLabels:{classes:"string[]"},returnType:"string",returnDescription:"Merged class name string with falsy values filtered out",handler:async({classes:n})=>l(S(...n)),examples:[{args:[`'["btn","active","large"]'`],result:"btn active large"}]},caseConvertTool:{name:"case_convert",description:"Converts text to the specified case format",inputSchema:{input:m.string().describe("Text to convert"),to:m.enum(["upper","lower","capitalize","camel","snake","kebab"]).describe("Target case format")},typeLabels:{input:"string",to:'"upper" | "lower" | "capitalize" | "camel" | "snake" | "kebab"'},returnType:"string",returnDescription:"Converted text in the target case format",handler:async({input:n,to:t})=>l(V(n,t)),examples:[{args:['"hello world"',"camel"],result:"helloWorld"},{args:['"helloWorld"',"snake"],result:"hello_world"},{args:['"hello world"',"kebab"],result:"hello-world"}]},truncateTool:{name:"truncate",description:"Truncates text to a maximum length and appends a suffix",inputSchema:{input:m.string().describe("Text to truncate"),maxLength:m.number().int().positive().describe("Maximum character length"),suffix:m.string().default("...").describe("Suffix to append when truncated")},typeLabels:{input:"string",maxLength:"number",suffix:"string"},returnType:"string",returnDescription:"Truncated text with the configured suffix appended if truncated",handler:async({input:n,maxLength:t,suffix:e})=>{let o=n.length<=t?n:n.slice(0,t-e.length)+e;return l(o)},examples:[{args:['"hello world long text"',"10"],result:"hello w..."},{args:['"hello world"',"8",'"\u2026"'],result:"hello w\u2026"}]}},_=y.cnTool,P=y.caseConvertTool,I=y.truncateTool;function V(n,t){switch(t){case"upper":return n.toUpperCase();case"lower":return n.toLowerCase();case"capitalize":return n.charAt(0).toUpperCase()+n.slice(1).toLowerCase();case"camel":return n.replace(/[-_\s]+(.)/g,(e,o)=>o.toUpperCase()).replace(/^(.)/,e=>e.toLowerCase());case"snake":return n.replace(/([A-Z])/g,"_$1").replace(/[-\s]+/g,"_").toLowerCase().replace(/^_/,"");case"kebab":return n.replace(/([A-Z])/g,"-$1").replace(/[_\s]+/g,"-").toLowerCase().replace(/^-/,"")}}import{z as T}from"zod";function K(n){let t=typeof n=="string"?JSON.parse(n):n,e={};function o(s,a){if(s===null||typeof s!="object"||Array.isArray(s)){e[a]=s;return}for(let[r,c]of Object.entries(s)){let g=a?`${a}.${r}`:r;o(c,g)}}return o(t,""),e}var Z={objectFlattenTool:{name:"object_flatten",description:"Flattens a nested JSON object of any depth into dot-notation key-value pairs. Accepts a JSON string and recursively flattens all levels. Arrays and primitives at any level are treated as leaf values.",inputSchema:{json:T.union([T.string(),T.record(T.string(),T.any())]).describe("JSON string or parsed object to flatten (unlimited depth)")},handler:async({json:n})=>{try{let e=K(n);return l(JSON.stringify(e,null,2))}catch(t){return t instanceof SyntaxError?l("Error: invalid JSON string \u2014 unable to parse input"):l(`Error: failed to flatten object \u2014 ${t.message}`)}},examples:[{args:[`'{"user":{"name":"Alice","address":{"city":"Seoul","zip":"12345"}},"active":true}'`],result:JSON.stringify({"user.name":"Alice","user.address.city":"Seoul","user.address.zip":"12345",active:!0},null,2)},{args:[`'{"a":{"b":{"c":{"d":{"e":"deep"}}}}}'`],result:JSON.stringify({"a.b.c.d.e":"deep"},null,2)}],returnType:"JsonObject",returnDescription:'Flattened object with dot-notation keys \u2014 e.g. { "a.b.c": value }',guidelines:["Arrays and primitives at any level are always treated as leaf values \u2014 they are never traversed.","The result is always a flat JSON object with dot-notation keys.","There is no depth limit \u2014 objects of any nesting level are fully flattened.","Input is accepted as a JSON string (MCP/CLI) and parsed internally."]}},B=Z.objectFlattenTool;import{z as b}from"zod";function F(n){let t=typeof n=="string"?JSON.parse(n):n,{first:e,last:o}=t.name,s=t.location.city;return`\uC774\uB984\uC740 ${e} ${o} \uC774\uACE0 \uD604\uC7AC ${s} \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4.`}var w={getUserTool:{name:"getUser",description:"RandomUser API \uD615\uC2DD\uC758 \uC0AC\uC6A9\uC790 \uAC1D\uCCB4\uB97C \uBC1B\uC544 \uC774\uB984\uACFC \uAC70\uC8FC \uB3C4\uC2DC\uB85C \uAD6C\uC131\uB41C \uD55C\uAE00 \uBB38\uC7A5\uC744 \uBC18\uD658\uD569\uB2C8\uB2E4. JSON \uBB38\uC790\uC5F4 \uB610\uB294 \uD30C\uC2F1\uB41C \uAC1D\uCCB4\uB97C \uC785\uB825\uBC1B\uC2B5\uB2C8\uB2E4.",inputSchema:{user:b.union([b.string(),b.record(b.string(),b.any())]).describe("RandomUser \uD615\uC2DD\uC758 JSON \uBB38\uC790\uC5F4 \uB610\uB294 \uAC1D\uCCB4 \u2014 name.first / name.last / location.city \uD544\uC218")},typeLabels:{user:"RandomUser"},typeDefs:{user:["interface RandomUser {",' gender: "male" | "female";'," name: {"," title: string;"," first: string;"," last: string;"," };"," location: {"," street: {"," number: number;"," name: string;"," };"," city: string;"," state: string;"," country: string;"," postcode: string | number;"," coordinates: {"," latitude: string;"," longitude: string;"," };"," timezone: {"," offset: string;"," description: string;"," };"," };"," email: string;"," login: {"," uuid: string;"," username: string;"," };"," dob: {"," date: string;"," age: number;"," };"," phone: string;"," cell: string;"," picture: {"," large: string;"," medium: string;"," thumbnail: string;"," };"," nat: string;","}"].join(`
64
+ `)},returnType:"string",returnDescription:'"\uC774\uB984\uC740 {first} {last} \uC774\uACE0 \uD604\uC7AC {city} \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4." \uD615\uC2DD\uC758 \uD55C\uAE00 \uBB38\uC7A5',handler:async({user:n})=>{try{let e=F(n);return l(e)}catch(t){return t instanceof SyntaxError?l("Error: invalid JSON string \u2014 unable to parse input"):l(`Error: failed to process user data \u2014 ${t.message}
65
65
 
66
- Input must include \`name.first\`, \`name.last\`, and \`location.city\`.`)}},examples:[{args:[`'{"name":{"title":"Mr","first":"Alice","last":"Kim"},"location":{"city":"Seoul"},"gender":"female","email":"alice@example.com","nat":"KR"}'`],result:"\uC774\uB984\uC740 Alice Kim \uC774\uACE0 \uD604\uC7AC Seoul \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4."}],guidelines:["\uC785\uB825\uC740 RandomUser API \uC2A4\uD399\uC744 \uB530\uB985\uB2C8\uB2E4. \uCD5C\uC18C\uD55C name.first, name.last, location.city \uD544\uB4DC\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.","JSON \uBB38\uC790\uC5F4(string)\uACFC \uD30C\uC2F1\uB41C \uAC1D\uCCB4 \uBAA8\uB450 \uD5C8\uC6A9\uB429\uB2C8\uB2E4 (CLI / MCP \uBAA8\uB450 \uB3D9\uC791).","\uACB0\uACFC\uB294 \uD56D\uC0C1 '\uC774\uB984\uC740 {first} {last} \uC774\uACE0 \uD604\uC7AC {city} \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4.' \uD615\uC2DD\uC758 \uD55C\uAE00 \uBB38\uC7A5\uC785\uB2C8\uB2E4."]}},F=Z.getUserTool;var B={...g,...S,...Z};export{x as cn,N as generateReadmeSkills,k as generateSkillMarkdown,B as tools};
66
+ Input must include \`name.first\`, \`name.last\`, and \`location.city\`.`)}},examples:[{args:[`'{"name":{"title":"Mr","first":"Alice","last":"Kim"},"location":{"city":"Seoul"},"gender":"female","email":"alice@example.com","nat":"KR"}'`],result:"\uC774\uB984\uC740 Alice Kim \uC774\uACE0 \uD604\uC7AC Seoul \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4."}],guidelines:["\uC785\uB825\uC740 RandomUser API \uC2A4\uD399\uC744 \uB530\uB985\uB2C8\uB2E4. \uCD5C\uC18C\uD55C name.first, name.last, location.city \uD544\uB4DC\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.","JSON \uBB38\uC790\uC5F4(string)\uACFC \uD30C\uC2F1\uB41C \uAC1D\uCCB4 \uBAA8\uB450 \uD5C8\uC6A9\uB429\uB2C8\uB2E4 (CLI / MCP \uBAA8\uB450 \uB3D9\uC791).","\uACB0\uACFC\uB294 \uD56D\uC0C1 '\uC774\uB984\uC740 {first} {last} \uC774\uACE0 \uD604\uC7AC {city} \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4.' \uD615\uC2DD\uC758 \uD55C\uAE00 \uBB38\uC7A5\uC785\uB2C8\uB2E4."]}},G=w.getUserTool;import{z as k}from"zod";var d=["API_KEY"];function Y(n){let t=n.filter(o=>!d.includes(o));if(t.length>0)throw new Error(`Invalid env key(s): ${t.join(", ")}. Allowed keys: ${d.join(", ")}`);let e={};for(let o of n){let s=process.env[o];s!==void 0&&(e[o]=s)}return e}var v={envGetTool:{name:"env_get",description:`MCP \uD074\uB77C\uC774\uC5B8\uD2B8 config\uC758 env \uD544\uB4DC\uB97C \uD1B5\uD574 \uC8FC\uC785\uB41C \uD658\uACBD \uBCC0\uC218 \uAC12\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4. \uC870\uD68C \uAC00\uB2A5\uD55C \uD0A4\uB294 \uD328\uD0A4\uC9C0\uC5D0\uC11C \uC81C\uACF5\uD558\uB294 \uD658\uACBD \uBCC0\uC218\uB85C \uD55C\uC815\uB429\uB2C8\uB2E4. \uD604\uC7AC \uC9C0\uC6D0: ${d.join(", ")}.`,inputSchema:{keys:k.array(k.string()).nonempty().describe(`\uC870\uD68C\uD560 \uD658\uACBD \uBCC0\uC218 \uC774\uB984 \uBAA9\uB85D (\uC720\uD6A8 \uD0A4: ${d.join(", ")})`)},typeLabels:{keys:"string[]"},typeDefs:{keys:["// @julong/mono-rele2-utils \uD658\uACBD \uBCC0\uC218 \uD0A4 \uBAA9\uB85D",...d.map(n=>`// "${n}"`),"","// MCP client config \uC608\uC2DC:","// {",'// "mcpServers": {','// "@julong/mono-rele2-utils": {','// "command": "npx",','// "args": ["-y", "@julong/mono-rele2-utils"],','// "env": {',...d.map(n=>`// "${n}": "<value>"`),"// }","// }","// }","// }"].join(`
67
+ `)},returnType:"Record<string, string>",returnDescription:"key: \uD658\uACBD \uBCC0\uC218 \uC774\uB984, value: \uD574\uB2F9 \uAC12 (\uC124\uC815\uB418\uC9C0 \uC54A\uC740 \uBCC0\uC218\uB294 \uACB0\uACFC\uC5D0\uC11C \uC81C\uC678)",handler:async({keys:n})=>{try{let t=Y(n);return Object.keys(t).length===0?l("(no matching environment variables found)"):l(JSON.stringify(t,null,2))}catch(t){return l(`Error: ${t.message}`)}},examples:[{args:[`'["API_KEY"]'`],result:JSON.stringify({API_KEY:"<value>"},null,2)}],guidelines:[`\uC870\uD68C \uAC00\uB2A5\uD55C \uD0A4: ${d.join(", ")}`,"MCP client config.json\uC758 env \uD544\uB4DC\uC5D0 \uC704 \uD0A4\uB4E4\uB9CC \uC124\uC815\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.","\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uD0A4\uB97C \uC694\uCCAD\uD558\uBA74 \uC5D0\uB7EC \uBA54\uC2DC\uC9C0\uC5D0 \uD5C8\uC6A9\uB41C \uD0A4 \uBAA9\uB85D\uC774 \uD45C\uC2DC\uB429\uB2C8\uB2E4.","\uC124\uC815\uB418\uC9C0 \uC54A\uC740 \uD658\uACBD \uBCC0\uC218\uB294 \uACB0\uACFC \uAC1D\uCCB4\uC5D0\uC11C \uC81C\uC678\uB429\uB2C8\uB2E4."]}},W=v.envGetTool;var q={...y,...Z,...w,...v};export{S as cn,U as generateReadmeSkills,R as generateSkillMarkdown,q as tools};
package/dist/server.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
- function r(e){return{content:[{type:"text",text:e}]}}import{McpServer as O}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as A}from"@modelcontextprotocol/sdk/server/stdio.js";function b(e,t){let n=new O(e);for(let o of t)n.registerTool(o.name,{description:o.description,inputSchema:o.inputSchema},o.handler);return n}async function S(e){let t=new A;await e.connect(t)}import{z as M}from"zod";import{z as P}from"zod";import{z as i}from"zod";function x(...e){return e.filter(Boolean).join(" ")}var a={cnTool:{name:"cn",description:"Merges class names, filtering out falsy values",inputSchema:{classes:i.array(i.string()).describe("List of class names to merge")},handler:async({classes:e})=>r(x(...e)),examples:[{args:[`'["btn","active","large"]'`],result:"btn active large"}]},caseConvertTool:{name:"case_convert",description:"Converts text to the specified case format",inputSchema:{input:i.string().describe("Text to convert"),to:i.enum(["upper","lower","capitalize","camel","snake","kebab"]).describe("Target case format")},handler:async({input:e,to:t})=>r(z(e,t)),examples:[{args:['"hello world"',"camel"],result:"helloWorld"},{args:['"helloWorld"',"snake"],result:"hello_world"},{args:['"hello world"',"kebab"],result:"hello-world"}]},truncateTool:{name:"truncate",description:"Truncates text to a maximum length and appends a suffix",inputSchema:{input:i.string().describe("Text to truncate"),maxLength:i.number().int().positive().describe("Maximum character length"),suffix:i.string().default("...").describe("Suffix to append when truncated")},handler:async({input:e,maxLength:t,suffix:n})=>{let o=e.length<=t?e:e.slice(0,t-n.length)+n;return r(o)},examples:[{args:['"hello world long text"',"10"],result:"hello w..."},{args:['"hello world"',"8",'"\u2026"'],result:"hello w\u2026"}]}},u=a.cnTool,d=a.caseConvertTool,f=a.truncateTool;function z(e,t){switch(t){case"upper":return e.toUpperCase();case"lower":return e.toLowerCase();case"capitalize":return e.charAt(0).toUpperCase()+e.slice(1).toLowerCase();case"camel":return e.replace(/[-_\s]+(.)/g,(n,o)=>o.toUpperCase()).replace(/^(.)/,n=>n.toLowerCase());case"snake":return e.replace(/([A-Z])/g,"_$1").replace(/[-\s]+/g,"_").toLowerCase().replace(/^_/,"");case"kebab":return e.replace(/([A-Z])/g,"-$1").replace(/[_\s]+/g,"-").toLowerCase().replace(/^-/,"")}}import{z as l}from"zod";function v(e){let t=typeof e=="string"?JSON.parse(e):e,n={};function o(s,p){if(s===null||typeof s!="object"||Array.isArray(s)){n[p]=s;return}for(let[h,j]of Object.entries(s)){let $=p?`${p}.${h}`:h;o(j,$)}}return o(t,""),n}var m={objectFlattenTool:{name:"object_flatten",description:"Flattens a nested JSON object of any depth into dot-notation key-value pairs. Accepts a JSON string and recursively flattens all levels. Arrays and primitives at any level are treated as leaf values.",inputSchema:{json:l.union([l.string(),l.record(l.string(),l.any())]).describe("JSON string or parsed object to flatten (unlimited depth)")},handler:async({json:e})=>{try{let n=v(e);return r(JSON.stringify(n,null,2))}catch(t){return t instanceof SyntaxError?r("Error: invalid JSON string \u2014 unable to parse input"):r(`Error: failed to flatten object \u2014 ${t.message}`)}},examples:[{args:[`'{"user":{"name":"Alice","address":{"city":"Seoul","zip":"12345"}},"active":true}'`],result:JSON.stringify({"user.name":"Alice","user.address.city":"Seoul","user.address.zip":"12345",active:!0},null,2)},{args:[`'{"a":{"b":{"c":{"d":{"e":"deep"}}}}}'`],result:JSON.stringify({"a.b.c.d.e":"deep"},null,2)}],guidelines:["Arrays and primitives at any level are always treated as leaf values \u2014 they are never traversed.","The result is always a flat JSON object with dot-notation keys.","There is no depth limit \u2014 objects of any nesting level are fully flattened.","Input is accepted as a JSON string (MCP/CLI) and parsed internally."]}},g=m.objectFlattenTool;import{z as c}from"zod";function k(e){let t=typeof e=="string"?JSON.parse(e):e,{first:n,last:o}=t.name,s=t.location.city;return`\uC774\uB984\uC740 ${n} ${o} \uC774\uACE0 \uD604\uC7AC ${s} \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4.`}var y={getUserTool:{name:"getUser",description:"RandomUser API \uD615\uC2DD\uC758 \uC0AC\uC6A9\uC790 \uAC1D\uCCB4\uB97C \uBC1B\uC544 \uC774\uB984\uACFC \uAC70\uC8FC \uB3C4\uC2DC\uB85C \uAD6C\uC131\uB41C \uD55C\uAE00 \uBB38\uC7A5\uC744 \uBC18\uD658\uD569\uB2C8\uB2E4. JSON \uBB38\uC790\uC5F4 \uB610\uB294 \uD30C\uC2F1\uB41C \uAC1D\uCCB4\uB97C \uC785\uB825\uBC1B\uC2B5\uB2C8\uB2E4.",inputSchema:{user:c.union([c.string(),c.record(c.string(),c.any())]).describe("RandomUser \uD615\uC2DD\uC758 JSON \uBB38\uC790\uC5F4 \uB610\uB294 \uAC1D\uCCB4 \u2014 name.first / name.last / location.city \uD544\uC218")},typeLabels:{user:"RandomUser"},typeDefs:{user:["interface RandomUser {",' gender: "male" | "female";'," name: {"," title: string;"," first: string;"," last: string;"," };"," location: {"," street: {"," number: number;"," name: string;"," };"," city: string;"," state: string;"," country: string;"," postcode: string | number;"," coordinates: {"," latitude: string;"," longitude: string;"," };"," timezone: {"," offset: string;"," description: string;"," };"," };"," email: string;"," login: {"," uuid: string;"," username: string;"," };"," dob: {"," date: string;"," age: number;"," };"," phone: string;"," cell: string;"," picture: {"," large: string;"," medium: string;"," thumbnail: string;"," };"," nat: string;","}"].join(`
3
- `)},handler:async({user:e})=>{try{let n=k(e);return r(n)}catch(t){return t instanceof SyntaxError?r("Error: invalid JSON string \u2014 unable to parse input"):r(`Error: failed to process user data \u2014 ${t.message}
2
+ function r(e){return{content:[{type:"text",text:e}]}}import{McpServer as z}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as D}from"@modelcontextprotocol/sdk/server/stdio.js";function v(e,n){let t=new z(e);for(let o of n)t.registerTool(o.name,{description:o.description,inputSchema:o.inputSchema},o.handler);return t}async function Z(e){let n=new D;await e.connect(n)}import{z as V}from"zod";import{z as F}from"zod";import{z as a}from"zod";function w(...e){return e.filter(Boolean).join(" ")}var l={cnTool:{name:"cn",description:"Merges class names, filtering out falsy values",inputSchema:{classes:a.array(a.string()).describe("List of class names to merge")},typeLabels:{classes:"string[]"},returnType:"string",returnDescription:"Merged class name string with falsy values filtered out",handler:async({classes:e})=>r(w(...e)),examples:[{args:[`'["btn","active","large"]'`],result:"btn active large"}]},caseConvertTool:{name:"case_convert",description:"Converts text to the specified case format",inputSchema:{input:a.string().describe("Text to convert"),to:a.enum(["upper","lower","capitalize","camel","snake","kebab"]).describe("Target case format")},typeLabels:{input:"string",to:'"upper" | "lower" | "capitalize" | "camel" | "snake" | "kebab"'},returnType:"string",returnDescription:"Converted text in the target case format",handler:async({input:e,to:n})=>r(R(e,n)),examples:[{args:['"hello world"',"camel"],result:"helloWorld"},{args:['"helloWorld"',"snake"],result:"hello_world"},{args:['"hello world"',"kebab"],result:"hello-world"}]},truncateTool:{name:"truncate",description:"Truncates text to a maximum length and appends a suffix",inputSchema:{input:a.string().describe("Text to truncate"),maxLength:a.number().int().positive().describe("Maximum character length"),suffix:a.string().default("...").describe("Suffix to append when truncated")},typeLabels:{input:"string",maxLength:"number",suffix:"string"},returnType:"string",returnDescription:"Truncated text with the configured suffix appended if truncated",handler:async({input:e,maxLength:n,suffix:t})=>{let o=e.length<=n?e:e.slice(0,n-t.length)+t;return r(o)},examples:[{args:['"hello world long text"',"10"],result:"hello w..."},{args:['"hello world"',"8",'"\u2026"'],result:"hello w\u2026"}]}},m=l.cnTool,g=l.caseConvertTool,y=l.truncateTool;function R(e,n){switch(n){case"upper":return e.toUpperCase();case"lower":return e.toLowerCase();case"capitalize":return e.charAt(0).toUpperCase()+e.slice(1).toLowerCase();case"camel":return e.replace(/[-_\s]+(.)/g,(t,o)=>o.toUpperCase()).replace(/^(.)/,t=>t.toLowerCase());case"snake":return e.replace(/([A-Z])/g,"_$1").replace(/[-\s]+/g,"_").toLowerCase().replace(/^_/,"");case"kebab":return e.replace(/([A-Z])/g,"-$1").replace(/[_\s]+/g,"-").toLowerCase().replace(/^-/,"")}}import{z as c}from"zod";function N(e){let n=typeof e=="string"?JSON.parse(e):e,t={};function o(s,u){if(s===null||typeof s!="object"||Array.isArray(s)){t[u]=s;return}for(let[j,A]of Object.entries(s)){let k=u?`${u}.${j}`:j;o(A,k)}}return o(n,""),t}var T={objectFlattenTool:{name:"object_flatten",description:"Flattens a nested JSON object of any depth into dot-notation key-value pairs. Accepts a JSON string and recursively flattens all levels. Arrays and primitives at any level are treated as leaf values.",inputSchema:{json:c.union([c.string(),c.record(c.string(),c.any())]).describe("JSON string or parsed object to flatten (unlimited depth)")},handler:async({json:e})=>{try{let t=N(e);return r(JSON.stringify(t,null,2))}catch(n){return n instanceof SyntaxError?r("Error: invalid JSON string \u2014 unable to parse input"):r(`Error: failed to flatten object \u2014 ${n.message}`)}},examples:[{args:[`'{"user":{"name":"Alice","address":{"city":"Seoul","zip":"12345"}},"active":true}'`],result:JSON.stringify({"user.name":"Alice","user.address.city":"Seoul","user.address.zip":"12345",active:!0},null,2)},{args:[`'{"a":{"b":{"c":{"d":{"e":"deep"}}}}}'`],result:JSON.stringify({"a.b.c.d.e":"deep"},null,2)}],returnType:"JsonObject",returnDescription:'Flattened object with dot-notation keys \u2014 e.g. { "a.b.c": value }',guidelines:["Arrays and primitives at any level are always treated as leaf values \u2014 they are never traversed.","The result is always a flat JSON object with dot-notation keys.","There is no depth limit \u2014 objects of any nesting level are fully flattened.","Input is accepted as a JSON string (MCP/CLI) and parsed internally."]}},b=T.objectFlattenTool;import{z as p}from"zod";function E(e){let n=typeof e=="string"?JSON.parse(e):e,{first:t,last:o}=n.name,s=n.location.city;return`\uC774\uB984\uC740 ${t} ${o} \uC774\uACE0 \uD604\uC7AC ${s} \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4.`}var h={getUserTool:{name:"getUser",description:"RandomUser API \uD615\uC2DD\uC758 \uC0AC\uC6A9\uC790 \uAC1D\uCCB4\uB97C \uBC1B\uC544 \uC774\uB984\uACFC \uAC70\uC8FC \uB3C4\uC2DC\uB85C \uAD6C\uC131\uB41C \uD55C\uAE00 \uBB38\uC7A5\uC744 \uBC18\uD658\uD569\uB2C8\uB2E4. JSON \uBB38\uC790\uC5F4 \uB610\uB294 \uD30C\uC2F1\uB41C \uAC1D\uCCB4\uB97C \uC785\uB825\uBC1B\uC2B5\uB2C8\uB2E4.",inputSchema:{user:p.union([p.string(),p.record(p.string(),p.any())]).describe("RandomUser \uD615\uC2DD\uC758 JSON \uBB38\uC790\uC5F4 \uB610\uB294 \uAC1D\uCCB4 \u2014 name.first / name.last / location.city \uD544\uC218")},typeLabels:{user:"RandomUser"},typeDefs:{user:["interface RandomUser {",' gender: "male" | "female";'," name: {"," title: string;"," first: string;"," last: string;"," };"," location: {"," street: {"," number: number;"," name: string;"," };"," city: string;"," state: string;"," country: string;"," postcode: string | number;"," coordinates: {"," latitude: string;"," longitude: string;"," };"," timezone: {"," offset: string;"," description: string;"," };"," };"," email: string;"," login: {"," uuid: string;"," username: string;"," };"," dob: {"," date: string;"," age: number;"," };"," phone: string;"," cell: string;"," picture: {"," large: string;"," medium: string;"," thumbnail: string;"," };"," nat: string;","}"].join(`
3
+ `)},returnType:"string",returnDescription:'"\uC774\uB984\uC740 {first} {last} \uC774\uACE0 \uD604\uC7AC {city} \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4." \uD615\uC2DD\uC758 \uD55C\uAE00 \uBB38\uC7A5',handler:async({user:e})=>{try{let t=E(e);return r(t)}catch(n){return n instanceof SyntaxError?r("Error: invalid JSON string \u2014 unable to parse input"):r(`Error: failed to process user data \u2014 ${n.message}
4
4
 
5
- Input must include \`name.first\`, \`name.last\`, and \`location.city\`.`)}},examples:[{args:[`'{"name":{"title":"Mr","first":"Alice","last":"Kim"},"location":{"city":"Seoul"},"gender":"female","email":"alice@example.com","nat":"KR"}'`],result:"\uC774\uB984\uC740 Alice Kim \uC774\uACE0 \uD604\uC7AC Seoul \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4."}],guidelines:["\uC785\uB825\uC740 RandomUser API \uC2A4\uD399\uC744 \uB530\uB985\uB2C8\uB2E4. \uCD5C\uC18C\uD55C name.first, name.last, location.city \uD544\uB4DC\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.","JSON \uBB38\uC790\uC5F4(string)\uACFC \uD30C\uC2F1\uB41C \uAC1D\uCCB4 \uBAA8\uB450 \uD5C8\uC6A9\uB429\uB2C8\uB2E4 (CLI / MCP \uBAA8\uB450 \uB3D9\uC791).","\uACB0\uACFC\uB294 \uD56D\uC0C1 '\uC774\uB984\uC740 {first} {last} \uC774\uACE0 \uD604\uC7AC {city} \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4.' \uD615\uC2DD\uC758 \uD55C\uAE00 \uBB38\uC7A5\uC785\uB2C8\uB2E4."]}},T=y.getUserTool;var de={...a,...m,...y};var D=b({name:"mono-rele2-utils",version:"1.0.0"},[u,d,f,g,T]);S(D).catch(e=>{console.error("[utils] server error:",e),process.exit(1)});
5
+ Input must include \`name.first\`, \`name.last\`, and \`location.city\`.`)}},examples:[{args:[`'{"name":{"title":"Mr","first":"Alice","last":"Kim"},"location":{"city":"Seoul"},"gender":"female","email":"alice@example.com","nat":"KR"}'`],result:"\uC774\uB984\uC740 Alice Kim \uC774\uACE0 \uD604\uC7AC Seoul \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4."}],guidelines:["\uC785\uB825\uC740 RandomUser API \uC2A4\uD399\uC744 \uB530\uB985\uB2C8\uB2E4. \uCD5C\uC18C\uD55C name.first, name.last, location.city \uD544\uB4DC\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.","JSON \uBB38\uC790\uC5F4(string)\uACFC \uD30C\uC2F1\uB41C \uAC1D\uCCB4 \uBAA8\uB450 \uD5C8\uC6A9\uB429\uB2C8\uB2E4 (CLI / MCP \uBAA8\uB450 \uB3D9\uC791).","\uACB0\uACFC\uB294 \uD56D\uC0C1 '\uC774\uB984\uC740 {first} {last} \uC774\uACE0 \uD604\uC7AC {city} \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4.' \uD615\uC2DD\uC758 \uD55C\uAE00 \uBB38\uC7A5\uC785\uB2C8\uB2E4."]}},x=h.getUserTool;import{z as O}from"zod";var i=["API_KEY"];function J(e){let n=e.filter(o=>!i.includes(o));if(n.length>0)throw new Error(`Invalid env key(s): ${n.join(", ")}. Allowed keys: ${i.join(", ")}`);let t={};for(let o of e){let s=process.env[o];s!==void 0&&(t[o]=s)}return t}var S={envGetTool:{name:"env_get",description:`MCP \uD074\uB77C\uC774\uC5B8\uD2B8 config\uC758 env \uD544\uB4DC\uB97C \uD1B5\uD574 \uC8FC\uC785\uB41C \uD658\uACBD \uBCC0\uC218 \uAC12\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4. \uC870\uD68C \uAC00\uB2A5\uD55C \uD0A4\uB294 \uD328\uD0A4\uC9C0\uC5D0\uC11C \uC81C\uACF5\uD558\uB294 \uD658\uACBD \uBCC0\uC218\uB85C \uD55C\uC815\uB429\uB2C8\uB2E4. \uD604\uC7AC \uC9C0\uC6D0: ${i.join(", ")}.`,inputSchema:{keys:O.array(O.string()).nonempty().describe(`\uC870\uD68C\uD560 \uD658\uACBD \uBCC0\uC218 \uC774\uB984 \uBAA9\uB85D (\uC720\uD6A8 \uD0A4: ${i.join(", ")})`)},typeLabels:{keys:"string[]"},typeDefs:{keys:["// @julong/mono-rele2-utils \uD658\uACBD \uBCC0\uC218 \uD0A4 \uBAA9\uB85D",...i.map(e=>`// "${e}"`),"","// MCP client config \uC608\uC2DC:","// {",'// "mcpServers": {','// "@julong/mono-rele2-utils": {','// "command": "npx",','// "args": ["-y", "@julong/mono-rele2-utils"],','// "env": {',...i.map(e=>`// "${e}": "<value>"`),"// }","// }","// }","// }"].join(`
6
+ `)},returnType:"Record<string, string>",returnDescription:"key: \uD658\uACBD \uBCC0\uC218 \uC774\uB984, value: \uD574\uB2F9 \uAC12 (\uC124\uC815\uB418\uC9C0 \uC54A\uC740 \uBCC0\uC218\uB294 \uACB0\uACFC\uC5D0\uC11C \uC81C\uC678)",handler:async({keys:e})=>{try{let n=J(e);return Object.keys(n).length===0?r("(no matching environment variables found)"):r(JSON.stringify(n,null,2))}catch(n){return r(`Error: ${n.message}`)}},examples:[{args:[`'["API_KEY"]'`],result:JSON.stringify({API_KEY:"<value>"},null,2)}],guidelines:[`\uC870\uD68C \uAC00\uB2A5\uD55C \uD0A4: ${i.join(", ")}`,"MCP client config.json\uC758 env \uD544\uB4DC\uC5D0 \uC704 \uD0A4\uB4E4\uB9CC \uC124\uC815\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.","\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uD0A4\uB97C \uC694\uCCAD\uD558\uBA74 \uC5D0\uB7EC \uBA54\uC2DC\uC9C0\uC5D0 \uD5C8\uC6A9\uB41C \uD0A4 \uBAA9\uB85D\uC774 \uD45C\uC2DC\uB429\uB2C8\uB2E4.","\uC124\uC815\uB418\uC9C0 \uC54A\uC740 \uD658\uACBD \uBCC0\uC218\uB294 \uACB0\uACFC \uAC1D\uCCB4\uC5D0\uC11C \uC81C\uC678\uB429\uB2C8\uB2E4."]}},$=S.envGetTool;var Se={...l,...T,...h,...S};var C=v({name:"mono-rele2-utils",version:"1.0.0"},[m,g,y,b,x,$]);Z(C).catch(e=>{console.error("[utils] server error:",e),process.exit(1)});
@@ -17,7 +17,7 @@ Merges class names, filtering out falsy values
17
17
 
18
18
  | arg | description |
19
19
  |-----|-------------|
20
- | `classes` | List of class names to merge |
20
+ | `classes` | Type: string[] — List of class names to merge |
21
21
 
22
22
  ### caseConvertTool
23
23
 
@@ -25,8 +25,8 @@ Converts text to the specified case format
25
25
 
26
26
  | arg | description |
27
27
  |-----|-------------|
28
- | `input` | Text to convert |
29
- | `to` | Target case format — `upper` \| `lower` \| `capitalize` \| `camel` \| `snake` \| `kebab` |
28
+ | `input` | Type: string — Text to convert |
29
+ | `to` | Type: "upper" | "lower" | "capitalize" | "camel" | "snake" | "kebab" — Target case format — `upper` \| `lower` \| `capitalize` \| `camel` \| `snake` \| `kebab` |
30
30
 
31
31
  ### truncateTool
32
32
 
@@ -34,9 +34,9 @@ Truncates text to a maximum length and appends a suffix
34
34
 
35
35
  | arg | description |
36
36
  |-----|-------------|
37
- | `input` | Text to truncate |
38
- | `maxLength` | Maximum character length |
39
- | `suffix` | Suffix to append when truncated — default: `...` |
37
+ | `input` | Type: string — Text to truncate |
38
+ | `maxLength` | Type: number — Maximum character length |
39
+ | `suffix` | Type: string — Suffix to append when truncated — default: `...` |
40
40
 
41
41
  ### objectFlattenTool
42
42
 
@@ -101,6 +101,33 @@ interface RandomUser {
101
101
  }
102
102
  ```
103
103
 
104
+ ### envGetTool
105
+
106
+ MCP 클라이언트 config의 env 필드를 통해 주입된 환경 변수 값을 조회합니다. 조회 가능한 키는 패키지에서 제공하는 환경 변수로 한정됩니다. 현재 지원: API_KEY.
107
+
108
+ | arg | description |
109
+ |-----|-------------|
110
+ | `keys` | Type: string[] — 조회할 환경 변수 이름 목록 (유효 키: API_KEY) |
111
+
112
+ **`keys`** type definition:
113
+ ```typescript
114
+ // @julong/mono-rele2-utils 환경 변수 키 목록
115
+ // "API_KEY"
116
+
117
+ // MCP client config 예시:
118
+ // {
119
+ // "mcpServers": {
120
+ // "@julong/mono-rele2-utils": {
121
+ // "command": "npx",
122
+ // "args": ["-y", "@julong/mono-rele2-utils"],
123
+ // "env": {
124
+ // "API_KEY": "<value>"
125
+ // }
126
+ // }
127
+ // }
128
+ // }
129
+ ```
130
+
104
131
  ## Examples
105
132
 
106
133
  - `mono-rele2-utils-cli cnTool '["btn","active","large"]'` => `btn active large`
@@ -119,6 +146,9 @@ interface RandomUser {
119
146
  "a.b.c.d.e": "deep"
120
147
  }`
121
148
  - `mono-rele2-utils-cli getUserTool '{"name":{"title":"Mr","first":"Alice","last":"Kim"},"location":{"city":"Seoul"},"gender":"female","email":"alice@example.com","nat":"KR"}'` => `이름은 Alice Kim 이고 현재 Seoul 에 살고 있습니다.`
149
+ - `mono-rele2-utils-cli envGetTool '["API_KEY"]'` => `{
150
+ "API_KEY": "<value>"
151
+ }`
122
152
 
123
153
  ## Guidelines
124
154
 
@@ -133,4 +163,8 @@ interface RandomUser {
133
163
  - 입력은 RandomUser API 스펙을 따릅니다. 최소한 name.first, name.last, location.city 필드가 필요합니다.
134
164
  - JSON 문자열(string)과 파싱된 객체 모두 허용됩니다 (CLI / MCP 모두 동작).
135
165
  - 결과는 항상 '이름은 {first} {last} 이고 현재 {city} 에 살고 있습니다.' 형식의 한글 문장입니다.
166
+ - 조회 가능한 키: API_KEY
167
+ - MCP client config.json의 env 필드에 위 키들만 설정할 수 있습니다.
168
+ - 유효하지 않은 키를 요청하면 에러 메시지에 허용된 키 목록이 표시됩니다.
169
+ - 설정되지 않은 환경 변수는 결과 객체에서 제외됩니다.
136
170
  - Run `mono-rele2-utils-cli` with no args to list all available skills
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@julong/mono-rele2-utils",
3
- "version": "1.20.0",
3
+ "version": "1.22.0",
4
4
  "description": "Use this skill to invoke text utility functions via the mono-rele2-utils CLI. Handles class name merging, case conversion, and text truncation.",
5
5
  "license": "ISC",
6
6
  "type": "module",