Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions apps/server/src/mcp/toolkits/preview/tools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,19 @@ const schemaHasDescription = (schema: unknown): boolean => {
.some((members) => members.some(schemaHasDescription));
};

const schemaHasMultipleAllOfDescriptions = (schema: unknown): boolean => {
if (!schema || typeof schema !== "object") return false;
const record = schema as Record<string, unknown>;
const allOf = Array.isArray(record.allOf) ? record.allOf : [];
const descriptionCount = allOf.filter(
(member) =>
member !== null &&
typeof member === "object" &&
typeof (member as Record<string, unknown>).description === "string",
).length;
return descriptionCount > 1 || Object.values(record).some(schemaHasMultipleAllOfDescriptions);
};

it("exports provider-compatible object schemas with described parameters", () => {
for (const tool of Object.values(PreviewToolkit.tools)) {
const schema = Tool.getJsonSchema(tool) as {
Expand All @@ -27,6 +40,9 @@ it("exports provider-compatible object schemas with described parameters", () =>
expect(schema.type, `${tool.name} must export a top-level object schema`).toBe("object");
expect(schema.anyOf, `${tool.name} must not export a root anyOf`).toBeUndefined();
expect(schema.oneOf, `${tool.name} must not export a root oneOf`).toBeUndefined();
if (tool.name === "preview_navigate") {
expect(schemaHasMultipleAllOfDescriptions(schema)).toBe(false);
}
expect(
schema.properties?.tabId,
`${tool.name} must allow an explicit collaborative browser tab target`,
Expand Down
17 changes: 6 additions & 11 deletions packages/contracts/src/previewAutomation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,10 @@ import {
import { ProviderInstanceId } from "./providerInstance.ts";

const BoundedUrl = Schema.String.check(Schema.isTrimmed())
.check(
Schema.isNonEmpty({
description:
"Absolute http(s) URL or a schemeless host such as t3.chat or localhost:5173. Schemeless public hosts use https; loopback hosts use http.",
}),
)
.check(Schema.isNonEmpty())
.check(Schema.isMaxLength(2048));
const URL_GUIDANCE =
"Absolute http(s) URL or a schemeless host such as t3.chat or localhost:5173. Schemeless public hosts use https; loopback hosts use http.";
const OptionalTimeoutMs = Schema.optional(
Schema.Int.check(Schema.isGreaterThan(0))
.check(Schema.isLessThanOrEqualTo(60_000))
Expand Down Expand Up @@ -83,8 +80,7 @@ export type PreviewAutomationStatus = typeof PreviewAutomationStatus.Type;
export const PreviewAutomationOpenInput = Schema.Struct({
...PreviewAutomationTabTargetFields,
url: Schema.optional(BoundedUrl).annotate({
description:
"Optional initial page URL, for example https://t3.chat or localhost:5173. Omit to open a blank tab.",
description: `Optional initial page URL. ${URL_GUIDANCE} Omit to open a blank tab.`,
}),
open: Schema.optional(
Schema.Boolean.annotate({
Expand Down Expand Up @@ -124,7 +120,7 @@ export const BrowserNavigationTarget = Schema.Union([
description: "Selects direct URL navigation.",
}),
url: BoundedUrl.annotate({
description: "Direct website URL.",
description: `Direct website URL. ${URL_GUIDANCE}`,
}),
}),
Schema.Struct({
Expand All @@ -151,8 +147,7 @@ export type BrowserNavigationTarget = typeof BrowserNavigationTarget.Type;
export const PreviewAutomationNavigateInput = Schema.Struct({
...PreviewAutomationTabTargetFields,
url: Schema.optional(BoundedUrl).annotate({
description:
"Website URL, for example https://t3.chat. Use this for public pages and directly reachable URLs.",
description: `Website URL. ${URL_GUIDANCE} Use this for public pages and directly reachable URLs.`,
}),
target: Schema.optional(
BrowserNavigationTarget.annotate({
Expand Down
Loading