Skip to content

Commit 3608f21

Browse files
committed
UI tweaks, code cleanup, documentation improvement around external MCP tools
1 parent 3880308 commit 3608f21

File tree

17 files changed

+113
-2518
lines changed

17 files changed

+113
-2518
lines changed

docs/custom-MCP-agent.md

Lines changed: 6 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ In the AWS account where the IDP solution is deployed, create a secret with your
147147

148148
1. **Navigate to AWS Secrets Manager** in the AWS Console
149149
2. **Find the External MCP Agents Secret**:
150-
- Look for a secret named `{StackName}/external-mcp-agents/credentials` (where StackName is your IDP stack name)
150+
- Look for a secret named `{StackName}/external-mcp-agents/credentials` (where StackName is your IDP stack name).
151151
- This secret is automatically created by the CloudFormation template with an empty array `[]`
152152
- You can find the exact secret name in your CloudFormation stack outputs
153153

@@ -243,6 +243,11 @@ The authentication process works as follows:
243243
- Verify secret exists at path: `{StackName}/external-mcp-agents/credentials` (check CloudFormation outputs for exact name)
244244
- Check secret contains a valid JSON array format (not a single object)
245245
- Review CloudWatch logs for agent registration errors
246+
- **Lambda Caching**: The ListAvailableAgentsFunction has caching that may delay new agents appearing for up to 15 minutes. To force refresh:
247+
- Find the function named `{StackName}-ListAvailableAgentsFunction-*` in AWS Console → Lambda → Functions
248+
- Go to Configuration → Environment variables
249+
- Add a temporary variable like `REFRESH=1` and save to restart the function
250+
- Remove the temporary variable after agents appear
246251

247252
**Authentication Failures:**
248253
- Verify Cognito User Pool ID and Client ID are correct
@@ -292,45 +297,6 @@ The authentication process works as follows:
292297
-d '{"method": "list_tools", "params": {}}'
293298
```
294299

295-
## Example MCP Tools
296-
297-
Here are examples of tools you might implement in your MCP server:
298-
299-
### Calculator Tool
300-
```python
301-
@mcp_server.tool()
302-
def calculator(expression: str) -> dict:
303-
"""Evaluate mathematical expressions safely."""
304-
try:
305-
result = eval(expression, {"__builtins__": {}})
306-
return {"result": result}
307-
except Exception as e:
308-
return {"error": str(e)}
309-
```
310-
311-
### Weather API Tool
312-
```python
313-
@mcp_server.tool()
314-
def get_weather(city: str) -> dict:
315-
"""Get current weather for a city."""
316-
# Call external weather API
317-
weather_data = call_weather_api(city)
318-
return {
319-
"city": city,
320-
"temperature": weather_data["temp"],
321-
"conditions": weather_data["conditions"]
322-
}
323-
```
324-
325-
### Database Query Tool
326-
```python
327-
@mcp_server.tool()
328-
def query_database(query: str) -> dict:
329-
"""Execute safe database queries."""
330-
# Implement with proper SQL injection protection
331-
results = execute_safe_query(query)
332-
return {"results": results}
333-
```
334300

335301
## Best Practices
336302

lib/idp_common_pkg/idp_common/agents/common/idp_agent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def __init__(
8080
Args:
8181
agent_name: Human-readable name for the agent (e.g., "Analytics Agent")
8282
agent_description: Description of what the agent does
83-
agent_id: Unique identifier for the agent (e.g., "analytics-20250813-v0-kaleko")
83+
agent_id: Unique identifier for the agent (e.g., "analytics-20250813-v0")
8484
agent: Existing Strands Agent instance to wrap (required)
8585
sample_queries: List of example queries that demonstrate the agent's capabilities
8686
job_id: Job ID for monitoring purposes. When provided with user_id, enables

lib/idp_common_pkg/idp_common/agents/factory/registry.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
agents = agent_factory.list_available_agents()
1515
1616
# Create an agent
17-
agent = agent_factory.create_agent("analytics-20250813-v0-kaleko", config=config)
17+
agent = agent_factory.create_agent("analytics-20250813-v0", config=config)
1818
"""
1919

2020
import logging
@@ -31,7 +31,7 @@
3131

3232
# Register analytics agent
3333
agent_factory.register_agent(
34-
agent_id="analytics-20250813-v0-kaleko",
34+
agent_id="Analytics-Agent-v1",
3535
agent_name="Analytics Agent",
3636
agent_description="""
3737
Converts natural language questions into SQL queries and generates visualizations from document data.
@@ -100,7 +100,9 @@
100100
]
101101
for field in required_fields:
102102
if field not in mcp_config:
103-
logger.warning(f"Skipping MCP config {i}: missing required field '{field}'")
103+
logger.warning(
104+
f"Skipping MCP config {i}: missing required field '{field}'"
105+
)
104106
continue
105107

106108
# Create wrapper function for this specific MCP config
@@ -113,6 +115,7 @@ def wrapper(config=None, session=None, model_id=None, **kwargs):
113115
mcp_server_config=mcp_server_config,
114116
**kwargs,
115117
)
118+
116119
return wrapper
117120

118121
# Try to discover available tools for dynamic description
@@ -126,7 +129,10 @@ def wrapper(config=None, session=None, model_id=None, **kwargs):
126129

127130
# Extract available tools for dynamic description
128131
dynamic_description = f"Agent {i} which connects to external MCP servers to provide additional tools and capabilities"
129-
if hasattr(test_strands_agent, "tool_names") and test_strands_agent.tool_names:
132+
if (
133+
hasattr(test_strands_agent, "tool_names")
134+
and test_strands_agent.tool_names
135+
):
130136
tool_names = list(test_strands_agent.tool_names)
131137
if tool_names:
132138
tools_list = ", ".join(tool_names)

src/ui/src/components/document-agents-layout/AgentQueryInput.jsx

Lines changed: 92 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import {
1212
SpaceBetween,
1313
ButtonDropdown,
1414
Checkbox,
15+
Modal,
16+
Header,
17+
Link,
1518
} from '@awsui/components-react';
1619
import listAgentJobs from '../../graphql/queries/listAgentJobs';
1720
import deleteAgentJob from '../../graphql/queries/deleteAgentJob';
@@ -40,6 +43,7 @@ const AgentQueryInput = ({ onSubmit, isSubmitting, selectedResult }) => {
4043
const [availableAgents, setAvailableAgents] = useState([]);
4144
const [selectedAgents, setSelectedAgents] = useState([]);
4245
const [isLoadingAgents, setIsLoadingAgents] = useState(false);
46+
const [showMcpInfoModal, setShowMcpInfoModal] = useState(false);
4347
const [hoveredAgent, setHoveredAgent] = useState(null);
4448
const [hoverTimeout, setHoverTimeout] = useState(null);
4549
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
@@ -81,6 +85,17 @@ const AgentQueryInput = ({ onSubmit, isSubmitting, selectedResult }) => {
8185
}
8286
};
8387

88+
const handleSelectAllAgents = (e) => {
89+
e.preventDefault();
90+
e.stopPropagation();
91+
const allSelected = selectedAgents.length === availableAgents.length;
92+
if (allSelected) {
93+
setSelectedAgents([]);
94+
} else {
95+
setSelectedAgents(availableAgents.map((agent) => agent.agent_id));
96+
}
97+
};
98+
8499
const fetchAvailableAgents = async () => {
85100
try {
86101
setIsLoadingAgents(true);
@@ -90,11 +105,6 @@ const AgentQueryInput = ({ onSubmit, isSubmitting, selectedResult }) => {
90105

91106
const agents = response?.data?.listAvailableAgents || [];
92107
setAvailableAgents(agents);
93-
94-
// Auto-select first agent if none selected
95-
if (agents.length > 0 && selectedAgents.length === 0) {
96-
setSelectedAgents([agents[0].agent_id]);
97-
}
98108
} catch (err) {
99109
logger.error('Error fetching available agents:', err);
100110
setAvailableAgents([]);
@@ -323,7 +333,7 @@ const AgentQueryInput = ({ onSubmit, isSubmitting, selectedResult }) => {
323333
// Create dropdown items with delete functionality
324334
const createDropdownItems = () => {
325335
if (queryHistory.length === 0) {
326-
return [{ text: 'No previous queries found', disabled: true }];
336+
return [{ text: 'No previous questions found', disabled: true }];
327337
}
328338

329339
return queryHistory.map((job) => {
@@ -404,9 +414,14 @@ const AgentQueryInput = ({ onSubmit, isSubmitting, selectedResult }) => {
404414
<form onSubmit={handleSubmit}>
405415
<SpaceBetween size="l">
406416
<Box>
407-
<Box fontSize="body-m" fontWeight="bold" padding={{ bottom: 'xs' }}>
408-
Select from available agents
409-
</Box>
417+
<SpaceBetween direction="horizontal" size="s" alignItems="center">
418+
<Box fontSize="body-m" fontWeight="bold">
419+
Select from available agents
420+
</Box>
421+
<Button variant="link" onClick={() => setShowMcpInfoModal(true)} fontSize="body-s">
422+
🚀 NEW: Integrate your own systems with MCP!
423+
</Button>
424+
</SpaceBetween>
410425
<div
411426
style={{
412427
maxHeight: '200px',
@@ -454,6 +469,17 @@ const AgentQueryInput = ({ onSubmit, isSubmitting, selectedResult }) => {
454469
</SpaceBetween>
455470
)}
456471
</div>
472+
{!isLoadingAgents && availableAgents.length > 0 && (
473+
<Box padding={{ top: 's' }}>
474+
<Button
475+
type="button"
476+
variant={selectedAgents.length === availableAgents.length ? 'normal' : 'primary'}
477+
onClick={handleSelectAllAgents}
478+
>
479+
{selectedAgents.length === availableAgents.length ? 'Deselect All Agents' : 'Select All Agents'}
480+
</Button>
481+
</Box>
482+
)}
457483
{hoveredAgent && (
458484
<div
459485
style={{
@@ -509,7 +535,7 @@ const AgentQueryInput = ({ onSubmit, isSubmitting, selectedResult }) => {
509535
selectedAgents.length > 0
510536
? availableAgents.find((agent) => agent.agent_id === selectedAgents[0])?.sample_queries?.[0] ||
511537
'Enter your question here...'
512-
: 'Select an agent first...'
538+
: 'Select at least one available agent to get started'
513539
}
514540
value={currentInputText}
515541
onChange={({ detail }) => updateAnalyticsState({ currentInputText: detail.value })}
@@ -526,16 +552,16 @@ const AgentQueryInput = ({ onSubmit, isSubmitting, selectedResult }) => {
526552
disabled={!currentInputText.trim() || selectedAgents.length === 0 || isSubmitting}
527553
fullWidth
528554
>
529-
{isSubmitting ? 'Submitting...' : 'Submit query'}
555+
{isSubmitting ? 'Submitting...' : 'Submit question'}
530556
</Button>
531557
<Button variant="normal" onClick={handleClearQuery} disabled={isSubmitting} fullWidth>
532-
Clear query
558+
Clear question
533559
</Button>
534560
</SpaceBetween>
535561
</Box>
536562
</Grid>
537563

538-
<FormField label="Previous queries">
564+
<FormField>
539565
<ButtonDropdown
540566
items={createDropdownItems()}
541567
onItemClick={handleDropdownItemClick}
@@ -544,16 +570,67 @@ const AgentQueryInput = ({ onSubmit, isSubmitting, selectedResult }) => {
544570
disabled={isSubmitting}
545571
>
546572
{(() => {
547-
if (!selectedOption) return 'Select a previous query';
573+
if (!selectedOption) return 'Select a previous question';
548574
if (selectedOption.label?.length > 40) {
549575
return `${selectedOption.label.substring(0, 40)}...`;
550576
}
551-
return selectedOption.label || 'Selected query';
577+
return selectedOption.label || 'Selected question';
552578
})()}
553579
</ButtonDropdown>
554580
</FormField>
555581
</SpaceBetween>
556582
</form>
583+
584+
<Modal
585+
onDismiss={() => setShowMcpInfoModal(false)}
586+
visible={showMcpInfoModal}
587+
header={<Header>Custom MCP Agents</Header>}
588+
footer={
589+
<Box float="right">
590+
<Button variant="primary" onClick={() => setShowMcpInfoModal(false)}>
591+
Close
592+
</Button>
593+
</Box>
594+
}
595+
>
596+
<SpaceBetween size="m">
597+
<Box>
598+
<Box fontWeight="bold" fontSize="body-m">
599+
What are MCP Agents?
600+
</Box>
601+
<Box>
602+
Model Context Protocol (MCP) agents allow you to connect external tools and services to extend the
603+
capabilities of your document analysis workflow.
604+
</Box>
605+
</Box>
606+
607+
<Box>
608+
<Box fontWeight="bold" fontSize="body-m">
609+
Adding Custom Agents
610+
</Box>
611+
<Box>
612+
You can add your own MCP agents by configuring external MCP servers in AWS Secrets Manager. This allows
613+
you to integrate custom tools, APIs, and services specific to your organization&apos;s needs without any
614+
code changes or redeployments.
615+
</Box>
616+
</Box>
617+
618+
<Box>
619+
<Box fontWeight="bold" fontSize="body-m">
620+
Learn More
621+
</Box>
622+
<Box>
623+
For detailed setup instructions and examples, see the{' '}
624+
<Link
625+
external
626+
href="https://github.com/aws-samples/genaiic-idp-accelerator/blob/main/docs/custom-MCP-agent.md"
627+
>
628+
Custom MCP Agent Documentation
629+
</Link>
630+
</Box>
631+
</Box>
632+
</SpaceBetween>
633+
</Modal>
557634
</>
558635
);
559636
};

src/ui/src/components/document-agents-layout/DocumentsAgentsLayout.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ const DocumentsAgentsLayout = () => {
255255
}, [jobId, jobStatus, updateAnalyticsState]);
256256

257257
return (
258-
<Container header={<Header variant="h1">Agent HQ</Header>}>
258+
<Container header={<Header variant="h2">Agent Analysis</Header>}>
259259
<SpaceBetween size="l">
260260
<AgentQueryInput onSubmit={handleSubmitQuery} isSubmitting={isSubmitting} selectedResult={null} />
261261

src/ui/src/components/document-agents-layout/tools-panel.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { HelpPanel, Icon } from '@awsui/components-react';
66
const ToolsPanel = () => {
77
return (
88
<HelpPanel
9-
header={<h2>Agent HQ</h2>}
9+
header={<h2>Agent Analysis</h2>}
1010
footer={
1111
<div>
1212
<h3>
@@ -24,7 +24,7 @@ const ToolsPanel = () => {
2424
>
2525
<div>
2626
<p>
27-
Use Agent HQ to interact with AI agents using natural language and receive interactive visualizations and
27+
Use Agent Analysis to interact with AI agents using natural language and receive interactive visualizations and
2828
insights.
2929
</p>
3030
<h3>How to use</h3>

0 commit comments

Comments
 (0)