Skip to content
Open
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
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,11 @@ The script automatically handles port assignments and logging for each server, m
Populate `agents/MatCreator/.env` with your model and Bohrium credentials (if using `dp` model).

```env
LLM_API_KEY="API_KEYS",
LLM_BASE_URL="API_KEYS",
BOHRIUM_USERNAME="",
BOHRIUM_PASSWORD="",
LLM_MODEL=YOUR_LLM_MODEL
LLM_API_KEY=YOUR_API_KEYS
LLM_BASE_URL=YOUR_BASE_URL
BOHRIUM_USERNAME=""
BOHRIUM_PASSWORD=""
BOHRIUM_PROJECT_ID=11111
```

Expand Down
19 changes: 17 additions & 2 deletions agents/MatCreator/structure_agent/agent.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from google.adk.agents import Agent
from google.adk.agents.remote_a2a_agent import RemoteA2aAgent
from google.adk.agents.remote_a2a_agent import AGENT_CARD_WELL_KNOWN_PATH
from google.adk.models.lite_llm import LiteLlm
from google.adk.tools.agent_tool import AgentTool
from google.adk.tools.mcp_tool import MCPToolset
Expand Down Expand Up @@ -27,6 +29,7 @@
- Inspect structure files to report frame counts, formulas, atom counts,
cells, periodicity flags, and available per-frame/per-atom properties.
- Apply entropy-based configuration selection to curate diverse structure sets.
- Build complex structures by calling the flexible_structure_agent when needed.
"""

instruction = """
Expand Down Expand Up @@ -64,9 +67,12 @@
warnings or anomalies.
- When chaining tools (e.g., build_bulk_crystal → perturb_atoms), clearly
indicate which artifact from the previous step you are using.
- If the user request contains complex structure manipulations beyond your
tools' capabilities, recognize the limitation gracefully and, transfer to the
flexible_structure_agent with a brief explanation.

Response format
- Plan: 13 bullets describing the immediate next step(s).
- Plan: 1-3 bullets describing the immediate next step(s).
- Action: the exact tool you will call.
- Result: brief summary with key outputs and absolute paths.
- Next: the immediate follow-up or final recap. Transfer back to parent agent if done.
Expand All @@ -80,6 +86,14 @@
)
)

remote_a2a_url = "http://localhost:8001"
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The hardcoded URL "http://localhost:8001" will not work in production environments or when the remote agent is deployed elsewhere. This should be configurable through environment variables or configuration files to support different deployment scenarios.

Suggested change
remote_a2a_url = "http://localhost:8001"
remote_a2a_url = os.getenv("REMOTE_A2A_URL", "http://localhost:8001")

Copilot uses AI. Check for mistakes.
flexible_structure_agent = RemoteA2aAgent(
name='flexible_structure_agent',
description="A structure agent that can handle very complex structure-related tasks. " \
"This is a remote agent, so always remember to transfer the outputs back to the parent agent after done.",
agent_card=f"{remote_a2a_url}/{AGENT_CARD_WELL_KNOWN_PATH}"
)

structure_agent = Agent(
name='structure_agent',
model=LiteLlm(
Expand All @@ -94,5 +108,6 @@
tools=[
toolset,
],
after_tool_callback=after_tool_callback
after_tool_callback=after_tool_callback,
sub_agents=[flexible_structure_agent], # Enable transfer to flexible_structure_agent
)
39 changes: 39 additions & 0 deletions tools/quest/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -1130,6 +1130,45 @@ def inspect_structure(
)


## ================================
## Tools for file transfer between local and remote structure agents
## ===============================

import requests
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The import statement for requests should be placed at the top of the file with other imports, not in the middle of the file before the function definition. This violates Python style guidelines (PEP 8) which recommend all imports be at the top of the file.

Copilot uses AI. Check for mistakes.

@mcp.tool()
def get_remote_files(file_urls: list[str], local_save_path: str="downloads") -> list[dict]:
"""
Downloads files from the remote agent to local storage.
Args:
file_urls: List of full URLs of files to download from the remote agent.
local_save_path: Local directory path to save the downloaded files.
"""
os.makedirs(local_save_path, exist_ok=True)
results: list[dict] = []
for file_url in file_urls:
# Extract filename from URL (the last part)
file_name = file_url.split('/')[-1]
local_file_path = os.path.join(local_save_path, file_name)
Comment on lines +1151 to +1152
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The filename extraction from the URL using file_url.split('/')[-1] is vulnerable to path traversal attacks. A malicious URL could contain path traversal sequences (e.g., '../../../etc/passwd') that would allow writing files outside the intended directory. The extracted filename should be sanitized to remove any directory separators or path traversal sequences.

Copilot uses AI. Check for mistakes.

# get the file contents from the remote URL
response = requests.get(file_url)
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The function lacks validation for the URL input, which could lead to security vulnerabilities. There is no check to ensure that URLs are from trusted sources or use safe protocols (e.g., only https). Additionally, there's no protection against Server-Side Request Forgery (SSRF) attacks where malicious URLs could be used to access internal resources.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The HTTP request lacks a timeout parameter, which could cause the function to hang indefinitely if the remote server doesn't respond. This could lead to resource exhaustion. Add a timeout parameter to the requests.get() call to prevent hanging requests.

Suggested change
response = requests.get(file_url)
response = requests.get(file_url, timeout=10)

Copilot uses AI. Check for mistakes.
response.raise_for_status()

# Write the raw content to file
with open(local_file_path, 'wb') as f:
f.write(response.content)

results.append({
"result": "File downloaded successfully",
"file_name": file_name,
"local_path": local_file_path
})
Comment on lines +1154 to +1166
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The function lacks proper error handling around the file operations. If an error occurs during writing (e.g., disk full, permission denied), the function will raise an unhandled exception. The partial file might be left on disk, and subsequent URLs in the list won't be processed. Consider wrapping file operations in try-except blocks and handling errors gracefully.

Copilot uses AI. Check for mistakes.

return results
Comment on lines +1140 to +1168
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The new get_remote_files function lacks test coverage. Given that the repository has test coverage for structure building functions in tests/test_structure_builder.py, this new file transfer functionality should also have tests covering successful downloads, error cases (network failures, invalid URLs, file write errors), and security scenarios (path traversal attempts).

Copilot uses AI. Check for mistakes.



if __name__ == "__main__":
create_workpath()
mcp.run(transport=args.transport)
Loading