basic-host: filter app-only tools, prioritize UI tools (#262)
* basic-host: filter non-ui tools * basic-host: filter app-only tools, prioritize UI tools - Filter out tools with visibility: ['app'] from host tool list - Sort tools to show UI-enabled tools first, then alphabetically - Add local isToolVisibleToModel helper in basic-host
This commit is contained in:
parent
bd892b6ce4
commit
8fa2961ffc
1 changed files with 35 additions and 4 deletions
|
|
@ -1,9 +1,30 @@
|
||||||
|
import { getToolUiResourceUri, McpUiToolMetaSchema } from "@modelcontextprotocol/ext-apps/app-bridge";
|
||||||
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
||||||
import { Component, type ErrorInfo, type ReactNode, StrictMode, Suspense, use, useEffect, useMemo, useRef, useState } from "react";
|
import { Component, type ErrorInfo, type ReactNode, StrictMode, Suspense, use, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import { callTool, connectToServer, hasAppHtml, initializeApp, loadSandboxProxy, log, newAppBridge, type ServerInfo, type ToolCallInfo, type ModelContext, type AppMessage } from "./implementation";
|
import { callTool, connectToServer, hasAppHtml, initializeApp, loadSandboxProxy, log, newAppBridge, type ServerInfo, type ToolCallInfo, type ModelContext, type AppMessage } from "./implementation";
|
||||||
import styles from "./index.module.css";
|
import styles from "./index.module.css";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a tool is visible to the model (not app-only).
|
||||||
|
* Tools with `visibility: ["app"]` should not be shown in tool lists.
|
||||||
|
*/
|
||||||
|
function isToolVisibleToModel(tool: { _meta?: Record<string, unknown> }): boolean {
|
||||||
|
const result = McpUiToolMetaSchema.safeParse(tool._meta?.ui);
|
||||||
|
if (!result.success) return true; // default: visible to model
|
||||||
|
const visibility = result.data.visibility;
|
||||||
|
if (!visibility) return true; // default: visible to model
|
||||||
|
return visibility.includes("model");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Compare tools: UI-enabled first, then alphabetically by name. */
|
||||||
|
function compareTools(a: Tool, b: Tool): number {
|
||||||
|
const aHasUi = !!getToolUiResourceUri(a);
|
||||||
|
const bHasUi = !!getToolUiResourceUri(b);
|
||||||
|
if (aHasUi && !bHasUi) return -1;
|
||||||
|
if (!aHasUi && bHasUi) return 1;
|
||||||
|
return a.name.localeCompare(b.name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract default values from a tool's JSON Schema inputSchema.
|
* Extract default values from a tool's JSON Schema inputSchema.
|
||||||
|
|
@ -80,7 +101,13 @@ function CallToolPanel({ serversPromise, addToolCall }: CallToolPanelProps) {
|
||||||
const [selectedTool, setSelectedTool] = useState("");
|
const [selectedTool, setSelectedTool] = useState("");
|
||||||
const [inputJson, setInputJson] = useState("{}");
|
const [inputJson, setInputJson] = useState("{}");
|
||||||
|
|
||||||
const toolNames = selectedServer ? Array.from(selectedServer.tools.keys()) : [];
|
// Filter out app-only tools, prioritize tools with UIs
|
||||||
|
const toolNames = selectedServer
|
||||||
|
? Array.from(selectedServer.tools.values())
|
||||||
|
.filter((tool) => isToolVisibleToModel(tool))
|
||||||
|
.sort(compareTools)
|
||||||
|
.map((tool) => tool.name)
|
||||||
|
: [];
|
||||||
|
|
||||||
const isValidJson = useMemo(() => {
|
const isValidJson = useMemo(() => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -93,10 +120,14 @@ function CallToolPanel({ serversPromise, addToolCall }: CallToolPanelProps) {
|
||||||
|
|
||||||
const handleServerSelect = (server: ServerInfo) => {
|
const handleServerSelect = (server: ServerInfo) => {
|
||||||
setSelectedServer(server);
|
setSelectedServer(server);
|
||||||
const [firstTool] = server.tools.keys();
|
// Filter out app-only tools, prioritize tools with UIs
|
||||||
setSelectedTool(firstTool ?? "");
|
const visibleTools = Array.from(server.tools.values())
|
||||||
|
.filter((tool) => isToolVisibleToModel(tool))
|
||||||
|
.sort(compareTools);
|
||||||
|
const firstTool = visibleTools[0]?.name ?? "";
|
||||||
|
setSelectedTool(firstTool);
|
||||||
// Set input JSON to tool defaults (if any)
|
// Set input JSON to tool defaults (if any)
|
||||||
setInputJson(getToolDefaults(server.tools.get(firstTool ?? "")));
|
setInputJson(getToolDefaults(server.tools.get(firstTool)));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleToolSelect = (toolName: string) => {
|
const handleToolSelect = (toolName: string) => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue