@402md/skillmd 0.1.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/dist/index.js ADDED
@@ -0,0 +1,723 @@
1
+ // src/parse.ts
2
+ import YAML from "yaml";
3
+
4
+ // src/constants.ts
5
+ var SKILL_TYPES = [
6
+ "API",
7
+ "SAAS",
8
+ "PRODUCT",
9
+ "SERVICE",
10
+ "SUBSCRIPTION",
11
+ "CONTENT"
12
+ ];
13
+ var HTTP_METHODS = [
14
+ "GET",
15
+ "POST",
16
+ "PUT",
17
+ "DELETE",
18
+ "PATCH"
19
+ ];
20
+ var PAYMENT_NETWORKS = [
21
+ "stellar",
22
+ "base",
23
+ "base-sepolia",
24
+ "stellar-testnet"
25
+ ];
26
+ var SKILL_TYPES_SET = new Set(SKILL_TYPES);
27
+ var HTTP_METHODS_SET = new Set(HTTP_METHODS);
28
+ var PAYMENT_NETWORKS_SET = new Set(PAYMENT_NETWORKS);
29
+ var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
30
+
31
+ // src/parse.ts
32
+ function parseSkillMd(content) {
33
+ const { data, body } = extractFrontmatter(content);
34
+ return buildManifest(data, body);
35
+ }
36
+ function parseFrontmatter(md) {
37
+ const match = md.match(FRONTMATTER_RE);
38
+ if (!match) {
39
+ return { data: { name: "unknown" }, body: md };
40
+ }
41
+ const data = YAML.parse(match[1]);
42
+ return { data, body: match[2] };
43
+ }
44
+ function extractFrontmatter(content) {
45
+ const match = content.match(FRONTMATTER_RE);
46
+ if (!match) {
47
+ throw new Error("Invalid SKILL.md: missing frontmatter delimiters (---)");
48
+ }
49
+ const data = YAML.parse(match[1]);
50
+ return { data, body: match[2].trim() };
51
+ }
52
+ function buildManifest(data, body) {
53
+ const name = requireString(data, "name");
54
+ const description = requireString(data, "description");
55
+ const base_url = requireString(data, "base_url");
56
+ const rawType = getString(data, "type") ?? "API";
57
+ const type = SKILL_TYPES_SET.has(rawType) ? rawType : "API";
58
+ const payment = parsePayment(data);
59
+ const endpoints = parseEndpoints(data);
60
+ return {
61
+ name,
62
+ displayName: getString(data, "displayName"),
63
+ description,
64
+ version: getString(data, "version"),
65
+ author: getString(data, "author"),
66
+ base_url,
67
+ type,
68
+ payment,
69
+ endpoints,
70
+ tags: getStringArray(data, "tags"),
71
+ category: getString(data, "category"),
72
+ sla: getString(data, "sla"),
73
+ rateLimit: getString(data, "rateLimit"),
74
+ sandbox: getString(data, "sandbox"),
75
+ body
76
+ };
77
+ }
78
+ function parsePayment(data) {
79
+ const raw = data.payment;
80
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
81
+ const payment = raw;
82
+ const networks = parseNetworks(payment.networks);
83
+ return {
84
+ networks,
85
+ asset: getString(payment, "asset") ?? "USDC",
86
+ payTo: requireString(payment, "payTo"),
87
+ payToEvm: getString(payment, "payToEvm"),
88
+ facilitator: getString(payment, "facilitator")
89
+ };
90
+ }
91
+ return {
92
+ networks: ["base"],
93
+ asset: "USDC",
94
+ payTo: ""
95
+ };
96
+ }
97
+ function parseNetworks(raw) {
98
+ if (!Array.isArray(raw)) return ["base"];
99
+ return raw.filter((n) => typeof n === "string" && PAYMENT_NETWORKS_SET.has(n)).map((n) => n);
100
+ }
101
+ function parseEndpoints(data) {
102
+ const raw = data.endpoints;
103
+ if (!Array.isArray(raw)) return [];
104
+ return raw.filter((e) => e && typeof e === "object").map((e) => {
105
+ const ep = e;
106
+ const method = (getString(ep, "method") ?? "POST").toUpperCase();
107
+ return {
108
+ path: getString(ep, "path") ?? "/",
109
+ method: HTTP_METHODS_SET.has(method) ? method : "POST",
110
+ description: getString(ep, "description") ?? "",
111
+ priceUsdc: getString(ep, "priceUsdc") ?? getString(ep, "price") ?? "0",
112
+ inputSchema: getObject(ep, "inputSchema"),
113
+ outputSchema: getObject(ep, "outputSchema")
114
+ };
115
+ });
116
+ }
117
+ function requireString(obj, key) {
118
+ const val = obj[key];
119
+ if (typeof val === "string") return val;
120
+ if (typeof val === "number" || typeof val === "boolean") return String(val);
121
+ throw new Error(`Missing required field: ${key}`);
122
+ }
123
+ function getString(obj, key) {
124
+ const val = obj[key];
125
+ if (typeof val === "string") return val;
126
+ if (typeof val === "number" || typeof val === "boolean") return String(val);
127
+ return void 0;
128
+ }
129
+ function getStringArray(obj, key) {
130
+ const val = obj[key];
131
+ if (!Array.isArray(val)) return void 0;
132
+ return val.filter((v) => typeof v === "string");
133
+ }
134
+ function getObject(obj, key) {
135
+ const val = obj[key];
136
+ if (val && typeof val === "object" && !Array.isArray(val)) {
137
+ return val;
138
+ }
139
+ return void 0;
140
+ }
141
+
142
+ // src/validate.ts
143
+ var NAME_RE = /^[a-z0-9][a-z0-9_-]*$/;
144
+ var SEMVER_RE = /^\d+\.\d+\.\d+/;
145
+ var PRICE_RE = /^\d+(\.\d+)?$/;
146
+ var EVM_ADDRESS_RE = /^0x[a-fA-F0-9]{40}$/;
147
+ var STELLAR_ADDRESS_RE = /^G[A-Z2-7]{55}$/;
148
+ function validateSkill(manifest) {
149
+ const errors = [];
150
+ const warnings = [];
151
+ validateName(manifest.name, errors);
152
+ validateDescription(manifest.description, errors);
153
+ validateBaseUrl(manifest.base_url, errors);
154
+ validateType(manifest.type, errors);
155
+ validatePayment(manifest.payment, errors, warnings);
156
+ validateEndpoints(manifest.endpoints, errors, warnings);
157
+ validateVersion(manifest.version, warnings);
158
+ validateTags(manifest.tags, warnings);
159
+ return {
160
+ valid: errors.length === 0,
161
+ errors,
162
+ warnings
163
+ };
164
+ }
165
+ function validateSkillMd(content) {
166
+ try {
167
+ const manifest = parseSkillMd(content);
168
+ return validateSkill(manifest);
169
+ } catch (err) {
170
+ return {
171
+ valid: false,
172
+ errors: [
173
+ {
174
+ field: "frontmatter",
175
+ message: err instanceof Error ? err.message : "Failed to parse SKILL.md",
176
+ code: "PARSE_ERROR"
177
+ }
178
+ ],
179
+ warnings: []
180
+ };
181
+ }
182
+ }
183
+ function validateName(name, errors) {
184
+ if (!name) {
185
+ errors.push({
186
+ field: "name",
187
+ message: "Name is required",
188
+ code: "REQUIRED"
189
+ });
190
+ return;
191
+ }
192
+ if (!NAME_RE.test(name)) {
193
+ errors.push({
194
+ field: "name",
195
+ message: "Name must be kebab-case (lowercase letters, numbers, hyphens, underscores)",
196
+ code: "INVALID_FORMAT"
197
+ });
198
+ }
199
+ if (name.length > 100) {
200
+ errors.push({
201
+ field: "name",
202
+ message: "Name must be 100 characters or fewer",
203
+ code: "TOO_LONG"
204
+ });
205
+ }
206
+ }
207
+ function validateDescription(description, errors) {
208
+ if (!description) {
209
+ errors.push({
210
+ field: "description",
211
+ message: "Description is required",
212
+ code: "REQUIRED"
213
+ });
214
+ }
215
+ if (description && description.length > 2e3) {
216
+ errors.push({
217
+ field: "description",
218
+ message: "Description must be 2000 characters or fewer",
219
+ code: "TOO_LONG"
220
+ });
221
+ }
222
+ }
223
+ function validateBaseUrl(base_url, errors) {
224
+ if (!base_url) {
225
+ errors.push({
226
+ field: "base_url",
227
+ message: "base_url is required",
228
+ code: "REQUIRED"
229
+ });
230
+ return;
231
+ }
232
+ try {
233
+ new URL(base_url);
234
+ } catch {
235
+ errors.push({
236
+ field: "base_url",
237
+ message: "base_url must be a valid URL",
238
+ code: "INVALID_URL"
239
+ });
240
+ }
241
+ }
242
+ function validateType(type, errors) {
243
+ if (!SKILL_TYPES_SET.has(type)) {
244
+ errors.push({
245
+ field: "type",
246
+ message: `Invalid type "${type}". Must be one of: ${[...SKILL_TYPES_SET].join(", ")}`,
247
+ code: "INVALID_ENUM"
248
+ });
249
+ }
250
+ }
251
+ function validatePayment(payment, errors, warnings) {
252
+ if (!payment.networks || payment.networks.length === 0) {
253
+ errors.push({
254
+ field: "payment.networks",
255
+ message: "At least one payment network is required",
256
+ code: "REQUIRED"
257
+ });
258
+ } else {
259
+ for (const network of payment.networks) {
260
+ if (!PAYMENT_NETWORKS_SET.has(network)) {
261
+ errors.push({
262
+ field: "payment.networks",
263
+ message: `Invalid network "${network}". Must be one of: ${[...PAYMENT_NETWORKS_SET].join(", ")}`,
264
+ code: "INVALID_ENUM"
265
+ });
266
+ }
267
+ }
268
+ }
269
+ if (!payment.payTo) {
270
+ errors.push({
271
+ field: "payment.payTo",
272
+ message: "payTo address is required",
273
+ code: "REQUIRED"
274
+ });
275
+ } else {
276
+ const isEvm = EVM_ADDRESS_RE.test(payment.payTo);
277
+ const isStellar = STELLAR_ADDRESS_RE.test(payment.payTo);
278
+ if (!isEvm && !isStellar) {
279
+ warnings.push({
280
+ field: "payment.payTo",
281
+ message: "payTo does not look like a valid Stellar or EVM address",
282
+ code: "SUSPICIOUS_ADDRESS"
283
+ });
284
+ }
285
+ }
286
+ if (payment.payToEvm && !EVM_ADDRESS_RE.test(payment.payToEvm)) {
287
+ errors.push({
288
+ field: "payment.payToEvm",
289
+ message: "payToEvm must be a valid EVM address (0x...)",
290
+ code: "INVALID_FORMAT"
291
+ });
292
+ }
293
+ if (payment.facilitator) {
294
+ try {
295
+ new URL(payment.facilitator);
296
+ } catch {
297
+ errors.push({
298
+ field: "payment.facilitator",
299
+ message: "facilitator must be a valid URL",
300
+ code: "INVALID_URL"
301
+ });
302
+ }
303
+ }
304
+ if (!payment.asset) {
305
+ warnings.push({
306
+ field: "payment.asset",
307
+ message: "No asset specified, defaulting to USDC",
308
+ code: "MISSING_OPTIONAL"
309
+ });
310
+ }
311
+ }
312
+ function validateEndpoints(endpoints, errors, warnings) {
313
+ if (!endpoints || endpoints.length === 0) {
314
+ errors.push({
315
+ field: "endpoints",
316
+ message: "At least one endpoint is required",
317
+ code: "REQUIRED"
318
+ });
319
+ return;
320
+ }
321
+ const seen = /* @__PURE__ */ new Set();
322
+ for (let i = 0; i < endpoints.length; i++) {
323
+ const ep = endpoints[i];
324
+ const prefix = `endpoints[${i}]`;
325
+ if (!ep.path || !ep.path.startsWith("/")) {
326
+ errors.push({
327
+ field: `${prefix}.path`,
328
+ message: "Endpoint path must start with /",
329
+ code: "INVALID_FORMAT"
330
+ });
331
+ }
332
+ if (!HTTP_METHODS_SET.has(ep.method)) {
333
+ errors.push({
334
+ field: `${prefix}.method`,
335
+ message: `Invalid method "${ep.method}"`,
336
+ code: "INVALID_ENUM"
337
+ });
338
+ }
339
+ if (!ep.description) {
340
+ warnings.push({
341
+ field: `${prefix}.description`,
342
+ message: "Endpoint is missing a description",
343
+ code: "MISSING_OPTIONAL"
344
+ });
345
+ }
346
+ if (!PRICE_RE.test(ep.priceUsdc)) {
347
+ errors.push({
348
+ field: `${prefix}.priceUsdc`,
349
+ message: `Invalid price "${ep.priceUsdc}". Must be a decimal string (e.g. "0.001")`,
350
+ code: "INVALID_FORMAT"
351
+ });
352
+ }
353
+ const key = `${ep.method} ${ep.path}`;
354
+ if (seen.has(key)) {
355
+ errors.push({
356
+ field: `${prefix}`,
357
+ message: `Duplicate endpoint: ${key}`,
358
+ code: "DUPLICATE"
359
+ });
360
+ }
361
+ seen.add(key);
362
+ }
363
+ }
364
+ function validateVersion(version, warnings) {
365
+ if (!version) {
366
+ warnings.push({
367
+ field: "version",
368
+ message: "No version specified",
369
+ code: "MISSING_OPTIONAL"
370
+ });
371
+ return;
372
+ }
373
+ if (!SEMVER_RE.test(version)) {
374
+ warnings.push({
375
+ field: "version",
376
+ message: 'Version should follow semver (e.g. "1.0.0")',
377
+ code: "INVALID_FORMAT"
378
+ });
379
+ }
380
+ }
381
+ function validateTags(tags, warnings) {
382
+ if (tags && tags.length > 20) {
383
+ warnings.push({
384
+ field: "tags",
385
+ message: "Too many tags (max 20)",
386
+ code: "TOO_MANY"
387
+ });
388
+ }
389
+ }
390
+
391
+ // src/generate.ts
392
+ import YAML2 from "yaml";
393
+ function generateSkillMd(config) {
394
+ const frontmatter = buildFrontmatter(config);
395
+ const yaml = YAML2.stringify(frontmatter, {
396
+ lineWidth: 0,
397
+ defaultStringType: "PLAIN",
398
+ defaultKeyType: "PLAIN"
399
+ }).trimEnd();
400
+ const body = config.body ?? generateDefaultBody(config.displayName ?? config.name, config.description);
401
+ return `---
402
+ ${yaml}
403
+ ---
404
+
405
+ ${body}
406
+ `;
407
+ }
408
+ function generateFromOpenAPI(spec, payment, options) {
409
+ const baseUrl = options?.baseUrlOverride ?? spec.servers?.[0]?.url ?? "https://api.example.com";
410
+ const defaultPrice = options?.defaultPrice ?? "0.001";
411
+ const pricing = options?.pricing;
412
+ const endpoints = [];
413
+ for (const [path, methods] of Object.entries(spec.paths)) {
414
+ const methodEntries = [
415
+ ["GET", methods.get],
416
+ ["POST", methods.post],
417
+ ["PUT", methods.put],
418
+ ["DELETE", methods.delete],
419
+ ["PATCH", methods.patch]
420
+ ];
421
+ for (const [method, operation] of methodEntries) {
422
+ if (!operation) continue;
423
+ const inputSchema = extractInputSchema(operation);
424
+ const outputSchema = extractOutputSchema(operation);
425
+ endpoints.push({
426
+ path,
427
+ method,
428
+ description: operation.summary ?? operation.description ?? `${method} ${path}`,
429
+ priceUsdc: resolvePrice(method, path, pricing, defaultPrice),
430
+ ...inputSchema && { inputSchema },
431
+ ...outputSchema && { outputSchema }
432
+ });
433
+ }
434
+ }
435
+ return {
436
+ name: slugify(spec.info.title),
437
+ displayName: spec.info.title,
438
+ description: spec.info.description ?? spec.info.title,
439
+ version: spec.info.version,
440
+ base_url: baseUrl,
441
+ type: "API",
442
+ payment,
443
+ endpoints,
444
+ body: ""
445
+ };
446
+ }
447
+ function toOpenAPI(manifest) {
448
+ const paths = {};
449
+ for (const ep of manifest.endpoints) {
450
+ const method = ep.method.toLowerCase();
451
+ if (!paths[ep.path]) paths[ep.path] = {};
452
+ const operation = {
453
+ summary: ep.description,
454
+ operationId: `${manifest.name}_${method}_${ep.path.replace(/\//g, "_").replace(/^_/, "")}`
455
+ };
456
+ if (ep.inputSchema && ["post", "put", "patch"].includes(method)) {
457
+ operation.requestBody = {
458
+ content: {
459
+ "application/json": { schema: ep.inputSchema }
460
+ }
461
+ };
462
+ }
463
+ operation.responses = {
464
+ "200": {
465
+ description: "Successful response",
466
+ ...ep.outputSchema && {
467
+ content: {
468
+ "application/json": { schema: ep.outputSchema }
469
+ }
470
+ }
471
+ },
472
+ "402": {
473
+ description: `Payment Required \u2014 ${ep.priceUsdc} USDC`
474
+ }
475
+ };
476
+ paths[ep.path][method] = operation;
477
+ }
478
+ return {
479
+ openapi: "3.0.3",
480
+ info: {
481
+ title: manifest.displayName ?? manifest.name,
482
+ description: manifest.description,
483
+ version: manifest.version ?? "1.0.0"
484
+ },
485
+ servers: [{ url: manifest.base_url }],
486
+ paths
487
+ };
488
+ }
489
+ function buildFrontmatter(config) {
490
+ const fm = {
491
+ name: config.name
492
+ };
493
+ if (config.displayName) fm.displayName = config.displayName;
494
+ fm.description = config.description;
495
+ if (config.version) fm.version = config.version;
496
+ if (config.author) fm.author = config.author;
497
+ fm.base_url = config.base_url;
498
+ fm.type = config.type ?? "API";
499
+ fm.payment = {
500
+ networks: config.payment.networks,
501
+ asset: config.payment.asset || "USDC",
502
+ payTo: config.payment.payTo,
503
+ ...config.payment.payToEvm && {
504
+ payToEvm: config.payment.payToEvm
505
+ },
506
+ ...config.payment.facilitator && {
507
+ facilitator: config.payment.facilitator
508
+ }
509
+ };
510
+ fm.endpoints = config.endpoints.map((ep) => {
511
+ const entry = {
512
+ path: ep.path,
513
+ method: ep.method,
514
+ description: ep.description,
515
+ priceUsdc: ep.priceUsdc
516
+ };
517
+ if (ep.inputSchema) entry.inputSchema = ep.inputSchema;
518
+ if (ep.outputSchema) entry.outputSchema = ep.outputSchema;
519
+ return entry;
520
+ });
521
+ if (config.tags?.length) fm.tags = config.tags;
522
+ if (config.category) fm.category = config.category;
523
+ if (config.sla) fm.sla = config.sla;
524
+ if (config.rateLimit) fm.rateLimit = config.rateLimit;
525
+ if (config.sandbox) fm.sandbox = config.sandbox;
526
+ return fm;
527
+ }
528
+ function generateDefaultBody(name, description) {
529
+ return `# ${name}
530
+
531
+ ${description}`;
532
+ }
533
+ function extractInputSchema(op) {
534
+ const content = op.requestBody?.content;
535
+ if (!content) return void 0;
536
+ const json = content["application/json"] ?? Object.values(content)[0];
537
+ return json?.schema;
538
+ }
539
+ function extractOutputSchema(op) {
540
+ if (!op.responses) return void 0;
541
+ const successResponse = op.responses["200"] ?? op.responses["201"] ?? op.responses["2xx"];
542
+ if (!successResponse?.content) return void 0;
543
+ const json = successResponse.content["application/json"] ?? Object.values(successResponse.content)[0];
544
+ return json?.schema;
545
+ }
546
+ function resolvePrice(method, path, pricing, defaultPrice) {
547
+ if (!pricing) return defaultPrice;
548
+ return pricing[`${method} ${path}`] ?? pricing["*"] ?? defaultPrice;
549
+ }
550
+ function slugify(text) {
551
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
552
+ }
553
+
554
+ // src/mcp.ts
555
+ function toMcpToolDefinitions(manifest) {
556
+ return manifest.endpoints.map((ep) => {
557
+ const slug = ep.path.replace(/^\//, "").replace(/\//g, "_").replace(/[^a-zA-Z0-9_-]/g, "");
558
+ const network = manifest.payment.networks[0] ?? "unknown";
559
+ return {
560
+ name: `${manifest.name}_${slug}`,
561
+ description: `${ep.description} (${ep.priceUsdc} USDC via ${network})`,
562
+ inputSchema: ep.inputSchema ?? { type: "object", properties: {} }
563
+ };
564
+ });
565
+ }
566
+
567
+ // src/schema.ts
568
+ var SKILLMD_JSON_SCHEMA = {
569
+ $schema: "https://json-schema.org/draft/2020-12/schema",
570
+ title: "SKILL.md Frontmatter",
571
+ description: "Schema for the YAML frontmatter in a SKILL.md file",
572
+ type: "object",
573
+ required: ["name", "description", "base_url", "payment", "endpoints"],
574
+ properties: {
575
+ name: {
576
+ type: "string",
577
+ pattern: "^[a-z0-9][a-z0-9_-]*$",
578
+ minLength: 1,
579
+ maxLength: 100,
580
+ description: "Unique skill identifier (kebab-case)"
581
+ },
582
+ displayName: {
583
+ type: "string",
584
+ maxLength: 200,
585
+ description: "Human-readable name"
586
+ },
587
+ description: {
588
+ type: "string",
589
+ minLength: 1,
590
+ maxLength: 2e3,
591
+ description: "What this skill does"
592
+ },
593
+ version: {
594
+ type: "string",
595
+ pattern: "^\\d+\\.\\d+\\.\\d+",
596
+ description: "Semantic version"
597
+ },
598
+ author: {
599
+ type: "string",
600
+ maxLength: 100
601
+ },
602
+ base_url: {
603
+ type: "string",
604
+ format: "uri",
605
+ description: "Base URL of the API"
606
+ },
607
+ type: {
608
+ type: "string",
609
+ enum: [...SKILL_TYPES],
610
+ default: "API"
611
+ },
612
+ payment: {
613
+ type: "object",
614
+ required: ["networks", "payTo"],
615
+ properties: {
616
+ networks: {
617
+ type: "array",
618
+ items: {
619
+ type: "string",
620
+ enum: [...PAYMENT_NETWORKS]
621
+ },
622
+ minItems: 1,
623
+ description: "Supported payment networks"
624
+ },
625
+ asset: {
626
+ type: "string",
627
+ default: "USDC",
628
+ description: "Payment asset"
629
+ },
630
+ payTo: {
631
+ type: "string",
632
+ minLength: 1,
633
+ description: "Recipient address (Stellar or EVM)"
634
+ },
635
+ payToEvm: {
636
+ type: "string",
637
+ pattern: "^0x[a-fA-F0-9]{40}$",
638
+ description: "EVM address (fallback)"
639
+ },
640
+ facilitator: {
641
+ type: "string",
642
+ format: "uri",
643
+ description: "Facilitator URL"
644
+ }
645
+ },
646
+ additionalProperties: false
647
+ },
648
+ endpoints: {
649
+ type: "array",
650
+ items: {
651
+ type: "object",
652
+ required: ["path", "method", "description", "priceUsdc"],
653
+ properties: {
654
+ path: {
655
+ type: "string",
656
+ pattern: "^/",
657
+ description: "Endpoint path (must start with /)"
658
+ },
659
+ method: {
660
+ type: "string",
661
+ enum: [...HTTP_METHODS]
662
+ },
663
+ description: {
664
+ type: "string",
665
+ minLength: 1
666
+ },
667
+ priceUsdc: {
668
+ type: "string",
669
+ pattern: "^\\d+(\\.\\d+)?$",
670
+ description: 'Price in USDC (e.g. "0.001")'
671
+ },
672
+ inputSchema: {
673
+ type: "object",
674
+ description: "JSON Schema for request body"
675
+ },
676
+ outputSchema: {
677
+ type: "object",
678
+ description: "JSON Schema for response body"
679
+ }
680
+ },
681
+ additionalProperties: false
682
+ },
683
+ minItems: 1
684
+ },
685
+ tags: {
686
+ type: "array",
687
+ items: { type: "string" },
688
+ maxItems: 20
689
+ },
690
+ category: {
691
+ type: "string"
692
+ },
693
+ sla: {
694
+ type: "string",
695
+ description: 'Uptime guarantee (e.g. "99.9%")'
696
+ },
697
+ rateLimit: {
698
+ type: "string",
699
+ description: 'Rate limit (e.g. "1000/hour")'
700
+ },
701
+ sandbox: {
702
+ type: "string",
703
+ format: "uri",
704
+ description: "Free test endpoint URL"
705
+ }
706
+ },
707
+ additionalProperties: true
708
+ };
709
+ export {
710
+ HTTP_METHODS,
711
+ PAYMENT_NETWORKS,
712
+ SKILLMD_JSON_SCHEMA,
713
+ SKILL_TYPES,
714
+ generateFromOpenAPI,
715
+ generateSkillMd,
716
+ parseFrontmatter,
717
+ parseSkillMd,
718
+ toMcpToolDefinitions,
719
+ toOpenAPI,
720
+ validateSkill,
721
+ validateSkillMd
722
+ };
723
+ //# sourceMappingURL=index.js.map