Skip to content

Commit 0ae6e45

Browse files
committed
CM-65100-add-file-support-codex-and-claude
1 parent 39f70a5 commit 0ae6e45

6 files changed

Lines changed: 87 additions & 1 deletion

File tree

cycode/cli/apps/ai_guardrails/ides/claude_code.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ def _read_claude_plugin(plugin_dir: Path) -> tuple[dict, dict]:
188188
servers: dict = mcp_config.get('mcpServers') or {}
189189
if servers:
190190
entry['mcp_server_names'] = list(servers.keys())
191+
entry['mcp_config_file'] = json.dumps(mcp_config)
191192
return entry, servers
192193

193194

cycode/cli/apps/ai_guardrails/ides/codex.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ def _read_codex_plugin(plugin_dir: Path) -> tuple[dict, dict]:
135135
servers = {}
136136
if servers:
137137
entry['mcp_server_names'] = list(servers.keys())
138+
entry['mcp_config_file'] = json.dumps(mcp_doc)
138139
return entry, servers
139140

140141

tests/cli/commands/ai_guardrails/ides/test_claude_code.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@
88
from pytest_mock import MockerFixture
99

1010
from cycode.cli.apps.ai_guardrails.ides.base import HookDecision
11-
from cycode.cli.apps.ai_guardrails.ides.claude_code import ClaudeCode, _email_from_config, load_claude_config
11+
from cycode.cli.apps.ai_guardrails.ides.claude_code import (
12+
ClaudeCode,
13+
_email_from_config,
14+
_read_claude_plugin,
15+
load_claude_config,
16+
)
1217
from cycode.cli.apps.ai_guardrails.scan.types import AiHookEventType
1318

1419

@@ -214,6 +219,36 @@ def test_email_none_when_no_oauth(mocker: MockerFixture) -> None:
214219
assert unified.ide_user_email is None
215220

216221

222+
# _read_claude_plugin
223+
224+
225+
def test_read_claude_plugin_includes_mcp_config_file(tmp_path: Path) -> None:
226+
mcp_content = {'mcpServers': {'aspire': {'command': 'aspire', 'args': ['mcp', 'start']}}}
227+
(tmp_path / '.mcp.json').write_text(json.dumps(mcp_content))
228+
229+
entry, servers = _read_claude_plugin(tmp_path)
230+
231+
assert 'mcp_config_file' in entry
232+
assert json.loads(entry['mcp_config_file']) == mcp_content
233+
assert servers == mcp_content['mcpServers']
234+
235+
236+
def test_read_claude_plugin_no_mcp_config_file_when_no_servers(tmp_path: Path) -> None:
237+
(tmp_path / '.mcp.json').write_text(json.dumps({'mcpServers': {}}))
238+
239+
entry, servers = _read_claude_plugin(tmp_path)
240+
241+
assert 'mcp_config_file' not in entry
242+
assert servers == {}
243+
244+
245+
def test_read_claude_plugin_no_mcp_config_file_when_missing(tmp_path: Path) -> None:
246+
entry, servers = _read_claude_plugin(tmp_path)
247+
248+
assert 'mcp_config_file' not in entry
249+
assert servers == {}
250+
251+
217252
# Session context
218253

219254

tests/cli/commands/ai_guardrails/ides/test_codex.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
_email_from_auth,
1717
_enable_codex_hooks_feature,
1818
_load_codex_config,
19+
_read_codex_plugin,
1920
)
2021
from cycode.cli.apps.ai_guardrails.scan.types import AiHookEventType
2122

@@ -333,3 +334,49 @@ def test_session_context_no_config() -> None:
333334
servers, plugins = Codex().get_session_context()
334335
assert servers == {}
335336
assert plugins == {}
337+
338+
339+
def _write_codex_plugin(plugin_dir: Path, mcp_doc: dict) -> None:
340+
"""Lay out a Codex plugin: manifest referencing .mcp.json + the MCP file itself."""
341+
(plugin_dir / '.codex-plugin').mkdir(parents=True, exist_ok=True)
342+
(plugin_dir / '.codex-plugin' / 'plugin.json').write_text(
343+
json.dumps({'name': 'demo', 'mcpServers': '.mcp.json'})
344+
)
345+
(plugin_dir / '.mcp.json').write_text(json.dumps(mcp_doc))
346+
347+
348+
def test_read_codex_plugin_includes_mcp_config_file(tmp_path: Path) -> None:
349+
mcp_content = {'mcpServers': {'dummy-server': {'command': 'dummy-command', 'args': ['serve']}}}
350+
_write_codex_plugin(tmp_path, mcp_content)
351+
352+
entry, servers = _read_codex_plugin(tmp_path)
353+
354+
assert json.loads(entry['mcp_config_file']) == mcp_content
355+
assert servers == mcp_content['mcpServers']
356+
357+
358+
def test_read_codex_plugin_mcp_config_file_bare_map(tmp_path: Path) -> None:
359+
# Codex MCP files may be a bare {name: cfg} map with no mcpServers wrapper.
360+
mcp_content = {'dummy-server': {'command': 'dummy-command'}}
361+
_write_codex_plugin(tmp_path, mcp_content)
362+
363+
entry, servers = _read_codex_plugin(tmp_path)
364+
365+
assert json.loads(entry['mcp_config_file']) == mcp_content
366+
assert servers == mcp_content
367+
368+
369+
def test_read_codex_plugin_no_mcp_config_file_when_no_servers(tmp_path: Path) -> None:
370+
_write_codex_plugin(tmp_path, {'mcpServers': {}})
371+
372+
entry, servers = _read_codex_plugin(tmp_path)
373+
374+
assert 'mcp_config_file' not in entry
375+
assert servers == {}
376+
377+
378+
def test_read_codex_plugin_no_mcp_config_file_when_no_manifest(tmp_path: Path) -> None:
379+
entry, servers = _read_codex_plugin(tmp_path)
380+
381+
assert 'mcp_config_file' not in entry
382+
assert servers == {}

tests/cli/commands/ai_guardrails/test_session_start_command.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ def test_claude_code_merges_plugin_mcp_servers_and_metadata(
282282
with patch('sys.stdin', new=StringIO(json.dumps(payload))):
283283
session_start_command(mock_ctx, ide='claude-code')
284284

285+
plugin_mcp = {'mcpServers': {'aspire': {'command': 'aspire', 'args': ['mcp', 'start']}}}
285286
mock_ai_client.report_session_context.assert_called_once_with(
286287
mcp_servers={
287288
'gitlab': {'command': 'npx'},
@@ -294,6 +295,7 @@ def test_claude_code_merges_plugin_mcp_servers_and_metadata(
294295
'version': '1.0.28',
295296
'description': 'Shared skills',
296297
'mcp_server_names': ['aspire'],
298+
'mcp_config_file': json.dumps(plugin_mcp),
297299
}
298300
},
299301
user_email=None,

tmp3.json

Whitespace-only changes.

0 commit comments

Comments
 (0)