Skip to content

Commit ad9eea6

Browse files
authored
Merge pull request #44 from DerekMelchin/bug-docker-mount
Mount local organization workspace
2 parents f4b37e8 + c68069a commit ad9eea6

File tree

3 files changed

+83
-0
lines changed

3 files changed

+83
-0
lines changed

src/main.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from tools.lean_versions import register_lean_version_tools
1616
from tools.ai import register_ai_tools
1717
from tools.mcp_server_version import register_mcp_server_version_tools
18+
from organization_workspace import OrganizationWorkspace
1819

1920
transport = os.getenv('MCP_TRANSPORT', 'stdio')
2021

@@ -45,5 +46,7 @@
4546
f(mcp)
4647

4748
if __name__ == "__main__":
49+
# Load the organization workspace.
50+
OrganizationWorkspace.load()
4851
# Run the server.
4952
mcp.run(transport=transport)

src/organization_workspace.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import os
2+
import json
3+
4+
5+
class OrganizationWorkspace:
6+
7+
# Load mount destination and source from environment variables.
8+
MOUNT_SOURCE = os.getenv('MOUNT_SOURCE_PATH')
9+
MOUNT_DESTINATION = os.getenv('MOUNT_DST_PATH')
10+
11+
available = False # Indicate if local disk access is available
12+
project_id_by_path = {}
13+
14+
@classmethod
15+
def load(cls):
16+
if not (cls.MOUNT_SOURCE and cls.MOUNT_DESTINATION):
17+
return
18+
if not os.path.exists(cls.MOUNT_DESTINATION):
19+
return
20+
for name in os.listdir(cls.MOUNT_DESTINATION):
21+
if name in ['.QuantConnect', 'data', 'lean.json']:
22+
continue
23+
cls._process_directory(os.path.join(cls.MOUNT_DESTINATION, name))
24+
cls.available = True
25+
26+
@classmethod
27+
def _process_directory(cls, path):
28+
# If the current directory contains a config.json file, then
29+
# it's a project, so save it's Id and path.
30+
config_path = os.path.join(path, 'config.json')
31+
if os.path.isfile(config_path):
32+
with open(config_path, 'r') as f:
33+
config_data = json.load(f)
34+
if 'cloud-id' in config_data:
35+
cls.project_id_by_path[path] = config_data['cloud-id']
36+
# Otherwise, it's a directory of projects, so recurse.
37+
else:
38+
for dir_name in os.listdir(path):
39+
sub_path = os.path.join(path, dir_name)
40+
if os.path.isdir(sub_path):
41+
cls._process_directory(sub_path)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import pytest
2+
import time
3+
import docker
4+
5+
from organization_workspace import OrganizationWorkspace
6+
from api_connection import USER_ID, API_TOKEN
7+
8+
def test_organization_workspace_mount():
9+
# Ensure the MOUNT_SOURCE environment variable is set.
10+
assert OrganizationWorkspace.MOUNT_SOURCE, 'MOUNT_SOURCE env var is not set.'
11+
# Create a Docker client.
12+
client = docker.from_env()
13+
# Start MCP Server inside a container.
14+
container = client.containers.run(
15+
image='quantconnect/mcp-server',
16+
environment={
17+
'QUANTCONNECT_USER_ID': USER_ID,
18+
'QUANTCONNECT_API_TOKEN': API_TOKEN
19+
},
20+
platform='linux/amd64',
21+
volumes={
22+
OrganizationWorkspace.MOUNT_SOURCE: {
23+
'bind': OrganizationWorkspace.MOUNT_DESTINATION, 'mode': 'ro'}
24+
},
25+
detach=True, # Run in background
26+
auto_remove=True # Equivalent to --rm
27+
)
28+
# Wait for the container to start running.
29+
time.sleep(5)
30+
# Check if the expected mount exists.
31+
assert any(
32+
mount['Type'] == 'bind' and
33+
mount['Source'] == OrganizationWorkspace.MOUNT_SOURCE and
34+
mount['Destination'] == OrganizationWorkspace.MOUNT_DESTINATION and
35+
mount['Mode'] == 'ro'
36+
for mount in container.attrs['Mounts']
37+
)
38+
# Stop the container.
39+
container.stop()

0 commit comments

Comments
 (0)