MCUP is a fork of the Model Context Protocol SDK that adds CLI-based user confirmation for mutating tool call actions (e.g., tools with names containing 'write', 'delete', 'update', 'create', 'modify'). Other actions like initialization and tool listing proceed without prompts.
pip install mcup
Alternatively, clone the repository and install in editable mode:
git clone https://github.com/fezhou2/mcup
cd mcup
pip install -e .
Use MCUPSession
with approval_mode='cli'
to enable CLI prompts for mutating tool calls. The example below uses a simple server provided in the repository, which supports fetch
(non-mutating, fetches website content) and write_data
(mutating, simulates writing data).
Start the server in a separate terminal:
cd examples/servers/simple-tool
export UV_INDEX=""
python3 -m mcp_simple_tool --transport stdio
Run the client to interact with the server:
import asyncio
from mcup.client.stdio import stdio_client
from mcup.shared.stdio import StdioServerParameters
from mcup import types
async def main():
server_params = StdioServerParameters(
command="python3",
args=["-m", "examples.servers.simple-tool.mcp_simple_tool", "--transport", "stdio"],
env={"UV_INDEX": ""},
)
async with stdio_client(server_params, approval_mode='cli') as session:
# List available tools
tools = await session.list_tools()
print(f"Available tools: {[t.name for t in tools.tools]}")
# Call fetch tool (non-mutating, no prompt)
result = await session.call_tool("fetch", {"url": "https://example.com"})
result_unstructured = result.content[0]
if isinstance(result_unstructured, types.TextContent):
print(f"Tool result (fetch): {result_unstructured.text[:100]}...")
print(f"Structured tool result (fetch): {result.structuredContent}")
# Call write_data tool (mutating, requires CLI approval)
result = await session.call_tool("write_data", {"data": "example"})
result_unstructured = result.content[0]
if isinstance(result_unstructured, types.TextContent):
print(f"Tool result (write_data): {result_unstructured.text}")
print(f"Structured tool result (write_data): {result.structuredContent}")
asyncio.run(main())
Expected Output:
Session initialized successfully
Available tools: ['fetch', 'write_data']
Tool result (fetch): <!doctype html><html><head> <title>Example Domain</title>...
Structured tool result (fetch): {'url': 'https://example.com'}
Approve MCUP tool call?
Details: {'tool_name': 'write_data', 'arguments': {'data': 'example'}}
(y/n): y
Tool result (write_data): Data written: example
Structured tool result (write_data): {'status': 'success', 'data': 'example'}
To verify the mcup
package’s functionality, including CLI approval for mutating tools, you can run the provided test suite.
-
Install dependencies and the
mcup
package:cd /path/to/mcup python3 -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate pip install -r requirements.txt pip install -e .
-
Start the server in a separate terminal:
cd /path/to/mcup/examples/servers/simple-tool export UV_INDEX="" python3 -m mcp_simple_tool --transport stdio
-
Run the tests:
cd /path/to/mcup pytest tests/client/test_stdio_client.py -v
- When prompted (
Approve action for write_data? [y/n]:
), typey
and press Enter.
- When prompted (
The test suite (tests/client/test_stdio_client.py
) verifies session initialization, tool listing, and the fetch
and write_data
tools. Below is an excerpt:
import asyncio
import pytest
from mcup.client.stdio import stdio_client
from mcup.shared.stdio import StdioServerParameters
from mcup import types
@pytest.fixture(scope="session")
def server_params():
"""Provide Stdio server parameters for all tests."""
return StdioServerParameters(
command="python3",
args=["-m", "examples.servers.simple-tool.mcp_simple_tool", "--transport", "stdio"],
env={"UV_INDEX": ""},
)
@pytest.mark.asyncio
async def test_list_tools(server_params):
"""Verify list_tools includes fetch and write_data."""
async with stdio_client(server_params, approval_mode="cli") as session:
tools = await session.list_tools()
assert isinstance(tools.tools, list), "list_tools should return a list"
assert "fetch" in [t.name for t in tools.tools], "fetch tool should be available"
assert "write_data" in [t.name for t in tools.tools], "write_data tool should be available"
print("Available tools:", [t.name for t in tools.tools])
@pytest.mark.asyncio
async def test_write_data_tool(server_params):
"""Test the write_data tool (mutating, with CLI prompt)."""
async with stdio_client(server_params, approval_mode="cli") as session:
# Enter 'y' at the CLI prompt when running
result = await session.call_tool("write_data", arguments={"data": "example"})
assert result.content, "Tool call should return content"
result_unstructured = result.content[0]
assert isinstance(result_unstructured, types.TextContent), "Result should be TextContent"
assert "Data written" in result_unstructured.text, "Write_data result should include Data written"
assert result.structuredContent == {"status": "success", "data": "example"}, "Structured result should match"
print(f"Tool result (write_data): {result_unstructured.text}")
Expected Test Output:
============================= test session starts =============================
collected 4 items
tests/client/test_stdio_client.py::test_initialize_session PASSED [ 25%]
tests/client/test_stdio_client.py::test_list_tools PASSED [ 50%]
Available tools: ['fetch', 'write_data']
tests/client/test_stdio_client.py::test_fetch_tool PASSED [ 75%]
Tool result (fetch): <!doctype html><html><head> <title>Example Domain</title>...
tests/client/test_stdio_client.py::test_write_data_tool PASSED [100%]
Approve action for write_data? [y/n]: y
Tool result (write_data): Data written: example
============================= 4 passed in 0.XXs =============================
Based on the Model Context Protocol SDK by Anthropic, originally maintained by:
- David Soria Parra ([email protected])
- Justin Spahr-Summers ([email protected])
MIT License (see LICENSE file).
MCP protocol with user confirmation before action.
No releases published
No packages published
- Python 100.0%
© 2025 GitHub, Inc.