(props: {
/** Full per-connection tool address `tools.<int>.<owner>.<conn>.<tool>`. */
address: ToolAddress;
/** Policy id `<integration>.<tool>` — the tree path and display value. */
toolName: string;
/** True for plugin-contributed static tools (policy-matched on their
* address verbatim, not the connection-wildcarded pattern). */
staticTool?: boolean;
/** Resolved effective policy — user-authored or plugin-default,
* unified into one shape. Surfaces in the header. */
policy?: EffectivePolicy;
/** When provided, the policy badge becomes a dropdown trigger that
* applies a user rule to this tool's exact id. */
onSetPolicy?: (pattern: string, action: ToolPolicyAction) => void;
onClearPolicy?: (pattern: string, policyId?: string) => void;
patternForDisplay?: (displayPattern: string) => string;
/** Run-tab wiring. When `integration` + `runToolName` are provided, a third
* "Run" tab hosts the per-connection tool tester. */
integration?: IntegrationSlug;
/** Bare `<tool>` address segment (may contain dots) for the Run tab. */
runToolName?: string;
/** This integration's connections across both owners. */
connections?: readonly Connection[];
/** Pre-select this connection in the Run tab (the selected tool's account). */
initialConnectionName?: ConnectionName | string | null;
})
| 158 | // --------------------------------------------------------------------------- |
| 159 | |
| 160 | export function ToolDetail(props: { |
| 161 | /** Full per-connection tool address `tools.<int>.<owner>.<conn>.<tool>`. */ |
| 162 | address: ToolAddress; |
| 163 | /** Policy id `<integration>.<tool>` — the tree path and display value. */ |
| 164 | toolName: string; |
| 165 | /** True for plugin-contributed static tools (policy-matched on their |
| 166 | * address verbatim, not the connection-wildcarded pattern). */ |
| 167 | staticTool?: boolean; |
| 168 | /** Resolved effective policy — user-authored or plugin-default, |
| 169 | * unified into one shape. Surfaces in the header. */ |
| 170 | policy?: EffectivePolicy; |
| 171 | /** When provided, the policy badge becomes a dropdown trigger that |
| 172 | * applies a user rule to this tool's exact id. */ |
| 173 | onSetPolicy?: (pattern: string, action: ToolPolicyAction) => void; |
| 174 | onClearPolicy?: (pattern: string, policyId?: string) => void; |
| 175 | patternForDisplay?: (displayPattern: string) => string; |
| 176 | /** Run-tab wiring. When `integration` + `runToolName` are provided, a third |
| 177 | * "Run" tab hosts the per-connection tool tester. */ |
| 178 | integration?: IntegrationSlug; |
| 179 | /** Bare `<tool>` address segment (may contain dots) for the Run tab. */ |
| 180 | runToolName?: string; |
| 181 | /** This integration's connections across both owners. */ |
| 182 | connections?: readonly Connection[]; |
| 183 | /** Pre-select this connection in the Run tab (the selected tool's account). */ |
| 184 | initialConnectionName?: ConnectionName | string | null; |
| 185 | }) { |
| 186 | const toolContract = useAtomValue(toolSchemaAtom(props.address)); |
| 187 | const [tab, setTab] = useState<"schema" | "typescript" | "run">("schema"); |
| 188 | const canRun = props.integration != null && props.runToolName != null; |
| 189 | // Don't strand the user on the Run tab when this ToolDetail has no run wiring. |
| 190 | const activeTab = tab === "run" && !canRun ? "schema" : tab; |
| 191 | const isBlocked = props.policy?.action === "block"; |
| 192 | |
| 193 | const data = useMemo(() => { |
| 194 | if (!AsyncResult.isSuccess(toolContract)) return null; |
| 195 | const v = toolContract.value; |
| 196 | const definitions = Object.entries(v.typeScriptDefinitions ?? {}).map(([name, body]) => ({ |
| 197 | name, |
| 198 | code: String(body), |
| 199 | })); |
| 200 | |
| 201 | return { |
| 202 | description: v.description, |
| 203 | inputSchema: v.inputSchema, |
| 204 | outputSchema: v.outputSchema, |
| 205 | schemaDefinitions: v.schemaDefinitions, |
| 206 | inputTypeScript: v.inputTypeScript ? `type Input = ${v.inputTypeScript}` : null, |
| 207 | outputTypeScript: v.outputTypeScript ? `type Output = ${v.outputTypeScript}` : null, |
| 208 | definitions, |
| 209 | }; |
| 210 | }, [toolContract]); |
| 211 | |
| 212 | const crumbs = breadcrumbParts(props.toolName); |
| 213 | const displayName = friendlyName(props.toolName); |
| 214 | |
| 215 | return ( |
| 216 | <div className="flex h-full flex-col overflow-hidden"> |
| 217 | {/* Header + tabs */} |
nothing calls this directly
no test coverage detected