sallyport 0.3.0__tar.gz
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.
- sallyport-0.3.0/.gitignore +43 -0
- sallyport-0.3.0/LICENSE +21 -0
- sallyport-0.3.0/PKG-INFO +83 -0
- sallyport-0.3.0/README.md +49 -0
- sallyport-0.3.0/pyproject.toml +118 -0
- sallyport-0.3.0/src/sallyport_daemon/__main__.py +428 -0
- sallyport-0.3.0/src/sallyport_daemon/bridge.py +281 -0
- sallyport-0.3.0/src/sallyport_daemon/local_tools.py +192 -0
- sallyport-0.3.0/src/sallyport_daemon/mcp_server.py +375 -0
- sallyport-0.3.0/src/sallyport_daemon/protocol.py +247 -0
- sallyport-0.3.0/src/sallyport_daemon/secret.py +89 -0
- sallyport-0.3.0/tests/__init__.py +0 -0
- sallyport-0.3.0/tests/test_cli_smoke.py +158 -0
- sallyport-0.3.0/tests/test_e2e.py +702 -0
- sallyport-0.3.0/tests/test_local_tools.py +247 -0
- sallyport-0.3.0/tests/test_main.py +567 -0
- sallyport-0.3.0/tests/test_mcp_server.py +209 -0
- sallyport-0.3.0/tests/test_protocol.py +470 -0
- sallyport-0.3.0/tests/test_secret.py +150 -0
- sallyport-0.3.0/tests/test_vectors.py +64 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# OS / editors
|
|
2
|
+
.DS_Store
|
|
3
|
+
*.swp
|
|
4
|
+
|
|
5
|
+
# Python
|
|
6
|
+
__pycache__/
|
|
7
|
+
*.pyc
|
|
8
|
+
*.egg-info/
|
|
9
|
+
build/
|
|
10
|
+
.venv/
|
|
11
|
+
.mypy_cache/
|
|
12
|
+
.ruff_cache/
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
|
|
15
|
+
# Node / extension
|
|
16
|
+
node_modules/
|
|
17
|
+
dist/
|
|
18
|
+
*.log
|
|
19
|
+
coverage/
|
|
20
|
+
.coverage
|
|
21
|
+
.coverage.*
|
|
22
|
+
htmlcov/
|
|
23
|
+
*.tsbuildinfo
|
|
24
|
+
*.zip
|
|
25
|
+
*.crx
|
|
26
|
+
*.pem
|
|
27
|
+
|
|
28
|
+
# Secrets (just in case anyone runs from-repo)
|
|
29
|
+
secret
|
|
30
|
+
.config/
|
|
31
|
+
.env
|
|
32
|
+
.env.*
|
|
33
|
+
|
|
34
|
+
# Local editor / tooling files
|
|
35
|
+
CLAUDE.md
|
|
36
|
+
.claude/
|
|
37
|
+
.mcp.json
|
|
38
|
+
.vscode/
|
|
39
|
+
.idea/
|
|
40
|
+
|
|
41
|
+
# Local-only reference material (not for redistribution)
|
|
42
|
+
kimi-reference/
|
|
43
|
+
ANALYSIS.md
|
sallyport-0.3.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ginkida
|
|
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.
|
sallyport-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sallyport
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: MCP server bridging Claude Code to a browser extension over WS+HMAC
|
|
5
|
+
Project-URL: Homepage, https://github.com/ginkida/sallyport
|
|
6
|
+
Project-URL: Repository, https://github.com/ginkida/sallyport
|
|
7
|
+
Project-URL: Issues, https://github.com/ginkida/sallyport/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/ginkida/sallyport/blob/main/CHANGELOG.md
|
|
9
|
+
Author: ginkida
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: ai-agents,anthropic,browser-automation,chrome,claude,claude-code,hmac,mcp,model-context-protocol,security
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
|
|
22
|
+
Classifier: Topic :: Security
|
|
23
|
+
Classifier: Topic :: Software Development
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Requires-Dist: mcp<2.0.0,>=1.0.0
|
|
26
|
+
Requires-Dist: websockets<17.0,>=13.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# sallyport
|
|
36
|
+
|
|
37
|
+
The Python MCP-server half of **[Sallyport](https://github.com/ginkida/sallyport)** —
|
|
38
|
+
a security-first bridge between Claude Code (or any MCP client) and your Chrome,
|
|
39
|
+
with explicit trust boundaries instead of implicit ones.
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
Claude Code ── MCP/stdio ──▶ daemon (this package) ── WS+HMAC ──▶ extension (MV3) ── CDP ──▶ Chrome
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The daemon speaks MCP on stdio and hosts an HMAC-authenticated WebSocket server
|
|
46
|
+
on `127.0.0.1:10086` that the companion Chrome extension connects into. Every
|
|
47
|
+
frame is signed (HMAC-SHA256 + timestamp + nonce); the extension enforces a
|
|
48
|
+
per-domain allowlist and a per-domain opt-in for arbitrary JS.
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
pip install sallyport
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The package installs the `sallyport-daemon` console command.
|
|
57
|
+
|
|
58
|
+
## Quickstart
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
sallyport-daemon doctor # check Python, secret file + perms, port; print the pairing block
|
|
62
|
+
claude mcp add sallyport sallyport-daemon # register with Claude Code
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Then load the unpacked extension (or the release zip) from the
|
|
66
|
+
[main repository](https://github.com/ginkida/sallyport) and paste the pairing
|
|
67
|
+
block into its popup.
|
|
68
|
+
|
|
69
|
+
- `sallyport-daemon list-tools` — print the tool catalogue (no daemon start)
|
|
70
|
+
- `sallyport-daemon serve` — WS-only mode (no MCP) for wire testing
|
|
71
|
+
- `sallyport-daemon exec <tool> k=v …` — fire one tool from the shell
|
|
72
|
+
|
|
73
|
+
## Security
|
|
74
|
+
|
|
75
|
+
The full threat model, the load-bearing invariants, and known limitations live
|
|
76
|
+
in [SECURITY.md](https://github.com/ginkida/sallyport/blob/main/SECURITY.md).
|
|
77
|
+
In short: loopback-only bind, HMAC on every frame, a domain allowlist enforced
|
|
78
|
+
in the extension, per-domain `evaluate` opt-in, password-field gates, and
|
|
79
|
+
daemon-side filesystem sandboxes for `upload`/`save_to_file`.
|
|
80
|
+
|
|
81
|
+
## License
|
|
82
|
+
|
|
83
|
+
MIT — see [LICENSE](https://github.com/ginkida/sallyport/blob/main/LICENSE).
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# sallyport
|
|
2
|
+
|
|
3
|
+
The Python MCP-server half of **[Sallyport](https://github.com/ginkida/sallyport)** —
|
|
4
|
+
a security-first bridge between Claude Code (or any MCP client) and your Chrome,
|
|
5
|
+
with explicit trust boundaries instead of implicit ones.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Claude Code ── MCP/stdio ──▶ daemon (this package) ── WS+HMAC ──▶ extension (MV3) ── CDP ──▶ Chrome
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The daemon speaks MCP on stdio and hosts an HMAC-authenticated WebSocket server
|
|
12
|
+
on `127.0.0.1:10086` that the companion Chrome extension connects into. Every
|
|
13
|
+
frame is signed (HMAC-SHA256 + timestamp + nonce); the extension enforces a
|
|
14
|
+
per-domain allowlist and a per-domain opt-in for arbitrary JS.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
pip install sallyport
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The package installs the `sallyport-daemon` console command.
|
|
23
|
+
|
|
24
|
+
## Quickstart
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
sallyport-daemon doctor # check Python, secret file + perms, port; print the pairing block
|
|
28
|
+
claude mcp add sallyport sallyport-daemon # register with Claude Code
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Then load the unpacked extension (or the release zip) from the
|
|
32
|
+
[main repository](https://github.com/ginkida/sallyport) and paste the pairing
|
|
33
|
+
block into its popup.
|
|
34
|
+
|
|
35
|
+
- `sallyport-daemon list-tools` — print the tool catalogue (no daemon start)
|
|
36
|
+
- `sallyport-daemon serve` — WS-only mode (no MCP) for wire testing
|
|
37
|
+
- `sallyport-daemon exec <tool> k=v …` — fire one tool from the shell
|
|
38
|
+
|
|
39
|
+
## Security
|
|
40
|
+
|
|
41
|
+
The full threat model, the load-bearing invariants, and known limitations live
|
|
42
|
+
in [SECURITY.md](https://github.com/ginkida/sallyport/blob/main/SECURITY.md).
|
|
43
|
+
In short: loopback-only bind, HMAC on every frame, a domain allowlist enforced
|
|
44
|
+
in the extension, per-domain `evaluate` opt-in, password-field gates, and
|
|
45
|
+
daemon-side filesystem sandboxes for `upload`/`save_to_file`.
|
|
46
|
+
|
|
47
|
+
## License
|
|
48
|
+
|
|
49
|
+
MIT — see [LICENSE](https://github.com/ginkida/sallyport/blob/main/LICENSE).
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "sallyport"
|
|
3
|
+
version = "0.3.0"
|
|
4
|
+
description = "MCP server bridging Claude Code to a browser extension over WS+HMAC"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
requires-python = ">=3.10"
|
|
9
|
+
authors = [{ name = "ginkida" }]
|
|
10
|
+
keywords = [
|
|
11
|
+
"mcp",
|
|
12
|
+
"model-context-protocol",
|
|
13
|
+
"claude",
|
|
14
|
+
"claude-code",
|
|
15
|
+
"anthropic",
|
|
16
|
+
"browser-automation",
|
|
17
|
+
"chrome",
|
|
18
|
+
"security",
|
|
19
|
+
"hmac",
|
|
20
|
+
"ai-agents",
|
|
21
|
+
]
|
|
22
|
+
classifiers = [
|
|
23
|
+
"Development Status :: 4 - Beta",
|
|
24
|
+
"Intended Audience :: Developers",
|
|
25
|
+
"Environment :: Console",
|
|
26
|
+
"Operating System :: OS Independent",
|
|
27
|
+
"Programming Language :: Python :: 3",
|
|
28
|
+
"Programming Language :: Python :: 3.10",
|
|
29
|
+
"Programming Language :: Python :: 3.11",
|
|
30
|
+
"Programming Language :: Python :: 3.12",
|
|
31
|
+
"Topic :: Software Development",
|
|
32
|
+
"Topic :: Internet :: WWW/HTTP :: Browsers",
|
|
33
|
+
"Topic :: Security",
|
|
34
|
+
]
|
|
35
|
+
dependencies = [
|
|
36
|
+
# Upper-bounded at the next major: the MCP SDK churns its API across the
|
|
37
|
+
# 1.x line, so an unbounded floor would let a fresh install pull a future
|
|
38
|
+
# release that breaks startup. Tested against mcp 1.26.0 / websockets 16.0.
|
|
39
|
+
"mcp>=1.0.0,<2.0.0",
|
|
40
|
+
"websockets>=13.0,<17.0",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[project.optional-dependencies]
|
|
44
|
+
dev = [
|
|
45
|
+
"pytest>=8.0",
|
|
46
|
+
"pytest-asyncio>=0.23",
|
|
47
|
+
"pytest-cov>=5.0",
|
|
48
|
+
"ruff>=0.6",
|
|
49
|
+
"mypy>=1.10",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
[project.scripts]
|
|
53
|
+
sallyport-daemon = "sallyport_daemon.__main__:main"
|
|
54
|
+
|
|
55
|
+
[project.urls]
|
|
56
|
+
Homepage = "https://github.com/ginkida/sallyport"
|
|
57
|
+
Repository = "https://github.com/ginkida/sallyport"
|
|
58
|
+
Issues = "https://github.com/ginkida/sallyport/issues"
|
|
59
|
+
Changelog = "https://github.com/ginkida/sallyport/blob/main/CHANGELOG.md"
|
|
60
|
+
|
|
61
|
+
[tool.pytest.ini_options]
|
|
62
|
+
asyncio_mode = "auto"
|
|
63
|
+
testpaths = ["tests"]
|
|
64
|
+
addopts = [
|
|
65
|
+
"--cov=sallyport_daemon",
|
|
66
|
+
"--cov-report=term-missing:skip-covered",
|
|
67
|
+
"--cov-fail-under=80",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
[tool.coverage.run]
|
|
71
|
+
branch = true
|
|
72
|
+
source = ["sallyport_daemon"]
|
|
73
|
+
|
|
74
|
+
[tool.coverage.report]
|
|
75
|
+
exclude_lines = [
|
|
76
|
+
"pragma: no cover",
|
|
77
|
+
"if __name__ == .__main__.:",
|
|
78
|
+
"raise NotImplementedError",
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
[tool.ruff]
|
|
82
|
+
target-version = "py310"
|
|
83
|
+
line-length = 100
|
|
84
|
+
|
|
85
|
+
[tool.ruff.lint]
|
|
86
|
+
select = ["E", "F", "W", "I", "B", "UP", "ASYNC", "S", "PT"]
|
|
87
|
+
ignore = ["S101"] # allow asserts in tests
|
|
88
|
+
|
|
89
|
+
[tool.ruff.lint.per-file-ignores]
|
|
90
|
+
# Tests legitimately: use hardcoded secrets (S105/S106), seed PRNG (S311),
|
|
91
|
+
# run blocking I/O in async contexts (ASYNC230/240), and spawn subprocesses
|
|
92
|
+
# with controlled arg lists (S603).
|
|
93
|
+
"tests/**" = ["S105", "S106", "S311", "S603", "ASYNC230", "ASYNC240"]
|
|
94
|
+
|
|
95
|
+
[tool.mypy]
|
|
96
|
+
python_version = "3.10"
|
|
97
|
+
strict = true
|
|
98
|
+
warn_return_any = true
|
|
99
|
+
warn_unused_ignores = true
|
|
100
|
+
mypy_path = "src"
|
|
101
|
+
packages = ["sallyport_daemon"]
|
|
102
|
+
explicit_package_bases = true
|
|
103
|
+
|
|
104
|
+
[[tool.mypy.overrides]]
|
|
105
|
+
# The mcp SDK's decorators are untyped — silence the noise from server.list_tools / call_tool.
|
|
106
|
+
module = "mcp.*"
|
|
107
|
+
ignore_missing_imports = true
|
|
108
|
+
|
|
109
|
+
[[tool.mypy.overrides]]
|
|
110
|
+
module = "sallyport_daemon.mcp_server"
|
|
111
|
+
disallow_untyped_decorators = false
|
|
112
|
+
|
|
113
|
+
[build-system]
|
|
114
|
+
requires = ["hatchling"]
|
|
115
|
+
build-backend = "hatchling.build"
|
|
116
|
+
|
|
117
|
+
[tool.hatch.build.targets.wheel]
|
|
118
|
+
packages = ["src/sallyport_daemon"]
|