/**
* Exposed tools card (mcp-ai-spec §6.2.4): live registry list grouped
* read/write, annotations, collapsible parameter tables, anonymous-access
* counts per access mode.
*/
import React, { useState } from 'react';
import { ChevronDown, ChevronUp } from 'lucide-react';
import type { McpStatus, McpToolInfo } from './types';
import { McpCard } from './card';
import { __, sprintf } from '../../lib/i18n';
export function ToolsList({ status }: { status: McpStatus }) {
const readTools = status.tools.filter((t) => t.scope !== 'write');
const writeTools = status.tools.filter((t) => t.scope === 'write');
return (
{writeTools.length > 0 && (
)}
);
}
function ToolGroup({ heading, tools }: { heading: string; tools: McpToolInfo[] }) {
return (
{heading}
{tools.map((tool) => (
))}
);
}
function ToolRow({ tool }: { tool: McpToolInfo }) {
const [open, setOpen] = useState(false);
const params = tool.input_schema?.properties || {};
const paramNames = Object.keys(params);
const required = tool.input_schema?.required || [];
return (
{open && (
{paramNames.length === 0 ? (
{__('No parameters.', 'llmagnet-llm-txt-generator')}
) : (
|
{__('Parameter', 'llmagnet-llm-txt-generator')}
|
{__('Type', 'llmagnet-llm-txt-generator')}
|
{__('Description', 'llmagnet-llm-txt-generator')}
|
{paramNames.map((name) => (
{name}
{required.includes(name) && (
*
)}
|
{params[name].type || '—'}
|
{params[name].description || '—'}
|
))}
)}
)}
);
}
function ScopeBadge({ write }: { write: boolean }) {
return (
{write
? __('Write', 'llmagnet-llm-txt-generator')
: __('Read', 'llmagnet-llm-txt-generator')}
);
}
function AnnotationBadge({
children,
tone = 'default',
}: {
children: React.ReactNode;
tone?: 'default' | 'warning' | 'info' | 'plan';
}) {
const tones: Record = {
default: 'bg-gray-100 text-gray-600',
warning: 'bg-red-100 text-red-700',
info: 'bg-teal-50 text-teal-700',
plan: 'bg-amber-100 text-amber-800',
};
return (
{children}
);
}