@vendoai/vendo 0.4.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/LICENSE +202 -0
- package/README.md +28 -0
- package/bin/vendo.mjs +4 -0
- package/dist/ai-sdk.d.ts +35 -0
- package/dist/ai-sdk.js +62 -0
- package/dist/auth-presets/auth-js.d.ts +14 -0
- package/dist/auth-presets/auth-js.js +91 -0
- package/dist/auth-presets/auth0.d.ts +23 -0
- package/dist/auth-presets/auth0.js +98 -0
- package/dist/auth-presets/clerk.d.ts +23 -0
- package/dist/auth-presets/clerk.js +65 -0
- package/dist/auth-presets/conformance.d.ts +42 -0
- package/dist/auth-presets/conformance.js +139 -0
- package/dist/auth-presets/identity.d.ts +56 -0
- package/dist/auth-presets/identity.js +152 -0
- package/dist/auth-presets/index.d.ts +14 -0
- package/dist/auth-presets/index.js +12 -0
- package/dist/auth-presets/jwt.d.ts +16 -0
- package/dist/auth-presets/jwt.js +54 -0
- package/dist/auth-presets/shared.d.ts +35 -0
- package/dist/auth-presets/shared.js +1 -0
- package/dist/auth-presets/supabase.d.ts +42 -0
- package/dist/auth-presets/supabase.js +229 -0
- package/dist/byo-approvals.d.ts +37 -0
- package/dist/byo-approvals.js +189 -0
- package/dist/capability-misses.d.ts +38 -0
- package/dist/capability-misses.js +172 -0
- package/dist/catalog.d.ts +19 -0
- package/dist/catalog.js +152 -0
- package/dist/cli/cloud/args.d.ts +3 -0
- package/dist/cli/cloud/args.js +32 -0
- package/dist/cli/cloud/auth.d.ts +17 -0
- package/dist/cli/cloud/auth.js +123 -0
- package/dist/cli/cloud/client.d.ts +28 -0
- package/dist/cli/cloud/client.js +133 -0
- package/dist/cli/cloud/command.d.ts +22 -0
- package/dist/cli/cloud/command.js +73 -0
- package/dist/cli/cloud/deploy.d.ts +26 -0
- package/dist/cli/cloud/deploy.js +187 -0
- package/dist/cli/cloud/device-login.d.ts +32 -0
- package/dist/cli/cloud/device-login.js +259 -0
- package/dist/cli/cloud/index.d.ts +5 -0
- package/dist/cli/cloud/index.js +59 -0
- package/dist/cli/cloud/keys.d.ts +2 -0
- package/dist/cli/cloud/keys.js +31 -0
- package/dist/cli/cloud/members.d.ts +3 -0
- package/dist/cli/cloud/members.js +25 -0
- package/dist/cli/cloud/output.d.ts +4 -0
- package/dist/cli/cloud/output.js +12 -0
- package/dist/cli/cloud/pending-claim.d.ts +29 -0
- package/dist/cli/cloud/pending-claim.js +38 -0
- package/dist/cli/cloud/read.d.ts +3 -0
- package/dist/cli/cloud/read.js +12 -0
- package/dist/cli/cloud/services.d.ts +2 -0
- package/dist/cli/cloud/services.js +9 -0
- package/dist/cli/cloud/session.d.ts +13 -0
- package/dist/cli/cloud/session.js +41 -0
- package/dist/cli/cloud-init.d.ts +60 -0
- package/dist/cli/cloud-init.js +149 -0
- package/dist/cli/dep-versions.d.ts +26 -0
- package/dist/cli/dep-versions.js +77 -0
- package/dist/cli/doctor-codes.d.ts +58 -0
- package/dist/cli/doctor-codes.js +60 -0
- package/dist/cli/doctor-live.d.ts +67 -0
- package/dist/cli/doctor-live.js +169 -0
- package/dist/cli/doctor.d.ts +52 -0
- package/dist/cli/doctor.js +467 -0
- package/dist/cli/eject.d.ts +29 -0
- package/dist/cli/eject.js +141 -0
- package/dist/cli/extract/apply.d.ts +26 -0
- package/dist/cli/extract/apply.js +123 -0
- package/dist/cli/extract/claude-cli-harness.d.ts +18 -0
- package/dist/cli/extract/claude-cli-harness.js +132 -0
- package/dist/cli/extract/claude-harness.d.ts +14 -0
- package/dist/cli/extract/claude-harness.js +127 -0
- package/dist/cli/extract/codex-cli-harness.d.ts +31 -0
- package/dist/cli/extract/codex-cli-harness.js +154 -0
- package/dist/cli/extract/delegate.d.ts +21 -0
- package/dist/cli/extract/delegate.js +77 -0
- package/dist/cli/extract/extraction.d.ts +65 -0
- package/dist/cli/extract/extraction.js +300 -0
- package/dist/cli/extract/gateway-fuel.d.ts +69 -0
- package/dist/cli/extract/gateway-fuel.js +69 -0
- package/dist/cli/extract/harness.d.ts +114 -0
- package/dist/cli/extract/harness.js +84 -0
- package/dist/cli/extract/index.d.ts +9 -0
- package/dist/cli/extract/index.js +9 -0
- package/dist/cli/extract/npx-engine-harness.d.ts +73 -0
- package/dist/cli/extract/npx-engine-harness.js +225 -0
- package/dist/cli/extract/stages.d.ts +149 -0
- package/dist/cli/extract/stages.js +398 -0
- package/dist/cli/framework.d.ts +8 -0
- package/dist/cli/framework.js +36 -0
- package/dist/cli/init-auth.d.ts +67 -0
- package/dist/cli/init-auth.js +142 -0
- package/dist/cli/init-scaffolds.d.ts +28 -0
- package/dist/cli/init-scaffolds.js +235 -0
- package/dist/cli/init.d.ts +115 -0
- package/dist/cli/init.js +1008 -0
- package/dist/cli/mcp/index.d.ts +7 -0
- package/dist/cli/mcp/index.js +59 -0
- package/dist/cli/mcp/registry.d.ts +9 -0
- package/dist/cli/mcp/registry.js +117 -0
- package/dist/cli/mcp/server-json.d.ts +12 -0
- package/dist/cli/mcp/server-json.js +65 -0
- package/dist/cli/mcp/server.schema.json +574 -0
- package/dist/cli/mcp/verify-domain.d.ts +11 -0
- package/dist/cli/mcp/verify-domain.js +48 -0
- package/dist/cli/playground/app/embed-entry.d.ts +20 -0
- package/dist/cli/playground/app/embed-entry.js +46 -0
- package/dist/cli/playground/app/fake-client.d.ts +3 -0
- package/dist/cli/playground/app/fake-client.js +215 -0
- package/dist/cli/playground/app/fixtures.d.ts +47 -0
- package/dist/cli/playground/app/fixtures.js +472 -0
- package/dist/cli/playground/app/main.d.ts +1 -0
- package/dist/cli/playground/app/main.js +108 -0
- package/dist/cli/playground/app/scenario-mount.d.ts +13 -0
- package/dist/cli/playground/app/scenario-mount.js +48 -0
- package/dist/cli/playground/app/scenarios.d.ts +25 -0
- package/dist/cli/playground/app/scenarios.js +152 -0
- package/dist/cli/playground/app/theme-editor.d.ts +13 -0
- package/dist/cli/playground/app/theme-editor.js +149 -0
- package/dist/cli/playground/app/theme-state.d.ts +29 -0
- package/dist/cli/playground/app/theme-state.js +151 -0
- package/dist/cli/playground/bundle.gen.d.ts +2 -0
- package/dist/cli/playground/bundle.gen.js +2 -0
- package/dist/cli/playground/embed-bundle.gen.d.ts +2 -0
- package/dist/cli/playground/embed-bundle.gen.js +2 -0
- package/dist/cli/playground.d.ts +33 -0
- package/dist/cli/playground.js +120 -0
- package/dist/cli/pretty.d.ts +48 -0
- package/dist/cli/pretty.js +330 -0
- package/dist/cli/refine.d.ts +43 -0
- package/dist/cli/refine.js +181 -0
- package/dist/cli/semantics.d.ts +31 -0
- package/dist/cli/semantics.js +122 -0
- package/dist/cli/shared.d.ts +68 -0
- package/dist/cli/shared.js +184 -0
- package/dist/cli/sync.d.ts +36 -0
- package/dist/cli/sync.js +202 -0
- package/dist/cli/theme/color.d.ts +15 -0
- package/dist/cli/theme/color.js +188 -0
- package/dist/cli/theme/css-vars.d.ts +13 -0
- package/dist/cli/theme/css-vars.js +40 -0
- package/dist/cli/theme/entry-candidates.d.ts +5 -0
- package/dist/cli/theme/entry-candidates.js +22 -0
- package/dist/cli/theme/extract-theme.d.ts +184 -0
- package/dist/cli/theme/extract-theme.js +358 -0
- package/dist/cli/theme/walk.d.ts +2 -0
- package/dist/cli/theme/walk.js +24 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +333 -0
- package/dist/cloud-apps.d.ts +17 -0
- package/dist/cloud-apps.js +46 -0
- package/dist/cloud-console.d.ts +32 -0
- package/dist/cloud-console.js +72 -0
- package/dist/cloud-tools.d.ts +23 -0
- package/dist/cloud-tools.js +196 -0
- package/dist/connections.d.ts +71 -0
- package/dist/connections.js +192 -0
- package/dist/deployment-identity.d.ts +12 -0
- package/dist/deployment-identity.js +69 -0
- package/dist/dev-creds/model.d.ts +65 -0
- package/dist/dev-creds/model.js +159 -0
- package/dist/dev-creds/resolve.d.ts +36 -0
- package/dist/dev-creds/resolve.js +57 -0
- package/dist/hosted-store.d.ts +52 -0
- package/dist/hosted-store.js +322 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +1 -0
- package/dist/mastra.d.ts +46 -0
- package/dist/mastra.js +88 -0
- package/dist/react.d.ts +10 -0
- package/dist/react.js +32 -0
- package/dist/refine.d.ts +244 -0
- package/dist/refine.js +599 -0
- package/dist/remixable.d.ts +18 -0
- package/dist/remixable.js +42 -0
- package/dist/runtime-capture.d.ts +18 -0
- package/dist/runtime-capture.js +111 -0
- package/dist/sandbox-wire.d.ts +101 -0
- package/dist/sandbox-wire.js +101 -0
- package/dist/sandbox.d.ts +38 -0
- package/dist/sandbox.js +389 -0
- package/dist/server.d.ts +216 -0
- package/dist/server.js +1320 -0
- package/dist/sync-impact.d.ts +14 -0
- package/dist/sync-impact.js +75 -0
- package/dist/turn-liveness.d.ts +33 -0
- package/dist/turn-liveness.js +105 -0
- package/dist/wire/approvals.d.ts +10 -0
- package/dist/wire/approvals.js +59 -0
- package/dist/wire/apps.d.ts +5 -0
- package/dist/wire/apps.js +155 -0
- package/dist/wire/automations.d.ts +5 -0
- package/dist/wire/automations.js +57 -0
- package/dist/wire/box.d.ts +3 -0
- package/dist/wire/box.js +268 -0
- package/dist/wire/connections.d.ts +5 -0
- package/dist/wire/connections.js +47 -0
- package/dist/wire/context.d.ts +22 -0
- package/dist/wire/context.js +190 -0
- package/dist/wire/doctor.d.ts +19 -0
- package/dist/wire/doctor.js +116 -0
- package/dist/wire/misc.d.ts +18 -0
- package/dist/wire/misc.js +227 -0
- package/dist/wire/shared.d.ts +158 -0
- package/dist/wire/shared.js +134 -0
- package/dist/wire/threads.d.ts +3 -0
- package/dist/wire/threads.js +68 -0
- package/package.json +151 -0
- package/skills/vendo-setup/SKILL.md +128 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Output, type TelemetryOptions } from "../shared.js";
|
|
2
|
+
export interface McpOptions {
|
|
3
|
+
output?: Output;
|
|
4
|
+
/** Injectable telemetry deps (matches init/doctor). */
|
|
5
|
+
telemetry?: TelemetryOptions;
|
|
6
|
+
}
|
|
7
|
+
export declare function runMcp(args: string[], options?: McpOptions): Promise<number>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { consoleOutput, withCommandRun } from "../shared.js";
|
|
2
|
+
import { runServerJson } from "./server-json.js";
|
|
3
|
+
import { runVerifyDomain } from "./verify-domain.js";
|
|
4
|
+
const HELP = `vendo mcp — MCP registry discovery tooling\n\nUsage:\n vendo mcp server-json [dir] --domain <domain> --url <public-mcp-url> [--force]\n vendo mcp verify-domain [dir] --domain <domain> --key-out <path> [--write-well-known <dir>]\n\nCommands:\n vendo mcp server-json Generate and validate the official registry server.json\n vendo mcp verify-domain Generate Ed25519 DNS and HTTP domain-verification material\n`;
|
|
5
|
+
const VALUE_OPTIONS = new Set(["--domain", "--url", "--key-out", "--write-well-known"]);
|
|
6
|
+
function option(args, name) {
|
|
7
|
+
const exact = args.indexOf(name);
|
|
8
|
+
if (exact >= 0) {
|
|
9
|
+
const value = args[exact + 1];
|
|
10
|
+
return value !== undefined && !value.startsWith("--") ? value : undefined;
|
|
11
|
+
}
|
|
12
|
+
return args.find((value) => value.startsWith(`${name}=`))?.slice(name.length + 1);
|
|
13
|
+
}
|
|
14
|
+
function target(args) {
|
|
15
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
16
|
+
const value = args[index];
|
|
17
|
+
if (VALUE_OPTIONS.has(value)) {
|
|
18
|
+
index += 1;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (!value.startsWith("--"))
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
return process.cwd();
|
|
25
|
+
}
|
|
26
|
+
export async function runMcp(args, options = {}) {
|
|
27
|
+
const output = options.output ?? consoleOutput;
|
|
28
|
+
const [command, ...commandArgs] = args;
|
|
29
|
+
if (command === undefined || command === "--help" || command === "-h") {
|
|
30
|
+
output.log(HELP);
|
|
31
|
+
return 0;
|
|
32
|
+
}
|
|
33
|
+
return withCommandRun({
|
|
34
|
+
command: "mcp",
|
|
35
|
+
...(options.telemetry === undefined ? {} : { telemetry: options.telemetry }),
|
|
36
|
+
}, async (failure) => {
|
|
37
|
+
if (command === "server-json") {
|
|
38
|
+
return runServerJson({
|
|
39
|
+
targetDir: target(commandArgs),
|
|
40
|
+
domain: option(commandArgs, "--domain"),
|
|
41
|
+
url: option(commandArgs, "--url"),
|
|
42
|
+
force: commandArgs.includes("--force"),
|
|
43
|
+
output,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
if (command === "verify-domain") {
|
|
47
|
+
return runVerifyDomain({
|
|
48
|
+
targetDir: target(commandArgs),
|
|
49
|
+
domain: option(commandArgs, "--domain"),
|
|
50
|
+
keyOut: option(commandArgs, "--key-out"),
|
|
51
|
+
writeWellKnown: option(commandArgs, "--write-well-known"),
|
|
52
|
+
output,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
failure.failedStep = "unknown-command";
|
|
56
|
+
output.error(`Unknown mcp command: ${command}\n\n${HELP}`);
|
|
57
|
+
return 1;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const SERVER_SCHEMA_URL = "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json";
|
|
2
|
+
export declare function normalizeDomain(input: string): string;
|
|
3
|
+
export declare function registryNamespace(domain: string): string;
|
|
4
|
+
export declare function packageSlug(name: string): string;
|
|
5
|
+
export declare function remoteUrls(value: unknown): string[];
|
|
6
|
+
/** 10-mcp §5 — validate with the vendored registry schema, then enforce the
|
|
7
|
+
* registry's domain-namespace rule that JSON Schema cannot express. */
|
|
8
|
+
export declare function validateRegistryServer(value: unknown): string[];
|
|
9
|
+
export declare function sameUrl(left: string, right: string): boolean;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import Ajv from "ajv";
|
|
2
|
+
import addFormats from "ajv-formats";
|
|
3
|
+
import { isPlainObject as isRecord } from "@vendoai/core";
|
|
4
|
+
import serverSchema from "./server.schema.json" with { type: "json" };
|
|
5
|
+
export const SERVER_SCHEMA_URL = "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json";
|
|
6
|
+
const ajv = new Ajv({ allErrors: true, strict: false });
|
|
7
|
+
addFormats(ajv);
|
|
8
|
+
const validatePinnedSchema = ajv.compile(serverSchema);
|
|
9
|
+
function schemaError(error) {
|
|
10
|
+
const path = error.instancePath.length > 0 ? error.instancePath : "/";
|
|
11
|
+
return `${path} ${error.message ?? "is invalid"}`;
|
|
12
|
+
}
|
|
13
|
+
export function normalizeDomain(input) {
|
|
14
|
+
const domain = input.trim().toLowerCase().replace(/\.$/, "");
|
|
15
|
+
const label = "[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?";
|
|
16
|
+
if (domain.length > 253 || !new RegExp(`^(?:${label}\\.)+${label}$`).test(domain)) {
|
|
17
|
+
throw new Error(`Invalid registry domain: ${input}`);
|
|
18
|
+
}
|
|
19
|
+
return domain;
|
|
20
|
+
}
|
|
21
|
+
export function registryNamespace(domain) {
|
|
22
|
+
return normalizeDomain(domain).split(".").reverse().join(".");
|
|
23
|
+
}
|
|
24
|
+
function registryDomain(name) {
|
|
25
|
+
const parts = name.split("/");
|
|
26
|
+
if (parts.length !== 2 || parts[0] === undefined || parts[0].length === 0)
|
|
27
|
+
return null;
|
|
28
|
+
const domain = parts[0].split(".").reverse().join(".").toLowerCase();
|
|
29
|
+
try {
|
|
30
|
+
return normalizeDomain(domain);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function packageSlug(name) {
|
|
37
|
+
const unscoped = name.split("/").at(-1) ?? name;
|
|
38
|
+
const slug = unscoped.toLowerCase()
|
|
39
|
+
.replace(/[^a-z0-9._-]+/g, "-")
|
|
40
|
+
.replace(/^[._-]+|[._-]+$/g, "");
|
|
41
|
+
return slug.length > 0 ? slug : "vendo";
|
|
42
|
+
}
|
|
43
|
+
function urlHost(value) {
|
|
44
|
+
try {
|
|
45
|
+
const parsed = new URL(value);
|
|
46
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:")
|
|
47
|
+
return null;
|
|
48
|
+
return parsed.hostname.toLowerCase().replace(/\.$/, "");
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function hostMatchesDomain(host, domain) {
|
|
55
|
+
const normalizedHost = host.toLowerCase().replace(/\.$/, "");
|
|
56
|
+
const normalizedDomain = normalizeDomain(domain);
|
|
57
|
+
return normalizedHost === normalizedDomain || normalizedHost.endsWith(`.${normalizedDomain}`);
|
|
58
|
+
}
|
|
59
|
+
export function remoteUrls(value) {
|
|
60
|
+
if (!isRecord(value) || !Array.isArray(value.remotes))
|
|
61
|
+
return [];
|
|
62
|
+
return value.remotes.flatMap((remote) => {
|
|
63
|
+
if (!isRecord(remote) || remote.type !== "streamable-http" || typeof remote.url !== "string")
|
|
64
|
+
return [];
|
|
65
|
+
return [remote.url];
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/** 10-mcp §5 — validate with the vendored registry schema, then enforce the
|
|
69
|
+
* registry's domain-namespace rule that JSON Schema cannot express. */
|
|
70
|
+
export function validateRegistryServer(value) {
|
|
71
|
+
const errors = [];
|
|
72
|
+
if (!validatePinnedSchema(value))
|
|
73
|
+
errors.push(...(validatePinnedSchema.errors ?? []).map(schemaError));
|
|
74
|
+
if (!isRecord(value))
|
|
75
|
+
return errors;
|
|
76
|
+
if (value.$schema !== undefined && value.$schema !== SERVER_SCHEMA_URL) {
|
|
77
|
+
errors.push(`/$schema must be ${SERVER_SCHEMA_URL}`);
|
|
78
|
+
}
|
|
79
|
+
const packages = Array.isArray(value.packages) ? value.packages : [];
|
|
80
|
+
const remotes = Array.isArray(value.remotes) ? value.remotes : [];
|
|
81
|
+
if (packages.length === 0 && remotes.length === 0) {
|
|
82
|
+
errors.push("/ must include at least one package or remote");
|
|
83
|
+
}
|
|
84
|
+
if (typeof value.name !== "string")
|
|
85
|
+
return errors;
|
|
86
|
+
const domain = registryDomain(value.name);
|
|
87
|
+
if (domain === null) {
|
|
88
|
+
errors.push("/name must use a reversible domain namespace");
|
|
89
|
+
return errors;
|
|
90
|
+
}
|
|
91
|
+
for (const remote of remotes) {
|
|
92
|
+
if (!isRecord(remote) || typeof remote.url !== "string")
|
|
93
|
+
continue;
|
|
94
|
+
const host = urlHost(remote.url);
|
|
95
|
+
if (host === null)
|
|
96
|
+
continue;
|
|
97
|
+
if (!hostMatchesDomain(host, domain)) {
|
|
98
|
+
errors.push(`/remotes remote URL must use ${domain} or one of its subdomains`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return errors;
|
|
102
|
+
}
|
|
103
|
+
export function sameUrl(left, right) {
|
|
104
|
+
try {
|
|
105
|
+
const normalize = (value) => {
|
|
106
|
+
const url = new URL(value);
|
|
107
|
+
url.hash = "";
|
|
108
|
+
if (url.pathname !== "/")
|
|
109
|
+
url.pathname = url.pathname.replace(/\/$/, "");
|
|
110
|
+
return url.href;
|
|
111
|
+
};
|
|
112
|
+
return normalize(left) === normalize(right);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type Output } from "../shared.js";
|
|
2
|
+
export interface ServerJsonOptions {
|
|
3
|
+
targetDir: string;
|
|
4
|
+
domain?: string;
|
|
5
|
+
url?: string;
|
|
6
|
+
force?: boolean;
|
|
7
|
+
prompt?: (question: string) => Promise<string>;
|
|
8
|
+
output?: Output;
|
|
9
|
+
}
|
|
10
|
+
/** 10-mcp §5 — generate the official-registry artifact from the same host
|
|
11
|
+
* package.json identity the door advertises, plus explicit public discovery. */
|
|
12
|
+
export declare function runServerJson(options: ServerJsonOptions): Promise<number>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { stdin, stdout } from "node:process";
|
|
4
|
+
import { createInterface } from "node:readline/promises";
|
|
5
|
+
import { consoleOutput, exists, writeText } from "../shared.js";
|
|
6
|
+
import { packageSlug, registryNamespace, SERVER_SCHEMA_URL, validateRegistryServer, } from "./registry.js";
|
|
7
|
+
async function promptOnce(question) {
|
|
8
|
+
const readline = createInterface({ input: stdin, output: stdout });
|
|
9
|
+
try {
|
|
10
|
+
return await readline.question(question);
|
|
11
|
+
}
|
|
12
|
+
finally {
|
|
13
|
+
readline.close();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async function hostIdentity(root) {
|
|
17
|
+
const raw = await readFile(join(root, "package.json"), "utf8");
|
|
18
|
+
const manifest = JSON.parse(raw);
|
|
19
|
+
const name = typeof manifest.name === "string" ? manifest.name : "vendo";
|
|
20
|
+
const version = typeof manifest.version === "string" ? manifest.version : "0.0.0";
|
|
21
|
+
const description = typeof manifest.description === "string" ? manifest.description : `${name} MCP server`;
|
|
22
|
+
return {
|
|
23
|
+
name,
|
|
24
|
+
version,
|
|
25
|
+
description,
|
|
26
|
+
...(typeof manifest.homepage === "string" ? { websiteUrl: manifest.homepage } : {}),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/** 10-mcp §5 — generate the official-registry artifact from the same host
|
|
30
|
+
* package.json identity the door advertises, plus explicit public discovery. */
|
|
31
|
+
export async function runServerJson(options) {
|
|
32
|
+
const root = resolve(options.targetDir);
|
|
33
|
+
const output = options.output ?? consoleOutput;
|
|
34
|
+
const path = join(root, "server.json");
|
|
35
|
+
if (!options.force && await exists(path)) {
|
|
36
|
+
output.error("server.json already exists; pass --force to overwrite it");
|
|
37
|
+
return 1;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const prompt = options.prompt ?? promptOnce;
|
|
41
|
+
const domain = options.domain ?? await prompt("Registry domain (for example example.com): ");
|
|
42
|
+
const publicUrl = options.url ?? await prompt("Public MCP URL: ");
|
|
43
|
+
const identity = await hostIdentity(root);
|
|
44
|
+
const server = {
|
|
45
|
+
$schema: SERVER_SCHEMA_URL,
|
|
46
|
+
name: `${registryNamespace(domain)}/${packageSlug(identity.name)}`,
|
|
47
|
+
description: identity.description,
|
|
48
|
+
version: identity.version,
|
|
49
|
+
remotes: [{ type: "streamable-http", url: publicUrl.trim() }],
|
|
50
|
+
...(identity.websiteUrl === undefined ? {} : { websiteUrl: identity.websiteUrl }),
|
|
51
|
+
};
|
|
52
|
+
const errors = validateRegistryServer(server);
|
|
53
|
+
if (errors.length > 0) {
|
|
54
|
+
output.error(`server.json is invalid:\n${errors.map((error) => `- ${error}`).join("\n")}`);
|
|
55
|
+
return 1;
|
|
56
|
+
}
|
|
57
|
+
await writeText(path, `${JSON.stringify(server, null, 2)}\n`);
|
|
58
|
+
output.log(`Wrote server.json for ${server.name}`);
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
output.error(`Could not generate server.json: ${error instanceof Error ? error.message : "unknown error"}`);
|
|
63
|
+
return 1;
|
|
64
|
+
}
|
|
65
|
+
}
|