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
34 changes: 34 additions & 0 deletions backend/routes/config_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@

router = APIRouter(prefix="/api", tags=["config"])

# Canvas tool description constant
CANVAS_TOOL_DESCRIPTION = (
"Display final rendered content in a visual canvas panel. "
"Use this for: 1) Complete code (not code discussions), "
"2) Final reports/documents (not report discussions), "
"3) Data visualizations, 4) Any polished content that should be "
"viewed separately from the conversation."
)


@router.get("/banners")
async def get_banners(current_user: str = Depends(get_current_user)):
Expand Down Expand Up @@ -126,6 +135,20 @@ async def get_config(
tools_info.append({
'server': 'canvas',
'tools': ['canvas'],
'tools_detailed': [{
'name': 'canvas',
'description': CANVAS_TOOL_DESCRIPTION,
'inputSchema': {
'type': 'object',
'properties': {
'content': {
'type': 'string',
'description': 'The content to display in the canvas. Can be markdown, code, or plain text.'
}
},
'required': ['content']
}
}],
'tool_count': 1,
'description': 'Canvas for showing final rendered content: complete code, reports, and polished documents. Use this to finalize your work. Most code and reports will be shown here.',
'is_exclusive': False,
Expand All @@ -140,9 +163,20 @@ async def get_config(

# Only include servers that have tools and user has access to
if server_tools: # Only show servers with actual tools
# Build detailed tool information including descriptions and input schemas
tools_detailed = []
for tool in server_tools:
tool_detail = {
'name': tool.name,
'description': tool.description or '',
'inputSchema': getattr(tool, 'inputSchema', {}) or {}
}
tools_detailed.append(tool_detail)

tools_info.append({
'server': server_name,
'tools': [tool.name for tool in server_tools],
'tools_detailed': tools_detailed,
'tool_count': len(server_tools),
'description': server_config.get('description', f'{server_name} tools'),
'is_exclusive': server_config.get('is_exclusive', False),
Expand Down
110 changes: 110 additions & 0 deletions backend/tests/test_tool_details_in_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
"""Test that tool details (description and inputSchema) are included in config API response."""

import pytest
import sys
import os

# Ensure backend is on path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))

from modules.mcp_tools.client import MCPToolManager


class FakeTool:
"""Mock tool object for testing."""
def __init__(self, name, description="", inputSchema=None):
self.name = name
self.description = description
self.inputSchema = inputSchema or {"type": "object", "properties": {}}


@pytest.fixture
def mock_mcp_manager(monkeypatch):
"""Create a mock MCP manager with test data."""
manager = MCPToolManager()

# Mock available_tools with detailed tool information
manager.available_tools = {
"test_server": {
"tools": [
FakeTool(
"test_tool",
"This is a test tool description",
{
"type": "object",
"properties": {
"arg1": {
"type": "string",
"description": "First argument"
},
"arg2": {
"type": "number",
"description": "Second argument"
}
},
"required": ["arg1"]
}
)
],
"config": {
"description": "Test server",
"is_exclusive": False,
"author": "Test Author"
}
}
}

manager.available_prompts = {}
return manager


def test_tools_detailed_includes_description_and_schema(mock_mcp_manager):
"""Test that tools_detailed field contains description and inputSchema."""
server_tools = mock_mcp_manager.available_tools["test_server"]["tools"]
server_config = mock_mcp_manager.available_tools["test_server"]["config"]

Check notice

Code scanning / CodeQL

Unused local variable Note test

Variable server_config is not used.

Copilot Autofix

AI 23 days ago

The best way to fix this problem is to simply remove the assignment to server_config at line 64. Since the assigned value is not used and the assignment itself does not cause any side effects, this removal is safe and will not affect the functionality of the test. The removal should only affect this single line in the code (server_config = ...), and no imports or further changes are needed.


Suggested changeset 1
backend/tests/test_tool_details_in_config.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/backend/tests/test_tool_details_in_config.py b/backend/tests/test_tool_details_in_config.py
--- a/backend/tests/test_tool_details_in_config.py
+++ b/backend/tests/test_tool_details_in_config.py
@@ -61,7 +61,6 @@
 def test_tools_detailed_includes_description_and_schema(mock_mcp_manager):
     """Test that tools_detailed field contains description and inputSchema."""
     server_tools = mock_mcp_manager.available_tools["test_server"]["tools"]
-    server_config = mock_mcp_manager.available_tools["test_server"]["config"]
     
     # Simulate what the config endpoint does
     tools_detailed = []
EOF
@@ -61,7 +61,6 @@
def test_tools_detailed_includes_description_and_schema(mock_mcp_manager):
"""Test that tools_detailed field contains description and inputSchema."""
server_tools = mock_mcp_manager.available_tools["test_server"]["tools"]
server_config = mock_mcp_manager.available_tools["test_server"]["config"]

# Simulate what the config endpoint does
tools_detailed = []
Copilot is powered by AI and may make mistakes. Always verify output.

# Simulate what the config endpoint does
tools_detailed = []
for tool in server_tools:
tool_detail = {
'name': tool.name,
'description': tool.description or '',
'inputSchema': getattr(tool, 'inputSchema', {}) or {}
}
tools_detailed.append(tool_detail)

# Verify the structure
assert len(tools_detailed) == 1
assert tools_detailed[0]['name'] == 'test_tool'
assert tools_detailed[0]['description'] == 'This is a test tool description'
assert 'inputSchema' in tools_detailed[0]
assert 'properties' in tools_detailed[0]['inputSchema']
assert 'arg1' in tools_detailed[0]['inputSchema']['properties']
assert tools_detailed[0]['inputSchema']['properties']['arg1']['type'] == 'string'
assert tools_detailed[0]['inputSchema']['properties']['arg1']['description'] == 'First argument'


def test_canvas_tool_has_detailed_info():
"""Test that canvas pseudo-tool has detailed information."""
canvas_tools_detailed = [{
'name': 'canvas',
'description': 'Display final rendered content in a visual canvas panel. Use this for: 1) Complete code (not code discussions), 2) Final reports/documents (not report discussions), 3) Data visualizations, 4) Any polished content that should be viewed separately from the conversation.',
'inputSchema': {
'type': 'object',
'properties': {
'content': {
'type': 'string',
'description': 'The content to display in the canvas. Can be markdown, code, or plain text.'
}
},
'required': ['content']
}
}]

# Verify canvas tool structure
assert len(canvas_tools_detailed) == 1
assert canvas_tools_detailed[0]['name'] == 'canvas'
assert 'description' in canvas_tools_detailed[0]
assert len(canvas_tools_detailed[0]['description']) > 0
assert 'inputSchema' in canvas_tools_detailed[0]
assert 'content' in canvas_tools_detailed[0]['inputSchema']['properties']
Loading
Loading