Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ applyTo: '**'
- This tool will help you identify issues and suggest fixes.
- This is especially useful for debugging and improving code quality.
- Try run it before writing new code & after completing so you can ensure everything works correctly.
- 🧪 When editing a page/component (especially `app/**/page.tsx`), use VS Code interaction error checks (`get_errors` / `#problems`) on the edited files before and after changes.
- ⚙️ Internal error-tool enable flow (required for page edits):
- 1) Activate VS Code interaction tools.
- 2) Run `get_errors` on the exact files being edited (not project-wide).
- 3) Fix reported issues.
- 4) Run `get_errors` again on those same files to verify clean state.
- 🌐 When unsure about framework/API behavior while editing UI pages, use internet research tools first (`#web`, `#websearch`, or `fetch_webpage`) and then apply fixes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The instruction mentions fetch_webpage, but the new tool added in this PR is fetchTool. To avoid confusion and ensure the instructions are accurate, it would be better to use the actual tool ID.

Suggested change
- 🌐 When unsure about framework/API behavior while editing UI pages, use internet research tools first (`#web`, `#websearch`, or `fetch_webpage`) and then apply fixes.
- 🌐 When unsure about framework/API behavior while editing UI pages, use internet research tools first (`#web`, `#websearch`, or `fetchTool`) and then apply fixes.

- 🚫 Do not run project-wide type checks/lint commands by default for page edits. Use targeted `get_errors` checks unless the user explicitly asks for `typecheck`/`lint` runs.
- 📌 To update your memory bank, use [#update-memory-bank] tool to add new information.
- 🛠 Mastra mcp tools use [#mastradocs], [#mastraChanges], [#mastraexamples] tool.
- These tools provide access to Mastra documentation, recent changes, and code examples.
Expand Down
30 changes: 26 additions & 4 deletions app/chat/components/main-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import {
WorkflowIcon,
CpuIcon,
ActivityIcon,
FolderTreeIcon,
NetworkIcon,
Loader2Icon,
PlusIcon,
} from 'lucide-react'
Expand Down Expand Up @@ -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')}
Expand Down Expand Up @@ -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)) && (
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This boolean logic is unnecessarily complex and hard to read. !(!(agent.provider ?? agent.modelId)) is equivalent to !!(agent.provider ?? agent.modelId). For conditional rendering, you can rely on truthiness directly, which is clearer.

Suggested change
{!(!(agent.provider ?? agent.modelId)) && (
{(agent.provider || agent.modelId) && (

<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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Simplify boolean checks for provider/modelId to improve readability.

!(!(agent.provider ?? agent.modelId)) and Boolean(agent.provider) are much less readable than !!(agent.provider || agent.modelId) and agent.provider && .... Please revert to the more idiomatic forms to keep the logic easy to scan and maintain.

Suggested implementation:

                                                                        {!!(agent.provider || agent.modelId) && (
                                                                                        {agent.provider && `${agent.provider} • `}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The (Boolean(agent.provider)) check is verbose. A simple truthiness check with agent.provider is sufficient and more idiomatic in this context.

Suggested change
{(Boolean(agent.provider)) && `${agent.provider} • `}
{agent.provider && `${agent.provider} • `}

{agent.modelId}
</span>
Comment on lines +309 to 316
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new condition !(!(agent.provider ?? agent.modelId)) changes behavior vs the previous !!(agent.provider || agent.modelId). With the nullish-coalescing version, an empty-string provider will prevent the badge from showing even when modelId is present. Use a truthy check (agent.provider || agent.modelId) or explicitly check each field so the model badge still renders when only modelId is set.

Copilot uses AI. Check for mistakes.
</div>
Expand All @@ -299,7 +321,7 @@ export function MainSidebar() {

{/* Description section */}
<div className="p-4 pt-3">
{agent.description ? (
{(agent.description) ? (
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The parentheses around agent.description in this ternary operator are unnecessary and can be removed for cleaner code.

Suggested change
{(agent.description) ? (
{agent.description ? (

<div className="space-y-2">
<span className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground/70">
Capabilities
Expand All @@ -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>
)}
Expand Down
117 changes: 117 additions & 0 deletions app/chat/mcp-a2a/page.tsx
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>
)
}
Loading
Loading