|
39 | 39 |
|
40 | 40 | class McpTools(StrEnum): |
41 | 41 | RUN_PYTHON_CODE = 'run_python_code' |
| 42 | + UPLOAD_FILE = 'upload_file' |
42 | 43 | UPLOAD_FILE_FROM_URI = 'upload_file_from_uri' |
43 | 44 | RETRIEVE_FILE = 'retrieve_file' |
44 | 45 | DELETE_FILE = 'delete_file' |
@@ -169,7 +170,7 @@ async def test_list_tools( |
169 | 170 | ) |
170 | 171 | else: |
171 | 172 | # Check tools |
172 | | - assert len(tools.tools) == 4 |
| 173 | + assert len(tools.tools) == 5 |
173 | 174 | # sort tools by their name |
174 | 175 | sorted_tools = sorted(tools.tools, key=lambda t: t.name) |
175 | 176 |
|
@@ -345,8 +346,59 @@ def do_GET(self): |
345 | 346 | httpd.server_close() |
346 | 347 | t.join(timeout=2) |
347 | 348 |
|
348 | | - @pytest.mark.parametrize('uri_type', ['http', 'file']) |
| 349 | + @pytest.mark.parametrize('file_type', ['text', 'bytes']) |
349 | 350 | async def test_upload_files( |
| 351 | + self, |
| 352 | + mcp_session: ClientSession, |
| 353 | + server_type: Literal['stdio', 'sse', 'streamable_http'], |
| 354 | + mount: bool | str, |
| 355 | + file_type: Literal['text', 'bytes'], |
| 356 | + tmp_path: Path, |
| 357 | + ) -> None: |
| 358 | + if mount is False: |
| 359 | + pytest.skip('No directory mounted.') |
| 360 | + result = await mcp_session.initialize() |
| 361 | + |
| 362 | + # Extract directory from response |
| 363 | + storageDir = self.get_dir_from_instructions(result.instructions) |
| 364 | + assert storageDir.is_dir() |
| 365 | + |
| 366 | + match file_type: |
| 367 | + case 'text': |
| 368 | + filename = 'data.csv' |
| 369 | + ctype = 'text/csv' |
| 370 | + result = await mcp_session.call_tool( |
| 371 | + McpTools.UPLOAD_FILE, {'type': 'text', 'filename': filename, 'text': CSV_DATA, 'blob': None} |
| 372 | + ) |
| 373 | + |
| 374 | + case 'bytes': |
| 375 | + filename = 'image.png' |
| 376 | + ctype = 'image/png' |
| 377 | + result = await mcp_session.call_tool( |
| 378 | + McpTools.UPLOAD_FILE, {'type': 'bytes', 'filename': filename, 'blob': BASE_64_IMAGE, 'text': None} |
| 379 | + ) |
| 380 | + |
| 381 | + assert result.isError is False |
| 382 | + assert len(result.content) == 1 |
| 383 | + content = result.content[0] |
| 384 | + assert isinstance(content, types.ResourceLink) |
| 385 | + assert str(content.uri) == f'file:///{filename}' |
| 386 | + assert content.name == filename |
| 387 | + assert content.mimeType is not None |
| 388 | + assert content.mimeType.startswith(ctype) |
| 389 | + |
| 390 | + createdFile = storageDir / filename |
| 391 | + assert createdFile.exists() |
| 392 | + assert createdFile.is_file() |
| 393 | + |
| 394 | + match file_type: |
| 395 | + case 'text': |
| 396 | + assert createdFile.read_text() == CSV_DATA |
| 397 | + case 'bytes': |
| 398 | + assert base64.b64encode(createdFile.read_bytes()).decode() == BASE_64_IMAGE |
| 399 | + |
| 400 | + @pytest.mark.parametrize('uri_type', ['http', 'file']) |
| 401 | + async def test_upload_files_with_uri( |
350 | 402 | self, |
351 | 403 | mcp_session: ClientSession, |
352 | 404 | server_type: Literal['stdio', 'sse', 'streamable_http'], |
@@ -400,7 +452,7 @@ async def test_download_files( |
400 | 452 | server_type: Literal['stdio', 'sse', 'streamable_http'], |
401 | 453 | mount: bool | str, |
402 | 454 | content_type: Literal['bytes', 'text'], |
403 | | - ): |
| 455 | + ) -> None: |
404 | 456 | if mount is False: |
405 | 457 | pytest.skip('No directory mounted.') |
406 | 458 | result = await mcp_session.initialize() |
|
0 commit comments