@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 +12 -0
- package/README.md +29 -14
- package/package.json +1 -1
- package/scripts/validate-endpoint.mjs +68 -10
- package/src/package-metadata.js +4 -7
- package/src/registry.d.ts +49 -1
- package/src/registry.js +1104 -36
- package/src/schemas.d.ts +3 -0
- package/src/schemas.js +62 -1
- package/src/submissions.d.ts +1 -2
- package/src/submissions.js +255 -84
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.
|
|
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,
|
|
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
|
|
59
|
-
|
|
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
|
|
64
|
-
|
|
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
|
|
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
|
|
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
|
-
- `
|
|
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;
|
|
172
|
-
|
|
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
|
@@ -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.
|
|
275
|
-
"get_submission_schema did not return
|
|
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
|
-
|
|
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.
|
|
319
|
-
"prepare_submission_draft did not return a canonical
|
|
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
|
-
"
|
|
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));
|
package/src/package-metadata.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
const
|
|
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,
|