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
9 changes: 7 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ FEATURE_SPLASH_SCREEN_ENABLED=false # Startup splash screen for displaying poli
# Useful for testing or managing multiple configurations.
#############################################
# SPLASH_CONFIG_FILE=splash-config.json # Splash screen configuration file name
# MCP_CONFIG_FILE=mcp.json # MCP servers configuration file name
# MCP_CONFIG_FILE=mcp.json # MCP servers configuration file name
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

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

[nitpick] Inconsistent spacing alignment in the comment. Line 88 has extra spaces before the comment delimiter, while other lines (87, 89, 90) have a single space. For consistency, align with the pattern used in lines 87, 89, and 90.

Suggested change
# MCP_CONFIG_FILE=mcp.json # MCP servers configuration file name
# MCP_CONFIG_FILE=mcp.json # MCP servers configuration file name

Copilot uses AI. Check for mistakes.
# LLM_CONFIG_FILE=llmconfig.yml # LLM models configuration file name
# HELP_CONFIG_FILE=help-config.json # Help page configuration file name

Expand Down Expand Up @@ -133,4 +133,9 @@ USE_MOCK_S3=true
# S3_USE_SSL=false


SECURITY_CSP_VALUE="default-src 'self'; img-src 'self' data: blob:; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self'; frame-src 'self' blob: data:; frame-ancestors 'self'"
# Content Security Policy (CSP) configuration
# IMPORTANT: To allow external URLs in iframes (for MCP tools that use iframe display),
# add the URLs to the frame-src directive. Example:
# SECURITY_CSP_VALUE="... frame-src 'self' blob: data: https://example.com https://dashboard.example.com; ..."
# HERE the www.sandia.gov is added as an allowed iframe source.
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

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

The comment "HERE the www.sandia.gov is added as an allowed iframe source" is unclear and grammatically awkward. Consider rephrasing to: "In this example, https://www.sandia.gov is added as an allowed iframe source."

Suggested change
# HERE the www.sandia.gov is added as an allowed iframe source.
# In this example, https://www.sandia.gov is added as an allowed iframe source.

Copilot uses AI. Check for mistakes.
SECURITY_CSP_VALUE="default-src 'self'; img-src 'self' data: blob:; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self'; frame-src 'self' blob: data: https://www.sandia.gov; frame-ancestors 'self'"
5 changes: 3 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ frontend/ React 19 + Vite + Tailwind; state via contexts (Chat/WS/Marketplace)
## MCP + RAG conventions
- MCP servers live in mcp.json (tools/prompts) and mcp-rag.json (RAG-only inventory). Fields: groups, transport|type, url|command/cwd, compliance_level.
- Transport detection order: explicit transport → command (stdio) → URL protocol (http/sse) → type fallback.
- Tool names exposed to LLM are fully-qualified: server_toolName. canvas_canvas is a pseudo-tool always available.
- Tool names exposed to LLM are fully-qualified: server_toolName. "canvas_canvas" is a pseudo-tool always available.
- RAG over MCP tools expected: rag_discover_resources, rag_get_raw_results, optional rag_get_synthesized_results. RAG resources and servers may include complianceLevel.
- When testing or developing MCP-related features, example configurations can be found in config/mcp-example-configs/ with individual mcp-{servername}.json files for testing individual servers.

## Compliance levels (explicit allowlist)
- Definitions in config/(overrides|defaults)/compliance-levels.json. core/compliance.py loads, normalizes aliases, and enforces allowed_with.
Expand Down Expand Up @@ -72,4 +73,4 @@ Common pitfalls: “uv not found” → install uv; frontend not loading → npm

# Style

No emojis please
No emojis please
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,8 @@ MCP servers defined in `config/defaults/mcp.json`. The backend:
3. Exposes tools to LLM via `ToolManagerProtocol`
4. Supports group-based access control

When testing or developing MCP-related features, example configurations can be found in config/mcp-example-configs/ with individual mcp-{servername}.json files for testing individual servers.

### Agent Modes
Three agent loop strategies implement different reasoning patterns:
- **ReAct** (`backend/application/chat/agent/react_loop.py`): Reason-Act-Observe cycle, good for tool-heavy tasks with structured reasoning
Expand Down
6 changes: 4 additions & 2 deletions GEMINI.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ python main.py # NEVER use uvicorn --reload (causes problems)
- **No Emojis**: No emojis should ever be added in this repo.
- **Linting**: Run `ruff check backend/` for Python and `npm run lint` for the frontend before committing.

When testing or developing MCP-related features, example configurations can be found in config/mcp-example-configs/ with individual mcp-{servername}.json files for testing individual servers.

Also read.

Also read.
/workspaces/atlas-ui-3/.github/copilot-instructions.md

and CLAUDE.md
and CLAUDE.md
77 changes: 59 additions & 18 deletions backend/application/chat/utilities/file_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,29 +84,40 @@ async def process_tool_artifacts(
) -> Dict[str, Any]:
"""
Process v2 MCP artifacts produced by a tool and return updated session context.

Pure function that handles tool files without side effects on input context.
"""
if not tool_result.artifacts or not file_manager:
return session_context
# Check if there's an iframe display configuration (no artifacts needed)
has_iframe_display = (
tool_result.display_config and
isinstance(tool_result.display_config, dict) and
tool_result.display_config.get("type") == "iframe" and
tool_result.display_config.get("url")
)

user_email = session_context.get("user_email")
if not user_email:
# Early return only if no artifacts AND no iframe display, or no file_manager
if (not tool_result.artifacts and not has_iframe_display) or not file_manager:
return session_context

# Work with a copy to avoid mutations
updated_context = dict(session_context)

# Process v2 artifacts
updated_context = await ingest_v2_artifacts(
session_context=updated_context,
tool_result=tool_result,
user_email=user_email,
file_manager=file_manager,
update_callback=update_callback
)

# Process v2 artifacts (only if we have artifacts)
if tool_result.artifacts:
user_email = session_context.get("user_email")
if not user_email:
return session_context

updated_context = await ingest_v2_artifacts(
session_context=updated_context,
tool_result=tool_result,
user_email=user_email,
file_manager=file_manager,
update_callback=update_callback
)

# Handle canvas file notifications with v2 display config
# This handles both artifact-based displays and iframe-only displays
await notify_canvas_files_v2(
session_context=updated_context,
tool_result=tool_result,
Expand Down Expand Up @@ -368,17 +379,47 @@ async def notify_canvas_files_v2(
) -> None:
"""
Send v2 canvas files notification with display configuration.

Pure function with no side effects on session context.
"""
if not update_callback or not tool_result.artifacts:
if not update_callback:
return


# Check if there's an iframe display configuration (no artifacts needed)
has_iframe_display = (
tool_result.display_config and
isinstance(tool_result.display_config, dict) and
tool_result.display_config.get("type") == "iframe" and
tool_result.display_config.get("url")
)

# If no artifacts and no iframe display, nothing to show
if not tool_result.artifacts and not has_iframe_display:
return

try:
# Get uploaded file references from session context
uploaded_refs = session_context.get("files", {})
artifact_names = [artifact.get("name") for artifact in tool_result.artifacts if artifact.get("name")]


# Handle iframe-only display (no artifacts)
if has_iframe_display and not artifact_names:
canvas_update = {
"type": "intermediate_update",
"update_type": "canvas_files",
"data": {
"files": [],
"display": tool_result.display_config
}
}
logger.info(
"Emitting canvas_files event for iframe display: url=%s, title=%s",
tool_result.display_config.get("url"),
tool_result.display_config.get("title", "Embedded Content"),
)
await update_callback(canvas_update)
return

if uploaded_refs and artifact_names:
canvas_files = []
for fname in artifact_names:
Expand Down
Loading
Loading