iflow-mcp_gustavo-meilus-mcp-web-snapshot 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iflow_mcp_gustavo_meilus_mcp_web_snapshot/__init__.py +0 -0
- iflow_mcp_gustavo_meilus_mcp_web_snapshot/registry.py +14 -0
- iflow_mcp_gustavo_meilus_mcp_web_snapshot/server.py +15 -0
- iflow_mcp_gustavo_meilus_mcp_web_snapshot/tools/__init__.py +1 -0
- iflow_mcp_gustavo_meilus_mcp_web_snapshot/tools/snapshot_url.py +228 -0
- iflow_mcp_gustavo_meilus_mcp_web_snapshot-0.1.0.dist-info/METADATA +306 -0
- iflow_mcp_gustavo_meilus_mcp_web_snapshot-0.1.0.dist-info/RECORD +10 -0
- iflow_mcp_gustavo_meilus_mcp_web_snapshot-0.1.0.dist-info/WHEEL +4 -0
- iflow_mcp_gustavo_meilus_mcp_web_snapshot-0.1.0.dist-info/entry_points.txt +2 -0
- iflow_mcp_gustavo_meilus_mcp_web_snapshot-0.1.0.dist-info/licenses/LICENSE +21 -0
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from mcp.server.fastmcp import FastMCP
|
|
2
|
+
from iflow_mcp_gustavo_meilus_mcp_web_snapshot.tools.snapshot_url import website_snapshot
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def register_all_tools(mcp_server: FastMCP) -> None:
|
|
6
|
+
"""
|
|
7
|
+
Register all tools with the MCP server.
|
|
8
|
+
|
|
9
|
+
Args:
|
|
10
|
+
mcp_server: The FastMCP server instance to register tools with
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
# Add more tools here as you create them
|
|
14
|
+
mcp_server.tool()(website_snapshot)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from mcp.server.fastmcp import FastMCP
|
|
2
|
+
from iflow_mcp_gustavo_meilus_mcp_web_snapshot.registry import register_all_tools
|
|
3
|
+
|
|
4
|
+
# Create an MCP server
|
|
5
|
+
mcp = FastMCP("Website Snapshot")
|
|
6
|
+
|
|
7
|
+
# Register all tools
|
|
8
|
+
register_all_tools(mcp)
|
|
9
|
+
|
|
10
|
+
if __name__ == "__main__":
|
|
11
|
+
mcp.run()
|
|
12
|
+
|
|
13
|
+
def main():
|
|
14
|
+
"""Entry point for the MCP server"""
|
|
15
|
+
mcp.run()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
from typing import Optional, List, Dict, Any
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
|
|
4
|
+
from mcp.types import TextContent
|
|
5
|
+
from playwright.async_api import async_playwright, Response, Request
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
from urllib.parse import urlparse
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AuthenticatedSnapshotArgs(BaseModel):
|
|
11
|
+
url: str = Field(
|
|
12
|
+
description="Path to navigate to after authentication (e.g., /leads)"
|
|
13
|
+
)
|
|
14
|
+
include_network: bool = Field(
|
|
15
|
+
default=True, description="Include network monitoring"
|
|
16
|
+
)
|
|
17
|
+
include_console: bool = Field(default=True, description="Include console messages")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class NetworkRequest:
|
|
22
|
+
request: Optional[Request] = None
|
|
23
|
+
response: Optional[Response] = None
|
|
24
|
+
request_body: Optional[str] = None
|
|
25
|
+
response_body: Optional[str] = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
CONFIG = {
|
|
29
|
+
"viewport": {"width": 1920, "height": 1080},
|
|
30
|
+
"user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36",
|
|
31
|
+
"timeout": 15000,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def should_show_content(content_type: str, content_length: int) -> bool:
|
|
36
|
+
return content_length <= 50000 and any(
|
|
37
|
+
content_type.startswith(t)
|
|
38
|
+
for t in ["application/json", "text/", "application/xml"]
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def format_requests(requests: List[NetworkRequest]) -> str:
|
|
43
|
+
if not requests:
|
|
44
|
+
return "No requests captured"
|
|
45
|
+
|
|
46
|
+
formatted = []
|
|
47
|
+
for req in requests:
|
|
48
|
+
if req.request:
|
|
49
|
+
formatted.append(f"🌐 {req.request.method} {req.request.url}")
|
|
50
|
+
formatted.append(
|
|
51
|
+
f" Status: {req.response.status if req.response else 'Pending'}"
|
|
52
|
+
)
|
|
53
|
+
if req.response_body:
|
|
54
|
+
formatted.append(f" Response: {req.response_body[:200]}...")
|
|
55
|
+
return "\n".join(formatted)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def format_console(messages: List[Any]) -> str:
|
|
59
|
+
if not messages:
|
|
60
|
+
return "No console messages"
|
|
61
|
+
return "\n".join(
|
|
62
|
+
[
|
|
63
|
+
f"🖥️ [{getattr(msg, 'type', 'log').upper()}] {getattr(msg, 'text', str(msg))}"
|
|
64
|
+
for msg in messages
|
|
65
|
+
]
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def add_element_refs(snapshot: str) -> str:
|
|
70
|
+
lines = snapshot.split("\n")
|
|
71
|
+
ref_counter = 1
|
|
72
|
+
|
|
73
|
+
for i, line in enumerate(lines):
|
|
74
|
+
if any(
|
|
75
|
+
keyword in line.lower()
|
|
76
|
+
for keyword in ["button", "link", "input", "textbox"]
|
|
77
|
+
):
|
|
78
|
+
lines[i] = f"{line} [ref={ref_counter}]"
|
|
79
|
+
ref_counter += 1
|
|
80
|
+
|
|
81
|
+
return "\n".join(lines)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def parse_refs(snapshot: str) -> List[Dict[str, str]]:
|
|
85
|
+
refs = []
|
|
86
|
+
for line in snapshot.split("\n"):
|
|
87
|
+
if "[ref=" in line:
|
|
88
|
+
ref_start = line.find("[ref=") + 5
|
|
89
|
+
ref_end = line.find("]", ref_start)
|
|
90
|
+
if ref_end > ref_start:
|
|
91
|
+
refs.append(
|
|
92
|
+
{
|
|
93
|
+
"ref": line[ref_start:ref_end],
|
|
94
|
+
"element": line[: line.find("[ref=")].strip(),
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
return refs
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def is_valid_url(url: str) -> bool:
|
|
101
|
+
parsed = urlparse(url)
|
|
102
|
+
return all([parsed.scheme, parsed.netloc])
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
async def website_snapshot(target_url: str) -> List[TextContent]:
|
|
106
|
+
"""
|
|
107
|
+
Take authenticated page snapshots with monitoring
|
|
108
|
+
Example: https://example.com
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
if not is_valid_url(target_url):
|
|
112
|
+
return [
|
|
113
|
+
TextContent(
|
|
114
|
+
type="text", text="Url must be valid, example: https://example.com"
|
|
115
|
+
)
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
async with async_playwright() as p:
|
|
119
|
+
browser = await p.chromium.launch(headless=True)
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
context = await browser.new_context(
|
|
123
|
+
viewport=CONFIG["viewport"], user_agent=CONFIG["user_agent"]
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
page = await context.new_page()
|
|
127
|
+
page.set_default_timeout(CONFIG["timeout"])
|
|
128
|
+
|
|
129
|
+
network_requests: List[NetworkRequest] = []
|
|
130
|
+
console_messages: List[Any] = []
|
|
131
|
+
|
|
132
|
+
# Setup monitoring
|
|
133
|
+
page.on("console", lambda msg: console_messages.append(msg))
|
|
134
|
+
|
|
135
|
+
async def handle_request(request: Request):
|
|
136
|
+
request_body = None
|
|
137
|
+
if request.method.upper() in ["POST", "PUT", "PATCH"]:
|
|
138
|
+
try:
|
|
139
|
+
request_body = await request.post_data()
|
|
140
|
+
except Exception:
|
|
141
|
+
pass
|
|
142
|
+
network_requests.append(
|
|
143
|
+
NetworkRequest(request=request, request_body=request_body)
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
async def handle_response(response: Response):
|
|
147
|
+
for entry in network_requests:
|
|
148
|
+
if entry.request == response.request and not entry.response:
|
|
149
|
+
entry.response = response
|
|
150
|
+
try:
|
|
151
|
+
content_type = response.headers.get("content-type", "")
|
|
152
|
+
content_length = int(
|
|
153
|
+
response.headers.get("content-length", "0")
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
if should_show_content(content_type, content_length):
|
|
157
|
+
entry.response_body = await response.text()
|
|
158
|
+
else:
|
|
159
|
+
entry.response_body = (
|
|
160
|
+
f"[{content_type} - {content_length} bytes]"
|
|
161
|
+
)
|
|
162
|
+
except Exception:
|
|
163
|
+
entry.response_body = "[Error reading response]"
|
|
164
|
+
break
|
|
165
|
+
|
|
166
|
+
page.on("request", handle_request)
|
|
167
|
+
page.on("response", handle_response)
|
|
168
|
+
|
|
169
|
+
# Navigate to target and capture snapshot
|
|
170
|
+
await page.goto(target_url, wait_until="domcontentloaded")
|
|
171
|
+
await page.wait_for_load_state("load", timeout=CONFIG["timeout"])
|
|
172
|
+
|
|
173
|
+
try:
|
|
174
|
+
await page.wait_for_selector(
|
|
175
|
+
"[data-testid], button, .MuiButton-root", timeout=10000
|
|
176
|
+
)
|
|
177
|
+
except Exception:
|
|
178
|
+
await page.wait_for_timeout(3000)
|
|
179
|
+
|
|
180
|
+
# Capture snapshot
|
|
181
|
+
aria_snapshot = await page.locator("body").aria_snapshot()
|
|
182
|
+
snapshot_with_refs = add_element_refs(aria_snapshot)
|
|
183
|
+
element_refs = parse_refs(snapshot_with_refs)
|
|
184
|
+
|
|
185
|
+
# Format output
|
|
186
|
+
output_parts = [
|
|
187
|
+
f"🔍 {await page.title()}",
|
|
188
|
+
f"📍 {page.url}",
|
|
189
|
+
"",
|
|
190
|
+
"🎭 Accessibility Snapshot:",
|
|
191
|
+
snapshot_with_refs,
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
output_parts.extend(
|
|
195
|
+
["", "🌐 Network Requests:", format_requests(network_requests)]
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
output_parts.extend(["", "🖥️ Console:", format_console(console_messages)])
|
|
199
|
+
|
|
200
|
+
summary_parts = [f"{len(element_refs)} elements"]
|
|
201
|
+
|
|
202
|
+
summary_parts.append(
|
|
203
|
+
f"{len([r for r in network_requests if r.request])} requests"
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
summary_parts.append(f"{len(console_messages)} console messages")
|
|
207
|
+
|
|
208
|
+
await context.close()
|
|
209
|
+
|
|
210
|
+
return [
|
|
211
|
+
TextContent(
|
|
212
|
+
type="text",
|
|
213
|
+
text=f"✅ Captured snapshot with {', '.join(summary_parts)}",
|
|
214
|
+
),
|
|
215
|
+
TextContent(type="text", text="\n".join(output_parts)),
|
|
216
|
+
TextContent(
|
|
217
|
+
type="text",
|
|
218
|
+
text="🎯 Element References:\n"
|
|
219
|
+
+ "\n".join(
|
|
220
|
+
[f"[ref={r['ref']}]: {r['element']}" for r in element_refs]
|
|
221
|
+
),
|
|
222
|
+
),
|
|
223
|
+
]
|
|
224
|
+
|
|
225
|
+
except Exception as error:
|
|
226
|
+
return [TextContent(type="text", text=f"❌ Failed: {str(error)}")]
|
|
227
|
+
finally:
|
|
228
|
+
await browser.close()
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: iflow-mcp_gustavo-meilus-mcp-web-snapshot
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Model Context Protocol (MCP) server that provides comprehensive website snapshot capabilities using Playwright for LLM integration
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Requires-Dist: mcp[cli]>=1.9.2
|
|
8
|
+
Requires-Dist: playwright>=1.52.0
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# MCP Web Snapshot
|
|
12
|
+
|
|
13
|
+
A Model Context Protocol (MCP) server that provides comprehensive website snapshot capabilities using [Playwright](https://playwright.dev). This server enables LLMs to capture and analyze web pages through structured accessibility snapshots, network monitoring, and console message collection.
|
|
14
|
+
|
|
15
|
+
## Key Features
|
|
16
|
+
|
|
17
|
+
- **🚀 Fast and lightweight**: Uses Playwright's accessibility tree for efficient snapshots
|
|
18
|
+
- **🎯 LLM-optimized**: Structured data output designed specifically for AI consumption
|
|
19
|
+
- **📊 Comprehensive monitoring**: Captures network requests, responses, and console messages
|
|
20
|
+
- **🔍 Element references**: Adds unique identifiers to interactive elements for precise targeting
|
|
21
|
+
- **🛡️ Production-ready**: Built-in error handling, resource limits, and timeout management
|
|
22
|
+
- **✅ Well-tested**: Comprehensive test suite with code coverage
|
|
23
|
+
|
|
24
|
+
## Requirements
|
|
25
|
+
|
|
26
|
+
- Python 3.11 or newer
|
|
27
|
+
- VS Code, Cursor, Windsurf, Claude Desktop or any other MCP client
|
|
28
|
+
|
|
29
|
+
## Getting Started
|
|
30
|
+
|
|
31
|
+
First, install the MCP Web Snapshot server with your client. A typical configuration looks like this:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"mcpServers": {
|
|
36
|
+
"mcp-web-snapshot": {
|
|
37
|
+
"command": "uv",
|
|
38
|
+
"args": [
|
|
39
|
+
"--directory",
|
|
40
|
+
"/path/to/mcp-web-snapshot",
|
|
41
|
+
"run",
|
|
42
|
+
"python",
|
|
43
|
+
"src/server.py"
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Install in VS Code
|
|
51
|
+
|
|
52
|
+
You can install the MCP Web Snapshot server using the VS Code CLI:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# For VS Code
|
|
56
|
+
code --add-mcp '{"name":"mcp-web-snapshot","command":"uv","args":["--directory","/path/to/mcp-web-snapshot","run","python","src/server.py"]}'
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
After installation, the MCP Web Snapshot server will be available for use with your GitHub Copilot agent in VS Code.
|
|
60
|
+
|
|
61
|
+
### Install in Cursor
|
|
62
|
+
|
|
63
|
+
Go to `Cursor Settings` → `MCP` → `Add new MCP Server`. Name to your liking, use `command` type with the command `uv` and args `["--directory", "/path/to/mcp-web-snapshot", "run", "python", "src/server.py"]`.
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"mcpServers": {
|
|
68
|
+
"mcp-web-snapshot": {
|
|
69
|
+
"command": "uv",
|
|
70
|
+
"args": [
|
|
71
|
+
"--directory",
|
|
72
|
+
"/path/to/mcp-web-snapshot",
|
|
73
|
+
"run",
|
|
74
|
+
"python",
|
|
75
|
+
"src/server.py"
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Development Setup
|
|
83
|
+
|
|
84
|
+
### Local Installation
|
|
85
|
+
|
|
86
|
+
1. Clone this repository:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
git clone https://github.com/your-username/mcp-web-snapshot.git
|
|
90
|
+
cd mcp-web-snapshot
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
2. Install dependencies using uv:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
uv sync
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
3. Install Playwright browsers:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
uv run playwright install
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
4. Run the server:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
uv run python src/server.py
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Project Structure
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
├── src/
|
|
115
|
+
│ ├── server.py # Main MCP server entry point
|
|
116
|
+
│ ├── registry.py # Tool registration logic
|
|
117
|
+
│ └── tools/
|
|
118
|
+
│ ├── __init__.py
|
|
119
|
+
│ └── snapshot_url.py # Web snapshot implementation
|
|
120
|
+
├── tests/ # Test suite
|
|
121
|
+
│ ├── __init__.py
|
|
122
|
+
│ ├── test_snapshot_url.py # Unit tests for website_snapshot
|
|
123
|
+
│ └── README.md # Test documentation
|
|
124
|
+
├── pyproject.toml # Project configuration
|
|
125
|
+
├── pytest.ini # Pytest configuration
|
|
126
|
+
├── uv.lock # Lock file for dependencies
|
|
127
|
+
└── README.md # This file
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Testing
|
|
131
|
+
|
|
132
|
+
The project includes a comprehensive test suite using pytest:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# Run all tests
|
|
136
|
+
uv run pytest
|
|
137
|
+
|
|
138
|
+
# Run with verbose output
|
|
139
|
+
uv run pytest -v
|
|
140
|
+
|
|
141
|
+
# Run with coverage report
|
|
142
|
+
PYTHONPATH=. uv run pytest --cov=src.tools.snapshot_url --cov-report=term-missing
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Test coverage currently stands at 92%, covering:
|
|
146
|
+
|
|
147
|
+
- Successful snapshot capture and formatting
|
|
148
|
+
- Network request and console message monitoring
|
|
149
|
+
- URL validation
|
|
150
|
+
- Error handling and recovery
|
|
151
|
+
|
|
152
|
+
## Available Tools
|
|
153
|
+
|
|
154
|
+
### Website Snapshot
|
|
155
|
+
|
|
156
|
+
**Tool**: `website_snapshot`
|
|
157
|
+
|
|
158
|
+
**Description**: Take comprehensive snapshots of web pages with monitoring capabilities
|
|
159
|
+
|
|
160
|
+
**Parameters**:
|
|
161
|
+
|
|
162
|
+
- `target_url` (string): The URL to capture (must be a valid URL with protocol)
|
|
163
|
+
|
|
164
|
+
**Features**:
|
|
165
|
+
|
|
166
|
+
- 🎭 **Accessibility Snapshot**: Captures the complete accessibility tree structure
|
|
167
|
+
- 🌐 **Network Monitoring**: Records all network requests and responses during page load
|
|
168
|
+
- 🖥️ **Console Messages**: Captures client-side console output (logs, warnings, errors)
|
|
169
|
+
- 🎯 **Element References**: Adds unique reference IDs to interactive elements
|
|
170
|
+
- 📊 **Performance Metrics**: Provides summary of captured elements and network activity
|
|
171
|
+
|
|
172
|
+
**Example Usage**:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
# Through MCP client
|
|
176
|
+
result = await website_snapshot("https://example.com")
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Output Structure**:
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
✅ Captured snapshot with 25 elements, 12 requests, 0 console messages
|
|
183
|
+
🔍 Example Website - Home Page
|
|
184
|
+
📍 https://example.com
|
|
185
|
+
|
|
186
|
+
🎭 Accessibility Snapshot:
|
|
187
|
+
- navigation "Main":
|
|
188
|
+
- link "Home": [ref=1]
|
|
189
|
+
- link "About": [ref=2]
|
|
190
|
+
- button "Contact" [ref=3]
|
|
191
|
+
- main:
|
|
192
|
+
- heading "Welcome to Example"
|
|
193
|
+
- link "Get Started": [ref=4]
|
|
194
|
+
|
|
195
|
+
🌐 Network Requests:
|
|
196
|
+
🌐 GET https://example.com
|
|
197
|
+
Status: 200
|
|
198
|
+
Response: <!DOCTYPE html><html>...
|
|
199
|
+
|
|
200
|
+
🖥️ Console:
|
|
201
|
+
No console messages
|
|
202
|
+
|
|
203
|
+
🎯 Element References:
|
|
204
|
+
[ref=1]: link "Home"
|
|
205
|
+
[ref=2]: link "About"
|
|
206
|
+
[ref=3]: button "Contact"
|
|
207
|
+
[ref=4]: link "Get Started"
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Configuration
|
|
211
|
+
|
|
212
|
+
The server can be configured through the `CONFIG` dictionary in `src/tools/snapshot_url.py`:
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
CONFIG = {
|
|
216
|
+
"viewport": {"width": 1920, "height": 1080},
|
|
217
|
+
"user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36",
|
|
218
|
+
"timeout": 15000, # 15 seconds
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Customization Options
|
|
223
|
+
|
|
224
|
+
- **Viewport Size**: Adjust browser window dimensions
|
|
225
|
+
- **User Agent**: Customize browser identification
|
|
226
|
+
- **Timeout**: Control maximum wait time for page operations
|
|
227
|
+
- **Content Filtering**: Modify response body size limits and content type filters
|
|
228
|
+
|
|
229
|
+
## Use Cases
|
|
230
|
+
|
|
231
|
+
### LLM-Guided Testing
|
|
232
|
+
|
|
233
|
+
Capture comprehensive page state for automated test generation:
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
"Please take a snapshot of https://myapp.com/login and help me create Page Object Model classes based on the discovered elements and structure."
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Web Content Analysis
|
|
240
|
+
|
|
241
|
+
Extract structured data for content analysis and monitoring:
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
"Take a snapshot of https://competitor.com/pricing and analyze any pricing changes or new features compared to what we discussed last week."
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Accessibility Auditing
|
|
248
|
+
|
|
249
|
+
Leverage accessibility tree data for compliance checking:
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
"Please capture a snapshot of https://myapp.com and identify any accessibility issues or areas for improvement based on WCAG guidelines."
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### API Integration Analysis
|
|
256
|
+
|
|
257
|
+
Monitor network activity to understand application behavior:
|
|
258
|
+
|
|
259
|
+
```
|
|
260
|
+
"Take a snapshot of https://dashboard.example.com after I log in and show me what API calls are being made so I can understand the data flow."
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Cross-Browser Testing Setup
|
|
264
|
+
|
|
265
|
+
Generate test scenarios based on captured interactions:
|
|
266
|
+
|
|
267
|
+
```
|
|
268
|
+
"Snapshot https://myapp.com/checkout and help me create comprehensive test cases that cover all the interactive elements and user workflows."
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Error Handling
|
|
272
|
+
|
|
273
|
+
The server includes comprehensive error handling:
|
|
274
|
+
|
|
275
|
+
- **Invalid URLs**: Returns helpful error messages for malformed URLs
|
|
276
|
+
- **Network Timeouts**: Configurable timeout limits with graceful fallbacks
|
|
277
|
+
- **Browser Crashes**: Automatic cleanup and resource management
|
|
278
|
+
- **Content Limits**: Smart filtering of large response bodies to prevent memory issues
|
|
279
|
+
|
|
280
|
+
## Contributing
|
|
281
|
+
|
|
282
|
+
1. Fork the repository
|
|
283
|
+
2. Create a feature branch: `git checkout -b feature-name`
|
|
284
|
+
3. Make your changes and add tests
|
|
285
|
+
4. Run tests: `uv run pytest`
|
|
286
|
+
5. Check test coverage: `PYTHONPATH=. uv run pytest --cov=src.tools.snapshot_url --cov-report=term-missing`
|
|
287
|
+
6. Ensure code style compliance: `uv run ruff check`
|
|
288
|
+
7. Submit a pull request
|
|
289
|
+
|
|
290
|
+
Please ensure all tests pass and maintain or improve the current test coverage.
|
|
291
|
+
|
|
292
|
+
## License
|
|
293
|
+
|
|
294
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
295
|
+
|
|
296
|
+
## Acknowledgments
|
|
297
|
+
|
|
298
|
+
- Inspired by [MCP Server Website Snapshot](https://www.linkedin.com/pulse/mcp-server-website-snapshot-gustavo-meilus-oiigf/) article
|
|
299
|
+
- Built on the foundation of [Microsoft's Playwright MCP](https://github.com/microsoft/playwright-mcp)
|
|
300
|
+
- Powered by [Playwright](https://playwright.dev) for reliable browser automation
|
|
301
|
+
- Uses [Model Context Protocol](https://modelcontextprotocol.io) for LLM integration
|
|
302
|
+
|
|
303
|
+
## Related Projects
|
|
304
|
+
|
|
305
|
+
- [Playwright MCP](https://github.com/microsoft/playwright-mcp) - Interactive browser automation MCP server
|
|
306
|
+
- [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk/) - MCP server python framework
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
iflow_mcp_gustavo_meilus_mcp_web_snapshot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
iflow_mcp_gustavo_meilus_mcp_web_snapshot/registry.py,sha256=3Vg-276l8f1SKfqRinxAPRLNQYQBXiGjZxxLh-zVF1c,411
|
|
3
|
+
iflow_mcp_gustavo_meilus_mcp_web_snapshot/server.py,sha256=9Pw4ZGSnl33nkTpvsYFRwfPb5iaBDZxaCWTcebdoSvA,334
|
|
4
|
+
iflow_mcp_gustavo_meilus_mcp_web_snapshot/tools/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
5
|
+
iflow_mcp_gustavo_meilus_mcp_web_snapshot/tools/snapshot_url.py,sha256=PbkfZV7OJawQK79VM5WZUvsd28htDyKAJW1FsrJxx1o,7651
|
|
6
|
+
iflow_mcp_gustavo_meilus_mcp_web_snapshot-0.1.0.dist-info/METADATA,sha256=wZuDOreRp34FPWdRc-BFt7c4x8nM_ZyQkEEhkYsFNu8,8975
|
|
7
|
+
iflow_mcp_gustavo_meilus_mcp_web_snapshot-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
8
|
+
iflow_mcp_gustavo_meilus_mcp_web_snapshot-0.1.0.dist-info/entry_points.txt,sha256=cNWUokXsdOIu2LXgjQatwmmJAmhttrEwe4zWWHjIpY4,91
|
|
9
|
+
iflow_mcp_gustavo_meilus_mcp_web_snapshot-0.1.0.dist-info/licenses/LICENSE,sha256=VUnkswHYoOJ_Yu1pMITTAU49A7dRnew17IBrr0qOqIg,1071
|
|
10
|
+
iflow_mcp_gustavo_meilus_mcp_web_snapshot-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Gustavo Meilus
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|