From 783c0f4d332618e4f15749b136703d8ae0a3ba8e Mon Sep 17 00:00:00 2001 From: Lina Tawfik Date: Thu, 3 Jul 2025 14:51:04 -0700 Subject: [PATCH] feat: add strict_mcp_config option to Python SDK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add strict_mcp_config boolean field to ClaudeCodeOptions - Pass --strict-mcp-config CLI flag when option is True - Add tests for the new option - Add example demonstrating usage - Update README with MCP server configuration docs This enables SDK users to ignore all file-based MCP configurations and use only programmatically specified servers. Fixes #45 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- README.md | 26 ++++++++++++ examples/strict_mcp_config_example.py | 41 +++++++++++++++++++ .../_internal/transport/subprocess_cli.py | 3 ++ src/claude_code_sdk/types.py | 1 + tests/test_transport.py | 21 ++++++++++ tests/test_types.py | 9 ++++ 6 files changed, 101 insertions(+) create mode 100644 examples/strict_mcp_config_example.py diff --git a/README.md b/README.md index fd91924..9141fbc 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,32 @@ options = ClaudeCodeOptions( ) ``` +### MCP Servers + +```python +# Configure MCP servers programmatically +options = ClaudeCodeOptions( + mcp_servers={ + "memory-server": { + "command": "npx", + "args": ["@modelcontextprotocol/server-memory"] + } + } +) + +# Use strict MCP config to ignore all file-based configurations +# This ensures ONLY your programmatically specified servers are used +options = ClaudeCodeOptions( + mcp_servers={ + "my-server": { + "command": "node", + "args": ["my-mcp-server.js"] + } + }, + strict_mcp_config=True # Ignore global/project MCP settings +) +``` + ## API Reference ### `query(prompt, options=None)` diff --git a/examples/strict_mcp_config_example.py b/examples/strict_mcp_config_example.py new file mode 100644 index 0000000..e604353 --- /dev/null +++ b/examples/strict_mcp_config_example.py @@ -0,0 +1,41 @@ +"""Example demonstrating how to use strict MCP config with Claude SDK. + +This example shows how to use the strict_mcp_config option to ensure +only your programmatically specified MCP servers are used, ignoring +any global or project-level MCP configurations. +""" + +from claude_code_sdk import ClaudeCodeSDK, ClaudeCodeOptions + +async def main(): + # Create options with strict MCP config enabled + # This ensures ONLY the MCP servers specified here will be used + options = ClaudeCodeOptions( + mcp_servers={ + "my-custom-server": { + "command": "npx", + "args": ["@modelcontextprotocol/server-memory"], + } + }, + strict_mcp_config=True, # Ignore all file-based MCP configurations + ) + + # Create SDK instance + sdk = ClaudeCodeSDK() + + # Query Claude with strict MCP config + async with await sdk.query( + "List the available MCP tools from the memory server", + options=options + ) as session: + async for message in session.stream(): + if message.type == "assistant": + print(f"Claude: {message.message.content}") + elif message.type == "result": + print(f"\nResult: {message.subtype}") + if message.result: + print(f"Final output: {message.result}") + +if __name__ == "__main__": + import asyncio + asyncio.run(main()) \ No newline at end of file diff --git a/src/claude_code_sdk/_internal/transport/subprocess_cli.py b/src/claude_code_sdk/_internal/transport/subprocess_cli.py index f4fbc58..2915ac8 100644 --- a/src/claude_code_sdk/_internal/transport/subprocess_cli.py +++ b/src/claude_code_sdk/_internal/transport/subprocess_cli.py @@ -116,6 +116,9 @@ class SubprocessCLITransport(Transport): ["--mcp-config", json.dumps({"mcpServers": self._options.mcp_servers})] ) + if self._options.strict_mcp_config: + cmd.append("--strict-mcp-config") + cmd.extend(["--print", self._prompt]) return cmd diff --git a/src/claude_code_sdk/types.py b/src/claude_code_sdk/types.py index bd3c726..5d2ce95 100644 --- a/src/claude_code_sdk/types.py +++ b/src/claude_code_sdk/types.py @@ -127,3 +127,4 @@ class ClaudeCodeOptions: model: str | None = None permission_prompt_tool_name: str | None = None cwd: str | Path | None = None + strict_mcp_config: bool = False diff --git a/tests/test_transport.py b/tests/test_transport.py index 65702bc..c2322d8 100644 --- a/tests/test_transport.py +++ b/tests/test_transport.py @@ -132,3 +132,24 @@ class TestSubprocessCLITransport: # So we just verify the transport can be created and basic structure is correct assert transport._prompt == "test" assert transport._cli_path == "/usr/bin/claude" + + def test_build_command_with_strict_mcp_config(self): + """Test building CLI command with strict MCP config.""" + transport = SubprocessCLITransport( + prompt="test", + options=ClaudeCodeOptions(strict_mcp_config=True), + cli_path="/usr/bin/claude", + ) + + cmd = transport._build_command() + assert "--strict-mcp-config" in cmd + + # Test that flag is not present when False + transport_no_strict = SubprocessCLITransport( + prompt="test", + options=ClaudeCodeOptions(strict_mcp_config=False), + cli_path="/usr/bin/claude", + ) + + cmd_no_strict = transport_no_strict._build_command() + assert "--strict-mcp-config" not in cmd_no_strict diff --git a/tests/test_types.py b/tests/test_types.py index 6046292..794266c 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -105,3 +105,12 @@ class TestOptions: ) assert options.model == "claude-3-5-sonnet-20241022" assert options.permission_prompt_tool_name == "CustomTool" + + def test_claude_code_options_with_strict_mcp_config(self): + """Test Options with strict MCP config.""" + options = ClaudeCodeOptions(strict_mcp_config=True) + assert options.strict_mcp_config is True + + # Test default value + default_options = ClaudeCodeOptions() + assert default_options.strict_mcp_config is False