lablink-mcp 0.1.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.
Files changed (64) hide show
  1. lablink_mcp-0.1.0/.github/workflows/ci.yml +31 -0
  2. lablink_mcp-0.1.0/.gitignore +45 -0
  3. lablink_mcp-0.1.0/CHANGELOG.md +69 -0
  4. lablink_mcp-0.1.0/CONTRIBUTING.md +104 -0
  5. lablink_mcp-0.1.0/LICENSE +21 -0
  6. lablink_mcp-0.1.0/PKG-INFO +426 -0
  7. lablink_mcp-0.1.0/README.md +353 -0
  8. lablink_mcp-0.1.0/docs/ARCHITECTURE.md +515 -0
  9. lablink_mcp-0.1.0/docs/agent_docs/agent_development.md +136 -0
  10. lablink_mcp-0.1.0/docs/agent_docs/readme_agent.md +98 -0
  11. lablink_mcp-0.1.0/docs/assets/banner-spec.md +198 -0
  12. lablink_mcp-0.1.0/docs/assets/banner.svg +262 -0
  13. lablink_mcp-0.1.0/examples/.env.example +8 -0
  14. lablink_mcp-0.1.0/examples/configs/external_saleae.toml +28 -0
  15. lablink_mcp-0.1.0/examples/configs/python_shell_env.toml +15 -0
  16. lablink_mcp-0.1.0/examples/configs/rest_daq.toml +28 -0
  17. lablink_mcp-0.1.0/examples/configs/serial_device.toml +25 -0
  18. lablink_mcp-0.1.0/examples/configs/ssh_pi.toml +26 -0
  19. lablink_mcp-0.1.0/examples/configs/visa_scope.toml +25 -0
  20. lablink_mcp-0.1.0/lablink/__init__.py +0 -0
  21. lablink_mcp-0.1.0/lablink/base.py +270 -0
  22. lablink_mcp-0.1.0/lablink/cli.py +135 -0
  23. lablink_mcp-0.1.0/lablink/config.py +124 -0
  24. lablink_mcp-0.1.0/lablink/event_logger.py +88 -0
  25. lablink_mcp-0.1.0/lablink/exceptions.py +9 -0
  26. lablink_mcp-0.1.0/lablink/interfaces/__init__.py +45 -0
  27. lablink_mcp-0.1.0/lablink/interfaces/external_mcp/__init__.py +6 -0
  28. lablink_mcp-0.1.0/lablink/interfaces/external_mcp/config.py +30 -0
  29. lablink_mcp-0.1.0/lablink/interfaces/external_mcp/driver.py +109 -0
  30. lablink_mcp-0.1.0/lablink/interfaces/python_shell/__init__.py +4 -0
  31. lablink_mcp-0.1.0/lablink/interfaces/python_shell/bootstrap.py +144 -0
  32. lablink_mcp-0.1.0/lablink/interfaces/python_shell/config.py +36 -0
  33. lablink_mcp-0.1.0/lablink/interfaces/python_shell/driver.py +709 -0
  34. lablink_mcp-0.1.0/lablink/interfaces/rest/__init__.py +6 -0
  35. lablink_mcp-0.1.0/lablink/interfaces/rest/config.py +31 -0
  36. lablink_mcp-0.1.0/lablink/interfaces/rest/driver.py +647 -0
  37. lablink_mcp-0.1.0/lablink/interfaces/serial/__init__.py +4 -0
  38. lablink_mcp-0.1.0/lablink/interfaces/serial/config.py +37 -0
  39. lablink_mcp-0.1.0/lablink/interfaces/serial/driver.py +571 -0
  40. lablink_mcp-0.1.0/lablink/interfaces/ssh/__init__.py +6 -0
  41. lablink_mcp-0.1.0/lablink/interfaces/ssh/config.py +22 -0
  42. lablink_mcp-0.1.0/lablink/interfaces/ssh/driver.py +759 -0
  43. lablink_mcp-0.1.0/lablink/interfaces/visa/__init__.py +6 -0
  44. lablink_mcp-0.1.0/lablink/interfaces/visa/config.py +26 -0
  45. lablink_mcp-0.1.0/lablink/interfaces/visa/driver.py +528 -0
  46. lablink_mcp-0.1.0/lablink/mcp_server.py +420 -0
  47. lablink_mcp-0.1.0/lablink/py.typed +0 -0
  48. lablink_mcp-0.1.0/lablink/session.py +72 -0
  49. lablink_mcp-0.1.0/pyproject.toml +65 -0
  50. lablink_mcp-0.1.0/server.json +37 -0
  51. lablink_mcp-0.1.0/tests/__init__.py +0 -0
  52. lablink_mcp-0.1.0/tests/conftest.py +19 -0
  53. lablink_mcp-0.1.0/tests/interfaces/__init__.py +0 -0
  54. lablink_mcp-0.1.0/tests/interfaces/test_external.py +199 -0
  55. lablink_mcp-0.1.0/tests/interfaces/test_python_shell.py +828 -0
  56. lablink_mcp-0.1.0/tests/interfaces/test_rest.py +462 -0
  57. lablink_mcp-0.1.0/tests/interfaces/test_serial.py +466 -0
  58. lablink_mcp-0.1.0/tests/interfaces/test_ssh.py +811 -0
  59. lablink_mcp-0.1.0/tests/interfaces/test_visa.py +295 -0
  60. lablink_mcp-0.1.0/tests/test_config.py +124 -0
  61. lablink_mcp-0.1.0/tests/test_dispatch.py +119 -0
  62. lablink_mcp-0.1.0/tests/test_fastmcp_late_registration.py +38 -0
  63. lablink_mcp-0.1.0/tests/test_logger.py +75 -0
  64. lablink_mcp-0.1.0/tests/test_shared_tools.py +197 -0
@@ -0,0 +1,31 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python ${{ matrix.python-version }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Install uv
25
+ uses: astral-sh/setup-uv@v4
26
+
27
+ - name: Install dependencies
28
+ run: uv pip install -e ".[dev]" --system
29
+
30
+ - name: Run tests
31
+ run: pytest tests/ -v
@@ -0,0 +1,45 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.pyo
5
+ *.pyc
6
+
7
+ # Virtual environments
8
+ .venv/
9
+ venv/
10
+ env/
11
+
12
+ # Distribution / packaging
13
+ dist/
14
+ build/
15
+ *.egg-info/
16
+ *.egg
17
+
18
+ # Pytest
19
+ .pytest_cache/
20
+
21
+ # Coverage
22
+ .coverage
23
+ htmlcov/
24
+
25
+ # Environment files
26
+ .env
27
+ .env.*
28
+ !examples/.env.example
29
+ uv.lock
30
+
31
+ # Editors
32
+ .vscode/
33
+ .idea/
34
+ *.swp
35
+ *.swo
36
+
37
+ # macOS
38
+ .DS_Store
39
+
40
+ # Claude Code local config
41
+ .claude/
42
+
43
+ # Device configs (user-local, not for version control)
44
+ # ~/.lablink/ lives outside the repo, but guard against accidents
45
+ *.toml.local
@@ -0,0 +1,69 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format is based on
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
5
+ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0] - 2026-05-30
10
+
11
+ First release of LabLink: five protocol drivers on a shared multi-driver
12
+ dispatch core, exposed to AI agents over MCP and to developers over a CLI.
13
+
14
+ ### Added
15
+
16
+ **Drivers**
17
+
18
+ | Driver | Transport | Operation tools |
19
+ |--------|-----------|----------------|
20
+ | `visa` | PyVISA (USB-TMC, TCPIP, GPIB, Serial-VISA) | `visa_query`, `visa_write` |
21
+ | `ssh` | Paramiko | `ssh_exec`, `ssh_shell_session`, `ssh_start_stream`, `ssh_read_stream`, `ssh_stop_stream` |
22
+ | `rest` | httpx | `rest_get`, `rest_post`, `rest_put`, `rest_patch`, `rest_delete` |
23
+ | `serial` | pyserial (RS232/RS422/RS485) | `serial_query`, `serial_write`, `serial_read`, `serial_flush` |
24
+ | `python_shell` | subprocess | `python_shell_exec`, `python_shell_eval` |
25
+
26
+ An `external` routing stub lets a device be handled by a vendor-supplied MCP
27
+ server, surfacing routing hints to the agent on `connect()`.
28
+
29
+ **SSH streaming.** `ssh_start_stream` / `ssh_read_stream` / `ssh_stop_stream`
30
+ buffer the output of long-running commands (log tails, continuous monitors) in a
31
+ bounded background queue, with clean teardown on stop and disconnect.
32
+
33
+ **python_shell.** A persistent subprocess REPL bound to a user-supplied
34
+ interpreter (`python_path`), bridging to vendor SDKs such as `nidaqmx` and
35
+ `picosdk` that have no VISA or network interface. State persists across calls
36
+ within a session. Communicates over a newline-delimited JSON wire protocol with
37
+ timeout, crash, and busy-state recovery.
38
+
39
+ **Architecture**
40
+
41
+ - Shared lifecycle tools (`connect`, `disconnect`, `list_devices`, `diagnose`)
42
+ dispatch via the config `type` field across all drivers.
43
+ - Per-driver operation tools register only when that driver's Python
44
+ dependencies are installed — missing deps are surfaced by `diagnose()`, never
45
+ a server crash.
46
+ - Driver ABC (`LabLinkDriver[ConfigT]`) with a `Generic[ConfigT]` session model.
47
+ - `AuthConfig` mixin for drivers that need credentials (SSH, REST), referenced by
48
+ environment variable name only — secrets never live in config files.
49
+ - `DocumentedConfig` mixin carrying `techmanual_document_ids` for
50
+ [techmanual.ai](https://techmanual.ai) integration on T&M instruments.
51
+ - JSONL event log with canonical `ts` / `op` / `alias` / `success` fields.
52
+ - Three-state session lookup (missing / wrong type / found) for precise error
53
+ hints.
54
+ - Optional dependency extras per driver (`[visa]`, `[ssh]`, `[rest]`, `[serial]`,
55
+ `[python_shell]`, `[all]`); the server runs with zero drivers installed.
56
+ - CLI mirroring the MCP tool surface for development and debugging, with the same
57
+ dependency gating.
58
+
59
+ ### Validated
60
+
61
+ - **VISA** — end-to-end on a Siglent SDS1104X-E (connect / diagnose / query /
62
+ write / device memory / event log).
63
+ - **SSH** — unit tests plus a hardware smoke test on a Raspberry Pi 4 (exec,
64
+ shell session, and live streaming of an `rtl_433` capture).
65
+ - **REST** — live against a public API across all five HTTP verbs.
66
+ - **Serial** — unit tests with a mocked `serial.Serial`.
67
+ - **python_shell** — unit tests plus real-subprocess integration tests exercising
68
+ the JSON wire protocol (exec, eval, exception/traceback, namespace persistence,
69
+ stdout capture, shutdown).
@@ -0,0 +1,104 @@
1
+ # Contributing to LabLink
2
+
3
+ Thank you for considering a contribution. This document covers the essentials
4
+ for getting oriented, making changes, and submitting them.
5
+
6
+ ---
7
+
8
+ ## Getting started
9
+
10
+ ```bash
11
+ git clone https://github.com/techmanual-ai/lablink-mcp
12
+ cd lablink-mcp
13
+ uv venv
14
+ uv pip install -e ".[dev]"
15
+ ```
16
+
17
+ Run the tests to confirm your environment is clean:
18
+
19
+ ```bash
20
+ pytest tests/
21
+ ```
22
+
23
+ All tests mock hardware drivers — no real instruments required.
24
+
25
+ ---
26
+
27
+ ## Project structure
28
+
29
+ ```text
30
+ lablink/
31
+ ├── mcp_server.py # FastMCP entrypoint; shared lifecycle tools
32
+ ├── cli.py # Click root; shared lifecycle commands
33
+ ├── base.py # Data models, config dataclasses, driver ABC
34
+ ├── config.py # TOML loader
35
+ ├── session.py # Session registry
36
+ ├── event_logger.py # JSONL event log
37
+ ├── exceptions.py # ConfigError, SessionError, DriverError
38
+ └── interfaces/
39
+ ├── visa/
40
+ ├── ssh/
41
+ ├── rest/
42
+ ├── serial/
43
+ ├── python_shell/
44
+ └── external_mcp/
45
+ ```
46
+
47
+ See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the full design.
48
+
49
+ ---
50
+
51
+ ## Adding a driver
52
+
53
+ 1. Create `lablink/interfaces/<type>/` with `driver.py`, `config.py`, `__init__.py`.
54
+ 2. Subclass `LabLinkDriver[YourConfig]`; implement `connect`, `disconnect`,
55
+ `diagnose`, `register_tools`, `register_cli_commands`.
56
+ 3. Subclass `DriverConfig` (`@dataclass(kw_only=True)`).
57
+ 4. Register it — one line in each of `DRIVER_REGISTRY` and
58
+ `DRIVER_CONFIG_REGISTRY` in `lablink/interfaces/__init__.py`.
59
+ 5. Write `tests/interfaces/test_<type>.py` with full mock coverage.
60
+ 6. Add `examples/configs/<type>_device.toml`.
61
+
62
+ No changes to `lablink/mcp_server.py` or `lablink/cli.py` are required.
63
+
64
+ ---
65
+
66
+ ## Coding standards
67
+
68
+ - Python 3.10+, PEP 8, strict type hints on all signatures.
69
+ - Google-style docstrings. MCP tool docstrings are load-bearing: they are
70
+ surfaced to agents as the tool description.
71
+ - Lazy-import all third-party driver deps inside `connect()`. Missing deps must
72
+ return a structured `{"success": false, "error": ..., "hint": ...}` dict —
73
+ never raise across the MCP boundary.
74
+ - Every tool call logs via `event_logger.log_event()`. Logging must never raise.
75
+ - Tests use `unittest.mock` to mock driver libraries. No real connections in CI.
76
+ - `@dataclass(kw_only=True)` is mandatory on every config dataclass and result
77
+ type (see [docs/ARCHITECTURE.md §5.1](docs/ARCHITECTURE.md)).
78
+
79
+ ---
80
+
81
+ ## Commit style
82
+
83
+ - Imperative mood: `Add REST driver`, `Fix VISA timeout reset`.
84
+ - One feature or fix per commit.
85
+ - Do not skip pre-commit hooks (`--no-verify`).
86
+
87
+ ---
88
+
89
+ ## Submitting a pull request
90
+
91
+ 1. Fork the repo and create a branch from `main`.
92
+ 2. Make your changes and add tests.
93
+ 3. Run `pytest tests/` and confirm it passes.
94
+ 4. Open a pull request with a clear description of what changed and why.
95
+
96
+ For significant changes or new drivers, open an issue first to discuss the
97
+ approach before investing time in implementation.
98
+
99
+ ---
100
+
101
+ ## License
102
+
103
+ By contributing, you agree that your contributions will be licensed under the
104
+ [MIT License](LICENSE).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 techmanual-ai
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.