-
Notifications
You must be signed in to change notification settings - Fork 6
feat: add new fetch tool and related components #121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -33,6 +33,8 @@ import { | |||||
| WorkflowIcon, | ||||||
| CpuIcon, | ||||||
| ActivityIcon, | ||||||
| FolderTreeIcon, | ||||||
| NetworkIcon, | ||||||
| Loader2Icon, | ||||||
| PlusIcon, | ||||||
| } from 'lucide-react' | ||||||
|
|
@@ -216,6 +218,26 @@ export function MainSidebar() { | |||||
| </SidebarGroupLabel> | ||||||
| <SidebarGroupContent> | ||||||
| <SidebarMenu> | ||||||
| <SidebarMenuItem> | ||||||
| <SidebarMenuButton | ||||||
| className="hover:bg-muted/50 transition-all duration-200" | ||||||
| onClick={() => handleNavClick('/chat/workspaces')} | ||||||
| > | ||||||
| <FolderTreeIcon className="mr-2 size-4 text-primary/70" /> | ||||||
| <span className="font-medium">Workspaces</span> | ||||||
| </SidebarMenuButton> | ||||||
| </SidebarMenuItem> | ||||||
|
|
||||||
| <SidebarMenuItem> | ||||||
| <SidebarMenuButton | ||||||
| className="hover:bg-muted/50 transition-all duration-200" | ||||||
| onClick={() => handleNavClick('/chat/mcp-a2a')} | ||||||
| > | ||||||
| <NetworkIcon className="mr-2 size-4 text-primary/70" /> | ||||||
| <span className="font-medium">MCP / A2A</span> | ||||||
| </SidebarMenuButton> | ||||||
| </SidebarMenuItem> | ||||||
|
|
||||||
| <Collapsible | ||||||
| open={openSection === 'agents'} | ||||||
| onOpenChange={() => toggleSection('agents')} | ||||||
|
|
@@ -284,12 +306,12 @@ export function MainSidebar() { | |||||
| <h3 className="font-bold text-sm tracking-tight text-foreground"> | ||||||
| {agent.name} | ||||||
| </h3> | ||||||
| {!!(agent.provider || agent.modelId) && ( | ||||||
| {!(!(agent.provider ?? agent.modelId)) && ( | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This boolean logic is unnecessarily complex and hard to read.
Suggested change
|
||||||
| <div className="mt-1.5 flex items-center gap-2"> | ||||||
| <div className="flex h-5 items-center rounded-md border border-primary/20 bg-primary/10 px-1.5 text-[9px] font-bold uppercase tracking-wider text-primary"> | ||||||
| <CpuIcon className="mr-1 size-3" /> | ||||||
| <span> | ||||||
| {agent.provider && `${agent.provider} • `} | ||||||
| {(Boolean(agent.provider)) && `${agent.provider} • `} | ||||||
|
Comment on lines
+309
to
+314
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Simplify boolean checks for provider/modelId to improve readability.
Suggested implementation: {!!(agent.provider || agent.modelId) && ( {agent.provider && `${agent.provider} • `}
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
| {agent.modelId} | ||||||
| </span> | ||||||
|
Comment on lines
+309
to
316
|
||||||
| </div> | ||||||
|
|
@@ -299,7 +321,7 @@ export function MainSidebar() { | |||||
|
|
||||||
| {/* Description section */} | ||||||
| <div className="p-4 pt-3"> | ||||||
| {agent.description ? ( | ||||||
| {(agent.description) ? ( | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
| <div className="space-y-2"> | ||||||
| <span className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground/70"> | ||||||
| Capabilities | ||||||
|
|
@@ -309,7 +331,7 @@ export function MainSidebar() { | |||||
| </p> | ||||||
| </div> | ||||||
| ) : ( | ||||||
| <p className="text-xs italic text-muted-foreground/60 italic leading-relaxed"> | ||||||
| <p className="text-xs italic text-muted-foreground/60 leading-relaxed"> | ||||||
| Specialized AI assistant ready to help with your task. | ||||||
| </p> | ||||||
| )} | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| 'use client' | ||
|
|
||
| import { useState } from 'react' | ||
| import { useMastraQuery } from '@/lib/hooks/use-mastra-query' | ||
|
|
||
| export default function McpA2APage() { | ||
| const { | ||
| useMcpServers, | ||
| useMcpServerTools, | ||
| useAgents, | ||
| useA2ACard, | ||
| } = useMastraQuery() | ||
|
|
||
| const serversResult = useMcpServers({ page: 0, perPage: 50 }) | ||
| const servers = serversResult.data?.servers ?? [] | ||
|
|
||
| const [selectedServerId, setSelectedServerId] = useState<string>('') | ||
| const activeServerId = selectedServerId || servers[0]?.id || '' | ||
|
|
||
| const toolsResult = useMcpServerTools(activeServerId) | ||
| const serverTools = toolsResult.data?.tools ?? [] | ||
|
|
||
| const agentsResult = useAgents() | ||
| const agents = agentsResult.data ?? [] | ||
|
|
||
| const [selectedAgentId, setSelectedAgentId] = useState<string>('') | ||
| const activeAgentId = selectedAgentId || agents[0]?.id || '' | ||
|
|
||
| const a2aCardResult = useA2ACard(activeAgentId) | ||
| const a2aCard = a2aCardResult.data | ||
|
|
||
| return ( | ||
| <div className="flex h-full min-h-0 flex-col gap-4 p-4 md:p-6"> | ||
| <h1 className="text-xl font-semibold">MCP / A2A</h1> | ||
|
|
||
| <div className="grid min-h-0 flex-1 grid-cols-1 gap-4 lg:grid-cols-2"> | ||
| <section className="min-h-0 overflow-auto rounded-lg border p-4"> | ||
| <div className="mb-3 flex items-center justify-between"> | ||
| <h2 className="text-sm font-medium">MCP Servers & Tools</h2> | ||
| <select | ||
| aria-label="Select MCP server" | ||
| title="Select MCP server" | ||
| className="rounded-md border bg-background px-2 py-1 text-sm" | ||
| value={activeServerId} | ||
| onChange={(e) => setSelectedServerId(e.target.value)} | ||
| > | ||
| {servers.map((server: { id: string; name?: string }) => ( | ||
| <option key={server.id} value={server.id}> | ||
| {server.name ?? server.id} | ||
| </option> | ||
| ))} | ||
| </select> | ||
| </div> | ||
|
|
||
| <ul className="space-y-2"> | ||
| {serverTools.length === 0 ? ( | ||
| <li className="text-sm text-muted-foreground">No MCP tools found.</li> | ||
| ) : ( | ||
| serverTools.map((tool: { id: string; description?: string }) => ( | ||
| <li key={tool.id} className="rounded-md border p-2"> | ||
| <div className="text-sm font-medium">{tool.id}</div> | ||
| {typeof tool.description === 'string' && tool.description.trim().length > 0 ? ( | ||
| <p className="mt-1 text-xs text-muted-foreground">{tool.description}</p> | ||
| ) : null} | ||
| </li> | ||
| )) | ||
| )} | ||
| </ul> | ||
| </section> | ||
|
|
||
| <section className="min-h-0 overflow-auto rounded-lg border p-4"> | ||
| <div className="mb-3 flex items-center justify-between"> | ||
| <h2 className="text-sm font-medium">A2A Agent Card</h2> | ||
| <select | ||
| aria-label="Select A2A agent" | ||
| title="Select A2A agent" | ||
| className="rounded-md border bg-background px-2 py-1 text-sm" | ||
| value={activeAgentId} | ||
| onChange={(e) => setSelectedAgentId(e.target.value)} | ||
| > | ||
| {agents.map((agent) => ( | ||
| <option key={agent.id} value={agent.id}> | ||
| {agent.name} | ||
| </option> | ||
| ))} | ||
| </select> | ||
| </div> | ||
|
|
||
| {!a2aCard ? ( | ||
| <p className="text-sm text-muted-foreground">No A2A card available.</p> | ||
| ) : ( | ||
| <div className="space-y-3"> | ||
| <div> | ||
| <div className="text-xs text-muted-foreground">Agent</div> | ||
| <div className="text-sm font-medium">{a2aCard.name ?? activeAgentId}</div> | ||
| </div> | ||
| <div> | ||
| <div className="text-xs text-muted-foreground">Description</div> | ||
| <div className="text-sm">{a2aCard.description ?? 'N/A'}</div> | ||
| </div> | ||
| <div> | ||
| <div className="text-xs text-muted-foreground">Skills</div> | ||
| <ul className="mt-1 list-disc pl-5 text-sm"> | ||
| {(a2aCard.skills ?? []).map((skill: { id?: string; name?: string }, idx: number) => ( | ||
| <li key={skill.id ?? skill.name ?? `skill-${idx}`}> | ||
| {skill.name ?? skill.id ?? 'Unnamed skill'} | ||
| </li> | ||
| ))} | ||
| </ul> | ||
| </div> | ||
| </div> | ||
| )} | ||
| </section> | ||
| </div> | ||
| </div> | ||
| ) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The instruction mentions
fetch_webpage, but the new tool added in this PR isfetchTool. To avoid confusion and ensure the instructions are accurate, it would be better to use the actual tool ID.