Created
February 3, 2026 08:27
-
-
Save kornysietsma/e849865af8a10741b41b0a9a90c471a0 to your computer and use it in GitHub Desktop.
Using a trivial MCP server to bypass Github Copilot's inability to view images
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env -S uv run --script | |
| # /// script | |
| # dependencies = [ | |
| # "mcp>=1.0.0", | |
| # "pillow>=10.0.0", | |
| # ] | |
| # requires-python = ">=3.12" | |
| # /// | |
| """ | |
| View Image MCP Server | |
| Stdio-based MCP server that provides image viewing capabilities. | |
| Accepts images via file path, converts to PNG, and returns with metadata. | |
| """ | |
| import sys | |
| import base64 | |
| import json | |
| import asyncio | |
| from io import BytesIO | |
| from PIL import Image | |
| from mcp.server import Server | |
| from mcp.server.stdio import stdio_server | |
| from mcp.types import Tool, TextContent, ImageContent | |
| # Supported image formats (Pillow format names) | |
| SUPPORTED_FORMATS = {"PNG", "JPEG", "WEBP", "GIF"} | |
| TOOL_DESCRIPTION = """USE THIS TOOL TO VIEW AND ANALYZE IMAGES. When you need to see the contents of an image file or analyze image data, you MUST use this tool. Do not attempt to describe images without first viewing them through this tool. | |
| Accepts filePath: Absolute path to an image file (PNG, JPEG, WebP, GIF) | |
| Returns the image in PNG format with metadata (dimensions, format). Always use this tool before describing or analyzing any image content.""" | |
| def load_image_from_path(file_path: str) -> tuple[Image.Image, str]: | |
| with Image.open(file_path) as img: | |
| if img.format not in SUPPORTED_FORMATS: | |
| raise ValueError(f"Unsupported format: {img.format}. Supported: {', '.join(SUPPORTED_FORMATS)}") | |
| return img.copy(), img.format | |
| def convert_to_png(image: Image.Image) -> bytes: | |
| buffer = BytesIO() | |
| image.save(buffer, format="PNG") | |
| return buffer.getvalue() | |
| def create_app() -> Server: | |
| server = Server("view-image") | |
| @server.list_tools() | |
| async def list_tools() -> list[Tool]: | |
| return [ | |
| Tool( | |
| name="view_image", | |
| description=TOOL_DESCRIPTION, | |
| inputSchema={ | |
| "type": "object", | |
| "properties": { | |
| "filePath": { | |
| "type": "string", | |
| "description": "Absolute path to an image file. Supports PNG, JPEG, WebP, GIF.", | |
| } | |
| }, | |
| "required": ["filePath"], | |
| }, | |
| ) | |
| ] | |
| @server.call_tool() | |
| async def call_tool(name: str, arguments: dict) -> list[TextContent | ImageContent]: | |
| if name != "view_image": | |
| raise ValueError(f"Unknown tool: {name}") | |
| file_path = arguments["filePath"] | |
| image, original_format = load_image_from_path(file_path) | |
| width, height = image.size | |
| png_bytes = convert_to_png(image) | |
| png_base64 = base64.b64encode(png_bytes).decode("utf-8") | |
| metadata = { | |
| "width": width, | |
| "height": height, | |
| "originalFormat": original_format.lower(), | |
| "filePath": file_path, | |
| } | |
| return [ | |
| TextContent( | |
| type="text", | |
| text=json.dumps(metadata), | |
| ), | |
| ImageContent( | |
| type="image", | |
| data=png_base64, | |
| mimeType="image/png", | |
| ), | |
| ] | |
| return server | |
| async def main(): | |
| server = create_app() | |
| async with stdio_server() as (read_stream, write_stream): | |
| await server.run( | |
| read_stream, | |
| write_stream, | |
| server.create_initialization_options(), | |
| ) | |
| if __name__ == "__main__": | |
| asyncio.run(main()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment