From 2cbcd4c85418747e3805fc7219c12908ca03a175 Mon Sep 17 00:00:00 2001 From: Vatche Isahagian Date: Mon, 26 Jan 2026 15:12:40 -0500 Subject: [PATCH 1/5] Add create_entity and delete_entity MCP tools - Add create_entity tool to create single entities with optional conflict resolution - Add delete_entity tool to delete entities by ID - Add 7 comprehensive test cases for new tools - Update README.md to document new MCP tools - All 172 tests passing Fixes #34 --- README.md | 2 + kaizen/frontend/mcp/mcp_server.py | 85 ++++++++++++++++ tests/e2e/test_mcp.py | 162 +++++++++++++++++++++++++++++- 3 files changed, 248 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cf7de604..9446a131 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,8 @@ npx @modelcontextprotocol/inspector@latest http://127.0.0.1:8201/sse --cli --met **Available tools:** - `get_guidelines(task: str)`: Get relevant guidelines for a specific task. - `save_trajectory(trajectory_data: str, task_id: str | None)`: Save a conversation trajectory and generate new tips. +- `create_entity(content: str, entity_type: str, metadata: str | None, enable_conflict_resolution: bool)`: Create a single entity in the namespace. +- `delete_entity(entity_id: str)`: Delete a specific entity by its ID. ## Documentation diff --git a/kaizen/frontend/mcp/mcp_server.py b/kaizen/frontend/mcp/mcp_server.py index 3cbd07e8..c58a097d 100644 --- a/kaizen/frontend/mcp/mcp_server.py +++ b/kaizen/frontend/mcp/mcp_server.py @@ -114,3 +114,88 @@ def save_trajectory( filters={"type": "trajectory", "task_id": task_id}, limit=1000, ) + + +@mcp.tool() +def create_entity( + content: str, + entity_type: str, + metadata: str | None = None, + enable_conflict_resolution: bool = False +) -> str: + """ + Create a single entity in the namespace. + + Args: + content: The searchable text or structured data for the entity + entity_type: The type/category of the entity (e.g., 'guideline', 'note', 'fact') + metadata: Optional JSON string containing arbitrary metadata related to the entity + enable_conflict_resolution: If True, uses LLM to check for conflicts with existing entities + + Returns: + JSON string with the entity update details (ADD/UPDATE/DELETE/NONE) and entity ID + """ + logger.info(f"Creating entity of type: {entity_type}") + ensure_namespace() + + # Parse metadata if provided + metadata_dict = json.loads(metadata) if metadata else None + + # Create the entity using the Entity schema + entity = Entity( + type=entity_type, + content=content, + metadata=metadata_dict + ) + + # Use KaizenClient.update_entities() to create the entity + updates = client.update_entities( + namespace_id=kaizen_config.namespace_id, + entities=[entity], + enable_conflict_resolution=enable_conflict_resolution + ) + + # Return the first (and only) update result + if updates: + update = updates[0] + return json.dumps({ + "event": update.event, + "id": update.id, + "type": update.type, + "content": update.content, + "metadata": update.metadata + }) + else: + return json.dumps({"error": "Entity creation failed"}) + + +@mcp.tool() +def delete_entity(entity_id: str) -> str: + """ + Delete a specific entity by its ID. + + Args: + entity_id: The unique identifier of the entity to delete + + Returns: + JSON string confirming deletion or error message + """ + logger.info(f"Deleting entity: {entity_id}") + ensure_namespace() + + try: + # Use KaizenClient.delete_entity_by_id() to delete the entity + client.delete_entity_by_id( + namespace_id=kaizen_config.namespace_id, + entity_id=entity_id + ) + return json.dumps({ + "success": True, + "message": f"Entity {entity_id} deleted successfully" + }) + except Exception as e: + logger.error(f"Error deleting entity {entity_id}: {str(e)}") + return json.dumps({ + "success": False, + "error": str(e) + }) diff --git a/tests/e2e/test_mcp.py b/tests/e2e/test_mcp.py index d7dad67d..37cde773 100644 --- a/tests/e2e/test_mcp.py +++ b/tests/e2e/test_mcp.py @@ -37,4 +37,164 @@ async def test_save_trajectory_and_retrieve_guidelines(mcp): 'task': 'What states do I have teammates in? Read the list from the states.txt file. use the filesystem mcp tool' }) guidelines = response.content[0].text - assert '# Guidelines for: ' in guidelines \ No newline at end of file + assert '# Guidelines for: ' in guidelines + + +@pytest.mark.e2e +async def test_create_entity_without_conflict_resolution(mcp): + """Test creating a single entity without conflict resolution.""" + async with Client(transport=mcp) as kaizen_mcp: + response = await kaizen_mcp.call_tool_mcp('create_entity', { + 'content': 'Always use type hints in Python functions', + 'entity_type': 'guideline', + 'metadata': json.dumps({ + 'category': 'code_quality', + 'language': 'python' + }), + 'enable_conflict_resolution': False + }) + + result = json.loads(response.content[0].text) + + # Verify entity was created (ADD event) + assert result['event'] == 'ADD' + assert 'id' in result + assert result['type'] == 'guideline' + assert result['content'] == 'Always use type hints in Python functions' + assert result['metadata']['category'] == 'code_quality' + + +@pytest.mark.e2e +async def test_create_entity_with_conflict_resolution(mcp): + """Test creating an entity with conflict resolution enabled.""" + async with Client(transport=mcp) as kaizen_mcp: + # Create first entity + response1 = await kaizen_mcp.call_tool_mcp('create_entity', { + 'content': 'Use descriptive variable names', + 'entity_type': 'guideline', + 'enable_conflict_resolution': False + }) + result1 = json.loads(response1.content[0].text) + assert result1['event'] == 'ADD' + + # Create similar entity with conflict resolution + response2 = await kaizen_mcp.call_tool_mcp('create_entity', { + 'content': 'Always use descriptive variable names', + 'entity_type': 'guideline', + 'enable_conflict_resolution': True + }) + result2 = json.loads(response2.content[0].text) + + # Should either UPDATE existing or NONE (depending on LLM decision) + assert result2['event'] in ['UPDATE', 'NONE', 'ADD'] + + +@pytest.mark.e2e +async def test_create_entity_without_metadata(mcp): + """Test creating an entity without optional metadata.""" + async with Client(transport=mcp) as kaizen_mcp: + response = await kaizen_mcp.call_tool_mcp('create_entity', { + 'content': 'Simple entity without metadata', + 'entity_type': 'note', + 'enable_conflict_resolution': False + }) + + result = json.loads(response.content[0].text) + + # Verify entity was created + assert result['event'] == 'ADD' + assert 'id' in result + assert result['type'] == 'note' + assert result['content'] == 'Simple entity without metadata' + + +@pytest.mark.e2e +async def test_delete_entity(mcp): + """Test deleting an entity via MCP.""" + async with Client(transport=mcp) as kaizen_mcp: + # Create an entity + create_response = await kaizen_mcp.call_tool_mcp('create_entity', { + 'content': 'Temporary test entity', + 'entity_type': 'test', + 'metadata': json.dumps({'temp': True}), + 'enable_conflict_resolution': False + }) + + created_entity = json.loads(create_response.content[0].text) + entity_id = created_entity['id'] + + # Delete the entity + delete_response = await kaizen_mcp.call_tool_mcp('delete_entity', { + 'entity_id': entity_id + }) + + result = json.loads(delete_response.content[0].text) + + # Verify deletion was successful + assert result['success'] is True + assert entity_id in result['message'] + + +@pytest.mark.e2e +async def test_delete_nonexistent_entity(mcp): + """Test deleting an entity that doesn't exist.""" + async with Client(transport=mcp) as kaizen_mcp: + delete_response = await kaizen_mcp.call_tool_mcp('delete_entity', { + 'entity_id': 'nonexistent-id-12345' + }) + + result = json.loads(delete_response.content[0].text) + + # Should return an error + assert result['success'] is False + assert 'error' in result + + +@pytest.mark.e2e +async def test_create_and_delete_workflow(mcp): + """Test complete workflow: create then delete.""" + async with Client(transport=mcp) as kaizen_mcp: + # Create entity + create_response = await kaizen_mcp.call_tool_mcp('create_entity', { + 'content': 'Workflow test entity', + 'entity_type': 'test', + 'metadata': json.dumps({'workflow': 'test'}), + 'enable_conflict_resolution': False + }) + created = json.loads(create_response.content[0].text) + entity_id = created['id'] + assert created['event'] == 'ADD' + + # Delete entity + delete_response = await kaizen_mcp.call_tool_mcp('delete_entity', { + 'entity_id': entity_id + }) + delete_result = json.loads(delete_response.content[0].text) + assert delete_result['success'] is True + + +@pytest.mark.e2e +async def test_create_multiple_entities_same_type(mcp): + """Test creating multiple entities of the same type.""" + async with Client(transport=mcp) as kaizen_mcp: + entity_ids = [] + + # Create 3 entities + for i in range(3): + response = await kaizen_mcp.call_tool_mcp('create_entity', { + 'content': f'Test guideline number {i}', + 'entity_type': 'guideline', + 'enable_conflict_resolution': False + }) + result = json.loads(response.content[0].text) + assert result['event'] == 'ADD' + entity_ids.append(result['id']) + + # Verify all have unique IDs + assert len(set(entity_ids)) == 3 + + # Clean up + for entity_id in entity_ids: + await kaizen_mcp.call_tool_mcp('delete_entity', { + 'entity_id': entity_id + }) \ No newline at end of file From 21821c813a7f23e08fe79db91d0f47ce590d4e89 Mon Sep 17 00:00:00 2001 From: Vatche Isahagian Date: Mon, 26 Jan 2026 15:42:31 -0500 Subject: [PATCH 2/5] address reviewer issues --- kaizen/frontend/mcp/mcp_server.py | 18 ++++++++++++++---- tests/e2e/test_mcp.py | 22 +++++++++++++++++++++- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/kaizen/frontend/mcp/mcp_server.py b/kaizen/frontend/mcp/mcp_server.py index c58a097d..a924e505 100644 --- a/kaizen/frontend/mcp/mcp_server.py +++ b/kaizen/frontend/mcp/mcp_server.py @@ -13,7 +13,7 @@ from kaizen.frontend.client.kaizen_client import KaizenClient from kaizen.llm.tips.tips import generate_tips from kaizen.schema.core import Entity, RecordedEntity -from kaizen.schema.exceptions import NamespaceNotFoundException +from kaizen.schema.exceptions import KaizenException, NamespaceNotFoundException logging.basicConfig(level=logging.INFO) logger = logging.getLogger("entities-mcp") @@ -139,7 +139,17 @@ def create_entity( ensure_namespace() # Parse metadata if provided - metadata_dict = json.loads(metadata) if metadata else None + metadata_dict = None + if metadata: + try: + metadata_dict = json.loads(metadata) + except json.JSONDecodeError as e: + logger.exception(f"Invalid JSON in metadata parameter: {str(e)}") + return json.dumps({ + "error": "Invalid metadata JSON", + "message": f"Failed to parse metadata: {str(e)}", + "invalid_metadata": metadata + }) # Create the entity using the Entity schema entity = Entity( @@ -193,8 +203,8 @@ def delete_entity(entity_id: str) -> str: "success": True, "message": f"Entity {entity_id} deleted successfully" }) - except Exception as e: - logger.error(f"Error deleting entity {entity_id}: {str(e)}") + except KaizenException as e: + logger.exception(f"Error deleting entity {entity_id}: {str(e)}") return json.dumps({ "success": False, "error": str(e) diff --git a/tests/e2e/test_mcp.py b/tests/e2e/test_mcp.py index 37cde773..68dc0e85 100644 --- a/tests/e2e/test_mcp.py +++ b/tests/e2e/test_mcp.py @@ -197,4 +197,24 @@ async def test_create_multiple_entities_same_type(mcp): for entity_id in entity_ids: await kaizen_mcp.call_tool_mcp('delete_entity', { 'entity_id': entity_id - }) \ No newline at end of file + }) + + +@pytest.mark.e2e +async def test_create_entity_with_invalid_json_metadata(mcp): + """Test creating an entity with invalid JSON metadata.""" + async with Client(transport=mcp) as kaizen_mcp: + response = await kaizen_mcp.call_tool_mcp('create_entity', { + 'content': 'Test entity with bad metadata', + 'entity_type': 'test', + 'metadata': '{invalid json here}', + 'enable_conflict_resolution': False + }) + + result = json.loads(response.content[0].text) + + # Should return an error + assert 'error' in result + assert result['error'] == 'Invalid metadata JSON' + assert 'message' in result + assert 'invalid_metadata' in result \ No newline at end of file From 2016b4cc76b982e14d96a641c1c687a6ffebe43c Mon Sep 17 00:00:00 2001 From: Vatche Isahagian Date: Wed, 28 Jan 2026 20:32:51 -0500 Subject: [PATCH 3/5] Fix: Lazy init for KaizenClient and handle None metadata in Milvus insert --- kaizen/backend/milvus.py | 22 ++++++++-- kaizen/frontend/mcp/mcp_server.py | 30 +++++++++---- tests/e2e/test_mcp.py | 72 ++++++++++++++++++++++++++++--- 3 files changed, 106 insertions(+), 18 deletions(-) diff --git a/kaizen/backend/milvus.py b/kaizen/backend/milvus.py index 4bfa83d9..0e446771 100644 --- a/kaizen/backend/milvus.py +++ b/kaizen/backend/milvus.py @@ -33,8 +33,12 @@ def deserialize_content(content: str): class MilvusEntityBackend(BaseEntityBackend): - milvus = MilvusClient(**milvus_client_settings.model_dump()) - embedding_model = SentenceTransformer(milvus_other_settings.embedding_model) + # Removed class attributes + + def __init__(self, config=None): + super().__init__(config) + self.milvus = MilvusClient(**milvus_client_settings.model_dump()) + self.embedding_model = SentenceTransformer(milvus_other_settings.embedding_model) def ready(self): _ = self.milvus.list_collections() @@ -143,19 +147,21 @@ def update_entities( updates = [] for entity in entities: content_str = serialize_content(entity.content) + # Convert None metadata to empty dict for Milvus compatibility + metadata = entity.metadata if entity.metadata is not None else {} entity_id = str(self.milvus.insert(collection_name=namespace_id, data={ 'type': entity_type, 'content': content_str, 'created_at': int(now.timestamp()), 'embedding': self.embedding_model.encode(content_str), - 'metadata': entity.metadata + 'metadata': metadata })['ids'][0]) updates.append(EntityUpdate( id=entity_id, type=entity_type, content=entity.content, event='ADD', - metadata=entity.metadata + metadata=metadata )) return updates @@ -191,6 +197,14 @@ def delete_entity_by_id(self, namespace_id: str, entity_id: str): self.validate_namespace(namespace_id) self.milvus.delete(collection_name=namespace_id, ids=[entity_id]) + def close(self): + """Close Milvus connection.""" + try: + if hasattr(self, 'milvus'): + self.milvus.close() + except Exception as e: + logger.warning(f"Error closing Milvus client: {e}") + entity_schema = CollectionSchema(fields=[ # Keep it as an INT64 or else you won't be able to list all entities. FieldSchema(name='id', is_primary=True, auto_id=True, dtype=DataType.INT64, max_length=128), diff --git a/kaizen/frontend/mcp/mcp_server.py b/kaizen/frontend/mcp/mcp_server.py index a924e505..c2564f04 100644 --- a/kaizen/frontend/mcp/mcp_server.py +++ b/kaizen/frontend/mcp/mcp_server.py @@ -19,14 +19,26 @@ logger = logging.getLogger("entities-mcp") mcp = FastMCP("entities") -client = KaizenClient() +_client = None + + +def get_client() -> KaizenClient: + """Get or create the KaizenClient singleton. + + This lazy initialization allows tests to configure settings + before the client is created. + """ + global _client + if _client is None: + _client = KaizenClient() + return _client def ensure_namespace(): try: - client.get_namespace_details(kaizen_config.namespace_id) + get_client().get_namespace_details(kaizen_config.namespace_id) except NamespaceNotFoundException: - client.create_namespace(kaizen_config.namespace_id) + get_client().create_namespace(kaizen_config.namespace_id) @mcp.tool() @@ -41,7 +53,7 @@ def get_guidelines(task: str) -> str: logger.info(f"Getting guidelines for task: {task}") ensure_namespace() # Get relevant guidelines - results = client.search_entities( + results = get_client().search_entities( namespace_id=kaizen_config.namespace_id, query=task, filters={"type": "guideline"}, @@ -85,14 +97,14 @@ def save_trajectory( ) ) - client.update_entities( + get_client().update_entities( namespace_id=kaizen_config.namespace_id, entities=entities, enable_conflict_resolution=False, ) tips = generate_tips(messages) - client.update_entities( + get_client().update_entities( namespace_id=kaizen_config.namespace_id, entities=[ Entity( @@ -109,7 +121,7 @@ def save_trajectory( enable_conflict_resolution=True, ) - return client.search_entities( + return get_client().search_entities( namespace_id=kaizen_config.namespace_id, filters={"type": "trajectory", "task_id": task_id}, limit=1000, @@ -159,7 +171,7 @@ def create_entity( ) # Use KaizenClient.update_entities() to create the entity - updates = client.update_entities( + updates = get_client().update_entities( namespace_id=kaizen_config.namespace_id, entities=[entity], enable_conflict_resolution=enable_conflict_resolution @@ -195,7 +207,7 @@ def delete_entity(entity_id: str) -> str: try: # Use KaizenClient.delete_entity_by_id() to delete the entity - client.delete_entity_by_id( + get_client().delete_entity_by_id( namespace_id=kaizen_config.namespace_id, entity_id=entity_id ) diff --git a/tests/e2e/test_mcp.py b/tests/e2e/test_mcp.py index 68dc0e85..558b861c 100644 --- a/tests/e2e/test_mcp.py +++ b/tests/e2e/test_mcp.py @@ -5,6 +5,9 @@ from pathlib import Path from dotenv import load_dotenv +import uuid +from kaizen.config.milvus import milvus_client_settings + __data__ = Path(__file__).parent.parent / 'data' load_dotenv() @@ -15,12 +18,71 @@ def mcp(): from kaizen.config.kaizen import kaizen_config # we change the namespace ID for these tests so we have to reset the loaded settings kaizen_config.__init__() + + # Use a unique DB file for each test to avoid socket/locking issues + # Milvus Lite has a 36 character limit on DB filenames + db_file = f"test_{uuid.uuid4().hex[:8]}.db" + original_uri = milvus_client_settings.uri + milvus_client_settings.uri = db_file + + # Reset the MCP server client to ensure it uses the new DB file + import kaizen.frontend.mcp.mcp_server as mcp_server_module + mcp_server_module._client = None + kaizen_client = KaizenClient() - # ensure clean test environment - kaizen_client.delete_namespace('test') - from kaizen.frontend.mcp.mcp_server import mcp - yield mcp - kaizen_client.delete_namespace('test') + # Create the test namespace + try: + kaizen_client.create_namespace('test') + except Exception: + pass + + yield mcp_server_module.mcp + + # Cleanup - close the backend connection properly + try: + kaizen_client.backend.close() + except Exception: + pass + + # Disconnect all pymilvus connections to ensure clean state for next test + try: + from pymilvus import connections + for alias, _ in connections.list_connections(): + try: + connections.disconnect(alias) + except Exception: + pass + except Exception: + pass + + # Release all Milvus Lite servers to fully clean up between tests + try: + from milvus_lite.server_manager import server_manager_instance + server_manager_instance.release_all() + except Exception: + pass + + # Reset the MCP server client + mcp_server_module._client = None + + # Restore original URI + milvus_client_settings.uri = original_uri + + # Clean up temp DB files + if os.path.exists(db_file): + try: + os.remove(db_file) + except Exception: + pass + if os.path.exists(f"{db_file}.lock"): + try: + os.remove(f"{db_file}.lock") + except Exception: + pass + + + + @pytest.mark.e2e async def test_save_trajectory_and_retrieve_guidelines(mcp): From 1f92f526992fd01605ec5d19bdba2c2abd1955f2 Mon Sep 17 00:00:00 2001 From: Vatche Isahagian Date: Wed, 28 Jan 2026 21:29:06 -0500 Subject: [PATCH 4/5] Fix: Handle non-integer entity IDs and mock LLM in conflict resolution test --- kaizen/backend/milvus.py | 7 +++++-- tests/e2e/test_mcp.py | 38 ++++++++++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/kaizen/backend/milvus.py b/kaizen/backend/milvus.py index 0e446771..7d5faf65 100644 --- a/kaizen/backend/milvus.py +++ b/kaizen/backend/milvus.py @@ -193,9 +193,12 @@ def search_entities( return [parse_milvus_entity(i) for i in results] def delete_entity_by_id(self, namespace_id: str, entity_id: str): - entity_id = int(entity_id) + try: + entity_id_int = int(entity_id) + except ValueError: + raise KaizenException(f"Invalid entity ID: {entity_id}. Entity IDs must be numeric.") self.validate_namespace(namespace_id) - self.milvus.delete(collection_name=namespace_id, ids=[entity_id]) + self.milvus.delete(collection_name=namespace_id, ids=[entity_id_int]) def close(self): """Close Milvus connection.""" diff --git a/tests/e2e/test_mcp.py b/tests/e2e/test_mcp.py index 558b861c..ce4586e1 100644 --- a/tests/e2e/test_mcp.py +++ b/tests/e2e/test_mcp.py @@ -129,6 +129,9 @@ async def test_create_entity_without_conflict_resolution(mcp): @pytest.mark.e2e async def test_create_entity_with_conflict_resolution(mcp): """Test creating an entity with conflict resolution enabled.""" + from unittest.mock import patch + from kaizen.schema.conflict_resolution import EntityUpdate + async with Client(transport=mcp) as kaizen_mcp: # Create first entity response1 = await kaizen_mcp.call_tool_mcp('create_entity', { @@ -138,17 +141,32 @@ async def test_create_entity_with_conflict_resolution(mcp): }) result1 = json.loads(response1.content[0].text) assert result1['event'] == 'ADD' + first_entity_id = result1['id'] - # Create similar entity with conflict resolution - response2 = await kaizen_mcp.call_tool_mcp('create_entity', { - 'content': 'Always use descriptive variable names', - 'entity_type': 'guideline', - 'enable_conflict_resolution': True - }) - result2 = json.loads(response2.content[0].text) - - # Should either UPDATE existing or NONE (depending on LLM decision) - assert result2['event'] in ['UPDATE', 'NONE', 'ADD'] + # Mock resolve_conflicts to avoid LLM call timeout + with patch('kaizen.backend.milvus.resolve_conflicts') as mock_resolve: + # Configure mock to return an UPDATE event + mock_resolve.return_value = [ + EntityUpdate( + id=str(first_entity_id), + type='guideline', + content='Always use descriptive variable names', + event='UPDATE', + metadata={} + ) + ] + + # Create similar entity with conflict resolution + response2 = await kaizen_mcp.call_tool_mcp('create_entity', { + 'content': 'Always use descriptive variable names', + 'entity_type': 'guideline', + 'enable_conflict_resolution': True + }) + result2 = json.loads(response2.content[0].text) + + # Should return what our mock returned + assert result2['event'] == 'UPDATE' + assert result2['id'] == str(first_entity_id) @pytest.mark.e2e From e37cd6b3ea2180c01a2930d0559ccc04a404c297 Mon Sep 17 00:00:00 2001 From: Vatche Isahagian Date: Wed, 28 Jan 2026 21:36:57 -0500 Subject: [PATCH 5/5] fix: improve Milvus test isolation and error handling --- kaizen/backend/milvus.py | 10 ++++++++++ tests/e2e/test_mcp.py | 5 +++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/kaizen/backend/milvus.py b/kaizen/backend/milvus.py index 7d5faf65..d5e2d49a 100644 --- a/kaizen/backend/milvus.py +++ b/kaizen/backend/milvus.py @@ -198,6 +198,16 @@ def delete_entity_by_id(self, namespace_id: str, entity_id: str): except ValueError: raise KaizenException(f"Invalid entity ID: {entity_id}. Entity IDs must be numeric.") self.validate_namespace(namespace_id) + + # Check if entity exists before deleting + existing = self.milvus.query( + collection_name=namespace_id, + filter=f"id == {entity_id_int}", + output_fields=["id"] + ) + if not existing: + raise KaizenException(f"Entity with ID {entity_id} not found in namespace {namespace_id}.") + self.milvus.delete(collection_name=namespace_id, ids=[entity_id_int]) def close(self): diff --git a/tests/e2e/test_mcp.py b/tests/e2e/test_mcp.py index ce4586e1..f54324de 100644 --- a/tests/e2e/test_mcp.py +++ b/tests/e2e/test_mcp.py @@ -219,13 +219,14 @@ async def test_delete_entity(mcp): async def test_delete_nonexistent_entity(mcp): """Test deleting an entity that doesn't exist.""" async with Client(transport=mcp) as kaizen_mcp: + # Use a numeric ID that doesn't exist (entity IDs are integers) delete_response = await kaizen_mcp.call_tool_mcp('delete_entity', { - 'entity_id': 'nonexistent-id-12345' + 'entity_id': '99999999' }) result = json.loads(delete_response.content[0].text) - # Should return an error + # Should return an error since entity doesn't exist assert result['success'] is False assert 'error' in result