mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-15 23:31:29 +00:00
feat(ui): add curated key tools preview for OpenAPI MCP servers
When selecting a popular API from the quick-picker, show the 8 most useful MCP tools for that API in a collapsible preview card. First 4 are shown by default; clicking "expand" shows all 8 with descriptions on hover. - Added `key_tools` array (8 tools each) to all 10 APIs in openapi_registry.json - New `KeyToolsPreview` component in OpenAPIFormSection with expand/collapse - Extended `OpenAPIRegistryEntry` type with `key_tools?: OpenAPIKeyTool[]`
This commit is contained in:
parent
2b2e0de2af
commit
f3d8a74cec
3 changed files with 167 additions and 17 deletions
|
|
@ -11,7 +11,17 @@
|
|||
"token_url": "https://github.com/login/oauth/access_token",
|
||||
"pkce": false,
|
||||
"docs_url": "https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app"
|
||||
}
|
||||
},
|
||||
"key_tools": [
|
||||
{ "name": "list_repos", "description": "List repositories for a user or organization" },
|
||||
{ "name": "get_file_contents", "description": "Read a file or directory from a repository" },
|
||||
{ "name": "list_issues", "description": "List issues in a repository with filters" },
|
||||
{ "name": "create_issue", "description": "Open a new issue in a repository" },
|
||||
{ "name": "list_pull_requests", "description": "List open and merged pull requests" },
|
||||
{ "name": "create_pull_request", "description": "Open a pull request between branches" },
|
||||
{ "name": "search_code", "description": "Search code across all GitHub repositories" },
|
||||
{ "name": "list_commits", "description": "List commits with authors and messages" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "atlassian",
|
||||
|
|
@ -24,7 +34,17 @@
|
|||
"token_url": "https://auth.atlassian.com/oauth/token",
|
||||
"pkce": true,
|
||||
"docs_url": "https://developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps/"
|
||||
}
|
||||
},
|
||||
"key_tools": [
|
||||
{ "name": "search_issues", "description": "Search Jira issues using JQL queries" },
|
||||
{ "name": "get_issue", "description": "Get full details of a Jira issue by key" },
|
||||
{ "name": "create_issue", "description": "Create a new Jira issue or bug report" },
|
||||
{ "name": "update_issue", "description": "Update issue fields like status, assignee, priority" },
|
||||
{ "name": "add_comment", "description": "Add a comment to an issue" },
|
||||
{ "name": "transition_issue", "description": "Move an issue through the workflow (e.g. In Progress → Done)" },
|
||||
{ "name": "list_projects", "description": "List all Jira projects in the workspace" },
|
||||
{ "name": "get_project", "description": "Get project details, board, and sprint info" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "figma",
|
||||
|
|
@ -37,7 +57,17 @@
|
|||
"token_url": "https://www.figma.com/api/oauth/token",
|
||||
"pkce": false,
|
||||
"docs_url": "https://www.figma.com/developers/api#oauth2"
|
||||
}
|
||||
},
|
||||
"key_tools": [
|
||||
{ "name": "get_file", "description": "Get the full node tree and structure of a Figma file" },
|
||||
{ "name": "get_file_nodes", "description": "Get specific nodes by ID from a Figma file" },
|
||||
{ "name": "get_image", "description": "Export nodes as PNG, SVG, or PDF" },
|
||||
{ "name": "get_comments", "description": "List all comments on a Figma file" },
|
||||
{ "name": "post_comment", "description": "Add a comment to a file or specific node" },
|
||||
{ "name": "get_team_projects", "description": "List all projects belonging to a team" },
|
||||
{ "name": "get_project_files", "description": "List all Figma files in a project" },
|
||||
{ "name": "get_file_versions", "description": "Get the version history of a file" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "google",
|
||||
|
|
@ -50,7 +80,17 @@
|
|||
"token_url": "https://oauth2.googleapis.com/token",
|
||||
"pkce": true,
|
||||
"docs_url": "https://developers.google.com/identity/protocols/oauth2/web-server"
|
||||
}
|
||||
},
|
||||
"key_tools": [
|
||||
{ "name": "list_messages", "description": "List Gmail messages and threads with search filters" },
|
||||
{ "name": "send_message", "description": "Send an email via Gmail" },
|
||||
{ "name": "list_events", "description": "List Google Calendar events in a date range" },
|
||||
{ "name": "create_event", "description": "Create a Calendar event with attendees and video link" },
|
||||
{ "name": "list_files", "description": "List files and folders in Google Drive" },
|
||||
{ "name": "get_file", "description": "Get Drive file metadata and download content" },
|
||||
{ "name": "search_files", "description": "Search Drive files by name, type, or content" },
|
||||
{ "name": "create_spreadsheet", "description": "Create a new Google Sheets spreadsheet" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stripe",
|
||||
|
|
@ -63,7 +103,17 @@
|
|||
"token_url": "https://connect.stripe.com/oauth/token",
|
||||
"pkce": false,
|
||||
"docs_url": "https://stripe.com/docs/connect/oauth-reference"
|
||||
}
|
||||
},
|
||||
"key_tools": [
|
||||
{ "name": "list_customers", "description": "List and search Stripe customers" },
|
||||
{ "name": "create_customer", "description": "Create a new Stripe customer record" },
|
||||
{ "name": "create_payment_intent", "description": "Create a payment intent for a charge" },
|
||||
{ "name": "list_subscriptions", "description": "List active and cancelled subscriptions" },
|
||||
{ "name": "create_subscription", "description": "Subscribe a customer to a price plan" },
|
||||
{ "name": "list_invoices", "description": "List invoices for a customer or subscription" },
|
||||
{ "name": "create_refund", "description": "Refund a charge fully or partially" },
|
||||
{ "name": "list_products", "description": "List products and their pricing plans" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "hubspot",
|
||||
|
|
@ -76,7 +126,17 @@
|
|||
"token_url": "https://api.hubspot.com/oauth/v1/token",
|
||||
"pkce": false,
|
||||
"docs_url": "https://developers.hubspot.com/docs/api/oauth-quickstart-guide"
|
||||
}
|
||||
},
|
||||
"key_tools": [
|
||||
{ "name": "search_contacts", "description": "Search CRM contacts by email, name, or custom properties" },
|
||||
{ "name": "create_contact", "description": "Create a new CRM contact with properties" },
|
||||
{ "name": "update_contact", "description": "Update contact properties like lifecycle stage or owner" },
|
||||
{ "name": "create_deal", "description": "Create a new sales deal linked to a contact or company" },
|
||||
{ "name": "list_deals", "description": "List deals with stage and pipeline filters" },
|
||||
{ "name": "create_note", "description": "Log a note or activity against a contact or deal" },
|
||||
{ "name": "get_pipeline", "description": "Get deal pipeline stages and probabilities" },
|
||||
{ "name": "associate_objects", "description": "Link CRM objects (e.g. contact → deal → company)" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "notion",
|
||||
|
|
@ -89,7 +149,17 @@
|
|||
"token_url": "https://api.notion.com/v1/oauth/token",
|
||||
"pkce": false,
|
||||
"docs_url": "https://developers.notion.com/docs/authorization"
|
||||
}
|
||||
},
|
||||
"key_tools": [
|
||||
{ "name": "search", "description": "Search pages and databases across the workspace" },
|
||||
{ "name": "get_page", "description": "Get a page and its properties" },
|
||||
{ "name": "create_page", "description": "Create a new page inside a database or as a subpage" },
|
||||
{ "name": "update_page", "description": "Update page properties (title, status, dates, etc.)" },
|
||||
{ "name": "query_database", "description": "Query a database with filters and sorts" },
|
||||
{ "name": "create_database_item", "description": "Add a new row/item to a Notion database" },
|
||||
{ "name": "append_block_children", "description": "Append content blocks (text, bullets, code) to a page" },
|
||||
{ "name": "get_database", "description": "Get a database schema including all property types" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "slack",
|
||||
|
|
@ -102,7 +172,17 @@
|
|||
"token_url": "https://slack.com/api/oauth.v2.access",
|
||||
"pkce": false,
|
||||
"docs_url": "https://api.slack.com/authentication/oauth-v2"
|
||||
}
|
||||
},
|
||||
"key_tools": [
|
||||
{ "name": "chat_post_message", "description": "Send a message to a channel or DM" },
|
||||
{ "name": "conversations_history", "description": "Get message history from a channel" },
|
||||
{ "name": "conversations_list", "description": "List all public and private channels" },
|
||||
{ "name": "conversations_replies", "description": "Get replies in a message thread" },
|
||||
{ "name": "users_list", "description": "List all members of the Slack workspace" },
|
||||
{ "name": "search_messages", "description": "Full-text search across all messages" },
|
||||
{ "name": "files_upload", "description": "Upload a file and share it in a channel" },
|
||||
{ "name": "reactions_add", "description": "Add an emoji reaction to a message" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "shopify",
|
||||
|
|
@ -115,7 +195,17 @@
|
|||
"token_url": "https://{shop}.myshopify.com/admin/oauth/access_token",
|
||||
"pkce": false,
|
||||
"docs_url": "https://shopify.dev/docs/apps/auth/get-access-tokens/authorization-code-grant"
|
||||
}
|
||||
},
|
||||
"key_tools": [
|
||||
{ "name": "list_products", "description": "List products with variants, pricing, and inventory" },
|
||||
{ "name": "get_product", "description": "Get full product details including all variants" },
|
||||
{ "name": "list_orders", "description": "List orders with status, customer, and line item filters" },
|
||||
{ "name": "get_order", "description": "Get full order details including shipping and payment" },
|
||||
{ "name": "list_customers", "description": "List customers with order history and tags" },
|
||||
{ "name": "update_order", "description": "Update order notes, tags, or shipping address" },
|
||||
{ "name": "create_fulfillment", "description": "Fulfill an order with tracking info" },
|
||||
{ "name": "list_inventory_levels", "description": "Get stock levels across locations" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "snowflake",
|
||||
|
|
@ -128,7 +218,17 @@
|
|||
"token_url": "https://<account>.snowflakecomputing.com/oauth/token-request",
|
||||
"pkce": false,
|
||||
"docs_url": "https://docs.snowflake.com/en/user-guide/oauth-custom"
|
||||
}
|
||||
},
|
||||
"key_tools": [
|
||||
{ "name": "execute_statement", "description": "Execute a SQL statement and get results" },
|
||||
{ "name": "fetch_results", "description": "Fetch paginated results from a running query" },
|
||||
{ "name": "cancel_statement", "description": "Cancel a running query by statement handle" },
|
||||
{ "name": "list_databases", "description": "List all accessible databases in the account" },
|
||||
{ "name": "list_schemas", "description": "List all schemas within a database" },
|
||||
{ "name": "list_tables", "description": "List tables and views in a schema" },
|
||||
{ "name": "describe_table", "description": "Get column definitions and data types for a table" },
|
||||
{ "name": "list_warehouses", "description": "List virtual warehouses and their current status" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import React, { useState } from "react";
|
||||
import { Form, Input, Tooltip } from "antd";
|
||||
import { InfoCircleOutlined } from "@ant-design/icons";
|
||||
import { InfoCircleOutlined, ToolOutlined } from "@ant-design/icons";
|
||||
import { FormInstance } from "antd/es/form";
|
||||
import { AUTH_TYPE } from "./types";
|
||||
import OpenAPIQuickPicker, { OpenAPIRegistryEntry } from "./OpenAPIQuickPicker";
|
||||
import OpenAPIQuickPicker, { OpenAPIRegistryEntry, OpenAPIKeyTool } from "./OpenAPIQuickPicker";
|
||||
|
||||
interface OpenAPIFormSectionProps {
|
||||
form: FormInstance;
|
||||
|
|
@ -16,11 +16,7 @@ interface OpenAPIFormSectionProps {
|
|||
* Encapsulates all OpenAPI-specific form fields:
|
||||
* - popular API quick-picker (logos)
|
||||
* - spec URL input
|
||||
*
|
||||
* When a preset is selected, it pre-fills the spec URL and OAuth 2.0 fields
|
||||
* directly on the Ant Design form instance, then notifies the parent so it
|
||||
* can keep its own formValues state in sync (since setFieldsValue bypasses
|
||||
* the Form onValuesChange callback).
|
||||
* - curated key tools preview (8 tools from registry, expandable)
|
||||
*/
|
||||
const OpenAPIFormSection: React.FC<OpenAPIFormSectionProps> = ({
|
||||
form,
|
||||
|
|
@ -28,9 +24,11 @@ const OpenAPIFormSection: React.FC<OpenAPIFormSectionProps> = ({
|
|||
onValuesChange,
|
||||
}) => {
|
||||
const [selectedPreset, setSelectedPreset] = useState<string | null>(null);
|
||||
const [keyTools, setKeyTools] = useState<OpenAPIKeyTool[]>([]);
|
||||
|
||||
const handlePresetSelect = (entry: OpenAPIRegistryEntry) => {
|
||||
setSelectedPreset(entry.name);
|
||||
setKeyTools(entry.key_tools ?? []);
|
||||
const updates = {
|
||||
spec_path: entry.spec_url,
|
||||
auth_type: AUTH_TYPE.OAUTH2,
|
||||
|
|
@ -68,8 +66,54 @@ const OpenAPIFormSection: React.FC<OpenAPIFormSectionProps> = ({
|
|||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
{keyTools.length > 0 && (
|
||||
<KeyToolsPreview tools={keyTools} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
interface KeyToolsPreviewProps {
|
||||
tools: OpenAPIKeyTool[];
|
||||
}
|
||||
|
||||
const KeyToolsPreview: React.FC<KeyToolsPreviewProps> = ({ tools }) => {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const shown = expanded ? tools : tools.slice(0, 4);
|
||||
|
||||
return (
|
||||
<div className="mb-4 p-3 bg-blue-50 border border-blue-100 rounded-lg">
|
||||
<div className="flex items-center gap-1.5 mb-2">
|
||||
<ToolOutlined className="text-blue-500 text-xs" />
|
||||
<span className="text-xs font-medium text-blue-700">
|
||||
Key tools from this API
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{shown.map((tool) => (
|
||||
<Tooltip key={tool.name} title={tool.description} placement="top">
|
||||
<span className="inline-flex items-center px-2 py-0.5 rounded bg-white border border-blue-200 text-xs text-blue-800 font-mono cursor-default hover:border-blue-400 transition-colors">
|
||||
{tool.name}
|
||||
</span>
|
||||
</Tooltip>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{tools.length > 4 && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setExpanded((v) => !v)}
|
||||
className="mt-2 text-xs text-blue-600 hover:text-blue-800 cursor-pointer bg-transparent border-none p-0"
|
||||
>
|
||||
{expanded
|
||||
? "Show less"
|
||||
: `+ ${tools.length - 4} more — all tools load from the spec below`}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OpenAPIFormSection;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@ import React, { useEffect, useState } from "react";
|
|||
import { Spin } from "antd";
|
||||
import { fetchOpenAPIRegistry } from "../networking";
|
||||
|
||||
export interface OpenAPIKeyTool {
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface OpenAPIRegistryEntry {
|
||||
name: string;
|
||||
title: string;
|
||||
|
|
@ -14,6 +19,7 @@ export interface OpenAPIRegistryEntry {
|
|||
pkce: boolean;
|
||||
docs_url: string;
|
||||
};
|
||||
key_tools?: OpenAPIKeyTool[];
|
||||
}
|
||||
|
||||
interface OpenAPIQuickPickerProps {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue