@heyclaude/mcp 0.2.0 → 0.3.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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # @heyclaude/mcp Changelog
2
2
 
3
+ ## 0.3.0 - Safety Metadata and Submission Policy
4
+
5
+ - Expose registry `safetyNotes` and `privacyNotes` in MCP search, detail,
6
+ copyable asset, comparison, and install guidance responses.
7
+ - Accept `safety_notes` and `privacy_notes` in submission draft helpers with
8
+ the same short-note limits used by HeyClaude intake.
9
+ - Support source-backed and copyable-content skill submissions without requiring
10
+ community ZIP/MCPB package hosting.
11
+ - Reflect the review-gated import policy: submission helpers can prepare issues
12
+ and local checks, but never create issues, open PRs, merge, publish, or mirror
13
+ package artifacts.
14
+
3
15
  ## 0.2.0 - Discovery and Submission Drafting
4
16
 
5
17
  - Add read-only discovery tools for server metadata, paginated category
package/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
  <a href="https://github.com/JSONbored/awesome-claude">GitHub</a> •
12
12
  <a href="https://www.npmjs.com/package/@heyclaude/mcp">npm</a> •
13
13
  <a href="https://heyclau.de/api/mcp">MCP endpoint</a> •
14
- <a href="https://github.com/JSONbored/awesome-claude/releases/tag/mcp-v0.2.0">v0.2.0 release</a>
14
+ <a href="https://github.com/JSONbored/awesome-claude/releases/tag/mcp-v0.3.0">v0.3.0 release</a>
15
15
  </p>
16
16
 
17
17
  Read-only Model Context Protocol server for the HeyClaude registry.
@@ -19,8 +19,8 @@ Read-only Model Context Protocol server for the HeyClaude registry.
19
19
  It exposes the same public registry surface used by the website and Raycast:
20
20
  search, entry details, platform compatibility, install guidance, generated
21
21
  adapters, feed discovery, and safe submission-draft helpers. It does not create
22
- GitHub issues, open pull requests, write local files, publish content, or manage
23
- accounts.
22
+ GitHub issues, open pull requests, write local files, publish content, host
23
+ packages, or manage accounts.
24
24
 
25
25
  No API key is required for the public endpoint. Abuse controls are handled with
26
26
  strict request validation, a 64 KiB body limit, and a dedicated Cloudflare
@@ -55,21 +55,30 @@ strict request validation, a 64 KiB body limit, and a dedicated Cloudflare
55
55
  rule adapters for skill packages.
56
56
  - `list_distribution_feeds` - discover public JSON, RSS, Atom, and platform
57
57
  feeds.
58
- - `get_submission_schema` - fetch category submission fields and issue template
59
- metadata.
58
+ - `get_submission_schema` - fetch category submission fields for PR-first
59
+ intake.
60
60
  - `validate_submission_draft` - validate a content submission draft locally.
61
61
  - `search_duplicate_entries` - check generated registry artifacts for likely
62
62
  duplicates before opening a submission.
63
- - `build_submission_urls` - build prefilled HeyClaude submit and GitHub issue
64
- URLs for human review.
63
+ - `build_submission_urls` - build prefilled HeyClaude submit and review URLs for human
64
+ review.
65
65
  - `get_category_submission_guidance` - fetch category-specific contribution
66
66
  guidance and required fields.
67
67
  - `prepare_submission_draft` - normalize and validate fields, then return a
68
- canonical issue title/body plus prefilled URLs.
68
+ canonical PR draft plus prefilled submit URL.
69
69
  - `get_submission_examples` - fetch category-specific example fields and
70
70
  templates for more complete submissions.
71
71
  - `review_submission_draft` - review schema errors, duplicate risk, and
72
- maintainer checklist items before a submission issue is opened.
72
+ maintainer checklist items before a submission PR is opened.
73
+ - `get_submission_policy` - fetch the read-only submission, artifact, import,
74
+ and maintainer-review policy.
75
+ - `explain_entry_trust` - explain source, package, safety, privacy, and review
76
+ metadata signals for one entry. This is a metadata review only and does not
77
+ provide malware scanning, automatic safety guarantees, or installation approval.
78
+ - `review_entry_safety` - compare 1-5 entries for source, package, safety, and
79
+ privacy metadata fit before install or recommendation. This is a metadata review
80
+ only and does not provide malware scanning, automatic safety guarantees, or
81
+ installation approval.
73
82
 
74
83
  ## Resources and Prompts
75
84
 
@@ -83,7 +92,7 @@ Workflow prompts are available for common client flows:
83
92
 
84
93
  - `find_best_asset`
85
94
  - `prepare_submission`
86
- - `review_submission_before_issue`
95
+ - `review_submission_before_pr`
87
96
  - `install_asset_safely`
88
97
 
89
98
  ## Local Stdio
@@ -168,8 +177,14 @@ checks the HTTP guards used by the remote route.
168
177
  - Remote endpoint requires JSON POST bodies, rejects payloads above 64 KiB, and
169
178
  uses the dedicated `API_MCP_RATE_LIMIT` Cloudflare binding at
170
179
  60 requests/minute/IP in production.
171
- - Submission tools prepare review drafts only; HeyClaude does not auto-publish
172
- MCP-submitted content.
180
+ - Submission tools prepare review drafts only; the MCP server does not perform
181
+ GitHub writes or publish submitted content.
182
+ - Source-backed, content-only PRs may be merged automatically after content
183
+ validation, Superagent, and private maintainer-agent review pass. Platform,
184
+ workflow, package, and generated-artifact changes are never auto-merged by
185
+ this path.
186
+ - Community ZIP/MCPB artifacts are review/quarantine material only. Public
187
+ HeyClaude-hosted downloads are maintainer-built package artifacts.
173
188
 
174
189
  ## npm Release Prep
175
190
 
@@ -184,10 +199,10 @@ Do not publish until the web branch has shipped, the production endpoint has
184
199
  been verified, and the package smoke test passes. The release checklist is:
185
200
 
186
201
  ```bash
187
- pnpm validate:mcp-endpoint -- --url https://heyclau.de/api/mcp --strict-tools
202
+ MCP_ENDPOINT_REQUIRE_SAFETY_METADATA=1 pnpm validate:mcp-endpoint -- --url https://heyclau.de/api/mcp --strict-tools
188
203
  pnpm --filter @heyclaude/mcp test
189
204
  pnpm --filter @heyclaude/mcp pack --dry-run
190
- MCP_PACKAGE_REMOTE_SMOKE_URL=https://heyclau.de/api/mcp pnpm validate:mcp-package
205
+ MCP_PACKAGE_REQUIRE_SAFETY_METADATA=1 MCP_PACKAGE_REMOTE_SMOKE_URL=https://heyclau.de/api/mcp pnpm validate:mcp-package
191
206
  ```
192
207
 
193
208
  Publishing should happen through the manual `Publish MCP Package` GitHub
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyclaude/mcp",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "Read-only MCP server for the HeyClaude registry.",
6
6
  "license": "MIT",
@@ -30,6 +30,10 @@ function parseArgs(argv) {
30
30
  args.set("strict-tools", "1");
31
31
  continue;
32
32
  }
33
+ if (value === "--require-safety-metadata") {
34
+ args.set("require-safety-metadata", "1");
35
+ continue;
36
+ }
33
37
  args.set(value.slice(2), argv[index + 1] ?? "");
34
38
  index += 1;
35
39
  }
@@ -46,6 +50,40 @@ function assert(condition, message) {
46
50
  if (!condition) throw new Error(message);
47
51
  }
48
52
 
53
+ function assertSubmitUrl(value) {
54
+ let url;
55
+ try {
56
+ url = new URL(String(value || ""));
57
+ } catch {
58
+ throw new Error(
59
+ "build_submission_urls did not return an absolute submit URL.",
60
+ );
61
+ }
62
+ assert(
63
+ url.protocol === "https:",
64
+ "build_submission_urls submit URL must use HTTPS.",
65
+ );
66
+ assert(
67
+ url.origin === "https://heyclau.de",
68
+ "build_submission_urls submit URL used the wrong origin.",
69
+ );
70
+ assert(
71
+ url.pathname === "/submit",
72
+ "build_submission_urls did not return the submit URL.",
73
+ );
74
+ }
75
+
76
+ function assertSafetyMetadataShape(payload, label) {
77
+ assert(
78
+ Array.isArray(payload?.safetyNotes),
79
+ `${label} did not expose safetyNotes as an array.`,
80
+ );
81
+ assert(
82
+ Array.isArray(payload?.privacyNotes),
83
+ `${label} did not expose privacyNotes as an array.`,
84
+ );
85
+ }
86
+
49
87
  async function validateHttpGuards(endpointUrl) {
50
88
  const options = await fetch(endpointUrl, { method: "OPTIONS" });
51
89
  assert(options.status === 204, `OPTIONS returned ${options.status}`);
@@ -177,6 +215,12 @@ async function validateMcpTools(endpointUrl, options = {}) {
177
215
  Array.isArray(search.entries) && search.entries.length > 0,
178
216
  "search_registry did not return entries.",
179
217
  );
218
+ if (options.requireSafetyMetadata) {
219
+ assertSafetyMetadataShape(
220
+ search.entries[0],
221
+ "search_registry first entry",
222
+ );
223
+ }
180
224
 
181
225
  if (toolNames.includes("server_info")) {
182
226
  const info = parseToolResult(
@@ -236,6 +280,9 @@ async function validateMcpTools(endpointUrl, options = {}) {
236
280
  detail.key === `${first.category}:${first.slug}`,
237
281
  "get_entry_detail returned the wrong entry.",
238
282
  );
283
+ if (options.requireSafetyMetadata) {
284
+ assertSafetyMetadataShape(detail.entry, "get_entry_detail entry");
285
+ }
239
286
 
240
287
  if (toolNames.includes("get_copyable_asset")) {
241
288
  const asset = parseToolResult(
@@ -271,9 +318,20 @@ async function validateMcpTools(endpointUrl, options = {}) {
271
318
  );
272
319
  assert(schema.ok === true, "get_submission_schema did not return ok.");
273
320
  assert(
274
- schema.issueTemplate?.template === "submit-mcp.yml",
275
- "get_submission_schema did not return the MCP issue template.",
321
+ schema.prIntake?.mode === "github_app_user_fork_pr",
322
+ "get_submission_schema did not return PR-first intake metadata.",
276
323
  );
324
+ if (options.requireSafetyMetadata) {
325
+ const fieldIds = schema.schema?.fields?.map((field) => field.id) || [];
326
+ assert(
327
+ fieldIds.includes("safety_notes"),
328
+ "get_submission_schema did not expose safety_notes.",
329
+ );
330
+ assert(
331
+ fieldIds.includes("privacy_notes"),
332
+ "get_submission_schema did not expose privacy_notes.",
333
+ );
334
+ }
277
335
 
278
336
  const urls = parseToolResult(
279
337
  await client.callTool({
@@ -292,10 +350,7 @@ async function validateMcpTools(endpointUrl, options = {}) {
292
350
  }),
293
351
  );
294
352
  assert(urls.ok === true, "build_submission_urls did not return ok.");
295
- assert(
296
- String(urls.githubIssueUrl || "").includes("template=submit-mcp.yml"),
297
- "build_submission_urls did not return an MCP issue URL.",
298
- );
353
+ assertSubmitUrl(urls.submitUrl);
299
354
 
300
355
  if (toolNames.includes("prepare_submission_draft")) {
301
356
  const prepared = parseToolResult(
@@ -315,12 +370,12 @@ async function validateMcpTools(endpointUrl, options = {}) {
315
370
  }),
316
371
  );
317
372
  assert(
318
- prepared.issueDraft?.body,
319
- "prepare_submission_draft did not return a canonical issue body.",
373
+ prepared.prDraft?.body,
374
+ "prepare_submission_draft did not return a canonical PR draft body.",
320
375
  );
321
376
  assert(
322
377
  String(prepared.submissionPolicy || "").includes(
323
- "does not auto-publish",
378
+ "may be merged automatically",
324
379
  ),
325
380
  "prepare_submission_draft did not expose the maintainer-reviewed policy.",
326
381
  );
@@ -348,6 +403,9 @@ const endpointUrl = endpointUrlRaw ? normalizeEndpointUrl(endpointUrlRaw) : "";
348
403
  const strictTools =
349
404
  args.get("strict-tools") === "1" ||
350
405
  process.env.MCP_ENDPOINT_STRICT_TOOLS === "1";
406
+ const requireSafetyMetadata =
407
+ args.get("require-safety-metadata") === "1" ||
408
+ process.env.MCP_ENDPOINT_REQUIRE_SAFETY_METADATA === "1";
351
409
 
352
410
  if (!endpointUrl) {
353
411
  console.error(
@@ -358,7 +416,7 @@ if (!endpointUrl) {
358
416
 
359
417
  try {
360
418
  await validateHttpGuards(endpointUrl);
361
- await validateMcpTools(endpointUrl, { strictTools });
419
+ await validateMcpTools(endpointUrl, { strictTools, requireSafetyMetadata });
362
420
  console.log(`Validated HeyClaude MCP endpoint at ${endpointUrl.toString()}`);
363
421
  } catch (error) {
364
422
  console.error(error instanceof Error ? error.message : String(error));
@@ -1,7 +1,4 @@
1
- import { createRequire } from "node:module";
2
-
3
- const require = createRequire(import.meta.url);
4
- const packageJson = require("../package.json");
5
-
6
- export const packageName = String(packageJson.name || "@heyclaude/mcp");
7
- export const packageVersion = String(packageJson.version || "0.0.0");
1
+ // Keep this Worker-safe: Cloudflare's bundle loader rejects runtime
2
+ // package.json specifiers inside the SSR/MCP route bundle.
3
+ export const packageName = "@heyclaude/mcp";
4
+ export const packageVersion = "0.3.0";
package/src/registry.d.ts CHANGED
@@ -27,6 +27,11 @@ export function searchRegistry(
27
27
  options?: RegistryArtifactLoaders,
28
28
  ): Promise<RegistryToolResult>;
29
29
 
30
+ export function planWorkflowToolbox(
31
+ args?: Record<string, unknown>,
32
+ options?: RegistryArtifactLoaders,
33
+ ): Promise<RegistryToolResult>;
34
+
30
35
  export function getServerInfo(
31
36
  args?: Record<string, unknown>,
32
37
  options?: RegistryArtifactLoaders,
@@ -81,7 +86,10 @@ export function listRegistryResourceTemplates(): Record<string, unknown>;
81
86
 
82
87
  export function readRegistryResource(
83
88
  args?: Record<string, unknown>,
84
- options?: RegistryArtifactLoaders,
89
+ options?: RegistryArtifactLoaders & {
90
+ publicApiBaseUrl?: string;
91
+ fetchPublicApi?: (apiPath: string) => Promise<unknown>;
92
+ },
85
93
  ): Promise<Record<string, unknown>>;
86
94
 
87
95
  export function listRegistryPrompts(): Record<string, unknown>;
@@ -150,6 +158,42 @@ export function reviewSubmissionDraft(
150
158
  options?: RegistryArtifactLoaders,
151
159
  ): Promise<RegistryToolResult>;
152
160
 
161
+ export function getSubmissionPolicy(
162
+ args?: Record<string, unknown>,
163
+ options?: RegistryArtifactLoaders,
164
+ ): Promise<RegistryToolResult>;
165
+
166
+ export function explainEntryTrust(
167
+ args?: Record<string, unknown>,
168
+ options?: RegistryArtifactLoaders,
169
+ ): Promise<RegistryToolResult>;
170
+
171
+ export function reviewEntrySafety(
172
+ args?: Record<string, unknown>,
173
+ options?: RegistryArtifactLoaders,
174
+ ): Promise<RegistryToolResult>;
175
+
176
+ export function listRegistryRecent(
177
+ options?: RegistryArtifactLoaders & {
178
+ publicApiBaseUrl?: string;
179
+ fetchPublicApi?: (apiPath: string) => Promise<unknown>;
180
+ },
181
+ ): Promise<RegistryToolResult>;
182
+
183
+ export function listRegistryTrending(
184
+ options?: RegistryArtifactLoaders & {
185
+ publicApiBaseUrl?: string;
186
+ fetchPublicApi?: (apiPath: string) => Promise<unknown>;
187
+ },
188
+ ): Promise<RegistryToolResult>;
189
+
190
+ export function listJobsActive(
191
+ options?: RegistryArtifactLoaders & {
192
+ publicApiBaseUrl?: string;
193
+ fetchPublicApi?: (apiPath: string) => Promise<unknown>;
194
+ },
195
+ ): Promise<RegistryToolResult>;
196
+
153
197
  export function callRegistryTool(
154
198
  name: string,
155
199
  args?: Record<string, unknown>,
@@ -158,6 +202,7 @@ export function callRegistryTool(
158
202
 
159
203
  export {
160
204
  SearchRegistryInputSchema,
205
+ PlanWorkflowToolboxInputSchema,
161
206
  ServerInfoInputSchema,
162
207
  ListCategoryEntriesInputSchema,
163
208
  RecentUpdatesInputSchema,
@@ -180,6 +225,9 @@ export {
180
225
  PrepareSubmissionDraftInputSchema,
181
226
  GetSubmissionExamplesInputSchema,
182
227
  ReviewSubmissionDraftInputSchema,
228
+ SubmissionPolicyInputSchema,
229
+ ExplainEntryTrustInputSchema,
230
+ ReviewEntrySafetyInputSchema,
183
231
  TOOL_INPUT_SCHEMAS,
184
232
  jsonSchemaForTool,
185
233
  jsonSchemaForToolOutput,