Skip to content

Commit d293d24

Browse files
authored
Merge branch 'main' into dependabot/npm_and_yarn/docs/npm_and_yarn-e4e91a86d1
2 parents 3a7c012 + 1bd2057 commit d293d24

5 files changed

Lines changed: 484 additions & 44 deletions

File tree

components/runners/ambient-runner/ambient_runner/bridges/claude/bridge.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,12 +218,16 @@ async def _initialize_run(
218218
await populate_mcp_server_credentials(self._context)
219219
self._last_creds_refresh = time.monotonic()
220220

221-
# If the caller changed, destroy the worker and rebuild MCP servers +
222-
# adapter so the new ClaudeSDKClient gets fresh mcp_servers config.
223-
# The session ID is preserved — --resume works because each SDK client
224-
# is a new CLI subprocess that spawns fresh MCP servers from os.environ.
221+
# Rebuild MCP servers when credentials may have changed.
222+
# On first run: always rebuild after credential fetch so credential-based servers
223+
# (e.g. Jira via session endpoint) that were missed during _setup_platform().
224+
# On user change: destroy worker so the new SDK client picks up fresh creds.
225225
user_changed = current_user_id != prev_user
226-
if user_changed and self._session_manager.get_existing(thread_id):
226+
if self._first_run:
227+
self._rebuild_mcp_servers()
228+
self._rebuild_system_prompt()
229+
self._adapter = None
230+
elif user_changed and self._session_manager.get_existing(thread_id):
227231
logger.info(
228232
f"User changed for thread={thread_id}, "
229233
"rebuilding MCP servers and adapter with new credentials"
@@ -719,6 +723,21 @@ def _rebuild_mcp_servers(self) -> None:
719723
self._allowed_tools = build_allowed_tools(self._mcp_servers)
720724
logger.info("Rebuilt MCP servers with updated credentials")
721725

726+
def _rebuild_system_prompt(self) -> None:
727+
"""Rebuild the system prompt with current env vars.
728+
729+
Called on first run after credential refresh so the prompt accurately
730+
reflects which integrations are configured (e.g. Jira via session
731+
endpoint). The initial build in _setup_platform() may have run before
732+
credentials were fully available.
733+
"""
734+
from ambient_runner.bridges.claude.prompts import build_sdk_system_prompt
735+
736+
self._system_prompt = build_sdk_system_prompt(
737+
self._context.workspace_path, self._cwd_path
738+
)
739+
logger.info("Rebuilt system prompt with updated credentials")
740+
722741
# ------------------------------------------------------------------
723742
# Private: adapter lifecycle
724743
# ------------------------------------------------------------------

components/runners/ambient-runner/ambient_runner/bridges/claude/mcp.py

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,24 @@ def build_mcp_servers(
140140
"Added credential MCP servers: %s", list(credential_mcp_servers.keys())
141141
)
142142

143+
# Fallback: add Jira MCP server from env vars when credentials are available but
144+
# no credential binding exists
145+
jira_server_name = _CREDENTIAL_MCP_REGISTRY["jira"]["server_name"]
146+
if (
147+
jira_server_name not in mcp_servers
148+
and os.getenv("JIRA_URL", "").strip()
149+
and os.getenv("JIRA_API_TOKEN", "").strip()
150+
):
151+
jira_entry = _CREDENTIAL_MCP_REGISTRY["jira"]
152+
mcp_servers[jira_server_name] = {
153+
"command": jira_entry["command"],
154+
"args": list(jira_entry["args"]),
155+
"env": {k: _expand_env_vars(v) for k, v in jira_entry["env"].items()},
156+
}
157+
logger.info(
158+
"Added Jira MCP server (credentials available via session endpoint)"
159+
)
160+
143161
# Gerrit MCP server (only if credentials are configured)
144162
gerrit_config = os.environ.get("GERRIT_CONFIG_PATH", "")
145163
if gerrit_config and Path(gerrit_config).exists():
@@ -243,11 +261,15 @@ def _build_sidecar_mcp_servers(credential_mcp_urls_raw: str) -> dict:
243261
try:
244262
credential_mcp_urls = json.loads(credential_mcp_urls_raw)
245263
except (json.JSONDecodeError, TypeError):
246-
logger.warning("Failed to parse CREDENTIAL_MCP_URLS — skipping credential MCP servers")
264+
logger.warning(
265+
"Failed to parse CREDENTIAL_MCP_URLS — skipping credential MCP servers"
266+
)
247267
return {}
248268

249269
if not isinstance(credential_mcp_urls, dict):
250-
logger.warning("CREDENTIAL_MCP_URLS is not a JSON object — skipping credential MCP servers")
270+
logger.warning(
271+
"CREDENTIAL_MCP_URLS is not a JSON object — skipping credential MCP servers"
272+
)
251273
return {}
252274

253275
servers: dict = {}
@@ -298,7 +320,11 @@ def _wait_for_sidecar_readiness(
298320
if not endpoints:
299321
return
300322

301-
logger.info("Waiting for %d credential sidecar(s) to become ready (timeout=%ds)", len(endpoints), int(timeout))
323+
logger.info(
324+
"Waiting for %d credential sidecar(s) to become ready (timeout=%ds)",
325+
len(endpoints),
326+
int(timeout),
327+
)
302328
deadline = time.monotonic() + timeout
303329
pending = list(endpoints)
304330

@@ -307,7 +333,9 @@ def _wait_for_sidecar_readiness(
307333
for name, host, port in pending:
308334
try:
309335
with socket.create_connection((host, port), timeout=1.0):
310-
logger.info("Credential sidecar %s ready at %s:%d", name, host, port)
336+
logger.info(
337+
"Credential sidecar %s ready at %s:%d", name, host, port
338+
)
311339
except (ConnectionRefusedError, OSError, socket.timeout):
312340
still_pending.append((name, host, port))
313341
pending = still_pending
@@ -316,7 +344,9 @@ def _wait_for_sidecar_readiness(
316344

317345
if pending:
318346
names = [p[0] for p in pending]
319-
logger.warning("Credential sidecar(s) not ready after %ds: %s", int(timeout), names)
347+
logger.warning(
348+
"Credential sidecar(s) not ready after %ds: %s", int(timeout), names
349+
)
320350

321351

322352
def _build_subprocess_mcp_servers() -> dict:

components/runners/ambient-runner/ambient_runner/bridges/gemini_cli/system_prompt.py

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -117,16 +117,31 @@ def _build_system_prompt(cwd_path: str) -> str:
117117
"""Build the full system.md content string."""
118118
from ambient_runner.platform.config import get_repos_config, load_ambient_config
119119
from ambient_runner.platform.prompts import (
120-
GITHUB_TOKEN_PROMPT,
121-
GITLAB_TOKEN_PROMPT,
122120
GIT_PUSH_INSTRUCTIONS_BODY,
123121
GIT_PUSH_INSTRUCTIONS_HEADER,
122+
GIT_PUSH_MCP_STEPS,
124123
GIT_PUSH_STEPS,
125-
MCP_INTEGRATIONS_PROMPT,
126124
WORKSPACE_FIXED_PATHS_PROMPT,
125+
_build_integrations_prompt,
126+
_detect_github,
127127
)
128128
from ambient_runner.platform.utils import derive_workflow_name
129129

130+
# Detect GitHub mode once so the git-push step selection is consistent
131+
# with what _build_integrations_prompt() will tell the model to use.
132+
_cmu: dict = {}
133+
_cmu_raw = os.getenv("CREDENTIAL_MCP_URLS", "").strip()
134+
if _cmu_raw:
135+
try:
136+
import json as _json
137+
138+
_parsed = _json.loads(_cmu_raw)
139+
if isinstance(_parsed, dict):
140+
_cmu = _parsed
141+
except (ValueError, TypeError):
142+
pass
143+
github_mode = _detect_github(_cmu)
144+
130145
# Pull in Gemini's dynamically-built default sections via variable substitution.
131146
# These are expanded at runtime by the CLI — no static text to maintain.
132147
sections = [
@@ -173,7 +188,8 @@ def _build_system_prompt(cwd_path: str) -> str:
173188
sections.append(GIT_PUSH_INSTRUCTIONS_BODY)
174189
for r in auto_push:
175190
sections.append(f"- **repos/{r.get('name', 'unknown')}/**")
176-
sections.append(GIT_PUSH_STEPS.format(branch=branch))
191+
push_steps = GIT_PUSH_MCP_STEPS if github_mode == "mcp" else GIT_PUSH_STEPS
192+
sections.append(push_steps.format(branch=branch))
177193

178194
# ---- Workflow directory ----
179195
if active_workflow_url:
@@ -200,14 +216,8 @@ def _build_system_prompt(cwd_path: str) -> str:
200216
except Exception as exc:
201217
logger.warning("Could not list uploaded files in %s: %s", uploads, exc)
202218

203-
# ---- MCP integration hints ----
204-
sections.append(MCP_INTEGRATIONS_PROMPT)
205-
206-
# ---- Token visibility ----
207-
if os.getenv("GITHUB_TOKEN"):
208-
sections.append(GITHUB_TOKEN_PROMPT)
209-
if os.getenv("GITLAB_TOKEN"):
210-
sections.append(GITLAB_TOKEN_PROMPT)
219+
# ---- Integration status — conditional on actual credential state ----
220+
sections.append(_build_integrations_prompt())
211221

212222
# ---- Workflow custom instructions ----
213223
ambient_config: dict = {}

0 commit comments

Comments
 (0)