mcp-edge 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.
- mcp_edge-0.1.0/.env.example +18 -0
- mcp_edge-0.1.0/.github/workflows/ci.yml +39 -0
- mcp_edge-0.1.0/.gitignore +58 -0
- mcp_edge-0.1.0/CHANGELOG.md +36 -0
- mcp_edge-0.1.0/LICENSE +21 -0
- mcp_edge-0.1.0/PKG-INFO +152 -0
- mcp_edge-0.1.0/README.md +107 -0
- mcp_edge-0.1.0/pyproject.toml +81 -0
- mcp_edge-0.1.0/src/mcp_edge/__init__.py +12 -0
- mcp_edge-0.1.0/src/mcp_edge/buffer.py +60 -0
- mcp_edge-0.1.0/src/mcp_edge/cli.py +108 -0
- mcp_edge-0.1.0/src/mcp_edge/client.py +72 -0
- mcp_edge-0.1.0/src/mcp_edge/codec.py +44 -0
- mcp_edge-0.1.0/src/mcp_edge/devices/__init__.py +7 -0
- mcp_edge-0.1.0/src/mcp_edge/devices/simulator.py +97 -0
- mcp_edge-0.1.0/src/mcp_edge/gateway.py +88 -0
- mcp_edge-0.1.0/src/mcp_edge/health.py +67 -0
- mcp_edge-0.1.0/src/mcp_edge/mcplite.py +99 -0
- mcp_edge-0.1.0/src/mcp_edge/pool.py +64 -0
- mcp_edge-0.1.0/src/mcp_edge/registry.py +68 -0
- mcp_edge-0.1.0/src/mcp_edge/schema.py +46 -0
- mcp_edge-0.1.0/src/mcp_edge/server.py +76 -0
- mcp_edge-0.1.0/src/mcp_edge/tiers.py +148 -0
- mcp_edge-0.1.0/src/mcp_edge/transports/__init__.py +8 -0
- mcp_edge-0.1.0/src/mcp_edge/transports/base.py +55 -0
- mcp_edge-0.1.0/src/mcp_edge/transports/loopback.py +47 -0
- mcp_edge-0.1.0/tests/__init__.py +1 -0
- mcp_edge-0.1.0/tests/test_buffer.py +83 -0
- mcp_edge-0.1.0/tests/test_cli.py +22 -0
- mcp_edge-0.1.0/tests/test_client.py +42 -0
- mcp_edge-0.1.0/tests/test_codec.py +34 -0
- mcp_edge-0.1.0/tests/test_gateway.py +69 -0
- mcp_edge-0.1.0/tests/test_health.py +66 -0
- mcp_edge-0.1.0/tests/test_mcplite.py +38 -0
- mcp_edge-0.1.0/tests/test_pool.py +60 -0
- mcp_edge-0.1.0/tests/test_registry.py +61 -0
- mcp_edge-0.1.0/tests/test_schema.py +76 -0
- mcp_edge-0.1.0/tests/test_server.py +49 -0
- mcp_edge-0.1.0/tests/test_simulator.py +60 -0
- mcp_edge-0.1.0/tests/test_smoke.py +16 -0
- mcp_edge-0.1.0/tests/test_tiers.py +54 -0
- mcp_edge-0.1.0/tests/test_transports.py +49 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# MCP-Edge example environment variables.
|
|
2
|
+
# Copy to `.env` and fill in your own values. NEVER commit your real `.env`.
|
|
3
|
+
|
|
4
|
+
# Wokwi CI — firmware-in-the-loop simulation (v0.1-0.2).
|
|
5
|
+
# Create a token at https://wokwi.com/dashboard/ci
|
|
6
|
+
WOKWI_CLI_TOKEN=
|
|
7
|
+
|
|
8
|
+
# Edge Impulse — Tier-4 inference example (v0.2).
|
|
9
|
+
# Project API key: EI project > Dashboard > Keys tab.
|
|
10
|
+
EI_API_KEY=
|
|
11
|
+
|
|
12
|
+
# Arduino IoT Cloud — device-source example (v0.2).
|
|
13
|
+
# OAuth2 client credentials: IoT Cloud > API keys.
|
|
14
|
+
ARDUINO_CLIENT_ID=
|
|
15
|
+
ARDUINO_CLIENT_SECRET=
|
|
16
|
+
|
|
17
|
+
# Note: your PyPI token lives in your own environment / keyring for `twine`.
|
|
18
|
+
# It is never stored in this repository.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: ${{ matrix.python-version }}
|
|
20
|
+
- name: Install
|
|
21
|
+
run: |
|
|
22
|
+
python -m pip install --upgrade pip
|
|
23
|
+
pip install -e ".[dev]"
|
|
24
|
+
- name: Lint
|
|
25
|
+
run: ruff check .
|
|
26
|
+
- name: Test
|
|
27
|
+
run: pytest -q --cov=mcp_edge --cov-report=term-missing
|
|
28
|
+
|
|
29
|
+
build:
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v4
|
|
33
|
+
- uses: actions/setup-python@v5
|
|
34
|
+
with:
|
|
35
|
+
python-version: "3.12"
|
|
36
|
+
- name: Build sdist and wheel
|
|
37
|
+
run: |
|
|
38
|
+
python -m pip install --upgrade pip build
|
|
39
|
+
python -m build
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Byte-compiled / optimized / cache
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
|
|
7
|
+
# Distribution / packaging
|
|
8
|
+
.Python
|
|
9
|
+
build/
|
|
10
|
+
dist/
|
|
11
|
+
downloads/
|
|
12
|
+
wheels/
|
|
13
|
+
*.egg-info/
|
|
14
|
+
.eggs/
|
|
15
|
+
*.egg
|
|
16
|
+
MANIFEST
|
|
17
|
+
|
|
18
|
+
# Virtual environments
|
|
19
|
+
.venv/
|
|
20
|
+
venv/
|
|
21
|
+
env/
|
|
22
|
+
ENV/
|
|
23
|
+
|
|
24
|
+
# Environment files / secrets / credentials
|
|
25
|
+
.env
|
|
26
|
+
.env.*
|
|
27
|
+
!.env.example
|
|
28
|
+
*.key
|
|
29
|
+
*.pem
|
|
30
|
+
*secret*
|
|
31
|
+
*secrets*
|
|
32
|
+
*.eim
|
|
33
|
+
|
|
34
|
+
# Testing / coverage
|
|
35
|
+
.pytest_cache/
|
|
36
|
+
.coverage
|
|
37
|
+
.coverage.*
|
|
38
|
+
htmlcov/
|
|
39
|
+
coverage.xml
|
|
40
|
+
.tox/
|
|
41
|
+
.cache/
|
|
42
|
+
|
|
43
|
+
# Type checking / linting
|
|
44
|
+
.mypy_cache/
|
|
45
|
+
.ruff_cache/
|
|
46
|
+
.dmypy.json
|
|
47
|
+
|
|
48
|
+
# Editors / OS
|
|
49
|
+
.vscode/
|
|
50
|
+
.idea/
|
|
51
|
+
*.swp
|
|
52
|
+
.DS_Store
|
|
53
|
+
Thumbs.db
|
|
54
|
+
|
|
55
|
+
# Build / sim artifacts / logs
|
|
56
|
+
*.log
|
|
57
|
+
*.vcd
|
|
58
|
+
screenshot*.png
|
|
@@ -0,0 +1,36 @@
|
|
|
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 adheres to
|
|
5
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0] - 2026-06-25
|
|
10
|
+
|
|
11
|
+
First public release. Alpha: the API is unstable and will change.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- Four-tier device taxonomy (`tiers`) mapping RFC 7228 device classes to MCP
|
|
15
|
+
strategies, with nine reference platforms.
|
|
16
|
+
- CBOR codec (`codec`) for the device link, plus a JSON size-comparison helper.
|
|
17
|
+
- Transport abstraction (`transports`) and an in-process loopback transport.
|
|
18
|
+
- MCP-Lite protocol (`mcplite`): CBOR-framed `tools/list`, `tools/call`,
|
|
19
|
+
`resources/read`.
|
|
20
|
+
- Device simulator (`devices.SimulatedDevice`) for hardware-free development and tests.
|
|
21
|
+
- Device client layer (`client`): the `DeviceClient` interface and `MCPLiteClient`.
|
|
22
|
+
- Device registry (`registry`) and the aggregating `Gateway`, which exposes every
|
|
23
|
+
device's tools under a device-prefixed composite namespace and routes calls back.
|
|
24
|
+
- Gateway exposed as an MCP server (`server.build_server`, `run_stdio`) built on the
|
|
25
|
+
SDK's low-level `Server`.
|
|
26
|
+
- `mcp-edge` CLI with `run [--demo]` to serve a gateway (optionally preloaded with
|
|
27
|
+
simulated devices) over stdio.
|
|
28
|
+
- Protocol adaptations: schema caching (`schema`), connection pooling (`pool`), and
|
|
29
|
+
offline request buffering (`buffer`).
|
|
30
|
+
- Health monitor (`health`) that probes devices and updates their connection state.
|
|
31
|
+
|
|
32
|
+
### Notes
|
|
33
|
+
- Real serial / BLE / Wi-Fi transports (and firmware-in-the-loop testing) arrive in a
|
|
34
|
+
later release; this version exercises the full path against simulated devices.
|
|
35
|
+
- Performance figures in the MCP-Edge paper are projected estimates, not measurements
|
|
36
|
+
taken from this code.
|
mcp_edge-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Muntaser Syed, Sheikh Abujar, and Sharun Akter
|
|
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.
|
mcp_edge-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mcp-edge
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Extend the Model Context Protocol (MCP) to edge and IoT devices: a bridging gateway, a lightweight MCP-Lite server, and a four-tier device taxonomy.
|
|
5
|
+
Project-URL: Homepage, https://github.com/jemsbhai/mcp-edge
|
|
6
|
+
Project-URL: Repository, https://github.com/jemsbhai/mcp-edge
|
|
7
|
+
Project-URL: Issues, https://github.com/jemsbhai/mcp-edge/issues
|
|
8
|
+
Author: Muntaser Syed, Sheikh Abujar, Sharun Akter
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agents,edge,embedded,gateway,iot,llm,mcp,model-context-protocol
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Internet
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Topic :: System :: Hardware
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: cbor2>=5.6
|
|
25
|
+
Requires-Dist: mcp<2,>=1.4
|
|
26
|
+
Provides-Extra: all
|
|
27
|
+
Requires-Dist: bleak>=0.22; extra == 'all'
|
|
28
|
+
Requires-Dist: pyserial>=3.5; extra == 'all'
|
|
29
|
+
Requires-Dist: zeroconf>=0.131; extra == 'all'
|
|
30
|
+
Provides-Extra: ble
|
|
31
|
+
Requires-Dist: bleak>=0.22; extra == 'ble'
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
34
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
39
|
+
Requires-Dist: twine>=5.0; extra == 'dev'
|
|
40
|
+
Provides-Extra: serial
|
|
41
|
+
Requires-Dist: pyserial>=3.5; extra == 'serial'
|
|
42
|
+
Provides-Extra: wifi
|
|
43
|
+
Requires-Dist: zeroconf>=0.131; extra == 'wifi'
|
|
44
|
+
Description-Content-Type: text/markdown
|
|
45
|
+
|
|
46
|
+
# MCP-Edge
|
|
47
|
+
|
|
48
|
+
[](https://github.com/jemsbhai/mcp-edge/actions/workflows/ci.yml)
|
|
49
|
+
|
|
50
|
+
**Extend the [Model Context Protocol (MCP)](https://modelcontextprotocol.io) to edge and IoT devices.**
|
|
51
|
+
|
|
52
|
+
MCP-Edge lets cloud LLM agents discover and invoke physical hardware through the same
|
|
53
|
+
tool interface they already use for software APIs. It provides:
|
|
54
|
+
|
|
55
|
+
- a **gateway** that bridges cloud-native MCP transports (SSE / HTTP) to constrained
|
|
56
|
+
device channels (UART, BLE, local Wi-Fi), presenting every downstream device as a
|
|
57
|
+
standard MCP tool provider;
|
|
58
|
+
- **MCP-Lite**, a lightweight MCP server for devices with as little as ~512 KB of RAM;
|
|
59
|
+
- a **four-tier device taxonomy** (constrained MCUs, smart IoT nodes, BLE-only
|
|
60
|
+
wearables, Linux-class edge computers) that maps an MCP strategy to each tier;
|
|
61
|
+
- protocol adaptations for constrained links: CBOR encoding, schema caching,
|
|
62
|
+
connection pooling, and offline request buffering.
|
|
63
|
+
|
|
64
|
+
> **Status: alpha, under active development.** The public API is unstable and will
|
|
65
|
+
> change. This repository is a *reference implementation* of the framework described in
|
|
66
|
+
> the MCP-Edge paper (IEEE Cloud Summit 2026). Performance figures reported in the paper
|
|
67
|
+
> are projected estimates, not measurements taken from this codebase. This release
|
|
68
|
+
> exercises the full gateway path against *simulated* devices; real serial/BLE/Wi-Fi
|
|
69
|
+
> transports arrive in a later version.
|
|
70
|
+
|
|
71
|
+
## Installation
|
|
72
|
+
|
|
73
|
+
Requires Python 3.10+.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pip install mcp-edge
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
For development, install from source:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
git clone https://github.com/jemsbhai/mcp-edge
|
|
83
|
+
cd mcp-edge
|
|
84
|
+
pip install -e ".[dev]"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Quickstart
|
|
88
|
+
|
|
89
|
+
**Run a demo gateway** — an MCP server exposing two simulated devices over stdio:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
mcp-edge run --demo
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
This serves a simulated sensor (`read_temp`, `read_humidity`) and a simulated ring
|
|
96
|
+
(`heart_rate`), each tool namespaced by device (`sensor-01/read_temp`, ...). To drive it
|
|
97
|
+
from an MCP client such as the MCP Inspector or Claude Desktop, configure a stdio server
|
|
98
|
+
with command `mcp-edge` and arguments `["run", "--demo"]`. The process logs to stderr and
|
|
99
|
+
waits for a client on stdin.
|
|
100
|
+
|
|
101
|
+
**Use the gateway as a library:**
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
import asyncio
|
|
105
|
+
|
|
106
|
+
from mcp_edge.client import MCPLiteClient
|
|
107
|
+
from mcp_edge.devices import SimulatedDevice
|
|
108
|
+
from mcp_edge.gateway import Gateway
|
|
109
|
+
from mcp_edge.registry import DeviceRegistry
|
|
110
|
+
from mcp_edge.tiers import Tier
|
|
111
|
+
from mcp_edge.transports import LoopbackTransport
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
async def main() -> None:
|
|
115
|
+
device = SimulatedDevice("sensor-01")
|
|
116
|
+
device.add_tool("read_temp", lambda args: {"celsius": 21.5})
|
|
117
|
+
|
|
118
|
+
transport = LoopbackTransport(device.handle)
|
|
119
|
+
await transport.open()
|
|
120
|
+
|
|
121
|
+
registry = DeviceRegistry()
|
|
122
|
+
registry.register("sensor-01", MCPLiteClient(transport), Tier.SMART_NODE)
|
|
123
|
+
|
|
124
|
+
gateway = Gateway(registry)
|
|
125
|
+
print([tool["name"] for tool in await gateway.list_tools()]) # ['sensor-01/read_temp']
|
|
126
|
+
print(await gateway.call_tool("sensor-01/read_temp", {})) # {'celsius': 21.5}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
asyncio.run(main())
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Roadmap
|
|
133
|
+
|
|
134
|
+
- [x] **v0.1** — gateway core, in-process (loopback) transport, protocol adaptations
|
|
135
|
+
(CBOR, schema caching, connection pooling, offline buffering), device simulator,
|
|
136
|
+
health monitor, CLI, hermetic CI
|
|
137
|
+
- [ ] **v0.2** — real transports (`pyserial` UART/USB, BLE, Wi-Fi/mDNS) and
|
|
138
|
+
[Wokwi](https://wokwi.com) firmware-in-the-loop tests (Arduino / ESP32 / RP2040)
|
|
139
|
+
- [ ] **v0.2+** — Edge Impulse (inference as an MCP tool) and Arduino IoT Cloud
|
|
140
|
+
(properties as MCP) integration examples; Renode / QEMU backends
|
|
141
|
+
|
|
142
|
+
## Development
|
|
143
|
+
|
|
144
|
+
```powershell
|
|
145
|
+
pip install -e ".[dev]"
|
|
146
|
+
pytest -q
|
|
147
|
+
ruff check .
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## License
|
|
151
|
+
|
|
152
|
+
MIT — see [LICENSE](LICENSE).
|
mcp_edge-0.1.0/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# MCP-Edge
|
|
2
|
+
|
|
3
|
+
[](https://github.com/jemsbhai/mcp-edge/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
**Extend the [Model Context Protocol (MCP)](https://modelcontextprotocol.io) to edge and IoT devices.**
|
|
6
|
+
|
|
7
|
+
MCP-Edge lets cloud LLM agents discover and invoke physical hardware through the same
|
|
8
|
+
tool interface they already use for software APIs. It provides:
|
|
9
|
+
|
|
10
|
+
- a **gateway** that bridges cloud-native MCP transports (SSE / HTTP) to constrained
|
|
11
|
+
device channels (UART, BLE, local Wi-Fi), presenting every downstream device as a
|
|
12
|
+
standard MCP tool provider;
|
|
13
|
+
- **MCP-Lite**, a lightweight MCP server for devices with as little as ~512 KB of RAM;
|
|
14
|
+
- a **four-tier device taxonomy** (constrained MCUs, smart IoT nodes, BLE-only
|
|
15
|
+
wearables, Linux-class edge computers) that maps an MCP strategy to each tier;
|
|
16
|
+
- protocol adaptations for constrained links: CBOR encoding, schema caching,
|
|
17
|
+
connection pooling, and offline request buffering.
|
|
18
|
+
|
|
19
|
+
> **Status: alpha, under active development.** The public API is unstable and will
|
|
20
|
+
> change. This repository is a *reference implementation* of the framework described in
|
|
21
|
+
> the MCP-Edge paper (IEEE Cloud Summit 2026). Performance figures reported in the paper
|
|
22
|
+
> are projected estimates, not measurements taken from this codebase. This release
|
|
23
|
+
> exercises the full gateway path against *simulated* devices; real serial/BLE/Wi-Fi
|
|
24
|
+
> transports arrive in a later version.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
Requires Python 3.10+.
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install mcp-edge
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
For development, install from source:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
git clone https://github.com/jemsbhai/mcp-edge
|
|
38
|
+
cd mcp-edge
|
|
39
|
+
pip install -e ".[dev]"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quickstart
|
|
43
|
+
|
|
44
|
+
**Run a demo gateway** — an MCP server exposing two simulated devices over stdio:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
mcp-edge run --demo
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
This serves a simulated sensor (`read_temp`, `read_humidity`) and a simulated ring
|
|
51
|
+
(`heart_rate`), each tool namespaced by device (`sensor-01/read_temp`, ...). To drive it
|
|
52
|
+
from an MCP client such as the MCP Inspector or Claude Desktop, configure a stdio server
|
|
53
|
+
with command `mcp-edge` and arguments `["run", "--demo"]`. The process logs to stderr and
|
|
54
|
+
waits for a client on stdin.
|
|
55
|
+
|
|
56
|
+
**Use the gateway as a library:**
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
import asyncio
|
|
60
|
+
|
|
61
|
+
from mcp_edge.client import MCPLiteClient
|
|
62
|
+
from mcp_edge.devices import SimulatedDevice
|
|
63
|
+
from mcp_edge.gateway import Gateway
|
|
64
|
+
from mcp_edge.registry import DeviceRegistry
|
|
65
|
+
from mcp_edge.tiers import Tier
|
|
66
|
+
from mcp_edge.transports import LoopbackTransport
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
async def main() -> None:
|
|
70
|
+
device = SimulatedDevice("sensor-01")
|
|
71
|
+
device.add_tool("read_temp", lambda args: {"celsius": 21.5})
|
|
72
|
+
|
|
73
|
+
transport = LoopbackTransport(device.handle)
|
|
74
|
+
await transport.open()
|
|
75
|
+
|
|
76
|
+
registry = DeviceRegistry()
|
|
77
|
+
registry.register("sensor-01", MCPLiteClient(transport), Tier.SMART_NODE)
|
|
78
|
+
|
|
79
|
+
gateway = Gateway(registry)
|
|
80
|
+
print([tool["name"] for tool in await gateway.list_tools()]) # ['sensor-01/read_temp']
|
|
81
|
+
print(await gateway.call_tool("sensor-01/read_temp", {})) # {'celsius': 21.5}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
asyncio.run(main())
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Roadmap
|
|
88
|
+
|
|
89
|
+
- [x] **v0.1** — gateway core, in-process (loopback) transport, protocol adaptations
|
|
90
|
+
(CBOR, schema caching, connection pooling, offline buffering), device simulator,
|
|
91
|
+
health monitor, CLI, hermetic CI
|
|
92
|
+
- [ ] **v0.2** — real transports (`pyserial` UART/USB, BLE, Wi-Fi/mDNS) and
|
|
93
|
+
[Wokwi](https://wokwi.com) firmware-in-the-loop tests (Arduino / ESP32 / RP2040)
|
|
94
|
+
- [ ] **v0.2+** — Edge Impulse (inference as an MCP tool) and Arduino IoT Cloud
|
|
95
|
+
(properties as MCP) integration examples; Renode / QEMU backends
|
|
96
|
+
|
|
97
|
+
## Development
|
|
98
|
+
|
|
99
|
+
```powershell
|
|
100
|
+
pip install -e ".[dev]"
|
|
101
|
+
pytest -q
|
|
102
|
+
ruff check .
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "mcp-edge"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Extend the Model Context Protocol (MCP) to edge and IoT devices: a bridging gateway, a lightweight MCP-Lite server, and a four-tier device taxonomy."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Muntaser Syed" },
|
|
14
|
+
{ name = "Sheikh Abujar" },
|
|
15
|
+
{ name = "Sharun Akter" },
|
|
16
|
+
]
|
|
17
|
+
keywords = ["mcp", "model-context-protocol", "edge", "iot", "llm", "agents", "gateway", "embedded"]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 3 - Alpha",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"License :: OSI Approved :: MIT License",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Topic :: Internet",
|
|
28
|
+
"Topic :: System :: Hardware",
|
|
29
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
30
|
+
]
|
|
31
|
+
dependencies = [
|
|
32
|
+
"mcp>=1.4,<2",
|
|
33
|
+
"cbor2>=5.6",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
serial = ["pyserial>=3.5"]
|
|
38
|
+
ble = ["bleak>=0.22"]
|
|
39
|
+
wifi = ["zeroconf>=0.131"]
|
|
40
|
+
all = ["mcp-edge[serial,ble,wifi]"]
|
|
41
|
+
dev = [
|
|
42
|
+
"pytest>=8.0",
|
|
43
|
+
"pytest-asyncio>=0.23",
|
|
44
|
+
"pytest-cov>=5.0",
|
|
45
|
+
"ruff>=0.6",
|
|
46
|
+
"mypy>=1.10",
|
|
47
|
+
"build>=1.2",
|
|
48
|
+
"twine>=5.0",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[project.scripts]
|
|
52
|
+
mcp-edge = "mcp_edge.cli:main"
|
|
53
|
+
|
|
54
|
+
[project.urls]
|
|
55
|
+
Homepage = "https://github.com/jemsbhai/mcp-edge"
|
|
56
|
+
Repository = "https://github.com/jemsbhai/mcp-edge"
|
|
57
|
+
Issues = "https://github.com/jemsbhai/mcp-edge/issues"
|
|
58
|
+
|
|
59
|
+
[tool.hatch.version]
|
|
60
|
+
path = "src/mcp_edge/__init__.py"
|
|
61
|
+
|
|
62
|
+
[tool.hatch.build.targets.wheel]
|
|
63
|
+
packages = ["src/mcp_edge"]
|
|
64
|
+
|
|
65
|
+
[tool.pytest.ini_options]
|
|
66
|
+
testpaths = ["tests"]
|
|
67
|
+
asyncio_mode = "auto"
|
|
68
|
+
addopts = "-ra"
|
|
69
|
+
|
|
70
|
+
[tool.ruff]
|
|
71
|
+
line-length = 100
|
|
72
|
+
target-version = "py310"
|
|
73
|
+
src = ["src", "tests"]
|
|
74
|
+
|
|
75
|
+
[tool.ruff.lint]
|
|
76
|
+
select = ["E", "F", "I", "UP", "B", "W"]
|
|
77
|
+
|
|
78
|
+
[tool.mypy]
|
|
79
|
+
python_version = "3.10"
|
|
80
|
+
ignore_missing_imports = true
|
|
81
|
+
warn_unused_ignores = true
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""MCP-Edge: extend the Model Context Protocol (MCP) to edge and IoT devices.
|
|
2
|
+
|
|
3
|
+
An early, in-development reference implementation of the MCP-Edge framework:
|
|
4
|
+
a gateway that bridges cloud-native MCP transports to constrained-device
|
|
5
|
+
channels, a lightweight MCP-Lite server, and a four-tier device taxonomy.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
__version__ = "0.1.0"
|
|
11
|
+
|
|
12
|
+
__all__ = ["__version__"]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Offline request buffering.
|
|
2
|
+
|
|
3
|
+
When a device is briefly unreachable, the gateway can buffer operations destined for it
|
|
4
|
+
and replay them in order once it returns, rather than dropping them. The buffer is a
|
|
5
|
+
bounded FIFO; on overflow it raises rather than silently discarding, and a flush that
|
|
6
|
+
fails part-way leaves the failed item (and the rest) queued for the next attempt.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from collections import deque
|
|
12
|
+
from collections.abc import Awaitable, Callable
|
|
13
|
+
from typing import Generic, TypeVar
|
|
14
|
+
|
|
15
|
+
T = TypeVar("T")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BufferFull(RuntimeError):
|
|
19
|
+
"""Raised when enqueuing onto a full buffer."""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class OfflineBuffer(Generic[T]):
|
|
23
|
+
"""A bounded FIFO buffer of operations for a temporarily unreachable device."""
|
|
24
|
+
|
|
25
|
+
def __init__(self, *, max_size: int = 128) -> None:
|
|
26
|
+
self.max_size = max_size
|
|
27
|
+
self._items: deque[T] = deque()
|
|
28
|
+
|
|
29
|
+
def __len__(self) -> int:
|
|
30
|
+
return len(self._items)
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def is_full(self) -> bool:
|
|
34
|
+
return len(self._items) >= self.max_size
|
|
35
|
+
|
|
36
|
+
def enqueue(self, item: T) -> None:
|
|
37
|
+
"""Append ``item``; raises :class:`BufferFull` if the buffer is full."""
|
|
38
|
+
if self.is_full:
|
|
39
|
+
raise BufferFull(f"buffer is full (max_size={self.max_size})")
|
|
40
|
+
self._items.append(item)
|
|
41
|
+
|
|
42
|
+
async def flush(self, handler: Callable[[T], Awaitable[None]]) -> int:
|
|
43
|
+
"""Apply ``handler`` to each item in FIFO order, removing it on success.
|
|
44
|
+
|
|
45
|
+
Returns the number flushed. If ``handler`` raises, the failed item stays at the
|
|
46
|
+
front (and the remaining items stay queued) so the operation can be retried.
|
|
47
|
+
"""
|
|
48
|
+
count = 0
|
|
49
|
+
while self._items:
|
|
50
|
+
await handler(self._items[0])
|
|
51
|
+
self._items.popleft()
|
|
52
|
+
count += 1
|
|
53
|
+
return count
|
|
54
|
+
|
|
55
|
+
def clear(self) -> None:
|
|
56
|
+
"""Discard all buffered items."""
|
|
57
|
+
self._items.clear()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
__all__ = ["OfflineBuffer", "BufferFull"]
|