Skip to content
Open
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
574 changes: 450 additions & 124 deletions app/_components/toolkit-docs/components/available-tools-table.tsx

Large diffs are not rendered by default.

48 changes: 30 additions & 18 deletions app/_components/toolkit-docs/components/dynamic-code-block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -351,26 +351,33 @@ export function DynamicCodeBlock({
return (
<div className="my-4">
{isExpanded ? (
<div className="mt-3 space-y-3">
<div className="flex flex-wrap items-center justify-between gap-3 rounded-md bg-muted/20 p-3">
<LanguageTabs
currentLanguage={selectedLanguage}
languages={availableLanguages}
onSelect={setSelectedLanguage}
/>
<div className="mt-3 overflow-hidden rounded-lg bg-neutral-dark/10">
<div className="flex flex-wrap items-center justify-between gap-3 bg-neutral-dark/20 px-4 py-2.5">
<div className="flex items-center gap-3">
<span className="text-sm font-medium text-foreground">
Example
</span>
<div className="h-4 w-px bg-neutral-dark-high/30" />
<LanguageTabs
currentLanguage={selectedLanguage}
languages={availableLanguages}
onSelect={setSelectedLanguage}
/>
</div>
<Button
className="text-muted-foreground hover:text-foreground"
aria-label="Hide example"
className="h-7 w-7 p-0 text-muted-foreground hover:text-foreground"
onClick={() => setIsExpanded(false)}
size="sm"
size="icon"
variant="ghost"
>
Close
<ChevronDown className="h-4 w-4 rotate-180 transition-transform" />
</Button>
</div>

<div className="relative rounded-md border border-muted/80">
<div className="flex items-center justify-between border-muted border-b bg-muted/5 px-3 py-2">
<span className="font-mono text-muted-foreground text-xs">
<div className="relative">
<div className="flex items-center justify-between border-b border-muted bg-muted/5 px-3 py-2">
<span className="font-mono text-xs text-muted-foreground">
{selectedLanguage.toLowerCase()}
</span>
<CopyButton
Expand Down Expand Up @@ -398,13 +405,18 @@ export function DynamicCodeBlock({
</div>
) : (
<Button
className="mt-2"
aria-expanded={false}
className="group mt-2 h-auto w-full justify-between rounded-lg bg-neutral-dark/10 px-4 py-3 text-left text-sm font-medium text-foreground hover:bg-neutral-dark/20"
onClick={() => setIsExpanded(true)}
size="sm"
variant="outline"
variant="ghost"
>
See example
<ChevronDown className="ml-1 h-3 w-3" />
<span className="flex flex-col items-start">
<span>See example</span>
<span className="text-muted-foreground/80 text-xs font-normal">
Expand Python and TypeScript snippets
</span>
</span>
<ChevronDown className="h-4 w-4 text-muted-foreground transition-transform group-hover:text-foreground" />
</Button>
)}
</div>
Expand Down
4 changes: 4 additions & 0 deletions app/_components/toolkit-docs/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export {
export { DynamicCodeBlock } from "./dynamic-code-block";
export { ParametersTable } from "./parameters-table";
export { ScopesDisplay } from "./scopes-display";
export {
buildBehaviorRows,
ToolMetadataSection,
} from "./tool-metadata-section";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exported function unused outside its own module

Low Severity

buildBehaviorRows is exported from components/index.ts (and re-exported from the top-level barrel) but is never imported or used anywhere outside tool-metadata-section.tsx. This is dead exported surface area that adds unnecessary API breadth.

Fix in Cursor Fix in Web

export { ToolSection } from "./tool-section";
export { ToolkitHeader } from "./toolkit-header";
export { ToolkitPage } from "./toolkit-page";
226 changes: 226 additions & 0 deletions app/_components/toolkit-docs/components/tool-metadata-section.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
"use client";

import { Badge } from "@arcadeai/design-system";
import { Check, ChevronDown, Lightbulb, Minus, X } from "lucide-react";
import {
TOOL_METADATA_FALLBACK_STYLE,
TOOL_METADATA_OPERATION_STYLES,
TOOL_METADATA_SERVICE_DOMAIN_STYLES,
} from "../constants";
import type { ToolMetadata, ToolMetadataBehavior } from "../types";

type BehaviorFlagKey = "readOnly" | "destructive" | "idempotent" | "openWorld";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant BehaviorFlagKey type definition

Low Severity

BehaviorFlagKey is defined locally in tool-metadata-section.tsx but the same type is exported from types/index.ts. available-tools-table imports it from types. Duplicating the type risks drift if one definition is updated without the other.

Additional Locations (1)

Fix in Cursor Fix in Web


const BEHAVIOR_LABELS: Record<BehaviorFlagKey, string> = {
readOnly: "Read only",
destructive: "Destructive",
idempotent: "Idempotent",
openWorld: "Open world",
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Behavior labels map and type duplicated across files

Low Severity

tool-metadata-section.tsx locally redefines the BehaviorFlagKey type (already exported from types/index.ts) and declares BEHAVIOR_LABELS — an identical copy of BEHAVIOR_FILTER_LABELS in available-tools-table.tsx. Both map the same four keys to the same four human-readable strings. These could be shared from a single source.

Additional Locations (1)

Fix in Cursor Fix in Web


const BEHAVIOR_DESCRIPTIONS: Record<BehaviorFlagKey, string> = {
readOnly: "Does not modify remote state.",
destructive: "May delete or overwrite remote data.",
idempotent: "Safe to retry without extra side effects.",
openWorld: "Can call out to external systems.",
};

export type BehaviorRow = {
key: BehaviorFlagKey;
label: string;
value: boolean | null;
};

export function buildBehaviorRows(
behavior: ToolMetadataBehavior
): readonly BehaviorRow[] {
return (Object.keys(BEHAVIOR_LABELS) as BehaviorFlagKey[]).map((key) => ({
key,
label: BEHAVIOR_LABELS[key],
value: behavior[key] ?? null,
}));
}

function formatEnumLabel(value: string): string {
const words = value.split("_");
return words
.map((word, index) => {
if (word === "crm") {
return "CRM";
}
if (index === 0) {
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
}
return word.toLowerCase();
})
.join(" ");
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Identical formatting function duplicated across two new files

Low Severity

formatEnumLabel in tool-metadata-section.tsx and formatMetadataLabel in available-tools-table.tsx are identical implementations — same split-on-underscore logic, same special case for "crm", same title-casing. If either is updated (e.g., adding more acronym handling), the other risks becoming inconsistent. These belong in a shared utility.

Additional Locations (1)

Fix in Cursor Fix in Web


function EnumBadge({
value,
styles,
}: {
value: string;
styles: Record<string, string>;
}) {
const styleClass = styles[value] ?? TOOL_METADATA_FALLBACK_STYLE;
return (
<Badge
className={`${styleClass} gap-1.5 border-0 text-xs font-medium`}
variant="secondary"
>
<span aria-hidden className="h-1.5 w-1.5 rounded-full bg-current/80" />
{formatEnumLabel(value)}
</Badge>
);
}

function BooleanBadge({ value }: { value: boolean | null }) {
if (value === null) {
return (
<Badge
className="border-0 bg-neutral-dark/40 text-muted-foreground/70"
variant="secondary"
>
<Minus className="mr-1 h-3 w-3" /> Unknown
</Badge>
);
}

return value ? (
<Badge
className="border-0 bg-emerald-500/15 text-emerald-300"
variant="secondary"
>
<Check className="mr-1 h-3 w-3" /> Yes
</Badge>
) : (
<Badge
className="border-0 bg-neutral-500/15 text-neutral-300"
variant="secondary"
>
<X className="mr-1 h-3 w-3" /> No
</Badge>
);
}

export function ToolMetadataSection({
metadata,
}: {
metadata?: ToolMetadata | null;
}) {
if (!metadata) {
return null;
}

const behaviorRows = buildBehaviorRows(metadata.behavior);
const hasOperations = metadata.behavior.operations.length > 0;
const hasServiceDomains = metadata.classification.serviceDomains.length > 0;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ToolMetadataSection crashes on partial metadata

Low Severity

ToolMetadataSection accesses metadata.behavior and metadata.classification without optional chaining. If metadata exists but is partial (e.g. missing behavior or classification, or behavior.operations / classification.serviceDomains undefined), the component will throw at runtime.

Fix in Cursor Fix in Web

const hasAnyBehaviorValue = behaviorRows.some((row) => row.value !== null);
const hasExtras =
metadata.extras != null && Object.keys(metadata.extras).length > 0;

if (
!(hasOperations || hasServiceDomains || hasAnyBehaviorValue || hasExtras)
) {
return null;
}

return (
<div className="mb-6 rounded-xl bg-neutral-dark/15 p-4">
<div className="mb-4 flex items-start gap-2.5">
<div className="mt-0.5 rounded-md bg-brand-accent/10 p-1.5 text-brand-accent">
<Lightbulb className="h-3.5 w-3.5" />
</div>
<div>
<h4 className="font-semibold text-foreground text-sm">
Execution hints
</h4>
<p className="mt-1 text-muted-foreground/75 text-xs">
Signals for MCP clients and agents about how this tool behaves.
</p>
</div>
</div>

<div className="space-y-3">
{(hasOperations || hasServiceDomains) && (
<div className="grid gap-3 md:grid-cols-2">
{hasOperations && (
<div className="rounded-lg bg-neutral-dark/20 p-3">
<span className="mb-2 block text-muted-foreground/80 text-xs font-medium">
Operations
</span>
<div className="flex flex-wrap items-center gap-2">
{metadata.behavior.operations.map((operation) => (
<EnumBadge
key={operation}
styles={TOOL_METADATA_OPERATION_STYLES}
value={operation}
/>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EnumBadge crashes on non-string enum values

Low Severity

EnumBadge receives operation and domain from metadata.behavior.operations and metadata.classification.serviceDomains and passes them to formatEnumLabel(value), which calls value.split("_"). If the JSON or API returns non-string values (e.g. numbers or null), a TypeError is thrown because non-strings have no split method.

Additional Locations (1)

Fix in Cursor Fix in Web

))}
</div>
</div>
)}

{hasServiceDomains && (
<div className="rounded-lg bg-neutral-dark/20 p-3">
<span className="mb-2 block text-muted-foreground/80 text-xs font-medium">
Service domains
</span>
<div className="flex flex-wrap items-center gap-2">
{metadata.classification.serviceDomains.map((domain) => (
<EnumBadge
key={domain}
styles={TOOL_METADATA_SERVICE_DOMAIN_STYLES}
value={domain}
/>
))}
</div>
</div>
)}
</div>
)}

{hasAnyBehaviorValue && (
<div className="rounded-lg bg-neutral-dark/20 p-3">
<span className="mb-2 block text-muted-foreground/80 text-xs font-medium">
MCP behavior
</span>
<div className="grid grid-cols-1 gap-2.5 sm:grid-cols-2">
{behaviorRows.map((row) => (
<div
className="rounded-md bg-neutral-dark/25 p-2.5"
key={row.key}
>
<div className="flex items-center justify-between gap-2">
<span className="font-medium text-foreground text-xs">
{row.label}
</span>
<div className="flex shrink-0">
<BooleanBadge value={row.value} />
</div>
</div>
<p className="mt-1.5 text-[11px] text-muted-foreground/70 leading-relaxed">
{BEHAVIOR_DESCRIPTIONS[row.key]}
</p>
</div>
))}
</div>
</div>
)}

{hasExtras && (
<details className="group mt-2 rounded-lg bg-neutral-dark/20">
<summary className="flex cursor-pointer list-none items-center justify-between gap-3 px-3 py-2.5 text-muted-foreground/85 text-xs font-medium">
Additional metadata
<ChevronDown className="h-3.5 w-3.5 transition-transform group-open:rotate-180" />
</summary>
<pre className="overflow-auto p-3 text-muted-foreground text-xs">
{JSON.stringify(metadata.extras, null, 2)}
</pre>
</details>
)}
</div>
</div>
);
}
2 changes: 2 additions & 0 deletions app/_components/toolkit-docs/components/tool-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { DynamicCodeBlock } from "./dynamic-code-block";
import { ParametersTable } from "./parameters-table";
import { ScopesDisplay } from "./scopes-display";
import { ToolMetadataSection } from "./tool-metadata-section";

const COPY_FEEDBACK_MS = 2000;
const JSON_PRETTY_PRINT_INDENT = 2;
Expand Down Expand Up @@ -484,6 +485,7 @@ export function ToolSection({
showSelection={showSelection}
tool={tool}
/>
<ToolMetadataSection metadata={tool.metadata} />
<ToolDescriptionSection showDescription={showDescription} tool={tool} />
<ToolParametersSection showParameters={showParameters} tool={tool} />
<ToolRequirementsSection
Expand Down
Loading