Skip to content

Commit e3f9f75

Browse files
authored
Merge pull request #94 from undoio/explain-additional-copilot-fixes
Explain additional Copilot CLI fixes
2 parents e33f0d9 + 5028654 commit e3f9f75

File tree

1 file changed

+16
-16
lines changed

1 file changed

+16
-16
lines changed

explain/copilot_cli_agent.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
"""
44

55
import asyncio
6+
import contextlib
67
import itertools
78
import json
89
import os
910
from dataclasses import dataclass
1011
from pathlib import Path
1112
from typing import ClassVar
1213

13-
from src.udbpy.fileutil import mkdtemp
14+
from src.udbpy.fileutil import mkdtemp, mkstemp
1415

1516
from .agents import BaseAgent
1617
from .assets import FRAMING_PROMPT, SYSTEM_PROMPT
@@ -78,23 +79,23 @@ async def ask(self, question: str, port: int, tools: list[str]) -> str:
7879
if self.log_level == "DEBUG":
7980
print(f"Connecting Copilot CLI to MCP server on port {port}")
8081

81-
copilot_config = {
82+
mcp_config = {
8283
"mcpServers": {
8384
"UDB_Server": {"type": "sse", "url": f"http://localhost:{port}/sse", "tools": ["*"]}
8485
}
8586
}
8687

88+
# We always need to re-create the MCP config as the dynamically allocated port may change
89+
# between invocations of the tool.
90+
mcp_config_fd, mcp_config_path = mkstemp(prefix="mcp_config", suffix=".json")
91+
with contextlib.closing(os.fdopen(mcp_config_fd, "w")) as mcp_config_file:
92+
json.dump(mcp_config, mcp_config_file)
93+
8794
# We run Copilot CLI with a temporary "home directory" so that we can apply a temporary MCP
8895
# configuration and rely on "--resume" finding our previous session automatically.
8996
if not self._tempdir:
9097
self._tempdir = mkdtemp(prefix="udb_explain_copilot_home")
9198

92-
# We always need to re-create the MCP config as the dynamically allocated port may change
93-
# between invocations of the tool.
94-
config_dir = self._tempdir / ".copilot"
95-
config_dir.mkdir(exist_ok=True)
96-
(config_dir / "mcp-config.json").write_text(json.dumps(copilot_config) + "\n")
97-
9899
result = ""
99100
copilot = None
100101

@@ -105,17 +106,12 @@ async def ask(self, question: str, port: int, tools: list[str]) -> str:
105106
prompt = question
106107

107108
allowed_tools = ["UDB_Server", "shell(grep)", "shell(find)", "shell(cat)", "shell(xargs)"]
108-
env_changes = {
109-
"XDG_CONFIG_HOME": str(self._tempdir),
110-
"XDG_STATE_HOME": str(self._tempdir),
111-
}
112-
if runtime_dir := os.environ.get("XDG_RUNTIME_DIR"):
113-
# Pass through runtime state so it has auth access.
114-
env_changes["XDG_RUNTIME_DIR"] = runtime_dir
115109

116110
try:
117111
copilot = await asyncio.create_subprocess_exec(
118112
str(self.agent_bin),
113+
"--additional-mcp-config",
114+
f"@{mcp_config_path}",
119115
# We can resume unambiguously without specifying a session ID because we're using a
120116
# temporary home directory for the state generated in this session.
121117
*(["--resume"] if self._resume else []),
@@ -127,7 +123,11 @@ async def ask(self, question: str, port: int, tools: list[str]) -> str:
127123
"claude-sonnet-4.5",
128124
"-p",
129125
prompt,
130-
env=os.environ.copy().update(env_changes),
126+
env={
127+
**os.environ,
128+
# Ensure state files are stored in our temporary home, so that resuming works.
129+
"XDG_STATE_HOME": str(self._tempdir),
130+
},
131131
stdin=asyncio.subprocess.PIPE,
132132
stdout=asyncio.subprocess.PIPE,
133133
stderr=asyncio.subprocess.PIPE,

0 commit comments

Comments
 (0)