175 lines
8.6 KiB
TypeScript
175 lines
8.6 KiB
TypeScript
import { z } from "zod"
|
|
|
|
const identifier = z.string().trim().min(1).max(200)
|
|
const serverId = z.number().int().positive().max(Number.MAX_SAFE_INTEGER)
|
|
const instant = z.string().datetime({ offset: true })
|
|
const uniqueStrings = z.array(identifier).max(1000).refine(values => new Set(values).size === values.length, "Повторяющиеся значения")
|
|
const serverIds = z.array(serverId).max(1000).refine(values => new Set(values).size === values.length, "Повторяющиеся серверы")
|
|
|
|
export const mcpModeSchema = z.enum(["observer", "approval", "autonomous"])
|
|
export const mcpLimitsSchema = z.object({
|
|
callsPerMinute: z.number().int().min(1).max(10000).default(60),
|
|
concurrentReads: z.number().int().min(1).max(100).default(4),
|
|
mutationsPerHour: z.number().int().min(1).max(10000).default(10),
|
|
serversPerPlan: z.number().int().min(1).max(1000).default(5),
|
|
objectsPerPlan: z.number().int().min(1).max(10000).default(100),
|
|
}).strict()
|
|
export const mcpScopeSchema = z.object({
|
|
serverIds,
|
|
operationIds: uniqueStrings,
|
|
allowGlobal: z.boolean().default(false),
|
|
}).strict()
|
|
export const mcpTemporaryGrantSchema = z.object({
|
|
id: identifier,
|
|
startsAt: instant,
|
|
expiresAt: instant,
|
|
scope: mcpScopeSchema,
|
|
}).strict().refine(grant => {
|
|
const duration = Date.parse(grant.expiresAt) - Date.parse(grant.startsAt)
|
|
return duration > 0 && duration <= 24 * 60 * 60 * 1000
|
|
}, "Временный доступ должен действовать не более 24 часов")
|
|
export const mcpConnectionPolicySchema = z.object({
|
|
id: identifier,
|
|
version: z.number().int().positive(),
|
|
status: z.enum(["active", "paused", "revoked"]),
|
|
expiresAt: instant,
|
|
mode: mcpModeSchema,
|
|
permissions: uniqueStrings,
|
|
scope: mcpScopeSchema,
|
|
dangerousPolicy: z.enum(["approval", "explicit-allow"]).default("approval"),
|
|
autonomousDangerousOperationIds: uniqueStrings.default([]),
|
|
limits: mcpLimitsSchema.default(() => mcpLimitsSchema.parse({})),
|
|
}).strict()
|
|
|
|
export const mcpTargetSchema = z.discriminatedUnion("scope", [
|
|
z.object({ scope: z.literal("servers"), serverIds: serverIds.refine(ids => ids.length > 0, "Выберите серверы") }).strict(),
|
|
z.object({ scope: z.literal("global") }).strict(),
|
|
z.object({ scope: z.literal("connection") }).strict(),
|
|
])
|
|
export const mcpObjectRefSchema = z.object({
|
|
kind: identifier,
|
|
id: identifier,
|
|
serverId: serverId.optional(),
|
|
}).strict()
|
|
export const mcpProtectedObjectSchema = mcpObjectRefSchema.extend({
|
|
protection: z.enum(["deny", "human-approval-required"]),
|
|
}).strict()
|
|
export type McpObjectRef = z.infer<typeof mcpObjectRefSchema>
|
|
export const mcpOperationDescriptorSchema = z.object({
|
|
id: identifier,
|
|
toolName: z.string().regex(/^mm_[a-z0-9_]+$/),
|
|
version: z.number().int().min(1).max(2147483647),
|
|
title: identifier,
|
|
section: identifier,
|
|
effect: z.enum(["read", "write", "diagnostic"]),
|
|
risk: z.enum(["ordinary", "dangerous"]),
|
|
scope: z.enum(["servers", "global", "connection"]),
|
|
requiredPermissions: uniqueStrings,
|
|
implementation: z.enum(["planned", "available"]),
|
|
annotations: z.object({
|
|
readOnlyHint: z.boolean(),
|
|
destructiveHint: z.boolean(),
|
|
idempotentHint: z.boolean(),
|
|
openWorldHint: z.boolean(),
|
|
}).strict(),
|
|
}).strict()
|
|
|
|
// This payload is hypothetical evidence for the human-only simulator, never authorization.
|
|
// Future executors must construct runtime evidence from trusted repositories and locks.
|
|
export const mcpPolicyPreviewRequestSchema = z.object({
|
|
operationId: identifier,
|
|
policy: mcpConnectionPolicySchema,
|
|
ownerPermissions: uniqueStrings,
|
|
target: mcpTargetSchema,
|
|
temporaryGrant: mcpTemporaryGrantSchema.optional(),
|
|
objectCount: z.number().int().nonnegative().max(1000000).default(0),
|
|
objects: z.array(mcpObjectRefSchema).max(1000).default([]),
|
|
protections: z.array(mcpProtectedObjectSchema).max(1000).default([]),
|
|
usage: z.object({
|
|
callsInMinute: z.number().int().nonnegative().default(0),
|
|
concurrentReads: z.number().int().nonnegative().default(0),
|
|
mutationsInHour: z.number().int().nonnegative().default(0),
|
|
}).strict().default(() => ({ callsInMinute: 0, concurrentReads: 0, mutationsInHour: 0 })),
|
|
auditAvailable: z.boolean().default(true),
|
|
}).strict()
|
|
|
|
export const mcpPolicyReasonCodeSchema = z.enum([
|
|
"UNKNOWN_OPERATION", "CONNECTION_INACTIVE", "CONNECTION_EXPIRED", "OPERATION_DENIED",
|
|
"OWNER_PERMISSION_MISSING", "CONNECTION_PERMISSION_MISSING", "TARGET_SCOPE_MISMATCH",
|
|
"SERVER_DENIED", "GLOBAL_DENIED", "GRANT_INACTIVE", "GRANT_SCOPE_DENIED",
|
|
"INVALID_OBJECT_SCOPE", "OBSERVER_WRITE_DENIED", "RATE_LIMITED", "BUDGET_EXCEEDED",
|
|
"PLAN_LIMIT_EXCEEDED", "PROTECTED_OBJECT", "AUDIT_UNAVAILABLE", "APPROVAL_REQUIRED",
|
|
])
|
|
export const mcpPolicyDecisionSchema = z.object({
|
|
decision: z.enum(["denied", "approval-required", "allowed"]),
|
|
reasons: z.array(z.object({ code: mcpPolicyReasonCodeSchema, message: z.string() }).strict()),
|
|
limits: mcpLimitsSchema,
|
|
}).strict()
|
|
export const mcpPolicyPreviewResponseSchema = z.object({
|
|
simulation: z.literal(true),
|
|
executionAvailable: z.literal(false),
|
|
evaluatedAt: instant,
|
|
policyVersion: z.number().int().positive(),
|
|
result: mcpPolicyDecisionSchema,
|
|
}).strict()
|
|
export const mcpCatalogResponseSchema = z.object({
|
|
stage: z.enum(["foundation", "runtime"]),
|
|
executionAvailable: z.boolean(),
|
|
operations: z.array(mcpOperationDescriptorSchema),
|
|
}).strict()
|
|
|
|
export type McpLimits = z.infer<typeof mcpLimitsSchema>
|
|
export type McpConnectionPolicy = z.infer<typeof mcpConnectionPolicySchema>
|
|
export type McpOperationDescriptor = z.infer<typeof mcpOperationDescriptorSchema>
|
|
export type McpPolicyPreviewRequest = z.infer<typeof mcpPolicyPreviewRequestSchema>
|
|
export type McpPolicyDecision = z.infer<typeof mcpPolicyDecisionSchema>
|
|
export type McpPolicyReasonCode = z.infer<typeof mcpPolicyReasonCodeSchema>
|
|
|
|
export const mcpConnectionCreateSchema = z.object({
|
|
name: z.string().trim().min(1).max(100),
|
|
expiresAt: instant,
|
|
mode: mcpModeSchema.default("observer"),
|
|
permissions: uniqueStrings,
|
|
scope: mcpScopeSchema,
|
|
dangerousPolicy: z.enum(["approval", "explicit-allow"]).default("approval"),
|
|
autonomousDangerousOperationIds: uniqueStrings.default([]),
|
|
limits: mcpLimitsSchema.default(() => mcpLimitsSchema.parse({})),
|
|
}).strict()
|
|
export const mcpConnectionSchema = z.object({
|
|
id: z.string().uuid(), name: z.string(), ownerId: z.string(),
|
|
policy: mcpConnectionPolicySchema, createdAt: instant, updatedAt: instant,
|
|
lastUsedAt: instant.nullable(), generationValid: z.boolean(),
|
|
ownerValidUntil: instant.optional(),
|
|
}).strict()
|
|
export const mcpConnectionsSchema = z.object({ connections: z.array(mcpConnectionSchema) }).strict()
|
|
export const mcpSettingsSchema = z.object({
|
|
enabled: z.boolean(), requestsRetentionDays: z.number().int().min(1).max(365),
|
|
auditRetentionDays: z.number().int().min(30).max(3650),
|
|
}).strict()
|
|
export const mcpSettingsPatchSchema = mcpSettingsSchema.partial().strict().refine(value => Object.keys(value).length > 0)
|
|
export const mcpPageQuerySchema = z.object({
|
|
limit: z.coerce.number().int().min(1).max(100).default(50),
|
|
operationId: z.string().uuid().optional(),
|
|
cursor: z.union([z.string().uuid(), z.string().regex(/^[1-9]\d{0,18}$/)]).optional(),
|
|
connectionId: z.string().uuid().optional(),
|
|
status: z.string().max(50).optional(),
|
|
tool: z.string().max(200).optional(),
|
|
userId: z.string().trim().min(1).max(200).optional(),
|
|
serverId: z.coerce.number().int().positive().max(Number.MAX_SAFE_INTEGER).optional(), from: instant.optional(), to: instant.optional(),
|
|
}).strict()
|
|
export type McpConnection = z.infer<typeof mcpConnectionSchema>
|
|
export type McpConnectionCreate = z.infer<typeof mcpConnectionCreateSchema>
|
|
|
|
const pollOwnerCapacitySchema=z.object({used:z.number().int().min(0).max(8),limit:z.literal(8),remaining:z.number().int().min(0).max(8)}).strict().refine(value=>value.used+value.remaining<=value.limit)
|
|
const pollGlobalCapacitySchema=z.object({used:z.number().int().min(0).max(128),limit:z.literal(128),remaining:z.number().int().min(0).max(128)}).strict().refine(value=>value.used+value.remaining<=value.limit)
|
|
/** Heavy reservations. Retained archive bytes/rows can further reduce the
|
|
* remaining capacity; this observation never grants an execution admission. */
|
|
export const mcpPollStorageCapacitySchema=z.discriminatedUnion("status",[
|
|
z.object({status:z.literal("unavailable")}).strict(),
|
|
z.object({status:z.literal("observed"),owner:pollOwnerCapacitySchema,
|
|
global:z.object({exhausted:z.boolean(),usage:pollGlobalCapacitySchema.nullable()}).strict()
|
|
.refine(value=>value.usage===null||value.exhausted===(value.usage.remaining===0)),
|
|
retentionDays:z.number().int().min(1).max(365)}).strict(),
|
|
])
|
|
export type McpPollStorageCapacity=z.infer<typeof mcpPollStorageCapacitySchema>
|