-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.py
More file actions
129 lines (97 loc) · 3.67 KB
/
Copy pathserver.py
File metadata and controls
129 lines (97 loc) · 3.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#!/usr/bin/env python3
"""
SnipMCP - Screenshot MCP Server
Provides a tool for Claude Code to fetch screenshots from the Desktop
using the #snip notation.
"""
import glob
import os
from pathlib import Path
from mcp.server.fastmcp import FastMCP, Image
# Initialize MCP server
mcp = FastMCP("snip")
# Desktop path - mounted from host
DESKTOP_PATH = os.environ.get("DESKTOP_PATH", "/desktop")
SCREENSHOT_PATTERN = "Screenshot*.png"
def get_screenshots_sorted() -> list[Path]:
"""Get all screenshots sorted by modification time (newest first)."""
pattern = os.path.join(DESKTOP_PATH, SCREENSHOT_PATTERN)
files = glob.glob(pattern)
# Sort by modification time, newest first
files.sort(key=lambda x: os.path.getmtime(x), reverse=True)
return [Path(f) for f in files]
def read_image_bytes(filepath: Path) -> bytes:
"""Read an image file and return as bytes."""
with open(filepath, "rb") as f:
return f.read()
@mcp.tool()
def screenshot(index: str = "latest") -> list:
"""
Fetch screenshot(s) from the Desktop.
Args:
index: Either "latest" for most recent, a single number like "0",
or comma-separated indices like "0,1,2" for multiple screenshots.
Index 0 is the oldest in the requested set.
Returns:
List of Image objects that Claude can view directly.
"""
screenshots = get_screenshots_sorted()
if not screenshots:
return ["No screenshots found on Desktop"]
if index == "latest":
filepath = screenshots[0]
image_bytes = read_image_bytes(filepath)
return [
f"Screenshot: {filepath.name}",
Image(data=image_bytes, format="png")
]
# Parse indices
try:
if "," in index:
indices = [int(i.strip()) for i in index.split(",")]
else:
indices = [int(index)]
except ValueError:
return [f"Invalid index format: {index}"]
# For multiple indices, we need to map them correctly
# User's #snip0 = oldest of the set, #snipN = newest
max_index = max(indices)
count_needed = max_index + 1
if count_needed > len(screenshots):
return [f"Requested index {max_index} but only {len(screenshots)} screenshots available"]
# Get the N most recent screenshots and reverse so index 0 = oldest of set
relevant_screenshots = list(reversed(screenshots[:count_needed]))
result = []
for idx in sorted(indices):
filepath = relevant_screenshots[idx]
image_bytes = read_image_bytes(filepath)
result.append(f"#snip{idx}: {filepath.name}")
result.append(Image(data=image_bytes, format="png"))
return result
@mcp.tool()
def list_screenshots(limit: int = 10) -> str:
"""
List available screenshots on the Desktop.
Args:
limit: Maximum number of screenshots to list (default 10)
Returns:
List of screenshot filenames with their indices.
"""
screenshots = get_screenshots_sorted()[:limit]
if not screenshots:
return "No screenshots found on Desktop"
lines = [f"Found {len(screenshots)} screenshots:"]
# Reverse so index 0 = oldest in the set
for i, s in enumerate(reversed(screenshots)):
lines.append(f" #snip{i}: {s.name}")
return "\n".join(lines)
@mcp.tool()
def health() -> str:
"""Check if the SnipMCP server is healthy and can access screenshots."""
screenshots = get_screenshots_sorted()
return f"Healthy. Desktop: {DESKTOP_PATH}, Screenshots available: {len(screenshots)}"
if __name__ == "__main__":
import uvicorn
# Get the SSE app and run with uvicorn for proper host binding
app = mcp.sse_app()
uvicorn.run(app, host="0.0.0.0", port=8000)