feat(agents): render tool parameters in modal

- Add parameters to tool API responses (agents + collections)
- Update ToolInfo interface to include parameters
- Render parameters grouped by required/optional
- Show parameter name, type, description, and default value
- Style with design system patterns (fieldsets, badges, etc.)
This commit is contained in:
Ajax Davis 2026-01-17 04:35:25 +10:00
parent 3e0923fc4a
commit f8302c92d6
9 changed files with 3550 additions and 246 deletions

View file

@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/dev/types/routes.d.ts";
import "./.next/types/routes.d.ts";
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

View file

@ -60,6 +60,7 @@ export async function GET(_request: NextRequest, context: RouteContext) {
id: true,
name: true,
description: true,
parameters: true,
package: {
select: {
npmPackageName: true,

View file

@ -86,6 +86,7 @@ export async function GET(
id: true,
name: true,
description: true,
parameters: true,
package: {
select: {
npmPackageName: true,

View file

@ -4,6 +4,14 @@ import { Button } from '@tpmjs/ui/Button/Button';
import { Icon } from '@tpmjs/ui/Icon/Icon';
import { useCallback, useState } from 'react';
export interface ToolParameter {
name: string;
type: string;
description: string;
required: boolean;
default?: unknown;
}
export interface ToolInfo {
id: string;
toolId: string;
@ -11,6 +19,7 @@ export interface ToolInfo {
id: string;
name: string;
description: string | null;
parameters: ToolParameter[] | null;
package: {
npmPackageName: string;
category: string;

View file

@ -18,6 +18,9 @@ export function ToolDetailsModal({ tool, open, onClose }: ToolDetailsModalProps)
if (!tool) return null;
const toolPageUrl = `/tool/${tool.tool.package.npmPackageName}/${tool.tool.name}`;
const parameters = tool.tool.parameters || [];
const requiredParams = parameters.filter((p) => p.required);
const optionalParams = parameters.filter((p) => !p.required);
return (
<Modal open={open} onClose={onClose} size="lg">
@ -58,19 +61,60 @@ export function ToolDetailsModal({ tool, open, onClose }: ToolDetailsModalProps)
)}
<div className="flex items-center gap-2">
<Badge variant="secondary">{tool.tool.package.category}</Badge>
{parameters.length > 0 && (
<Badge variant="outline">
{parameters.length} param{parameters.length !== 1 ? 's' : ''}
</Badge>
)}
</div>
</div>
</fieldset>
{/* note about parameters */}
{/* parameters fieldset */}
<fieldset className="border border-dashed border-border p-4">
<legend className="px-2 font-mono text-xs text-foreground-tertiary lowercase">
parameters
</legend>
<p className="text-sm text-foreground-secondary">
This tool accepts parameters defined in its input schema. View the full tool page for
detailed parameter documentation and usage examples.
</p>
{parameters.length === 0 ? (
<p className="text-sm text-foreground-tertiary italic">No parameters defined</p>
) : (
<div className="space-y-4">
{/* Required parameters */}
{requiredParams.length > 0 && (
<div>
<div className="flex items-center gap-2 mb-3">
<div className="w-2 h-2 rounded-full bg-error" />
<span className="font-mono text-xs text-foreground-secondary uppercase tracking-wide">
Required ({requiredParams.length})
</span>
</div>
<div className="space-y-3">
{requiredParams.map((param) => (
<ParameterRow key={param.name} param={param} />
))}
</div>
</div>
)}
{/* Optional parameters */}
{optionalParams.length > 0 && (
<div className={requiredParams.length > 0 ? 'pt-4 border-t border-dashed border-border/50' : ''}>
<div className="flex items-center gap-2 mb-3">
<div className="w-2 h-2 rounded-full bg-foreground-tertiary" />
<span className="font-mono text-xs text-foreground-secondary uppercase tracking-wide">
Optional ({optionalParams.length})
</span>
</div>
<div className="space-y-3">
{optionalParams.map((param) => (
<ParameterRow key={param.name} param={param} />
))}
</div>
</div>
)}
</div>
)}
</fieldset>
</div>
@ -94,3 +138,37 @@ export function ToolDetailsModal({ tool, open, onClose }: ToolDetailsModalProps)
</Modal>
);
}
interface ParameterRowProps {
param: {
name: string;
type: string;
description: string;
required: boolean;
default?: unknown;
};
}
function ParameterRow({ param }: ParameterRowProps) {
return (
<div className="group p-3 rounded-lg bg-surface-secondary/30 hover:bg-surface-secondary/50 transition-colors">
<div className="flex items-start justify-between gap-3 mb-1.5">
<code className="font-mono text-sm text-foreground font-medium">{param.name}</code>
<code className="font-mono text-xs px-1.5 py-0.5 rounded bg-surface-secondary text-foreground-secondary">
{param.type}
</code>
</div>
{param.description && (
<p className="text-sm text-foreground-secondary leading-relaxed">{param.description}</p>
)}
{param.default !== undefined && (
<div className="mt-2 flex items-center gap-2">
<span className="text-xs text-foreground-tertiary">default:</span>
<code className="font-mono text-xs px-1.5 py-0.5 rounded bg-primary/10 text-primary">
{JSON.stringify(param.default)}
</code>
</div>
)}
</div>
);
}