Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline (Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/* packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
38 lines
1.1 KiB
TypeScript
38 lines
1.1 KiB
TypeScript
import * as z from "zod";
|
|
|
|
export const McpTransportSchema = z.enum(["streamable_http", "sse", "stdio"]);
|
|
export type McpTransport = z.infer<typeof McpTransportSchema>;
|
|
|
|
export function isLocalMcpHost(hostname: string): boolean {
|
|
return (
|
|
hostname === "localhost" ||
|
|
hostname === "127.0.0.1" ||
|
|
hostname === "[::1]" ||
|
|
hostname === "::1"
|
|
);
|
|
}
|
|
|
|
export const McpRemoteEndpointSchema = z
|
|
.string()
|
|
.max(2048)
|
|
.url()
|
|
.refine((value) => {
|
|
try {
|
|
if (value.endsWith("#")) return false;
|
|
const url = new URL(value);
|
|
if (url.username || url.password || url.hash) return false;
|
|
if (url.protocol === "https:") return true;
|
|
return url.protocol === "http:" && isLocalMcpHost(url.hostname);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}, "MCP remote endpoint must be an HTTPS URL without credentials or a fragment (HTTP is allowed only for localhost)");
|
|
|
|
export const McpHeadersSchema = z
|
|
.record(z.string().regex(/^[A-Za-z0-9-]+$/), z.string().max(4096))
|
|
.superRefine((value, ctx) => {
|
|
if (Object.keys(value).length > 32) {
|
|
ctx.addIssue({ code: "custom", message: "At most 32 headers are allowed" });
|
|
}
|
|
});
|