@heyclaude/mcp 0.1.2 → 0.2.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/CHANGELOG.md +13 -0
- package/README.md +49 -4
- package/package.json +1 -1
- package/scripts/validate-endpoint.mjs +172 -6
- package/src/registry.d.ts +91 -0
- package/src/registry.js +1036 -11
- package/src/remote-proxy.d.ts +20 -0
- package/src/remote-proxy.js +155 -42
- package/src/schemas.d.ts +12 -0
- package/src/schemas.js +119 -0
- package/src/server.js +40 -1
- package/src/submissions.d.ts +13 -0
- package/src/submissions.js +226 -0
package/src/submissions.js
CHANGED
|
@@ -472,6 +472,232 @@ export function validateSubmissionDraftFromSpec(spec, args = {}) {
|
|
|
472
472
|
};
|
|
473
473
|
}
|
|
474
474
|
|
|
475
|
+
function singularLabel(value) {
|
|
476
|
+
const label = normalizeText(value || "Entry");
|
|
477
|
+
return label.endsWith("s") ? label.slice(0, -1) : label;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function exampleValueForField(fieldId, category, label) {
|
|
481
|
+
switch (fieldId) {
|
|
482
|
+
case "name":
|
|
483
|
+
case "title":
|
|
484
|
+
return `Example ${singularLabel(label)}`;
|
|
485
|
+
case "slug":
|
|
486
|
+
return `example-${category || "entry"}`;
|
|
487
|
+
case "category":
|
|
488
|
+
return category;
|
|
489
|
+
case "github_url":
|
|
490
|
+
return `https://github.com/example/example-${category || "entry"}`;
|
|
491
|
+
case "docs_url":
|
|
492
|
+
case "source_url":
|
|
493
|
+
return `https://example.com/${category || "entry"}/docs`;
|
|
494
|
+
case "download_url":
|
|
495
|
+
return `https://example.com/${category || "entry"}/download.zip`;
|
|
496
|
+
case "brand_name":
|
|
497
|
+
return "Example";
|
|
498
|
+
case "brand_domain":
|
|
499
|
+
return "example.com";
|
|
500
|
+
case "author":
|
|
501
|
+
return "@example";
|
|
502
|
+
case "contact_email":
|
|
503
|
+
return "@example";
|
|
504
|
+
case "tags":
|
|
505
|
+
return "claude, workflow, example";
|
|
506
|
+
case "description":
|
|
507
|
+
return `A practical ${singularLabel(label).toLowerCase()} for Claude users that includes source-backed setup and usage details.`;
|
|
508
|
+
case "card_description":
|
|
509
|
+
return `Practical ${singularLabel(label).toLowerCase()} for Claude workflows.`;
|
|
510
|
+
case "full_copyable_content":
|
|
511
|
+
return `# Example ${singularLabel(label)}\n\nUse this complete content as the copyable public artifact.`;
|
|
512
|
+
case "install_command":
|
|
513
|
+
return `npx -y example-${category || "entry"}`;
|
|
514
|
+
case "usage_snippet":
|
|
515
|
+
return `Use this ${singularLabel(label).toLowerCase()} to speed up a Claude workflow.`;
|
|
516
|
+
case "command_syntax":
|
|
517
|
+
return `/example-${category || "entry"} <input>`;
|
|
518
|
+
case "trigger":
|
|
519
|
+
return "PostToolUse";
|
|
520
|
+
case "guide_content":
|
|
521
|
+
return `# Example ${singularLabel(label)}\n\nExplain the setup, usage, verification, and troubleshooting steps.`;
|
|
522
|
+
case "items":
|
|
523
|
+
return `- Example ${singularLabel(label)} item\n- Source-backed companion resource`;
|
|
524
|
+
case "script_language":
|
|
525
|
+
return "bash";
|
|
526
|
+
case "skill_type":
|
|
527
|
+
return "workflow";
|
|
528
|
+
case "skill_level":
|
|
529
|
+
return "intermediate";
|
|
530
|
+
case "verification_status":
|
|
531
|
+
return "validated";
|
|
532
|
+
case "verified_at":
|
|
533
|
+
return "2026-05-17";
|
|
534
|
+
case "config_snippet":
|
|
535
|
+
return JSON.stringify({ example: true }, null, 2);
|
|
536
|
+
case "retrieval_sources":
|
|
537
|
+
return "- https://example.com/docs";
|
|
538
|
+
case "tested_platforms":
|
|
539
|
+
return "Claude Code, Codex, Cursor";
|
|
540
|
+
case "prerequisites":
|
|
541
|
+
return "- Node.js 20+\n- Claude-compatible MCP client";
|
|
542
|
+
case "troubleshooting_section":
|
|
543
|
+
return "If setup fails, verify the install command and source URL first.";
|
|
544
|
+
case "installation_order":
|
|
545
|
+
return "Install dependencies, configure the entry, then run the verification step.";
|
|
546
|
+
case "estimated_setup_time":
|
|
547
|
+
return "10 minutes";
|
|
548
|
+
case "difficulty":
|
|
549
|
+
return "intermediate";
|
|
550
|
+
default:
|
|
551
|
+
return `Example ${fieldId.replaceAll("_", " ")}`;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
function exampleForCategory(spec, category) {
|
|
556
|
+
const model = modelFor(spec, category);
|
|
557
|
+
const fields = Object.fromEntries(
|
|
558
|
+
(model?.fields || []).map((field) => [
|
|
559
|
+
field.id,
|
|
560
|
+
exampleValueForField(field.id, category, model?.label),
|
|
561
|
+
]),
|
|
562
|
+
);
|
|
563
|
+
fields.category = category;
|
|
564
|
+
|
|
565
|
+
const minimalFields = {};
|
|
566
|
+
for (const field of requiredFields(model)) {
|
|
567
|
+
minimalFields[field] = fields[field];
|
|
568
|
+
}
|
|
569
|
+
minimalFields.category = category;
|
|
570
|
+
|
|
571
|
+
for (const field of [
|
|
572
|
+
"name",
|
|
573
|
+
"description",
|
|
574
|
+
"github_url",
|
|
575
|
+
"docs_url",
|
|
576
|
+
"install_command",
|
|
577
|
+
"usage_snippet",
|
|
578
|
+
"download_url",
|
|
579
|
+
"guide_content",
|
|
580
|
+
"items",
|
|
581
|
+
]) {
|
|
582
|
+
if (fields[field]) minimalFields[field] = fields[field];
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
return {
|
|
586
|
+
category,
|
|
587
|
+
label: model?.label || category,
|
|
588
|
+
template: model?.template || templateFor(spec, category)?.template || "",
|
|
589
|
+
requiredFields: requiredFields(model),
|
|
590
|
+
minimalFields,
|
|
591
|
+
completeFields: fields,
|
|
592
|
+
notes: [
|
|
593
|
+
"Use canonical source URLs and avoid affiliate/referral links.",
|
|
594
|
+
"The generated issue draft is a maintainer-reviewed submission, not automatic publication.",
|
|
595
|
+
],
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
export function getSubmissionExamplesFromSpec(spec, args = {}) {
|
|
600
|
+
const category = selectedCategory(spec, args.category);
|
|
601
|
+
if (args.category && !category) {
|
|
602
|
+
return {
|
|
603
|
+
ok: false,
|
|
604
|
+
error: {
|
|
605
|
+
code: "not_found",
|
|
606
|
+
message: `No HeyClaude submission examples found for ${args.category}.`,
|
|
607
|
+
},
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
const categories = category ? [category] : categoryKeys(spec);
|
|
612
|
+
return {
|
|
613
|
+
ok: true,
|
|
614
|
+
categories: categories.map((key) => exampleForCategory(spec, key)),
|
|
615
|
+
reviewModel:
|
|
616
|
+
"Examples are draft helpers only; maintainers review accepted submissions before import.",
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
export function prepareSubmissionDraftFromSpec(spec, args = {}) {
|
|
621
|
+
const fields = normalizeSubmissionFields(args.fields || {});
|
|
622
|
+
const validation = validateAgainstSpec(spec, fields);
|
|
623
|
+
const issueDraft = validation.category
|
|
624
|
+
? buildIssueDraftFromSpec(spec, validation.normalized)
|
|
625
|
+
: null;
|
|
626
|
+
const urls = buildSubmissionUrlsFromSpec(spec, {
|
|
627
|
+
fields: validation.normalized,
|
|
628
|
+
includeIssueBody: true,
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
return {
|
|
632
|
+
ok: true,
|
|
633
|
+
valid: validation.valid,
|
|
634
|
+
category: validation.category,
|
|
635
|
+
slug: validation.normalized.slug || "",
|
|
636
|
+
normalizedFields: validation.normalized,
|
|
637
|
+
requiredFields: validation.requiredFields || [],
|
|
638
|
+
missingRequiredFields: validation.missingRequiredFields || [],
|
|
639
|
+
errors: validation.errors,
|
|
640
|
+
warnings: validation.warnings,
|
|
641
|
+
issueDraft,
|
|
642
|
+
submitUrl: urls.submitUrl,
|
|
643
|
+
githubIssueUrl: urls.githubIssueUrl,
|
|
644
|
+
reviewChecklist: [
|
|
645
|
+
"Confirm category fit and required fields before opening the issue.",
|
|
646
|
+
"Check for existing registry entries with the same source, slug, or title.",
|
|
647
|
+
"Verify source URLs, install commands, and copied content before maintainer approval.",
|
|
648
|
+
"Disclose paid, sponsored, affiliate, or commercial content separately from free community submissions.",
|
|
649
|
+
],
|
|
650
|
+
submissionPolicy:
|
|
651
|
+
"This tool prepares a review issue only. HeyClaude does not auto-publish MCP-submitted content.",
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
export function reviewSubmissionDraftFromSpec(spec, args = {}, entries = []) {
|
|
656
|
+
const validation = validateAgainstSpec(spec, args.fields || {});
|
|
657
|
+
const duplicateArgs = {
|
|
658
|
+
category: validation.category,
|
|
659
|
+
slug: validation.normalized.slug,
|
|
660
|
+
title: validation.normalized.title || validation.normalized.name,
|
|
661
|
+
name: validation.normalized.name,
|
|
662
|
+
sourceUrl:
|
|
663
|
+
validation.normalized.github_url ||
|
|
664
|
+
validation.normalized.docs_url ||
|
|
665
|
+
validation.normalized.source_url,
|
|
666
|
+
brandDomain: validation.normalized.brand_domain,
|
|
667
|
+
limit: args.duplicateLimit || 5,
|
|
668
|
+
};
|
|
669
|
+
const duplicates = searchDuplicateEntries(entries, duplicateArgs);
|
|
670
|
+
const recommendedAction = validation.valid
|
|
671
|
+
? duplicates.count > 0
|
|
672
|
+
? "review_possible_duplicate"
|
|
673
|
+
: "open_review_issue"
|
|
674
|
+
: "fix_required_fields";
|
|
675
|
+
|
|
676
|
+
return {
|
|
677
|
+
ok: true,
|
|
678
|
+
valid: validation.valid,
|
|
679
|
+
category: validation.category,
|
|
680
|
+
slug: validation.normalized.slug || "",
|
|
681
|
+
recommendedAction,
|
|
682
|
+
errors: validation.errors,
|
|
683
|
+
warnings: validation.warnings,
|
|
684
|
+
missingRequiredFields: validation.missingRequiredFields || [],
|
|
685
|
+
duplicateReview: duplicates,
|
|
686
|
+
reviewChecklist: [
|
|
687
|
+
"Schema-valid is not publish-valid; maintainer review is still required.",
|
|
688
|
+
"Check category fit against the actual artifact, not only the submitter's selected category.",
|
|
689
|
+
"Verify source availability, install commands, and any credential/payment-sensitive behavior.",
|
|
690
|
+
"Reject or request edits for affiliate/referral links, unsupported claims, or unclear paid/sponsored positioning.",
|
|
691
|
+
],
|
|
692
|
+
nextSteps: validation.valid
|
|
693
|
+
? [
|
|
694
|
+
"Open or update the canonical GitHub submission issue.",
|
|
695
|
+
"Apply maintainer review labels only after source and duplicate checks.",
|
|
696
|
+
]
|
|
697
|
+
: ["Fix required fields and validation errors before opening an issue."],
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
|
|
475
701
|
function entrySourceUrls(entry) {
|
|
476
702
|
return [
|
|
477
703
|
entry.documentationUrl,
|