Skip to content

Commit 87d1beb

Browse files
committed
Init
1 parent bc103d0 commit 87d1beb

29 files changed

+3527
-0
lines changed

.dockerignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.venv
2+
.env
3+
.git
4+
.gitlab-ci.yml
5+
.gitignore
6+
__pycache__
7+
gl-sast-report.json
8+
9+
Dockerfile
10+
README.md
11+
dev_tests
12+
data/

.github/workflows/docker.yaml

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#
2+
name: Create and publish a Docker image for Litmus MCP Server
3+
4+
on:
5+
push:
6+
branches: ['main']
7+
tags:
8+
- "v*.*.*"
9+
pull_request:
10+
branches: ['main']
11+
env:
12+
REGISTRY: ghcr.io
13+
IMAGE_NAME: ${{ github.repository }}
14+
PLATFORMS: "linux/amd64"
15+
16+
jobs:
17+
docker-build:
18+
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
19+
name: Build Docker image (ghcr.io/litmusautomation/litmus-mcp-server)
20+
runs-on: ubuntu-latest
21+
permissions:
22+
contents: read
23+
packages: write
24+
attestations: write
25+
id-token: write
26+
27+
steps:
28+
- name: Checkout repository
29+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
30+
31+
- name: Log in to the Container registry
32+
if: ${{ github.event_name != 'pull_request' }}
33+
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
34+
with:
35+
registry: ${{ env.REGISTRY }}
36+
username: ${{ github.repository_owner }}
37+
password: ${{ secrets.GITHUB_TOKEN }}
38+
39+
- name: Extract metadata (tags, labels) for Docker
40+
id: meta
41+
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
42+
env:
43+
DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index
44+
with:
45+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
46+
tags: |
47+
type=ref,event=branch
48+
type=ref,event=pr
49+
type=semver,pattern={{version}}
50+
type=semver,pattern={{major}}.{{minor}}
51+
type=sha
52+
53+
- name: Set up QEMU
54+
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
55+
- name: Set up Docker Buildx
56+
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
57+
with:
58+
platforms: ${{ env.PLATFORMS }}
59+
60+
- name: Build and push Docker image
61+
id: push
62+
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
63+
with:
64+
platforms: ${{ env.PLATFORMS }}
65+
push: ${{ github.event_name != 'pull_request' }}
66+
sbom: true
67+
tags: ${{ steps.meta.outputs.tags }}
68+
labels: ${{ steps.meta.outputs.labels }}
69+
annotations: ${{ steps.meta.outputs.annotations }}
70+
cache-from: type=gha
71+
cache-to: type=gha,mode=max
72+
73+
# - name: Generate artifact attestation
74+
# if: ${{ github.event_name != 'pull_request' }}
75+
# uses: actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2 # v2.2.3
76+
# with:
77+
# subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}
78+
# subject-digest: ${{ steps.push.outputs.digest }}

.gitignore

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Python-generated files
2+
__pycache__/
3+
*.py[oc]
4+
build/
5+
dist/
6+
wheels/
7+
*.egg-info
8+
9+
# Virtual environments
10+
.venv
11+
12+
# local env
13+
.env
14+
.idea
15+
dev_tests
16+
.directory

.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.12

Dockerfile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
FROM python:3.12-slim
2+
LABEL authors="Litmus Automation, Inc."
3+
4+
5+
RUN apt-get update && apt-get install -y \
6+
build-essential curl \
7+
&& rm -rf /var/lib/apt/lists/*
8+
9+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
10+
COPY --from=ghcr.io/astral-sh/uv:latest /uvx /bin/uvx
11+
WORKDIR /app
12+
13+
COPY . .
14+
RUN uv venv && uv sync --all-groups
15+
16+
ENV PATH="/app/.venv/bin:$PATH"
17+
18+
#CMD python src/server.py --transport=sse & python src/webclient.py src/server.py && wait
19+
RUN chmod +x run.sh
20+
CMD ["./run.sh"]

README.md

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
<p align="center">
2+
<a href="https://litmus.io">
3+
<picture>
4+
<source media="(prefers-color-scheme: light)" srcset="static/litmus-logo-light.svg" />
5+
<source media="(prefers-color-scheme: dark)" srcset="static/litmus-logo-dark.svg" />
6+
<img src="static/litmus-logo-light.svg" height="60" alt="Litmus logo" />
7+
</picture>
8+
</a>
9+
</p>
10+
11+
<p align="center">
12+
<a href="https://docs.litmus.io">
13+
<img src="https://img.shields.io/badge/Litmus-Docs-2acfa6?style=flat-square" alt="Documentation" />
14+
</a>
15+
<a href="https://www.linkedin.com/company/litmus-automation/" >
16+
<img src="https://img.shields.io/badge/LinkedIn-Follow-0a66c2?style=flat-square" alt="Follow on LinkedIn" />
17+
</a>
18+
</p>
19+
20+
# Litmus MCP Server
21+
22+
The official [Litmus Automation](https://litmus.io) **Model Context Protocol (MCP) Server** enables LLMs and intelligent systems to interact with [Litmus Edge](https://litmus.io/products/litmus-edge) for device configuration, monitoring, and management. It is built on top of the MCP SDK and adheres to the [Model Context Protocol spec](https://modelcontextprotocol.io/).
23+
24+
<div>
25+
<picture>
26+
<source media="(prefers-color-scheme: light)" srcset="static/MCP-server-arch-diagram.png" />
27+
<img src="static/MCP-server-arch-diagram.png" alt="Litmus MCP Server Architecture Diagram" />
28+
</picture>
29+
</div>
30+
31+
## Table of Contents
32+
33+
- [Getting Started](#getting-started)
34+
- [Quick Launch (Docker)](#quick-launch-docker)
35+
- [Cursor IDE Setup](#cursor-ide-setup)
36+
- [API](#api)
37+
- [Usage](#usage)
38+
- [Server-Sent Events (SSE)](#server-sent-events-sse)
39+
- [Litmus Central](#litmus-central)
40+
- [Integrations](#integrations)
41+
- [Cursor IDE](#cursor-ide)
42+
- [Claude Desktop](#claude-desktop)
43+
- [VS Code / Copilot](#vs-code--copilot)
44+
- [Windsurf](#windsurf)
45+
46+
---
47+
48+
## Getting Started
49+
50+
### Quick Launch (Docker)
51+
52+
Run the server in Docker:
53+
54+
```bash
55+
docker run -d --name litmus-mcp-server -p 8000:8000 ghcr.io/litmusautomation/litmus-mcp-server/mcp:latest
56+
```
57+
58+
### Cursor IDE Setup
59+
60+
Example `mcp.json` configuration:
61+
62+
```json
63+
{
64+
"mcpServers": {
65+
"litmus-mcp-server": {
66+
"url": "http://<IP Address>:8000/sse"
67+
}
68+
}
69+
}
70+
```
71+
72+
See the [Cursor docs](https://docs.cursor.com/context/model-context-protocol) for more info.
73+
74+
---
75+
76+
## API
77+
78+
| Category | Function Name | Description |
79+
|---------------------------|----------------------------------------|-------------|
80+
| **Edge System Config** | `get_current_environment_config` | Get current environment configuration used for Litmus Edge connectivity. |
81+
| | `update_environment_config` | Update environment variable config for connecting to Litmus Edge. |
82+
| | `get_current_config` | Retrieve current Litmus Edge instance configuration. |
83+
| | `update_config` | Update configuration of the device or container running Litmus Edge. |
84+
| **DeviceHub** | `get_litmusedge_driver_list` | List supported Litmus Edge drivers. |
85+
| | `get_devicehub_devices` | List devices configured in DeviceHub. |
86+
| | `get_devicehub_device_tags` | Retrieve tags for a specific DeviceHub device. |
87+
| | `get_current_value_of_devicehub_tag` | Get current value of a specific device tag. |
88+
| | `create_devicehub_device` | Register a new DeviceHub device. Supports various protocols and templates for register-based data polling. |
89+
| **Device Identity** | `get_litmusedge_friendly_name` | Retrieve the user-friendly name of the device. |
90+
| | `set_litmusedge_friendly_name` | Assign or update the friendly name. |
91+
| **LEM Integration** | `get_cloud_activation_status` | Check cloud activation and Litmus Edge Manager (LEM) connection status. |
92+
| **Docker Management** | `get_all_containers_on_litmusedge` | List all containers on Litmus Edge. |
93+
| | `run_docker_container_on_litmusedge` | Launch a Docker container via Litmus Edge Marketplace (not the MCP host). |
94+
| **Topic Subscription** | `get_current_value_on_topic` | Subscribe to current values on a Litmus Edge topic. Use global `NATS_STATUS = False` to unsubscribe. |
95+
| | `get_multiple_values_from_topic` | Retrieve multiple values from a topic for plotting or batch access. |
96+
97+
---
98+
99+
## Usage
100+
101+
### Server-Sent Events (SSE)
102+
103+
This server supports the [MCP SSE transport](https://modelcontextprotocol.io/docs/concepts/transports#server-sent-events-sse) for real-time communication.
104+
105+
- **Client endpoint:** `http://<server-ip>:8000/sse`
106+
- **Default binding:** `0.0.0.0:8000/sse`
107+
- **Communication:**
108+
- Server → Client: Streamed via SSE
109+
- Client → Server: HTTP POST
110+
111+
---
112+
113+
## Litmus Central
114+
115+
Download or try Litmus Edge via [Litmus Central](https://central.litmus.io).
116+
117+
---
118+
119+
## Integrations
120+
121+
### Cursor IDE
122+
123+
Add to `~/.cursor/mcp.json` or `.cursor/mcp.json`:
124+
125+
```json
126+
{
127+
"mcpServers": {
128+
"litmus-mcp-server": {
129+
"url": "http://<IP Address>:8000/sse"
130+
}
131+
}
132+
}
133+
```
134+
135+
[Cursor docs](https://docs.cursor.com/context/model-context-protocol)
136+
137+
---
138+
139+
### Claude Desktop
140+
141+
Add to `claude_desktop_config.json`:
142+
143+
```json
144+
{
145+
"mcpServers": {
146+
"litmus-mcp-server": {
147+
"url": "http://<IP Address>:8000/sse"
148+
}
149+
}
150+
}
151+
```
152+
153+
[Anthropic Docs](https://docs.anthropic.com/en/docs/agents-and-tools/mcp)
154+
155+
---
156+
157+
### VS Code / GitHub Copilot
158+
159+
#### Manual Configuration
160+
161+
In VS Code:
162+
Open User Settings (JSON) → Add:
163+
164+
```json
165+
{
166+
"mcpServers": {
167+
"litmus-mcp-server": {
168+
"url": "http://<IP Address>:8000/sse"
169+
}
170+
}
171+
}
172+
```
173+
174+
Or use `.vscode/mcp.json` in your project.
175+
176+
[VS Code MCP Docs](https://code.visualstudio.com/docs/copilot/chat/mcp-servers)
177+
178+
---
179+
180+
### Windsurf
181+
182+
Add to `~/.codeium/windsurf/mcp_config.json`:
183+
184+
```json
185+
{
186+
"mcpServers": {
187+
"litmus-mcp-server": {
188+
"url": "http://<IP Address>:8000/sse"
189+
}
190+
}
191+
}
192+
```
193+
194+
[Windsurf MCP Docs](https://docs.windsurf.com/windsurf/mcp)
195+
196+
---
197+
198+
© 2025 Litmus Automation, Inc. All rights reserved.

pyproject.toml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
[project]
2+
name = "litmus-mcp-server"
3+
version = "0.1.0"
4+
description = "Litmus MCP Server and client combo"
5+
readme = "README.md"
6+
requires-python = ">=3.12"
7+
dependencies = [
8+
"fastapi>=0.115.12",
9+
"jinja2>=3.1.6",
10+
"litmussdk",
11+
"mcp[cli]>=1.6.0",
12+
"nats-py>=2.10.0",
13+
"numpy>=2.2.5",
14+
"python-multipart>=0.0.20",
15+
]
16+
17+
[dependency-groups]
18+
lint = [
19+
"black>=25.1.0",
20+
"radon>=6.0.1",
21+
"ruff>=0.11.4",
22+
]
23+
llm-sdks = [
24+
"anthropic>=0.49.0",
25+
"openai-agents>=0.0.13",
26+
]
27+
test = []
28+
29+
[tool.uv.sources]
30+
litmussdk = { url = "https://github.com/litmusautomation/litmus-sdk-releases/releases/download/1.0.0/litmussdk-1.0.0-py3-none-any.whl" }
31+
32+
[tool.ruff]
33+
exclude = [
34+
".venv",
35+
"venv",
36+
"site-packages",
37+
"build",
38+
"dist",
39+
".pytest_cache",
40+
".ruff_cache",
41+
"__init__.py",
42+
"tester"
43+
]
44+
line-length = 88
45+
indent-width = 4
46+
[tool.ruff.lint.pydocstyle]
47+
convention = "google"

run.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/bin/bash
2+
set -e
3+
4+
# Start server
5+
mcp run src/server.py --transport=sse &
6+
SERVER_PID=$!
7+
8+
# Start web client
9+
python src/web_client.py src/server.py &
10+
CLIENT_PID=$!
11+
12+
# Wait for both
13+
wait $SERVER_PID $CLIENT_PID

src/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)