Files
PaperClipAI/packages/shared/src/validators/tool-access.ts
T
3abe9e2134 build(deps): bump zod from 3.25.76 to 4.4.3 (#11719)
Bumps [zod](https://github.com/colinhacks/zod) from 3.25.76 to 4.4.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/colinhacks/zod/releases">zod's
releases</a>.</em></p>
<blockquote>
<h2>v4.4.3</h2>
<h2>Commits:</h2>
<ul>
<li>4c2fa95ce3f3390fbc522324e406b4e9e89b88f9 docs: use Zernio primary
wordmark for gold sponsor logo</li>
<li>2aeec83eb135e3a83756e973ef44845fc5a455d2 docs: prune lapsed gold
sponsors and rebalance logo sizing</li>
<li>7391be88ac1ee5cd02057f5ccc012a1f5df4efd0 docs: prune lapsed
silver/bronze sponsors and add active ones</li>
<li>2c703322a21b4e2b12f33f49ea8430c451a68b4f docs: normalize bronze
sponsor logos to github avatar pattern</li>
<li>9195250cab0e7950efe39c3926d6c203b4b0a170 docs: remove Mintlify from
bronze sponsors (churned)</li>
<li>b8dffe9e62f17e6571e6249d05cc5102b54d94e4 docs: remove Numeric and
Speakeasy (2+ missed monthly cycles)</li>
<li>1cab69383fcdeae2a366d5e2a2fc4d8fc765d168 fix(v4): restore catch
handling for absent object keys (<a
href="https://redirect.github.com/colinhacks/zod/issues/5937">#5937</a>)
(<a
href="https://redirect.github.com/colinhacks/zod/issues/5939">#5939</a>)</li>
<li>c2be4f819064eed62c7c350a2d399b5faecd15f8 fix(v4): generalize
optin/fallback to transform; restore preprocess on absent keys (<a
href="https://redirect.github.com/colinhacks/zod/issues/5941">#5941</a>)</li>
<li>f3c9ec03ba7a28ae72d25cc295f38674bee0f559 4.4.3</li>
<li>1fb56a5c18c27102dbc92260a4007c7732a0ccca docs: document release
procedure in AGENTS.md</li>
</ul>
<h2>v4.4.2</h2>
<h2>Commits:</h2>
<ul>
<li>0c62df0ea19fd05abdf90473e9eef7eea530fab2 Clean up docs navigation
and stale labels (<a
href="https://redirect.github.com/colinhacks/zod/issues/5901">#5901</a>)</li>
<li>20cc794895cc8604fe0c87d83a5d1c3f89fad0ac chore: add security policy
and refresh tooling deps</li>
<li>6fbe07b0177efdd1bf1c0b05160e70d7a0702337 fix(docs): heading anchor
links now include the hash so it doesnt scoll all the way up, follows
navbar logic (<a
href="https://redirect.github.com/colinhacks/zod/issues/5791">#5791</a>)</li>
<li>4bbed1b1c73eca4ce9e59b1189ed236aa6c8b5bd Tighten discriminated union
option typing</li>
<li>bbac3e567e7fccfaaf7cdc97f1ce30c295e2c908 Update PR guidance for
agents</li>
<li>cf0dc942a32805c292fff59ade20a7ace980735a Merge remote-tracking
branch 'origin/main' into fix-discriminated-union-key-constraint</li>
<li>292c894a5fd2aa42e527900b83d8d7a3009a709c docs: add Zernio gold
sponsor</li>
<li>1fc9f311c28dcf80d0bb5a36b177086cbc3d8eca docs: document codec
inversion</li>
<li>1373c85da9aeff704a9762d27bc58699618aefb7 docs: remove AI disclosure
guidance</li>
<li>e20d02b473c08e3a4e557bc610b1b5fac079b649 chore: ignore triage
notes</li>
<li>e58ea4d91b1dfe8194b73508203213cbc7e9c936 docs: test Zod Mini tab
code heights</li>
<li>905761a5d127e8d5dd2ebb3bc88c75cb0b8149ff docs: document preprocess
input type narrowing</li>
<li>bf64bac850d4dee2b7dde7e64909d5d796d32043 chore: tighten test
guidance in AGENTS.md</li>
<li>8ec4e73f4c4693b6361ad591be40fb41eb8a9f95 chore: update play.ts
scratch</li>
<li>02c2baf7d0d615872fa4528a8020603b71211702 Make z.preprocess defer
optionality to inner schema (<a
href="https://redirect.github.com/colinhacks/zod/issues/5929">#5929</a>)</li>
<li>88015df8e25c44fb5385eb3ef28935119cd5edea fix(docs): drop deprecated
<code>baseUrl</code> from tsconfig</li>
<li>c59d4474e3b4cad1b323462186cf607178ce8267 4.4.2</li>
</ul>
<h2>v4.4.1</h2>
<h2>Commits:</h2>
<ul>
<li>481f7be4238c83ed58183f921b2646f340a91c6a ci: gate release publishing
on full test workflow</li>
<li>95ccab423aec720b2523c3a64cdc7e3204537cc7 test(v3): restore optional
undefined expectations</li>
<li>cede2c63739a5823d6aa5093d291e9a111da943d fix(v4): reject tuple holes
before required defaults (<a
href="https://redirect.github.com/colinhacks/zod/issues/5900">#5900</a>)</li>
<li>edd0bf0f5ada4a8dc581c259407d7bbad0a71ea7 release: 4.4.1</li>
<li>180d83d1dbe6a59260710cc8637a3dea2281ee56 docs: remove Jazz featured
sponsor</li>
</ul>
<h2>v4.4.0</h2>
<h2>4.4.0</h2>
<p>This is a minor release with a wide set of correctness and soundness
fixes. Some fixes intentionally make Zod stricter, so code that depended
on previously accepted invalid or ambiguous inputs may need small
updates.</p>
<h2>Potentially breaking bug fixes</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/colinhacks/zod/commit/1fb56a5c18c27102dbc92260a4007c7732a0ccca"><code>1fb56a5</code></a>
docs: document release procedure in AGENTS.md</li>
<li><a
href="https://github.com/colinhacks/zod/commit/f3c9ec03ba7a28ae72d25cc295f38674bee0f559"><code>f3c9ec0</code></a>
4.4.3</li>
<li><a
href="https://github.com/colinhacks/zod/commit/c2be4f819064eed62c7c350a2d399b5faecd15f8"><code>c2be4f8</code></a>
fix(v4): generalize optin/fallback to transform; restore preprocess on
absent...</li>
<li><a
href="https://github.com/colinhacks/zod/commit/1cab69383fcdeae2a366d5e2a2fc4d8fc765d168"><code>1cab693</code></a>
fix(v4): restore catch handling for absent object keys (<a
href="https://redirect.github.com/colinhacks/zod/issues/5937">#5937</a>)
(<a
href="https://redirect.github.com/colinhacks/zod/issues/5939">#5939</a>)</li>
<li><a
href="https://github.com/colinhacks/zod/commit/b8dffe9e62f17e6571e6249d05cc5102b54d94e4"><code>b8dffe9</code></a>
docs: remove Numeric and Speakeasy (2+ missed monthly cycles)</li>
<li><a
href="https://github.com/colinhacks/zod/commit/9195250cab0e7950efe39c3926d6c203b4b0a170"><code>9195250</code></a>
docs: remove Mintlify from bronze sponsors (churned)</li>
<li><a
href="https://github.com/colinhacks/zod/commit/2c703322a21b4e2b12f33f49ea8430c451a68b4f"><code>2c70332</code></a>
docs: normalize bronze sponsor logos to github avatar pattern</li>
<li><a
href="https://github.com/colinhacks/zod/commit/7391be88ac1ee5cd02057f5ccc012a1f5df4efd0"><code>7391be8</code></a>
docs: prune lapsed silver/bronze sponsors and add active ones</li>
<li><a
href="https://github.com/colinhacks/zod/commit/2aeec83eb135e3a83756e973ef44845fc5a455d2"><code>2aeec83</code></a>
docs: prune lapsed gold sponsors and rebalance logo sizing</li>
<li><a
href="https://github.com/colinhacks/zod/commit/4c2fa95ce3f3390fbc522324e406b4e9e89b88f9"><code>4c2fa95</code></a>
docs: use Zernio primary wordmark for gold sponsor logo</li>
<li>Additional commits viewable in <a
href="https://github.com/colinhacks/zod/compare/v3.25.76...v4.4.3">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for zod since your current version.</p>
</details>
<br />

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Priya Raman <priya@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-21 00:04:14 -07:00

817 lines
37 KiB
TypeScript

import { z } from "zod";
import {
CONNECTION_TOKEN_ISSUANCE_PATHS,
SECRET_PROJECTION_CLASSES,
TOOL_ACTION_REQUEST_STATUSES,
TOOL_APPLICATION_STATUSES,
TOOL_APPLICATION_TYPES,
TOOL_AUDIT_EVENT_TYPES,
TOOL_AUDIT_OUTCOMES,
TOOL_CATALOG_ENTRY_KINDS,
TOOL_CATALOG_ENTRY_STATUSES,
TOOL_CONNECTION_HEALTH_STATUSES,
TOOL_CONNECTION_KINDS,
TOOL_INVOCATION_APPROVAL_STATES,
TOOL_INVOCATION_STATUSES,
TOOL_MCP_GATEWAY_CONTEXT_SCOPE_TYPES,
TOOL_MCP_GATEWAY_DEFAULT_PROFILE_MODES,
TOOL_MCP_GATEWAY_STATUSES,
TOOL_MCP_GATEWAY_TOKEN_ACTIONS,
TOOL_MCP_GATEWAY_TOKEN_SUBJECT_TYPES,
TOOL_POLICY_DECISIONS,
TOOL_POLICY_TYPES,
TOOL_PROFILE_BINDING_TARGET_TYPES,
TOOL_PROFILE_DEFAULT_ACTIONS,
TOOL_PROFILE_ENTRY_EFFECTS,
TOOL_PROFILE_ENTRY_SELECTOR_TYPES,
TOOL_PROFILE_STATUSES,
TOOL_RATE_LIMIT_WINDOW_KINDS,
TOOL_RISK_LEVELS,
TOOL_RUNTIME_KINDS,
TOOL_RUNTIME_SLOT_STATUSES,
} from "../constants.js";
import { jsonSchemaSchema } from "./plugin.js";
import { objectWithoutDefaults } from "./partial.js";
export const toolApplicationTypeSchema = z.enum(TOOL_APPLICATION_TYPES);
export const toolApplicationStatusSchema = z.enum(TOOL_APPLICATION_STATUSES);
export const toolConnectionTransportSchema = z.enum(["mcp_remote", "rest_api", "local_stdio"]);
export const toolConnectionAuthKindSchema = z.enum(["oauth", "api_key", "none"]);
export const toolConnectionOwnershipSchema = z.enum(["platform_shared", "platform_provisioned", "customer", "dcr"]);
export const connectionGrantKindSchema = z.enum(["workspace", "user"]);
export const connectionGrantStatusSchema = z.enum(["active", "revoked", "expired", "needs_reauthorization"]);
export const toolConnectionStatusSchema = z.enum(["draft", "active", "disabled", "archived"]);
export const toolConnectionInstallTargetTypeSchema = z.enum(["company", "agent"]);
export const toolCredentialPlacementSchema = z.enum(["header", "env"]);
export const toolConnectionKindSchema = z.enum(TOOL_CONNECTION_KINDS);
export const toolConnectionHealthStatusSchema = z.enum(TOOL_CONNECTION_HEALTH_STATUSES);
export const toolCatalogEntryKindSchema = z.enum(TOOL_CATALOG_ENTRY_KINDS);
export const toolCatalogEntryStatusSchema = z.enum(TOOL_CATALOG_ENTRY_STATUSES);
export const toolRiskLevelSchema = z.enum(TOOL_RISK_LEVELS);
export const toolProfileStatusSchema = z.enum(TOOL_PROFILE_STATUSES);
export const toolProfileDefaultActionSchema = z.enum(TOOL_PROFILE_DEFAULT_ACTIONS);
export const toolProfileEntrySelectorTypeSchema = z.enum(TOOL_PROFILE_ENTRY_SELECTOR_TYPES);
export const toolProfileEntryEffectSchema = z.enum(TOOL_PROFILE_ENTRY_EFFECTS);
export const toolProfileBindingTargetTypeSchema = z.enum(TOOL_PROFILE_BINDING_TARGET_TYPES);
export const toolPolicyTypeSchema = z.enum(TOOL_POLICY_TYPES);
export const toolPolicyDecisionSchema = z.enum(TOOL_POLICY_DECISIONS);
export const toolInvocationStatusSchema = z.enum(TOOL_INVOCATION_STATUSES);
export const toolInvocationApprovalStateSchema = z.enum(TOOL_INVOCATION_APPROVAL_STATES);
export const toolMcpGatewayStatusSchema = z.enum(TOOL_MCP_GATEWAY_STATUSES);
export const toolMcpGatewayDefaultProfileModeSchema = z.enum(TOOL_MCP_GATEWAY_DEFAULT_PROFILE_MODES);
export const toolMcpGatewayContextScopeTypeSchema = z.enum(TOOL_MCP_GATEWAY_CONTEXT_SCOPE_TYPES);
export const toolMcpGatewayTokenSubjectTypeSchema = z.enum(TOOL_MCP_GATEWAY_TOKEN_SUBJECT_TYPES);
export const toolMcpGatewayTokenActionSchema = z.enum(TOOL_MCP_GATEWAY_TOKEN_ACTIONS);
export const toolActionRequestStatusSchema = z.enum(TOOL_ACTION_REQUEST_STATUSES);
export const toolAuditEventTypeSchema = z.enum(TOOL_AUDIT_EVENT_TYPES);
export const toolAuditOutcomeSchema = z.enum(TOOL_AUDIT_OUTCOMES);
export const toolRuntimeKindSchema = z.enum(TOOL_RUNTIME_KINDS);
export const toolRuntimeSlotStatusSchema = z.enum(TOOL_RUNTIME_SLOT_STATUSES);
export const toolRateLimitWindowKindSchema = z.enum(TOOL_RATE_LIMIT_WINDOW_KINDS);
const safeKeyPattern = /^[a-z0-9][a-z0-9._:-]*$/i;
const sensitiveConfigKeyPattern =
/^(access[-_]?key([-_]?id)?|api[-_]?key|authorization|bearer|client[-_]?secret|credential|credentials|jwt|password|passwd|private[-_]?key|refresh[-_]?token|secret|secret[-_]?access[-_]?key|secret[-_]?key|session[-_]?token|token)$/i;
function rejectSensitiveConfigKeys(value: unknown, ctx: z.RefinementCtx, path: Array<string | number> = []) {
if (!value || typeof value !== "object") return;
if (Array.isArray(value)) {
value.forEach((entry, index) => rejectSensitiveConfigKeys(entry, ctx, [...path, index]));
return;
}
for (const [key, nested] of Object.entries(value)) {
if (sensitiveConfigKeyPattern.test(key)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: [...path, key],
message: `Tool access config cannot persist sensitive field: ${key}. Use credentialSecretRefs instead.`,
});
}
rejectSensitiveConfigKeys(nested, ctx, [...path, key]);
}
}
export const toolCredentialSecretRefSchema = z.object({
secretId: z.string().guid(),
versionSelector: z.union([z.literal("latest"), z.number().int().positive()]).optional(),
configPath: z.string().trim().min(1).max(200),
required: z.boolean().optional(),
label: z.string().trim().max(120).optional().nullable(),
projectionClass: z.enum(SECRET_PROJECTION_CLASSES).optional(),
projectionAllowlistKey: z.string().trim().min(1).max(160).optional().nullable(),
keyScope: z.string().trim().min(1).max(160).optional(),
expiresAt: z.string().datetime({ offset: true }).optional(),
});
export const mcpConnectionCredentialRefSchema = z.object({
name: z.string().trim().min(1).max(120),
secretId: z.string().guid(),
version: z.union([z.literal("latest"), z.number().int().positive()]).optional(),
placement: toolCredentialPlacementSchema,
key: z.string().trim().min(1).max(160),
prefix: z.string().max(120).nullable().optional(),
});
export const toolTransportConfigSchema = z.record(z.string(), z.unknown()).superRefine(rejectSensitiveConfigKeys);
export const toolRedactedValueSummarySchema = z.object({
summary: z.string().max(4000),
sizeBytes: z.number().int().min(0).optional().nullable(),
sha256: z.string().trim().regex(/^[a-f0-9]{64}$/i).optional().nullable(),
redactedFields: z.array(z.string().trim().min(1).max(200)).default([]).optional(),
artifactId: z.string().guid().optional().nullable(),
});
export const createToolApplicationSchema = z.object({
applicationKey: z.string().trim().min(1).max(160).regex(safeKeyPattern).optional(),
name: z.string().trim().min(1).max(160),
description: z.string().max(4000).optional().nullable(),
type: toolApplicationTypeSchema,
status: toolApplicationStatusSchema.optional(),
pluginId: z.string().guid().optional().nullable(),
ownerAgentId: z.string().guid().optional().nullable(),
ownerUserId: z.string().optional().nullable(),
metadata: z.record(z.string(), z.unknown()).optional().nullable(),
});
export type CreateToolApplication = z.infer<typeof createToolApplicationSchema>;
export const updateToolApplicationSchema = createToolApplicationSchema.partial().refine(
(value) => Object.keys(value).length > 0,
{ message: "At least one tool application field is required" },
);
export type UpdateToolApplication = z.infer<typeof updateToolApplicationSchema>;
export const createToolConnectionSchema = z.object({
applicationId: z.string().guid().optional(),
applicationName: z.string().trim().min(1).max(160).optional(),
name: z.string().trim().min(1).max(160),
transport: toolConnectionTransportSchema.optional(),
authKind: toolConnectionAuthKindSchema.default("none"),
ownership: toolConnectionOwnershipSchema.default("customer"),
status: toolConnectionStatusSchema.optional(),
connectionKind: toolConnectionKindSchema.default("managed"),
config: toolTransportConfigSchema.optional(),
transportConfig: toolTransportConfigSchema.default({}),
credentialRefs: z.array(mcpConnectionCredentialRefSchema).optional(),
credentialSecretRefs: z.array(toolCredentialSecretRefSchema).default([]),
enabled: z.boolean().optional(),
});
export type CreateToolConnection = z.infer<typeof createToolConnectionSchema>;
export const updateToolConnectionSchema = objectWithoutDefaults(createToolConnectionSchema.omit({ applicationId: true })).partial().refine(
(value) => Object.keys(value).length > 0,
{ message: "At least one tool connection field is required" },
);
export type UpdateToolConnection = z.infer<typeof updateToolConnectionSchema>;
export const connectionGrantSchema = z.object({
id: z.string().guid(),
companyId: z.string().guid(),
connectionId: z.string().guid(),
kind: connectionGrantKindSchema,
subjectUserId: z.string().nullable(),
providerTenant: z.object({
name: z.string().trim().min(1).max(200).optional(),
externalId: z.string().trim().min(1).max(400).optional(),
}).nullable(),
credentialSecretRefs: z.array(toolCredentialSecretRefSchema),
status: connectionGrantStatusSchema,
isDefault: z.boolean(),
createdByAgentId: z.string().guid().nullable(),
createdByUserId: z.string().nullable(),
revokedAt: z.coerce.date().nullable(),
revokedByAgentId: z.string().guid().nullable(),
revokedByUserId: z.string().nullable(),
lastUsedAt: z.coerce.date().nullable(),
createdAt: z.coerce.date(),
updatedAt: z.coerce.date(),
}).superRefine((grant, ctx) => {
if ((grant.kind === "user") !== Boolean(grant.subjectUserId)) {
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["subjectUserId"], message: "User grants require a subject user; workspace grants must not have one" });
}
});
export const putToolConnectionInstallsSchema = z.object({
installs: z.array(z.object({
targetType: toolConnectionInstallTargetTypeSchema,
targetId: z.string().trim().min(1).max(200),
})).max(1000),
}).strict();
export type PutToolConnectionInstalls = z.infer<typeof putToolConnectionInstallsSchema>;
export const connectionTokenIssuancePathSchema = z.enum(CONNECTION_TOKEN_ISSUANCE_PATHS);
export const connectionTokenScopeSchema = z.union([
z.string().trim().min(1).max(500),
z.array(z.string().trim().min(1).max(240)).max(100),
]);
export const connectionTokenSubjectSchema = z.discriminatedUnion("type", [
z.object({ type: z.literal("app") }).strict(),
z.object({ type: z.literal("user"), userId: z.string().trim().min(1).max(500) }).strict(),
]);
export const connectionTokenRequestSchema = z.object({
subject: connectionTokenSubjectSchema.optional().default({ type: "app" }),
scope: connectionTokenScopeSchema.optional(),
requestedTtlSeconds: z.number().int().positive().max(86_400).optional(),
grantId: z.string().guid().optional(),
}).strict();
export const startConnectionAuthorizationSchema = z.object({
subjectUserId: z.string().trim().min(1).max(500),
scopes: z.array(z.string().trim().min(1).max(240)).max(100).optional(),
returnTo: z.string().trim().max(2000).optional(),
}).strict();
export type ConnectionTokenRequestInput = z.infer<typeof connectionTokenRequestSchema>;
const envKeyPattern = /^[A-Z_][A-Z0-9_]*$/i;
export const toolStdioTemplateToolSchema = z.object({
name: z.string().trim().min(1).max(240),
title: z.string().trim().max(240).optional().nullable(),
description: z.string().max(8000).optional().nullable(),
inputSchema: jsonSchemaSchema.optional().nullable(),
annotations: z.record(z.string(), z.unknown()).optional().nullable(),
});
export const createToolStdioCommandTemplateSchema = z.object({
templateId: z.string().trim().min(1).max(160).regex(safeKeyPattern),
name: z.string().trim().min(1).max(160),
description: z.string().max(4000).optional().nullable(),
command: z.string().trim().min(1).max(2000),
args: z.array(z.string().max(2000)).max(100).default([]),
envKeys: z.array(z.string().trim().min(1).max(160).regex(envKeyPattern)).max(200).default([]),
tools: z.array(toolStdioTemplateToolSchema).max(500).default([]),
});
export type CreateToolStdioCommandTemplate = z.infer<typeof createToolStdioCommandTemplateSchema>;
export const disableToolStdioCommandTemplateSchema = z.object({
reason: z.string().trim().max(1000).optional().nullable(),
});
export type DisableToolStdioCommandTemplate = z.infer<typeof disableToolStdioCommandTemplateSchema>;
export const connectToolAppSchema = z.object({
galleryKey: z.string().trim().min(1).max(120).optional(),
link: z.string().trim().url().max(2000).optional(),
name: z.string().trim().min(1).max(160).optional(),
credentialValues: z.record(z.string().trim().min(1).max(200), z.string().min(1)).optional(),
configValues: z.record(z.string().trim().min(1).max(200), z.unknown()).optional(),
applicationId: z.string().guid().optional(),
}).refine(
(value) => Boolean(value.galleryKey) !== Boolean(value.link),
{ message: "Provide exactly one of galleryKey or link" },
);
export type ConnectToolApp = z.infer<typeof connectToolAppSchema>;
export const reconnectToolAppSchema = z.object({
credentialValues: z.record(z.string().trim().min(1).max(200), z.string().min(1)),
});
export type ReconnectToolApp = z.infer<typeof reconnectToolAppSchema>;
export const finishToolAppSchema = z.object({
enabledCatalogEntryIds: z.array(z.string().guid()).max(500).default([]),
askFirstCatalogEntryIds: z.array(z.string().guid()).max(500).default([]),
reviewedCatalogEntryIds: z.array(z.string().guid()).max(500).optional(),
access: z.union([
z.literal("all_agents"),
z.object({ agentIds: z.array(z.string().guid()).min(1).max(250) }),
]),
});
export type FinishToolApp = z.infer<typeof finishToolAppSchema>;
export const upsertToolCatalogEntrySchema = z.object({
applicationId: z.string().guid(),
connectionId: z.string().guid(),
entryKind: toolCatalogEntryKindSchema.default("tool"),
toolName: z.string().trim().min(1).max(240),
title: z.string().trim().max(240).optional().nullable(),
description: z.string().max(8000).optional().nullable(),
inputSchema: jsonSchemaSchema.optional().nullable(),
outputSchema: jsonSchemaSchema.optional().nullable(),
annotations: z.record(z.string(), z.unknown()).optional().nullable(),
riskLevel: toolRiskLevelSchema.default("medium"),
isReadOnly: z.boolean().default(false),
isWrite: z.boolean().default(false),
isDestructive: z.boolean().default(false),
status: toolCatalogEntryStatusSchema.default("active"),
version: z.string().trim().max(200).optional().nullable(),
schemaHash: z.string().trim().max(128).optional().nullable(),
});
export type UpsertToolCatalogEntry = z.infer<typeof upsertToolCatalogEntrySchema>;
export const createToolProfileSchema = z.object({
profileKey: z.string().trim().min(1).max(160).regex(safeKeyPattern),
name: z.string().trim().min(1).max(160),
description: z.string().max(4000).optional().nullable(),
status: toolProfileStatusSchema.default("active"),
defaultAction: toolProfileDefaultActionSchema.default("deny"),
metadata: z.record(z.string(), z.unknown()).optional().nullable(),
});
export type CreateToolProfile = z.infer<typeof createToolProfileSchema>;
export const updateToolProfileSchema = objectWithoutDefaults(createToolProfileSchema).partial().refine(
(value) => Object.keys(value).length > 0,
{ message: "At least one tool profile field is required" },
);
export type UpdateToolProfile = z.infer<typeof updateToolProfileSchema>;
export const createToolProfileEntrySchema = z.object({
profileId: z.string().guid(),
selectorType: toolProfileEntrySelectorTypeSchema,
effect: toolProfileEntryEffectSchema.default("include"),
applicationId: z.string().guid().optional().nullable(),
connectionId: z.string().guid().optional().nullable(),
catalogEntryId: z.string().guid().optional().nullable(),
toolName: z.string().trim().min(1).max(240).optional().nullable(),
riskLevel: toolRiskLevelSchema.optional().nullable(),
conditions: z.record(z.string(), z.unknown()).optional().nullable(),
});
export type CreateToolProfileEntry = z.infer<typeof createToolProfileEntrySchema>;
export const createToolProfileEntryForProfileSchema = createToolProfileEntrySchema.omit({ profileId: true });
export type CreateToolProfileEntryForProfile = z.infer<typeof createToolProfileEntryForProfileSchema>;
export const updateToolProfileEntrySchema = objectWithoutDefaults(createToolProfileEntryForProfileSchema).partial().refine(
(value) => Object.keys(value).length > 0,
{ message: "At least one tool profile entry field is required" },
);
export type UpdateToolProfileEntry = z.infer<typeof updateToolProfileEntrySchema>;
export const createToolProfileWithEntriesSchema = createToolProfileSchema.extend({
entries: z.array(createToolProfileEntryForProfileSchema).max(250).optional(),
});
export type CreateToolProfileWithEntries = z.infer<typeof createToolProfileWithEntriesSchema>;
export const duplicateToolProfileSchema = z.object({
name: z.string().trim().min(1).max(160),
includeAssignments: z.boolean().default(false),
});
export type DuplicateToolProfile = z.infer<typeof duplicateToolProfileSchema>;
export const updateToolProfileWithEntriesSchema = objectWithoutDefaults(createToolProfileSchema).partial().extend({
entries: z.array(createToolProfileEntryForProfileSchema).max(250).optional(),
}).refine(
(value) => Object.keys(value).length > 0,
{ message: "At least one tool profile field is required" },
);
export type UpdateToolProfileWithEntries = z.infer<typeof updateToolProfileWithEntriesSchema>;
export const reviewToolProfileNewToolsSchema = z.object({
decisions: z.array(z.object({
catalogEntryId: z.string().guid(),
decision: z.enum(["allow", "keep_blocked"]),
})).min(1).max(250),
});
export type ReviewToolProfileNewTools = z.infer<typeof reviewToolProfileNewToolsSchema>;
export const deleteToolProfileSchema = z.object({
force: z.boolean().default(false),
reassignToProfileId: z.string().guid().optional(),
}).prefault({});
export type DeleteToolProfile = z.infer<typeof deleteToolProfileSchema>;
export const createToolProfileBindingSchema = z.object({
profileId: z.string().guid(),
targetType: toolProfileBindingTargetTypeSchema,
targetId: z.string().trim().min(1).max(200),
priority: z.number().int().min(0).max(10000).default(100),
metadata: z.record(z.string(), z.unknown()).optional().nullable(),
});
export type CreateToolProfileBinding = z.infer<typeof createToolProfileBindingSchema>;
export const createToolProfileBindingForProfileSchema = createToolProfileBindingSchema.omit({ profileId: true });
export type CreateToolProfileBindingForProfile = z.infer<typeof createToolProfileBindingForProfileSchema>;
export const unbindToolProfileBindingSchema = createToolProfileBindingForProfileSchema.pick({
targetType: true,
targetId: true,
});
export type UnbindToolProfileBinding = z.infer<typeof unbindToolProfileBindingSchema>;
const headerNameSchema = z.string().trim().min(1).max(120).regex(/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/);
export const toolMcpGatewayAuthConfigSchema = z.object({
version: z.literal(1).default(1),
bearer: z.object({
enabled: z.boolean().default(true),
tokenPrefix: z.literal("pcgw").default("pcgw"),
defaultTtlSeconds: z.number().int().positive().max(31_536_000).nullable().default(7_776_000),
requireFiniteExpiry: z.boolean().default(true),
longLivedTokenRequiresOverride: z.boolean().default(true),
}).prefault({}),
oauth: z.object({
enabled: z.literal(false).default(false),
reservedFor: z.literal("v1_5").default("v1_5"),
protectedResourceMetadataPath: z.string().trim().max(240).optional().nullable(),
dynamicClientRegistration: z.literal(false).optional(),
authorizationCodePkce: z.literal(false).optional(),
}).prefault({}),
});
export const toolMcpGatewayHeaderPolicySchema = z.object({
version: z.literal(1).default(1),
callerPassthrough: z.object({
enabled: z.boolean().default(false),
allowedHeaders: z.array(headerNameSchema).max(50).default([]),
}).prefault({}),
staticHeaders: z.array(z.object({
name: headerNameSchema,
valueRef: z.string().trim().max(240).optional().nullable(),
value: z.string().max(4000).optional().nullable(),
})).max(50).default([]),
generatedMetadata: z.object({
enabled: z.boolean().default(false),
allowedHeaders: z.array(headerNameSchema).max(20).default([]),
}).prefault({}),
responseHeaders: z.object({
forwardMcpRequiredHeaders: z.boolean().default(true),
forwardSafeCacheHeaders: z.boolean().default(true),
}).prefault({}),
});
export const toolMcpGatewayMetadataPolicySchema = z.object({
version: z.literal(1).default(1),
forwardCompanyId: z.boolean().default(false),
forwardGatewayId: z.boolean().default(false),
forwardProjectId: z.boolean().default(false),
forwardIssueId: z.boolean().default(false),
forwardAgentId: z.boolean().default(false),
forwardRunId: z.boolean().default(false),
forwardCorrelationId: z.boolean().default(true),
});
export const toolMcpGatewayOnDemandToolsConfigSchema = z.object({
enabled: z.boolean().default(false),
searchToolName: z.literal("search_tools").default("search_tools"),
runToolName: z.literal("run_tool").default("run_tool"),
});
export const createToolMcpGatewaySchema = z.object({
name: z.string().trim().min(1).max(160),
slug: z.string().trim().min(1).max(120).regex(safeKeyPattern).optional(),
displaySlug: z.string().trim().min(1).max(120).regex(safeKeyPattern).optional(),
description: z.string().max(4000).optional().nullable(),
profileId: z.string().guid(),
defaultProfileMode: toolMcpGatewayDefaultProfileModeSchema.default("gateway_only").optional(),
contextScopeType: toolMcpGatewayContextScopeTypeSchema.default("none").optional(),
contextScopeId: z.string().trim().min(1).max(200).optional().nullable(),
agentId: z.string().guid().optional().nullable(),
projectId: z.string().guid().optional().nullable(),
issueId: z.string().guid().optional().nullable(),
approvalIssueId: z.string().guid().optional().nullable(),
authConfig: toolMcpGatewayAuthConfigSchema.optional(),
headerPolicy: toolMcpGatewayHeaderPolicySchema.optional(),
metadataPolicy: toolMcpGatewayMetadataPolicySchema.optional(),
onDemandToolsConfig: toolMcpGatewayOnDemandToolsConfigSchema.optional(),
metadata: z.record(z.string(), z.unknown()).optional().nullable(),
});
export type CreateToolMcpGateway = z.infer<typeof createToolMcpGatewaySchema>;
export const updateToolMcpGatewaySchema = objectWithoutDefaults(createToolMcpGatewaySchema)
.partial()
.extend({ status: toolMcpGatewayStatusSchema.optional() })
.refine((value) => Object.keys(value).length > 0, { message: "At least one gateway field is required" });
export type UpdateToolMcpGateway = z.infer<typeof updateToolMcpGatewaySchema>;
export const createToolMcpGatewayTokenSchema = z.object({
name: z.string().trim().min(1).max(160),
subjectType: toolMcpGatewayTokenSubjectTypeSchema.default("gateway_client").optional(),
subjectId: z.string().trim().min(1).max(240).optional().nullable(),
clientLabel: z.string().trim().min(1).max(160),
ownerNote: z.string().trim().min(1).max(1000),
allowedActions: z.array(toolMcpGatewayTokenActionSchema).min(1).max(TOOL_MCP_GATEWAY_TOKEN_ACTIONS.length).default(["tools/list", "tools/call"]).optional(),
expiresAt: z.coerce.date().optional().nullable(),
expiryOverrideReason: z.string().trim().min(1).max(1000).optional().nullable(),
}).superRefine((value, ctx) => {
if (value.subjectType && value.subjectType !== "gateway_client") {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["subjectType"],
message: "Public V1 token minting only supports gateway_client subjects; heartbeat_run is runtime-managed, while board_user and agent are reserved for later OAuth/user-bound flows.",
});
}
if (value.expiresAt === null && !value.expiryOverrideReason) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["expiryOverrideReason"],
message: "Non-expiring gateway tokens require an override reason.",
});
}
});
export type CreateToolMcpGatewayToken = z.infer<typeof createToolMcpGatewayTokenSchema>;
const argumentConditionSchema = z.object({
fieldEquals: z.record(z.string().trim().min(1).max(120), z.unknown()).optional(),
fieldNotEquals: z.record(z.string().trim().min(1).max(120), z.unknown()).optional(),
fieldIn: z.record(z.string().trim().min(1).max(120), z.array(z.unknown()).min(1).max(100)).optional(),
fieldMatches: z.record(z.string().trim().min(1).max(120), z.string().trim().min(1).max(500)).optional(),
fieldExists: z.array(z.string().trim().min(1).max(120)).max(100).optional(),
fieldAbsent: z.array(z.string().trim().min(1).max(120)).max(100).optional(),
}).strict().refine(
(value) => Object.values(value).some((nested) => Array.isArray(nested) ? nested.length > 0 : Boolean(nested && Object.keys(nested).length > 0)),
{ message: "Argument conditions must include at least one field predicate" },
);
const timeWindowConditionSchema = z.object({
startAt: z.string().trim().datetime({ offset: true }).optional(),
endAt: z.string().trim().datetime({ offset: true }).optional(),
daysOfWeekUtc: z.array(z.number().int().min(0).max(6)).max(7).optional(),
startHourUtc: z.number().int().min(0).max(23).optional(),
endHourUtc: z.number().int().min(0).max(24).optional(),
}).strict().refine(
(value) => value.startAt || value.endAt || value.daysOfWeekUtc?.length || value.startHourUtc !== undefined || value.endHourUtc !== undefined,
{ message: "timeWindow must include at least one bound" },
);
const actorConditionSchema = z.object({
actorType: z.enum(["agent", "user", "system", "plugin"]).optional(),
actorTypes: z.array(z.enum(["agent", "user", "system", "plugin"])).max(20).optional(),
agentId: z.string().guid().optional(),
agentIds: z.array(z.string().guid()).max(100).optional(),
}).strict();
const contextConditionSchema = z.object({
projectId: z.string().guid().optional(),
projectIds: z.array(z.string().guid()).max(100).optional(),
routineId: z.string().guid().optional(),
routineIds: z.array(z.string().guid()).max(100).optional(),
issueId: z.string().guid().optional(),
issueIds: z.array(z.string().guid()).max(100).optional(),
requireIssue: z.boolean().optional(),
requireProject: z.boolean().optional(),
requireRoutine: z.boolean().optional(),
}).strict();
const credentialScopeConditionSchema = z.object({
applicationId: z.string().guid().optional(),
applicationIds: z.array(z.string().guid()).max(100).optional(),
connectionId: z.string().guid().optional(),
connectionIds: z.array(z.string().guid()).max(100).optional(),
catalogEntryId: z.string().guid().optional(),
catalogEntryIds: z.array(z.string().guid()).max(100).optional(),
applicationKey: z.string().trim().min(1).max(160).optional(),
applicationKeys: z.array(z.string().trim().min(1).max(160)).max(100).optional(),
providerType: z.string().trim().min(1).max(160).optional(),
providerTypes: z.array(z.string().trim().min(1).max(160)).max(100).optional(),
}).strict();
export const toolPolicyConditionsSchema = z.object({
arguments: argumentConditionSchema.optional(),
args: argumentConditionSchema.optional(),
actor: actorConditionSchema.optional(),
context: contextConditionSchema.optional(),
risk: z.object({
levels: z.array(toolRiskLevelSchema).max(20).optional(),
max: toolRiskLevelSchema.optional(),
isWrite: z.boolean().optional(),
isDestructive: z.boolean().optional(),
}).strict().optional(),
credentialScope: credentialScopeConditionSchema.optional(),
trustBoundary: z.object({
providerType: z.string().trim().min(1).max(160).optional(),
providerTypes: z.array(z.string().trim().min(1).max(160)).max(100).optional(),
applicationKey: z.string().trim().min(1).max(160).optional(),
applicationKeys: z.array(z.string().trim().min(1).max(160)).max(100).optional(),
remoteHttpOnly: z.boolean().optional(),
paperclipSelfOnly: z.boolean().optional(),
}).strict().optional(),
timeWindow: timeWindowConditionSchema.optional(),
}).strict().refine(
(value) => Object.keys(value).length > 0,
{ message: "Tool policy conditions must include at least one supported condition group" },
);
export const createToolPolicySchema = z.object({
name: z.string().trim().min(1).max(160),
description: z.string().max(4000).optional().nullable(),
policyType: toolPolicyTypeSchema,
priority: z.number().int().min(0).max(10000).default(100),
enabled: z.boolean().default(true),
selectors: z.record(z.string(), z.unknown()).default({}),
conditions: toolPolicyConditionsSchema.optional().nullable(),
config: z.record(z.string(), z.unknown()).optional().nullable(),
});
export type CreateToolPolicy = z.infer<typeof createToolPolicySchema>;
export const updateToolPolicySchema = objectWithoutDefaults(createToolPolicySchema).partial().refine(
(value) => Object.keys(value).length > 0,
{ message: "At least one tool policy field is required" },
);
export type UpdateToolPolicy = z.infer<typeof updateToolPolicySchema>;
export const reorderToolPoliciesSchema = z.object({
policyIds: z.array(z.string().guid()).min(1).max(500),
});
export type ReorderToolPolicies = z.infer<typeof reorderToolPoliciesSchema>;
export const duplicateToolPolicySchema = z.object({
name: z.string().trim().min(1).max(160).optional(),
});
export type DuplicateToolPolicy = z.infer<typeof duplicateToolPolicySchema>;
export const createToolInvocationSchema = z.object({
idempotencyKey: z.string().trim().min(1).max(300).optional().nullable(),
issueId: z.string().guid().optional().nullable(),
runId: z.string().guid().optional().nullable(),
applicationId: z.string().guid().optional().nullable(),
connectionId: z.string().guid().optional().nullable(),
catalogEntryId: z.string().guid().optional().nullable(),
toolName: z.string().trim().min(1).max(240),
argumentsHash: z.string().trim().max(128).optional().nullable(),
argumentsSummary: toolRedactedValueSummarySchema.optional().nullable(),
});
export type CreateToolInvocation = z.infer<typeof createToolInvocationSchema>;
export const createToolActionRequestSchema = z.object({
invocationId: z.string().guid(),
issueId: z.string().guid().optional().nullable(),
canonicalArgumentsHash: z.string().trim().min(1).max(128),
canonicalArgumentsSummary: toolRedactedValueSummarySchema,
signedArguments: z.string().trim().max(4096).optional().nullable(),
previewMarkdown: z.string().max(20_000).optional().nullable(),
expiresAt: z.coerce.date().optional().nullable(),
});
export type CreateToolActionRequest = z.infer<typeof createToolActionRequestSchema>;
export const toolConnectionTestCallSchema = z.object({
agentId: z.string().guid(),
toolName: z.string().trim().min(1).max(240),
parameters: z.unknown().optional(),
});
export type ToolConnectionTestCallInput = z.infer<typeof toolConnectionTestCallSchema>;
export const importMcpJsonSchema = z.object({
mcpJson: z.union([z.string(), z.record(z.string(), z.unknown())]),
});
export type ImportMcpJson = z.infer<typeof importMcpJsonSchema>;
export const toolAccessSelectorSchema = z.object({
actorType: z.enum(["agent", "user", "system", "plugin"]).optional(),
agentId: z.string().guid().optional(),
agentIds: z.array(z.string().guid()).optional(),
projectId: z.string().guid().optional(),
projectIds: z.array(z.string().guid()).optional(),
routineId: z.string().guid().optional(),
routineIds: z.array(z.string().guid()).optional(),
issueId: z.string().guid().optional(),
issueIds: z.array(z.string().guid()).optional(),
gatewayId: z.string().guid().optional(),
gatewayIds: z.array(z.string().guid()).optional(),
gatewayPublicId: z.string().trim().min(1).max(120).regex(safeKeyPattern).optional(),
gatewayPublicIds: z.array(z.string().trim().min(1).max(120).regex(safeKeyPattern)).optional(),
gatewayTokenId: z.string().guid().optional(),
gatewayTokenIds: z.array(z.string().guid()).optional(),
clientSubjectType: toolMcpGatewayTokenSubjectTypeSchema.optional(),
clientSubjectTypes: z.array(toolMcpGatewayTokenSubjectTypeSchema).optional(),
clientName: z.string().trim().min(1).max(160).optional(),
clientNames: z.array(z.string().trim().min(1).max(160)).optional(),
externalClient: z.boolean().optional(),
applicationId: z.string().guid().optional(),
applicationIds: z.array(z.string().guid()).optional(),
connectionId: z.string().guid().optional(),
connectionIds: z.array(z.string().guid()).optional(),
catalogEntryId: z.string().guid().optional(),
catalogEntryIds: z.array(z.string().guid()).optional(),
toolName: z.string().trim().min(1).max(240).optional(),
toolNames: z.array(z.string().trim().min(1).max(240)).optional(),
riskLevel: toolRiskLevelSchema.optional(),
riskLevels: z.array(toolRiskLevelSchema).optional(),
});
export const toolRateLimitRuleSchema = z.object({
limit: z.number().int().positive().max(1_000_000),
windowSeconds: z.number().int().positive().max(31_536_000),
keyBy: z.array(z.enum(["company", "agent", "application", "connection", "tool"])).optional(),
});
export const toolTrustRuleArgumentFiltersSchema = z.object({
allowAny: z.boolean().optional(),
exactHash: z.string().trim().regex(/^[a-f0-9]{64}$/i).optional().nullable(),
allowedHashes: z.array(z.string().trim().regex(/^[a-f0-9]{64}$/i)).max(100).optional(),
fieldEquals: z.record(z.string().trim().min(1).max(120), z.unknown()).optional(),
fieldNotEquals: z.record(z.string().trim().min(1).max(120), z.unknown()).optional(),
fieldIn: z.record(z.string().trim().min(1).max(120), z.array(z.unknown()).min(1).max(100)).optional(),
fieldMatches: z.record(z.string().trim().min(1).max(120), z.string().trim().min(1).max(500)).optional(),
fieldExists: z.array(z.string().trim().min(1).max(120)).max(100).optional(),
fieldAbsent: z.array(z.string().trim().min(1).max(120)).max(100).optional(),
}).refine(
(value) => value.allowAny === true
|| Boolean(value.exactHash)
|| Boolean(value.allowedHashes?.length)
|| Boolean(value.fieldEquals && Object.keys(value.fieldEquals).length > 0)
|| Boolean(value.fieldNotEquals && Object.keys(value.fieldNotEquals).length > 0)
|| Boolean(value.fieldIn && Object.keys(value.fieldIn).length > 0)
|| Boolean(value.fieldMatches && Object.keys(value.fieldMatches).length > 0)
|| Boolean(value.fieldExists?.length)
|| Boolean(value.fieldAbsent?.length),
{ message: "Trust-rule argument filters must specify allowAny, a hash filter, or a field predicate" },
);
export const toolTrustRuleScopeSchema = z.object({
includeAgent: z.boolean().optional(),
includeProject: z.boolean().optional(),
includeIssue: z.boolean().optional(),
includeApplication: z.boolean().optional(),
includeConnection: z.boolean().optional(),
includeCatalogEntry: z.boolean().optional(),
includeTool: z.boolean().optional(),
});
export const toolTrustRuleBatchApprovalSchema = z.object({
enabled: z.boolean().optional(),
maxBatchSize: z.number().int().positive().max(100).optional(),
windowSeconds: z.number().int().positive().max(31_536_000).optional(),
});
export const createToolTrustRuleFromActionRequestSchema = z.object({
name: z.string().trim().min(1).max(160).optional(),
description: z.string().max(4000).optional().nullable(),
priority: z.number().int().min(0).max(10000).default(40),
approvalThreshold: z.number().int().min(1).max(50).default(2),
selectors: toolAccessSelectorSchema.optional(),
scope: toolTrustRuleScopeSchema.optional(),
argumentFilters: toolTrustRuleArgumentFiltersSchema.optional(),
expiresAt: z.coerce.date().optional().nullable(),
batchApproval: toolTrustRuleBatchApprovalSchema.optional().nullable(),
});
export type CreateToolTrustRuleFromActionRequest = z.infer<typeof createToolTrustRuleFromActionRequestSchema>;
export const revokeToolTrustRuleSchema = z.object({
reason: z.string().trim().max(1000).optional().nullable(),
});
export type RevokeToolTrustRule = z.infer<typeof revokeToolTrustRuleSchema>;
export const toolPolicyTestRequestSchema = z.object({
companyId: z.string().guid(),
actor: z.object({
actorType: z.enum(["agent", "user", "system", "plugin"]),
actorId: z.string().trim().min(1).max(240),
agentId: z.string().guid().optional().nullable(),
}),
runContext: z.object({
heartbeatRunId: z.string().guid().optional().nullable(),
issueId: z.string().guid().optional().nullable(),
projectId: z.string().guid().optional().nullable(),
routineId: z.string().guid().optional().nullable(),
gatewayId: z.string().guid().optional().nullable(),
gatewayPublicId: z.string().trim().min(1).max(120).regex(safeKeyPattern).optional().nullable(),
gatewayTokenId: z.string().guid().optional().nullable(),
clientSubjectType: toolMcpGatewayTokenSubjectTypeSchema.optional().nullable(),
clientSubjectId: z.string().trim().min(1).max(240).optional().nullable(),
clientName: z.string().trim().min(1).max(160).optional().nullable(),
externalClient: z.boolean().optional().nullable(),
}).optional().nullable(),
request: z.object({
applicationId: z.string().guid().optional().nullable(),
connectionId: z.string().guid().optional().nullable(),
catalogEntryId: z.string().guid().optional().nullable(),
toolName: z.string().trim().min(1).max(240),
arguments: z.unknown().optional(),
idempotencyKey: z.string().trim().min(1).max(512).optional().nullable(),
sideEffecting: z.boolean().optional(),
}),
consumeRateLimit: z.boolean().optional(),
writeAuditEvent: z.boolean().optional(),
});
export type ToolPolicyTestRequestInput = z.infer<typeof toolPolicyTestRequestSchema>;