feat: added tools flag to choose mcpui
This commit is contained in:
parent
973d109859
commit
373b9fc296
3 changed files with 247 additions and 28 deletions
183
MCP_UI.md
183
MCP_UI.md
|
|
@ -210,35 +210,182 @@ Each UI resource follows the MCP-UI protocol structure:
|
|||
- `ui://app-detail/{org}/{name}/{version}` - Single app detail view
|
||||
- `ui://app-instances-dashboard` - Instance listing dashboard
|
||||
|
||||
## Configuration
|
||||
|
||||
### How Clients Discover UI Support
|
||||
|
||||
Tools that support UI rendering advertise this capability in their metadata via the `tools/list` response:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "tools/list",
|
||||
"result": {
|
||||
"tools": [
|
||||
{
|
||||
"name": "list_apps",
|
||||
"description": "List all Edge Connect applications matching the specified filter. Supports UI rendering when _meta.ui_standard is set to 'mcpui'.",
|
||||
"_meta": {
|
||||
"ui_support": "mcpui"
|
||||
},
|
||||
"inputSchema": {...}
|
||||
},
|
||||
{
|
||||
"name": "show_app",
|
||||
"description": "Retrieve a specific Edge Connect application by its key. Supports UI rendering when _meta.ui_standard is set to 'mcpui'.",
|
||||
"_meta": {
|
||||
"ui_support": "mcpui"
|
||||
},
|
||||
"inputSchema": {...}
|
||||
},
|
||||
{
|
||||
"name": "list_app_instances",
|
||||
"description": "List all Edge Connect application instances. Supports UI rendering when _meta.ui_standard is set to 'mcpui'.",
|
||||
"_meta": {
|
||||
"ui_support": "mcpui"
|
||||
},
|
||||
"inputSchema": {...}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `_meta.ui_support` field indicates which UI standard(s) the tool supports. Clients can use this information to decide whether to request UI rendering when calling the tool.
|
||||
|
||||
### UI Standard Flag (Per-Call Control)
|
||||
|
||||
The server respects the client's UI rendering preferences via the `_meta.ui_standard` field in individual tool call requests. This allows **MCP clients** to control whether UI resources should be rendered on a **per-call basis**.
|
||||
|
||||
**Available UI Standards**:
|
||||
- `"mcpui"` - MCP-UI protocol (renders interactive HTML dashboards)
|
||||
- `""` (empty string) or omitted - No UI rendering (text-only responses)
|
||||
- Custom standards can be added in the future
|
||||
|
||||
**How Clients Request UI Rendering**:
|
||||
|
||||
Clients specify their UI preference in the `_meta` field of the `tools/call` request:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 2,
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "list_apps",
|
||||
"arguments": {
|
||||
"region": "EU"
|
||||
},
|
||||
"_meta": {
|
||||
"ui_standard": "mcpui"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Examples**:
|
||||
|
||||
**To enable MCP-UI rendering for a specific call**:
|
||||
```json
|
||||
{
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "list_apps",
|
||||
"arguments": {...},
|
||||
"_meta": {
|
||||
"ui_standard": "mcpui"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To disable UI rendering** (text-only response):
|
||||
```json
|
||||
{
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "list_apps",
|
||||
"arguments": {...}
|
||||
// No _meta field, or _meta.ui_standard omitted/empty
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Server Implementation**:
|
||||
|
||||
The server checks the client's `_meta.ui_standard` preference on each tool call:
|
||||
|
||||
```go
|
||||
// Extract client's UI standard preference from tool call _meta
|
||||
func getUIStandardFromRequest(req *mcp.CallToolRequest) string {
|
||||
if req.Params == nil || req.Params.Meta == nil {
|
||||
return ""
|
||||
}
|
||||
uiStandard, ok := req.Params.Meta["ui_standard"]
|
||||
// ... return the string value
|
||||
}
|
||||
|
||||
// Check if client wants UI rendering for this call
|
||||
if !shouldRenderUI(req, "mcpui") {
|
||||
// Return text-only response
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{&mcp.TextContent{Text: result}},
|
||||
}, nil, nil
|
||||
}
|
||||
|
||||
// ... proceed with UI generation
|
||||
```
|
||||
|
||||
**Default Behavior**:
|
||||
- If the client does not include `_meta.ui_standard` in the tool call, the server returns text-only responses
|
||||
- The three tools (`list_apps`, `show_app`, `list_app_instances`) support UI rendering when requested
|
||||
- If UI rendering fails, the tools gracefully fall back to text-only responses
|
||||
- The `shouldRenderUI()` helper function checks the call's `_meta` field before attempting UI generation
|
||||
|
||||
**Benefits**:
|
||||
- **Per-Call Control**: Clients decide on each call whether they want UI rendering
|
||||
- **No Configuration Required**: Clients don't need to set preferences during initialization
|
||||
- **Flexibility**: Support multiple UI standards in the future
|
||||
- **Discovery**: Clients learn about UI support from `tools/list` response
|
||||
- **Backward Compatibility**: Existing clients without UI support work seamlessly
|
||||
- **Graceful Degradation**: Always provides text-based fallback
|
||||
- **Performance**: Avoid UI generation overhead when clients don't request it
|
||||
|
||||
## Usage
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. **MCP Client Support**: Your MCP client must support UI resources (embedded resources in tool responses)
|
||||
2. **Edge Connect Configuration**: Server must be properly configured with Edge Connect credentials
|
||||
3. **Client UI Capability**: Client must include `_meta.ui_standard` field in tool call requests to receive UI resources
|
||||
|
||||
### Example Workflow
|
||||
|
||||
1. **List Applications**:
|
||||
```bash
|
||||
# User asks: "List all Edge Connect applications"
|
||||
# Server executes list_apps tool
|
||||
# Returns: Text summary + Interactive HTML dashboard
|
||||
```
|
||||
1. **Client Discovers Tools**:
|
||||
- Client sends `tools/list` request
|
||||
- Server responds with tool list including `_meta.ui_support` field
|
||||
- Client sees that `list_apps`, `show_app`, and `list_app_instances` support `"mcpui"`
|
||||
|
||||
2. **View Application Details**:
|
||||
```bash
|
||||
# User asks: "Show me details for app myorg/myapp:1.0.0"
|
||||
# Server executes show_app tool
|
||||
# Returns: JSON data + Interactive detail view
|
||||
```
|
||||
2. **List Applications with UI**:
|
||||
- User asks: "List all Edge Connect applications"
|
||||
- Client calls `list_apps` with `_meta.ui_standard: "mcpui"`
|
||||
- Server returns: Text summary + Interactive HTML dashboard
|
||||
|
||||
3. **List Instances**:
|
||||
```bash
|
||||
# User asks: "Show all application instances"
|
||||
# Server executes list_app_instances tool
|
||||
# Returns: Text summary + Interactive dashboard
|
||||
```
|
||||
3. **View Application Details with UI**:
|
||||
- User asks: "Show me details for app myorg/myapp:1.0.0"
|
||||
- Client calls `show_app` with `_meta.ui_standard: "mcpui"`
|
||||
- Server returns: JSON data + Interactive detail view
|
||||
|
||||
4. **List Instances without UI**:
|
||||
- User asks: "Show all application instances as text"
|
||||
- Client calls `list_app_instances` without `_meta.ui_standard`
|
||||
- Server returns: Text-only response (no UI)
|
||||
|
||||
**Key Points**:
|
||||
- Each tool call can independently request UI rendering or not
|
||||
- Clients that don't support UI simply omit the `_meta.ui_standard` field
|
||||
- Clients can mix UI and non-UI calls based on context or user preferences
|
||||
|
||||
### Graceful Degradation
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,14 @@ This server includes **rich, interactive web-based visualizations** powered by [
|
|||
- **🔍 Application Detail View** - Comprehensive property display with JSON viewer
|
||||
- **⚡ Instances Dashboard** - Interactive table with status indicators and instance management
|
||||
|
||||
For clients that don't support UI resources, the server gracefully falls back to text-based responses. See [MCP_UI.md](./MCP_UI.md) for full documentation.
|
||||
**UI Standard Support**: The server respects client preferences for UI rendering on a per-call basis via the `_meta.ui_standard` field:
|
||||
- Clients include `"_meta": {"ui_standard": "mcpui"}` in `tools/call` requests to receive MCP-UI interactive dashboards
|
||||
- Clients omit `_meta.ui_standard` or set it to `""` to receive text-only responses
|
||||
- Tools advertise UI support via `_meta.ui_support` field in `tools/list` responses
|
||||
- Server supports multiple UI standards for future extensibility
|
||||
- This gives clients full control on each call whether they want to render UI resources
|
||||
|
||||
For clients that don't support UI resources, the server gracefully falls back to text-based responses. See [MCP_UI.md](./MCP_UI.md) for full documentation and configuration details.
|
||||
|
||||
### Edge Connect API Tools
|
||||
|
||||
|
|
|
|||
83
tools.go
83
tools.go
|
|
@ -117,6 +117,40 @@ func getProtocolFromRequest(req *mcp.CallToolRequest) mcpuiserver.ProtocolType {
|
|||
return mcpuiserver.ParseProtocolFromInitialize(paramsMap)
|
||||
}
|
||||
|
||||
// getUIStandardFromRequest extracts the ui_standard value from the tool call's _meta field
|
||||
// Returns empty string if not set or not a string
|
||||
func getUIStandardFromRequest(req *mcp.CallToolRequest) string {
|
||||
// Get _meta from the tool call params
|
||||
if req.Params == nil || req.Params.Meta == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Look for ui_standard in the _meta field
|
||||
uiStandard, ok := req.Params.Meta["ui_standard"]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
uiStandardStr, ok := uiStandard.(string)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
return uiStandardStr
|
||||
}
|
||||
|
||||
// shouldRenderUI checks if UI rendering should be performed based on client's tool call _meta
|
||||
// Returns true if client's ui_standard is set to the requested standard (e.g., "mcpui")
|
||||
func shouldRenderUI(req *mcp.CallToolRequest, standard string) bool {
|
||||
uiStandard := getUIStandardFromRequest(req)
|
||||
if uiStandard == "" {
|
||||
return false // No UI standard specified by client, don't render
|
||||
}
|
||||
|
||||
// Check if the client's UI standard matches the requested standard
|
||||
return uiStandard == standard
|
||||
}
|
||||
|
||||
// filterAppInstances performs client-side partial matching on app instances
|
||||
func filterAppInstances(instances []v2.AppInstance, organization, instanceName, cloudletOrg, cloudletName, appOrg, appName, appVersion *string) []v2.AppInstance {
|
||||
if organization == nil && instanceName == nil &&
|
||||
|
|
@ -324,7 +358,10 @@ func registerShowAppTool(s *mcp.Server) {
|
|||
|
||||
mcp.AddTool(s, &mcp.Tool{
|
||||
Name: "show_app",
|
||||
Description: "Retrieve a specific Edge Connect application by its key.",
|
||||
Description: "Retrieve a specific Edge Connect application by its key. Supports UI rendering when _meta.ui_standard is set to 'mcpui'.",
|
||||
Meta: mcp.Meta{
|
||||
"ui_support": "mcpui",
|
||||
},
|
||||
}, func(ctx context.Context, req *mcp.CallToolRequest, a args) (*mcp.CallToolResult, any, error) {
|
||||
region := config.DefaultRegion
|
||||
if a.Region != nil {
|
||||
|
|
@ -347,6 +384,14 @@ func registerShowAppTool(s *mcp.Server) {
|
|||
return nil, nil, fmt.Errorf("failed to serialize app: %w", err)
|
||||
}
|
||||
|
||||
// Check if UI rendering is enabled by the client
|
||||
if !shouldRenderUI(req, "mcpui") {
|
||||
// UI rendering is disabled, return text-only response
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{&mcp.TextContent{Text: string(appJSON)}},
|
||||
}, nil, nil
|
||||
}
|
||||
|
||||
// Parse protocol from MCP initialize request
|
||||
protocol := getProtocolFromRequest(req)
|
||||
|
||||
|
|
@ -388,7 +433,10 @@ func registerListAppsTool(s *mcp.Server) {
|
|||
|
||||
mcp.AddTool(s, &mcp.Tool{
|
||||
Name: "list_apps",
|
||||
Description: "List all Edge Connect applications matching the specified filter. Supports partial (substring) matching for all filter fields.",
|
||||
Description: "List all Edge Connect applications matching the specified filter. Supports partial (substring) matching for all filter fields. Supports UI rendering when _meta.ui_standard is set to 'mcpui'.",
|
||||
Meta: mcp.Meta{
|
||||
"ui_support": "mcpui",
|
||||
},
|
||||
}, func(ctx context.Context, req *mcp.CallToolRequest, a args) (*mcp.CallToolResult, any, error) {
|
||||
region := config.DefaultRegion
|
||||
if a.Region != nil {
|
||||
|
|
@ -417,6 +465,16 @@ func registerListAppsTool(s *mcp.Server) {
|
|||
return nil, nil, fmt.Errorf("failed to serialize apps: %w", err)
|
||||
}
|
||||
|
||||
result := fmt.Sprintf("Found %d apps:\n%s", len(apps), string(appsJSON))
|
||||
|
||||
// Check if UI rendering is enabled by the client
|
||||
if !shouldRenderUI(req, "mcpui") {
|
||||
// UI rendering is disabled, return text-only response
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{&mcp.TextContent{Text: result}},
|
||||
}, nil, nil
|
||||
}
|
||||
|
||||
// Parse protocol from MCP initialize request
|
||||
protocol := getProtocolFromRequest(req)
|
||||
|
||||
|
|
@ -424,14 +482,11 @@ func registerListAppsTool(s *mcp.Server) {
|
|||
uiResource, err := createAppListUI(apps, region, protocol)
|
||||
if err != nil {
|
||||
// If UI creation fails, just return text content
|
||||
result := fmt.Sprintf("Found %d apps:\n%s", len(apps), string(appsJSON))
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{&mcp.TextContent{Text: result}},
|
||||
}, nil, nil
|
||||
}
|
||||
|
||||
result := fmt.Sprintf("Found %d apps:\n%s", len(apps), string(appsJSON))
|
||||
|
||||
// Convert UIResource to MCP EmbeddedResource
|
||||
resourceContents := &mcp.ResourceContents{
|
||||
URI: uiResource.Resource.URI,
|
||||
|
|
@ -745,7 +800,10 @@ func registerListAppInstancesTool(s *mcp.Server) {
|
|||
|
||||
mcp.AddTool(s, &mcp.Tool{
|
||||
Name: "list_app_instances",
|
||||
Description: "List all Edge Connect application instances matching the specified filter. Supports partial (substring) matching for all filter fields.",
|
||||
Description: "List all Edge Connect application instances matching the specified filter. Supports partial (substring) matching for all filter fields. Supports UI rendering when _meta.ui_standard is set to 'mcpui'.",
|
||||
Meta: mcp.Meta{
|
||||
"ui_support": "mcpui",
|
||||
},
|
||||
}, func(ctx context.Context, req *mcp.CallToolRequest, a args) (*mcp.CallToolResult, any, error) {
|
||||
region := config.DefaultRegion
|
||||
if a.Region != nil {
|
||||
|
|
@ -783,6 +841,16 @@ func registerListAppInstancesTool(s *mcp.Server) {
|
|||
return nil, nil, fmt.Errorf("failed to serialize app instances: %w", err)
|
||||
}
|
||||
|
||||
result := fmt.Sprintf("Found %d app instances:\n%s", len(appInsts), string(appInstsJSON))
|
||||
|
||||
// Check if UI rendering is enabled by the client
|
||||
if !shouldRenderUI(req, "mcpui") {
|
||||
// UI rendering is disabled, return text-only response
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{&mcp.TextContent{Text: result}},
|
||||
}, nil, nil
|
||||
}
|
||||
|
||||
// Parse protocol from MCP initialize request
|
||||
protocol := getProtocolFromRequest(req)
|
||||
|
||||
|
|
@ -790,14 +858,11 @@ func registerListAppInstancesTool(s *mcp.Server) {
|
|||
uiResource, err := createAppInstanceListUI(appInsts, region, protocol)
|
||||
if err != nil {
|
||||
// If UI creation fails, just return text content
|
||||
result := fmt.Sprintf("Found %d app instances:\n%s", len(appInsts), string(appInstsJSON))
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{&mcp.TextContent{Text: result}},
|
||||
}, nil, nil
|
||||
}
|
||||
|
||||
result := fmt.Sprintf("Found %d app instances:\n%s", len(appInsts), string(appInstsJSON))
|
||||
|
||||
// Convert UIResource to MCP EmbeddedResource
|
||||
resourceContents := &mcp.ResourceContents{
|
||||
URI: uiResource.Resource.URI,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue