lobbywatch-mcp 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.
- lobbywatch_mcp-0.3.0/.dockerignore +35 -0
- lobbywatch_mcp-0.3.0/.github/workflows/ci.yml +36 -0
- lobbywatch_mcp-0.3.0/.github/workflows/publish.yml +86 -0
- lobbywatch_mcp-0.3.0/.gitignore +36 -0
- lobbywatch_mcp-0.3.0/CHANGELOG.md +170 -0
- lobbywatch_mcp-0.3.0/CONTRIBUTING.md +99 -0
- lobbywatch_mcp-0.3.0/Dockerfile +73 -0
- lobbywatch_mcp-0.3.0/EXAMPLES.md +71 -0
- lobbywatch_mcp-0.3.0/LICENSE +34 -0
- lobbywatch_mcp-0.3.0/PKG-INFO +270 -0
- lobbywatch_mcp-0.3.0/README.de.md +213 -0
- lobbywatch_mcp-0.3.0/README.md +227 -0
- lobbywatch_mcp-0.3.0/assets/README.md +6 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/applicability.json +342 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/applicability.txt +55 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/audit-meta.json +13 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/audit-report.md +1094 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/checklist.txt +47 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/ARCH-002-use-case-tags.md +35 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/ARCH-003-not-found-suggestions.md +33 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/ARCH-008-resources-and-prompts.md +33 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/ARCH-009-tool-annotations-missing.md +36 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/ARCH-012-protocol-version-pinning.md +32 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/OBS-001-protocol-vs-execution-errors.md +36 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/OBS-002-mask-error-details.md +35 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/OBS-003-structured-logging.md +32 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/OBS-006-opentelemetry-tracing.md +32 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/OPS-002-doku-standard-ascii-diagram-and-broken-ci-badge.md +34 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/SCALE-002-stateful-lb-undocumented.md +33 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/SCALE-003-haproxy-stick-table-missing.md +32 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/SCALE-004-containerization.md +32 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/SCALE-006-resource-limits.md +32 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/SDK-001-lifespan-asynccontextmanager-missing.md +44 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/SDK-002-pydantic-typed-tool-returns.md +32 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/SDK-003-context-injection.md +34 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/SDK-004-cors-mcp-session-id.md +34 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/SEC-004-ssrf-search-path.md +49 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/SEC-005-dns-rebinding-pinning.md +32 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/SEC-007-container-sandboxing.md +32 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/SEC-016-zero-binding-default.md +44 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/SEC-018-input-validation-pydantic-strict.md +34 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/SEC-021-egress-allow-list.md +34 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/findings/SEC-022-namespace-prefix-rugpull.md +33 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/profile.json +26 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/summary.json +278 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T115855-Z-lobbywatch-mcp/verification-results.json +516 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T133506-Z-lobbywatch-mcp/applicability.json +342 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T133506-Z-lobbywatch-mcp/audit-meta.json +13 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T133506-Z-lobbywatch-mcp/audit-report.md +131 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T133506-Z-lobbywatch-mcp/findings/OPS-002-ascii-diagram-missing.md +50 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T133506-Z-lobbywatch-mcp/profile.json +26 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T133506-Z-lobbywatch-mcp/summary.json +100 -0
- lobbywatch_mcp-0.3.0/audits/2026-05-09T133506-Z-lobbywatch-mcp/verification-results.json +434 -0
- lobbywatch_mcp-0.3.0/claude_desktop_config.json +15 -0
- lobbywatch_mcp-0.3.0/deploy/docker-compose.example.yml +60 -0
- lobbywatch_mcp-0.3.0/docs/deployment.md +136 -0
- lobbywatch_mcp-0.3.0/pyproject.toml +93 -0
- lobbywatch_mcp-0.3.0/src/lobbywatch_mcp/__init__.py +13 -0
- lobbywatch_mcp-0.3.0/src/lobbywatch_mcp/__main__.py +86 -0
- lobbywatch_mcp-0.3.0/src/lobbywatch_mcp/_observability.py +214 -0
- lobbywatch_mcp-0.3.0/src/lobbywatch_mcp/client.py +332 -0
- lobbywatch_mcp-0.3.0/src/lobbywatch_mcp/config.py +45 -0
- lobbywatch_mcp-0.3.0/src/lobbywatch_mcp/models.py +166 -0
- lobbywatch_mcp-0.3.0/src/lobbywatch_mcp/server.py +694 -0
- lobbywatch_mcp-0.3.0/tests/__init__.py +0 -0
- lobbywatch_mcp-0.3.0/tests/conftest.py +121 -0
- lobbywatch_mcp-0.3.0/tests/test_client.py +119 -0
- lobbywatch_mcp-0.3.0/tests/test_live.py +40 -0
- lobbywatch_mcp-0.3.0/tests/test_observability.py +112 -0
- lobbywatch_mcp-0.3.0/tests/test_server.py +193 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Build context hygiene — keep image small and leak-free.
|
|
2
|
+
.git
|
|
3
|
+
.github
|
|
4
|
+
.venv
|
|
5
|
+
venv
|
|
6
|
+
env
|
|
7
|
+
__pycache__
|
|
8
|
+
*.py[cod]
|
|
9
|
+
*.egg-info
|
|
10
|
+
build
|
|
11
|
+
dist
|
|
12
|
+
.pytest_cache
|
|
13
|
+
.ruff_cache
|
|
14
|
+
.mypy_cache
|
|
15
|
+
.coverage
|
|
16
|
+
htmlcov
|
|
17
|
+
|
|
18
|
+
# Documentation that doesn't belong in the runtime image.
|
|
19
|
+
audits
|
|
20
|
+
docs
|
|
21
|
+
EXAMPLES.md
|
|
22
|
+
CONTRIBUTING.md
|
|
23
|
+
CHANGELOG.md
|
|
24
|
+
README.de.md
|
|
25
|
+
|
|
26
|
+
# Tests are not needed at runtime.
|
|
27
|
+
tests
|
|
28
|
+
|
|
29
|
+
# Local secrets / IDE state.
|
|
30
|
+
.env
|
|
31
|
+
.env.*
|
|
32
|
+
.idea
|
|
33
|
+
.vscode
|
|
34
|
+
*.swp
|
|
35
|
+
.DS_Store
|
|
@@ -0,0 +1,36 @@
|
|
|
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.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
|
+
cache: pip
|
|
24
|
+
|
|
25
|
+
- name: Install
|
|
26
|
+
run: |
|
|
27
|
+
python -m pip install --upgrade pip
|
|
28
|
+
pip install -e ".[dev]"
|
|
29
|
+
|
|
30
|
+
- name: Lint (ruff)
|
|
31
|
+
run: |
|
|
32
|
+
ruff check .
|
|
33
|
+
ruff format --check .
|
|
34
|
+
|
|
35
|
+
- name: Test (offline only)
|
|
36
|
+
run: pytest -m "not live" -q
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
# Tag-triggered release. Builds wheel + sdist and uploads via PyPI's
|
|
4
|
+
# Trusted Publisher (OIDC) — no PYPI_API_TOKEN secret required.
|
|
5
|
+
#
|
|
6
|
+
# Setup (one-time, on PyPI):
|
|
7
|
+
# 1. Create the project on PyPI (or skip — PyPI auto-creates on first
|
|
8
|
+
# successful trusted publish).
|
|
9
|
+
# 2. PyPI → Account → Publishing → Add pending publisher
|
|
10
|
+
# Owner: malkreide
|
|
11
|
+
# Repo: lobbywatch-mcp
|
|
12
|
+
# Workflow: publish.yml
|
|
13
|
+
# Environment: pypi (must match the `environment:` below)
|
|
14
|
+
# 3. Push a tag matching v*.*.* — this workflow takes it from there.
|
|
15
|
+
|
|
16
|
+
on:
|
|
17
|
+
push:
|
|
18
|
+
tags:
|
|
19
|
+
- "v*.*.*"
|
|
20
|
+
|
|
21
|
+
permissions:
|
|
22
|
+
contents: read
|
|
23
|
+
|
|
24
|
+
jobs:
|
|
25
|
+
build:
|
|
26
|
+
name: Build distribution
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
|
|
32
|
+
- name: Set up Python 3.13
|
|
33
|
+
uses: actions/setup-python@v5
|
|
34
|
+
with:
|
|
35
|
+
python-version: "3.13"
|
|
36
|
+
|
|
37
|
+
- name: Verify tag matches pyproject.toml version
|
|
38
|
+
run: |
|
|
39
|
+
tag="${GITHUB_REF##*/}" # e.g. v0.3.0
|
|
40
|
+
tag_version="${tag#v}" # 0.3.0
|
|
41
|
+
manifest_version=$(python -c "import tomllib,sys; print(tomllib.loads(open('pyproject.toml','rb').read().decode())['project']['version'])")
|
|
42
|
+
if [ "$tag_version" != "$manifest_version" ]; then
|
|
43
|
+
echo "::error::Tag $tag (=> $tag_version) does not match pyproject.toml version $manifest_version"
|
|
44
|
+
exit 1
|
|
45
|
+
fi
|
|
46
|
+
echo "Tag and manifest agree on version $manifest_version"
|
|
47
|
+
|
|
48
|
+
- name: Install build tooling
|
|
49
|
+
run: |
|
|
50
|
+
python -m pip install --upgrade pip
|
|
51
|
+
pip install build
|
|
52
|
+
|
|
53
|
+
- name: Build wheel + sdist
|
|
54
|
+
run: python -m build
|
|
55
|
+
|
|
56
|
+
- name: Upload artefacts
|
|
57
|
+
uses: actions/upload-artifact@v4
|
|
58
|
+
with:
|
|
59
|
+
name: dist
|
|
60
|
+
path: dist/
|
|
61
|
+
|
|
62
|
+
publish:
|
|
63
|
+
name: Publish to PyPI
|
|
64
|
+
needs: build
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
|
|
67
|
+
# OIDC trusted-publisher requirements.
|
|
68
|
+
environment:
|
|
69
|
+
name: pypi
|
|
70
|
+
url: https://pypi.org/p/lobbywatch-mcp
|
|
71
|
+
permissions:
|
|
72
|
+
id-token: write # required for trusted publishing
|
|
73
|
+
|
|
74
|
+
steps:
|
|
75
|
+
- name: Download build artefacts
|
|
76
|
+
uses: actions/download-artifact@v4
|
|
77
|
+
with:
|
|
78
|
+
name: dist
|
|
79
|
+
path: dist/
|
|
80
|
+
|
|
81
|
+
- name: Publish via Trusted Publisher
|
|
82
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
83
|
+
with:
|
|
84
|
+
# No `password:` — OIDC handles auth.
|
|
85
|
+
# No `repository-url:` — defaults to upload.pypi.org.
|
|
86
|
+
print-hash: true
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging
|
|
7
|
+
build/
|
|
8
|
+
dist/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
*.egg
|
|
11
|
+
|
|
12
|
+
# Virtual environments
|
|
13
|
+
.venv/
|
|
14
|
+
venv/
|
|
15
|
+
env/
|
|
16
|
+
|
|
17
|
+
# Caches
|
|
18
|
+
.pytest_cache/
|
|
19
|
+
.ruff_cache/
|
|
20
|
+
.mypy_cache/
|
|
21
|
+
.coverage
|
|
22
|
+
htmlcov/
|
|
23
|
+
|
|
24
|
+
# IDE
|
|
25
|
+
.idea/
|
|
26
|
+
.vscode/
|
|
27
|
+
*.swp
|
|
28
|
+
|
|
29
|
+
# Local secrets
|
|
30
|
+
.env
|
|
31
|
+
.env.local
|
|
32
|
+
.env.*.local
|
|
33
|
+
|
|
34
|
+
# OS
|
|
35
|
+
.DS_Store
|
|
36
|
+
Thumbs.db
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `.github/workflows/publish.yml` — tag-triggered PyPI release via
|
|
12
|
+
Trusted Publisher (OIDC, no API token in repo secrets). Verifies that
|
|
13
|
+
the pushed tag matches `pyproject.toml`'s `version` before publishing.
|
|
14
|
+
One-time setup on PyPI: register `malkreide/lobbywatch-mcp` ·
|
|
15
|
+
`publish.yml` · environment `pypi` as a pending publisher.
|
|
16
|
+
- ASCII architecture diagram in README.md / README.de.md (audit OPS-002
|
|
17
|
+
closure — was the single remaining cosmetic finding from the
|
|
18
|
+
2026-05-09 re-audit). Visualises the dump-first / dataIF-fallback
|
|
19
|
+
split plus the SSRF-guarded outbound HTTP path.
|
|
20
|
+
|
|
21
|
+
## [0.3.0] - 2026-05-09
|
|
22
|
+
|
|
23
|
+
Audit-driven hardening release. Closes 24 of 25 findings from the
|
|
24
|
+
initial mcp-audit-skill v1.0.0 run; the re-audit
|
|
25
|
+
(`audits/2026-05-09T133506-Z-lobbywatch-mcp/`) reports
|
|
26
|
+
`production_ready: true` (41 pass, 0 fail, 1 cosmetic partial).
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
- Structured JSON logging via structlog (audit OBS-003). Opt-in with
|
|
30
|
+
`LOBBYWATCH_MCP_LOG_FORMAT=json` — default stays `text` to preserve
|
|
31
|
+
the quiet stdio experience. Each tool invocation generates a
|
|
32
|
+
16-char correlation id that's bound into the structlog context vars
|
|
33
|
+
and surfaces in every JSON line as `correlation_id`.
|
|
34
|
+
- OpenTelemetry distributed tracing (audit OBS-006). Opt-in with
|
|
35
|
+
`LOBBYWATCH_MCP_OTEL_ENABLED=1`; OTel is an optional dep installed
|
|
36
|
+
via `pip install 'lobbywatch-mcp[obs]'`. Wraps each tool call in a
|
|
37
|
+
`tool.<name>` span and auto-instruments the httpx client. The
|
|
38
|
+
`_observability.observed_tool` async context manager keeps OBS-003
|
|
39
|
+
+ OBS-006 in sync — every tool body is wrapped exactly once.
|
|
40
|
+
- Lifespan startup now logs the active MCP `protocolVersion` (audit
|
|
41
|
+
ARCH-012 closure: SDK upper-bound was already pinned in 0.2.0; this
|
|
42
|
+
closes the remaining "log it for operators" gap).
|
|
43
|
+
- New optional dependency group `obs` covering
|
|
44
|
+
`opentelemetry-api`, `opentelemetry-sdk`,
|
|
45
|
+
`opentelemetry-exporter-otlp-proto-http`,
|
|
46
|
+
`opentelemetry-instrumentation-httpx`. Without it, tracing config
|
|
47
|
+
warns and no-ops; logging stays available either way.
|
|
48
|
+
- Multi-stage `Dockerfile` and `.dockerignore` (audit SCALE-004). Runtime
|
|
49
|
+
image runs as `uid 1000`, read-only-rootfs compatible, no build
|
|
50
|
+
toolchain in the final stage (audit SEC-007).
|
|
51
|
+
- `deploy/docker-compose.example.yml` with hardening defaults:
|
|
52
|
+
`read_only`, `cap_drop: [ALL]`, `no-new-privileges`, tmpfs cache,
|
|
53
|
+
loopback-only port bind, healthcheck, and `deploy.resources` limits
|
|
54
|
+
(256 MiB / 0.1 CPU request, 512 MiB / 0.5 CPU limit) — audit SCALE-006.
|
|
55
|
+
- `docs/deployment.md`: trust-model recap, container build/run commands,
|
|
56
|
+
resource-sizing rationale, HAProxy stick-table example for sticky LBs
|
|
57
|
+
(audit SCALE-002, SCALE-003), and a Kubernetes egress `NetworkPolicy`
|
|
58
|
+
template (audit SEC-021).
|
|
59
|
+
- SSRF / DNS-rebinding guard in the httpx client: an event hook re-resolves
|
|
60
|
+
every outbound host and refuses connections to RFC1918 / link-local /
|
|
61
|
+
loopback / cloud-metadata addresses (audit SEC-005). Hardcoded URL
|
|
62
|
+
allow-list + `follow_redirects=False` + this guard provide
|
|
63
|
+
defence-in-depth.
|
|
64
|
+
- Optional CORS support for HTTP/SSE deployments via the
|
|
65
|
+
`LOBBYWATCH_MCP_CORS_ORIGINS` env var (comma-separated origin list).
|
|
66
|
+
When set, the FastMCP ASGI app is wrapped with a Starlette
|
|
67
|
+
`CORSMiddleware` that exposes `Mcp-Session-Id` to browser clients
|
|
68
|
+
(audit SDK-004). Default (empty) emits no CORS headers.
|
|
69
|
+
- `lobbywatch_refresh_dump` now accepts a `Context` parameter and emits
|
|
70
|
+
`ctx.info` start/end notifications around the dump download (audit
|
|
71
|
+
SDK-003), giving long-running clients useful progress feedback.
|
|
72
|
+
- Fuzzy-match suggestions on missed lookups (audit ARCH-003).
|
|
73
|
+
`lobbywatch_get_parlamentarier` and `lobbywatch_list_interessenbindungen`
|
|
74
|
+
return up to three near-miss candidates (rapidfuzz WRatio in
|
|
75
|
+
[50, 70)) in a new `suggestions` field, so the LLM can offer
|
|
76
|
+
"did you mean…?" instead of treating the empty result as truth. New
|
|
77
|
+
`LobbywatchClient.find_candidates()` powers this.
|
|
78
|
+
- MCP Resources and Prompts (audit ARCH-008):
|
|
79
|
+
- Resource `lobbywatch://attribution` (text/plain) — the CC BY-SA 4.0
|
|
80
|
+
licence string for clients that want to display it standalone.
|
|
81
|
+
- Prompt `lobbywatch_anchor_demo` — parameterised by `branche`, scaffolds
|
|
82
|
+
the canonical Schulamt / KI-Fachgruppe demo query.
|
|
83
|
+
- Prompt `lobbywatch_top_lobbyists_by_party` — parameterised by `partei`,
|
|
84
|
+
surfaces the top-10 ranking with transparency metadata.
|
|
85
|
+
|
|
86
|
+
### Changed
|
|
87
|
+
- Tool-boundary error handling: upstream `RuntimeError` /
|
|
88
|
+
`httpx.HTTPError` are explicitly converted to `McpError` with code
|
|
89
|
+
`INTERNAL_ERROR` (audit OBS-001). Previously these fell through to
|
|
90
|
+
FastMCP's auto-coercion — same wire effect, but the categorisation is
|
|
91
|
+
now intentional and operators get a structured `logger.error` for the
|
|
92
|
+
underlying exception.
|
|
93
|
+
- HTTP/SSE transport now runs through `uvicorn.run` directly on
|
|
94
|
+
`mcp.streamable_http_app()` / `mcp.sse_app()` so middleware can be
|
|
95
|
+
attached. `stdio` transport is unchanged.
|
|
96
|
+
- All eight tools now declare typed Pydantic return models instead of
|
|
97
|
+
`dict[str, Any]` (audit SDK-002). FastMCP synthesises a real JSON
|
|
98
|
+
schema for each tool's output, giving downstream clients (Inspector,
|
|
99
|
+
custom UIs, schema-aware LLMs) field-level type information instead
|
|
100
|
+
of opaque `additionalProperties: true`. Wire format is unchanged.
|
|
101
|
+
- Tool docstrings now carry `Use cases:` blocks (audit ARCH-002) with
|
|
102
|
+
three concrete example queries each, sharpening LLM tool selection.
|
|
103
|
+
- `USER_AGENT` bumped to `lobbywatch-mcp/0.3.0`.
|
|
104
|
+
|
|
105
|
+
### Audit verification
|
|
106
|
+
|
|
107
|
+
- **Production-ready:** ✅ yes
|
|
108
|
+
- **Audit run-id:** `2026-05-09T133506-Z-lobbywatch-mcp`
|
|
109
|
+
- **Skill version:** `1.0.0`
|
|
110
|
+
- **Catalog hash:** `091f446b27965044…`
|
|
111
|
+
- **Check results:** 41 pass · 0 fail · 1 partial · 2 todo
|
|
112
|
+
- **Remaining finding:** `OPS-002` — README ASCII architecture diagram
|
|
113
|
+
(cosmetic, ~10 min effort).
|
|
114
|
+
|
|
115
|
+
## [0.2.0] - 2026-05-09
|
|
116
|
+
|
|
117
|
+
### Changed (Breaking)
|
|
118
|
+
- **Tool names now carry a `lobbywatch_` namespace prefix** (audit SEC-022).
|
|
119
|
+
All eight tools were renamed:
|
|
120
|
+
`get_parlamentarier` → `lobbywatch_get_parlamentarier`,
|
|
121
|
+
`list_interessenbindungen` → `lobbywatch_list_interessenbindungen`,
|
|
122
|
+
`search_parlamentarier_nach_branche` → `lobbywatch_search_parlamentarier_nach_branche`,
|
|
123
|
+
`get_lobbygruppe` → `lobbywatch_get_lobbygruppe`,
|
|
124
|
+
`get_ranking` → `lobbywatch_get_ranking`,
|
|
125
|
+
`get_transparenzquote` → `lobbywatch_get_transparenzquote`,
|
|
126
|
+
`refresh_dump` → `lobbywatch_refresh_dump`,
|
|
127
|
+
`dump_status` → `lobbywatch_dump_status`.
|
|
128
|
+
Existing clients referencing the old names must be updated.
|
|
129
|
+
|
|
130
|
+
### Added
|
|
131
|
+
- FastMCP `lifespan` context manager owns the `LobbywatchClient` lifecycle
|
|
132
|
+
(audit SDK-001). The shared `httpx.AsyncClient` is now closed cleanly on
|
|
133
|
+
server shutdown; previously it leaked at process exit.
|
|
134
|
+
- Input validation at tool boundaries (audit SEC-018):
|
|
135
|
+
`name_or_id` and `branche_query` bound to 1–200 chars; `kommission` /
|
|
136
|
+
`partei` bound to ≤80 chars; `limit` bounded to 1–200 (search) /
|
|
137
|
+
1–100 (ranking); `kriterium` is now a `Literal` enum so invalid values
|
|
138
|
+
are rejected at the schema layer instead of via runtime `ValueError`.
|
|
139
|
+
|
|
140
|
+
### Changed
|
|
141
|
+
- `mcp[cli]` dependency is now bounded `<2.0.0` (audit ARCH-012).
|
|
142
|
+
- `USER_AGENT` bumped to `lobbywatch-mcp/0.2.0`.
|
|
143
|
+
|
|
144
|
+
## [0.1.0] - 2026-04-21
|
|
145
|
+
|
|
146
|
+
### Added
|
|
147
|
+
- Initial scaffold for `lobbywatch-mcp`, part of the Swiss Public Data MCP Portfolio.
|
|
148
|
+
- Hybrid data access: dump-first (weekly Lobbywatch JSON export) with live
|
|
149
|
+
`dataIF` fallback for lobby groups.
|
|
150
|
+
- Phase 1 tools:
|
|
151
|
+
- `get_parlamentarier` — profile lookup with fuzzy name match
|
|
152
|
+
- `list_interessenbindungen` — conflict-of-interest records per MP
|
|
153
|
+
- `search_parlamentarier_nach_branche` — branche + commission filter
|
|
154
|
+
- `get_lobbygruppe` — live lobby group fetch via dataIF
|
|
155
|
+
- `get_ranking` — top-N by criterion, commission/party filters
|
|
156
|
+
- `get_transparenzquote` — distribution of verguetungstransparenz labels
|
|
157
|
+
- `refresh_dump`, `dump_status` — cache control
|
|
158
|
+
- Retry + exponential backoff (2s/4s/8s) for dump downloads — tolerant of upstream HTTP 503 blips observed live.
|
|
159
|
+
- Dual transport: `stdio` (default) and `streamable-http` / `sse`.
|
|
160
|
+
- Pydantic v2 response envelopes carrying CC BY-SA 4.0 attribution.
|
|
161
|
+
- CI matrix (Python 3.11–3.13), ruff lint/format, respx-mocked unit tests.
|
|
162
|
+
- Tag-triggered PyPI publish workflow via OIDC Trusted Publisher.
|
|
163
|
+
- Live test suite (`@pytest.mark.live`) excluded from CI.
|
|
164
|
+
|
|
165
|
+
### Known limitations
|
|
166
|
+
- The upstream `/table/parlamentarier/...` dataIF endpoint returns empty
|
|
167
|
+
responses at release time; all parliamentarian-facing tools therefore use
|
|
168
|
+
the weekly dump. Lobby group lookups go through the live API.
|
|
169
|
+
- `zutrittsberechtigungen` are present in the data model but empty in the
|
|
170
|
+
essential dump used here — surfaced as a stub for future expansion.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Contributing / Mitwirken
|
|
2
|
+
|
|
3
|
+
[🇬🇧 English](#english) · [🇩🇪 Deutsch](#deutsch)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## English
|
|
8
|
+
|
|
9
|
+
Thanks for your interest in improving `lobbywatch-mcp`. This project is part of the [Swiss Public Data MCP Portfolio](https://github.com/malkreide). Contributions of any size — bug reports, documentation fixes, new tools, performance work — are welcome.
|
|
10
|
+
|
|
11
|
+
### Development setup
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
git clone https://github.com/malkreide/lobbywatch-mcp.git
|
|
15
|
+
cd lobbywatch-mcp
|
|
16
|
+
python -m venv .venv
|
|
17
|
+
source .venv/bin/activate
|
|
18
|
+
pip install -e ".[dev]"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Running tests
|
|
22
|
+
|
|
23
|
+
Offline only (fast, what CI runs):
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pytest -m "not live"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Including live API + dump probes:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pytest -m live
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Linting
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
ruff check .
|
|
39
|
+
ruff format .
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Design principles
|
|
43
|
+
|
|
44
|
+
1. **No-Auth-First.** All Phase 1 tools must work without credentials.
|
|
45
|
+
2. **Live validation before coding.** If you add a tool that hits an upstream endpoint, verify the endpoint with a live probe first and document what you found in the PR.
|
|
46
|
+
3. **Attribution is non-negotiable.** Every response goes through a Pydantic envelope that carries the CC BY-SA 4.0 credit. Don't route around it.
|
|
47
|
+
4. **Portfolio synergy.** If a feature fits better in another server (`parlament-mcp`, `register-mcp`, …), suggest that instead of growing this one.
|
|
48
|
+
|
|
49
|
+
### Pull requests
|
|
50
|
+
|
|
51
|
+
Follow Conventional Commits (`feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:`) and update `CHANGELOG.md` under `[Unreleased]`.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Deutsch
|
|
56
|
+
|
|
57
|
+
Danke für das Interesse an `lobbywatch-mcp`. Das Projekt ist Teil des [Swiss Public Data MCP Portfolios](https://github.com/malkreide). Beiträge jeder Grössenordnung — Bug-Reports, Dokumentation, neue Tools, Performance — sind willkommen.
|
|
58
|
+
|
|
59
|
+
### Entwicklungs-Setup
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
git clone https://github.com/malkreide/lobbywatch-mcp.git
|
|
63
|
+
cd lobbywatch-mcp
|
|
64
|
+
python -m venv .venv
|
|
65
|
+
source .venv/bin/activate
|
|
66
|
+
pip install -e ".[dev]"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Tests ausführen
|
|
70
|
+
|
|
71
|
+
Nur offline (schnell, entspricht CI):
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pytest -m "not live"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Inklusive Live-Probes gegen API und Dump:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pytest -m live
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Linting
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
ruff check .
|
|
87
|
+
ruff format .
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Design-Prinzipien
|
|
91
|
+
|
|
92
|
+
1. **No-Auth-First.** Alle Phase-1-Tools müssen ohne Credentials funktionieren.
|
|
93
|
+
2. **Live-Validierung vor dem Coden.** Wer ein Tool gegen einen Upstream-Endpoint baut, probiert den Endpoint zuerst live aus und dokumentiert die Erkenntnisse im PR.
|
|
94
|
+
3. **Namensnennung ist nicht verhandelbar.** Jede Antwort läuft durch ein Pydantic-Envelope mit der CC-BY-SA-4.0-Attribution. Nicht umgehen.
|
|
95
|
+
4. **Portfolio-Synergie.** Wenn ein Feature besser in einen anderen Server passt (`parlament-mcp`, `register-mcp`, …), besser dort einbringen als diesen Server aufblähen.
|
|
96
|
+
|
|
97
|
+
### Pull Requests
|
|
98
|
+
|
|
99
|
+
Conventional Commits verwenden (`feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:`) und `CHANGELOG.md` unter `[Unreleased]` nachführen.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1.7
|
|
2
|
+
#
|
|
3
|
+
# Multi-stage build for lobbywatch-mcp (audit SCALE-004, SEC-007, SCALE-006).
|
|
4
|
+
# Hardening goals:
|
|
5
|
+
# - Non-root runtime user (uid/gid 1000)
|
|
6
|
+
# - read-only rootfs compatible (cache lives in /home/lobbywatch/.cache,
|
|
7
|
+
# mount as tmpfs in compose/k8s)
|
|
8
|
+
# - No build toolchain or apt cache in the runtime image
|
|
9
|
+
# - Image small enough for routine pulls (~80 MB on python:slim)
|
|
10
|
+
|
|
11
|
+
ARG PYTHON_VERSION=3.13
|
|
12
|
+
|
|
13
|
+
# ---------------------------------------------------------------------------
|
|
14
|
+
# Builder: install the project into an isolated venv we can copy out.
|
|
15
|
+
# ---------------------------------------------------------------------------
|
|
16
|
+
FROM python:${PYTHON_VERSION}-slim AS builder
|
|
17
|
+
|
|
18
|
+
ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \
|
|
19
|
+
PIP_NO_CACHE_DIR=1 \
|
|
20
|
+
PYTHONDONTWRITEBYTECODE=1 \
|
|
21
|
+
PYTHONUNBUFFERED=1
|
|
22
|
+
|
|
23
|
+
WORKDIR /build
|
|
24
|
+
|
|
25
|
+
# Install into a venv so we can COPY just /opt/venv to the runtime image.
|
|
26
|
+
RUN python -m venv /opt/venv
|
|
27
|
+
ENV PATH="/opt/venv/bin:$PATH"
|
|
28
|
+
|
|
29
|
+
# Copy only what hatchling needs to build the wheel.
|
|
30
|
+
COPY pyproject.toml README.md LICENSE ./
|
|
31
|
+
COPY src ./src
|
|
32
|
+
|
|
33
|
+
RUN pip install --no-cache-dir .
|
|
34
|
+
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
# Runtime: minimal image, non-root user, no build tools.
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
FROM python:${PYTHON_VERSION}-slim AS runtime
|
|
39
|
+
|
|
40
|
+
LABEL org.opencontainers.image.source="https://github.com/malkreide/lobbywatch-mcp" \
|
|
41
|
+
org.opencontainers.image.description="MCP server for the Lobbywatch.ch lobby database" \
|
|
42
|
+
org.opencontainers.image.licenses="MIT"
|
|
43
|
+
|
|
44
|
+
# Create a fixed-uid non-root user. uid 1000 is the convention; matches the
|
|
45
|
+
# default Pod SecurityContext in most k8s presets and avoids volume-permission
|
|
46
|
+
# surprises on host-mount setups.
|
|
47
|
+
RUN groupadd --system --gid 1000 lobbywatch \
|
|
48
|
+
&& useradd --system --uid 1000 --gid 1000 \
|
|
49
|
+
--home-dir /home/lobbywatch --shell /sbin/nologin lobbywatch \
|
|
50
|
+
&& mkdir -p /home/lobbywatch/.cache/lobbywatch-mcp \
|
|
51
|
+
&& chown -R lobbywatch:lobbywatch /home/lobbywatch
|
|
52
|
+
|
|
53
|
+
COPY --from=builder /opt/venv /opt/venv
|
|
54
|
+
|
|
55
|
+
ENV PATH="/opt/venv/bin:$PATH" \
|
|
56
|
+
PYTHONDONTWRITEBYTECODE=1 \
|
|
57
|
+
PYTHONUNBUFFERED=1 \
|
|
58
|
+
LOBBYWATCH_MCP_TRANSPORT=http \
|
|
59
|
+
LOBBYWATCH_MCP_HOST=0.0.0.0 \
|
|
60
|
+
LOBBYWATCH_MCP_PORT=8000 \
|
|
61
|
+
LOBBYWATCH_MCP_CACHE_DIR=/home/lobbywatch/.cache/lobbywatch-mcp
|
|
62
|
+
|
|
63
|
+
USER 1000:1000
|
|
64
|
+
WORKDIR /home/lobbywatch
|
|
65
|
+
EXPOSE 8000
|
|
66
|
+
|
|
67
|
+
# In-image healthcheck: ensure the entry point still imports cleanly.
|
|
68
|
+
# This is intentionally lightweight — full liveness/readiness probes belong on
|
|
69
|
+
# the orchestrator (see docs/deployment.md).
|
|
70
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
71
|
+
CMD python -c "import lobbywatch_mcp" || exit 1
|
|
72
|
+
|
|
73
|
+
ENTRYPOINT ["lobbywatch-mcp"]
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Use Cases & Examples — lobbywatch-mcp
|
|
2
|
+
|
|
3
|
+
Real-world queries by audience. Indicate per example whether an API key is required.
|
|
4
|
+
|
|
5
|
+
> **Hinweis zur Authentifizierung:** Der `lobbywatch-mcp` Server benötigt **keinen API-Key** und keine Authentifizierung für den Zugriff auf die öffentlichen Daten.
|
|
6
|
+
|
|
7
|
+
### 🏫 Bildung & Schule
|
|
8
|
+
Lehrpersonen, Schulbehörden, Fachreferent:innen
|
|
9
|
+
|
|
10
|
+
**Interessenbindungen im Bildungswesen**
|
|
11
|
+
«Welche Mitglieder der nationalrätlichen Bildungskommission (WBK-N) haben deklarierte Interessenbindungen zu Bildungsverlagen oder privaten Bildungsträgern?»
|
|
12
|
+
→ `search_parlamentarier_nach_branche(branche_query="Bildung", kommission="WBK-N")`
|
|
13
|
+
Warum nützlich: Erlaubt Lehrpersonen und Schulbehörden, mögliche Befangenheiten bei bildungspolitischen Entscheidungen zu erkennen.
|
|
14
|
+
|
|
15
|
+
**Transparenz bei Bildungspolitiker:innen**
|
|
16
|
+
«Wie transparent sind die Mitglieder der Bildungskommission (WBK-N) bezüglich ihrer Entschädigungen bei Nebeneinkünften?»
|
|
17
|
+
→ `get_transparenzquote(kommission="WBK-N")`
|
|
18
|
+
Warum nützlich: Zeigt auf, wie offen die zuständigen Politiker:innen ihre finanziellen Interessen im Bildungsbereich deklarieren.
|
|
19
|
+
|
|
20
|
+
### 👨👩👧 Eltern & Schulgemeinde
|
|
21
|
+
Elternräte, interessierte Erziehungsberechtigte
|
|
22
|
+
|
|
23
|
+
**Verbindungen zu Krankenkassen**
|
|
24
|
+
«Gibt es Parlamentarier aus meinem Kanton, die bezahlte Mandate bei Krankenkassen haben, und wie beeinflusst das familienpolitische Vorlagen?»
|
|
25
|
+
→ `search_parlamentarier_nach_branche(branche_query="Krankenkasse")`
|
|
26
|
+
Warum nützlich: Hilft Eltern, die Hintergründe von Entscheidungen zu Gesundheits- und Prämienfragen zu verstehen.
|
|
27
|
+
|
|
28
|
+
**Einfluss von Familienorganisationen**
|
|
29
|
+
«Welche Nationalräte sind mit Lobbygruppen aus dem Bereich Familie oder Kinderbetreuung verbunden?»
|
|
30
|
+
→ `search_parlamentarier_nach_branche(branche_query="Familie")`
|
|
31
|
+
Warum nützlich: Macht sichtbar, welche Politiker:innen direkte Verbindungen zu familienpolitischen Interessenvertretungen pflegen.
|
|
32
|
+
|
|
33
|
+
### 🗳️ Bevölkerung & öffentliches Interesse
|
|
34
|
+
Allgemeine Öffentlichkeit, politisch und gesellschaftlich Interessierte
|
|
35
|
+
|
|
36
|
+
**Ranking der Interessenbindungen**
|
|
37
|
+
«Welche Nationalrätinnen oder Ständeräte haben am meisten bezahlte Nebenmandate (hauptberufliche Interessenbindungen)?»
|
|
38
|
+
→ `get_ranking(kriterium="anzahl_hauptberuflich", limit=10)`
|
|
39
|
+
Warum nützlich: Bietet der Öffentlichkeit einen klaren Überblick über die politische Unabhängigkeit und mögliche zeitliche Überbelastung von Gewählten.
|
|
40
|
+
|
|
41
|
+
**Überprüfung einer Lobbygruppe**
|
|
42
|
+
«Wer sitzt für den Verband 'economiesuisse' im Parlament und welche Organisationen sind damit verknüpft?»
|
|
43
|
+
→ `get_lobbygruppe(name_or_id="economiesuisse")`
|
|
44
|
+
Warum nützlich: Schafft Transparenz über den direkten Einfluss grosser Wirtschaftsverbände auf die Gesetzgebung.
|
|
45
|
+
|
|
46
|
+
### 🤖 KI-Interessierte & Entwickler:innen
|
|
47
|
+
MCP-Enthusiast:innen, Forscher:innen, Prompt Engineers, öffentliche Verwaltung
|
|
48
|
+
|
|
49
|
+
**Analyse politischer Netzwerke (mit parlament-mcp)**
|
|
50
|
+
«Finde heraus, wer für 'economiesuisse' lobbyiert, und suche dann mit parlament-mcp nach Vorstössen dieser Personen zum Thema Unternehmenssteuern.»
|
|
51
|
+
→ `get_lobbygruppe(name_or_id="economiesuisse")` (lobbywatch-mcp)
|
|
52
|
+
→ `search_affairs(query="Unternehmenssteuer")` (parlament-mcp: https://github.com/malkreide/parlament-mcp)
|
|
53
|
+
Warum nützlich: Demonstriert die mächtige Kombination von Lobby-Netzwerken mit tatsächlichem Abstimmungs- und Vorstossverhalten.
|
|
54
|
+
|
|
55
|
+
**Branchen-Profiling und parlamentarisches Wirken**
|
|
56
|
+
«Welche Politiker:innen sind im Bereich 'Pharma' aktiv und welche Vorstösse haben sie in der letzten Session eingereicht?»
|
|
57
|
+
→ `search_parlamentarier_nach_branche(branche_query="Pharma")` (lobbywatch-mcp)
|
|
58
|
+
→ `get_parlamentarier(name_or_id="[Name]")` (lobbywatch-mcp)
|
|
59
|
+
Warum nützlich: Verbindet finanzielle Interessen direkt mit parlamentarischem Handeln durch serverübergreifende oder kombinierte Abfragen.
|
|
60
|
+
|
|
61
|
+
### 🔧 Technische Referenz: Tool-Auswahl nach Anwendungsfall
|
|
62
|
+
|
|
63
|
+
| Ich möchte… | Tool(s) | Auth nötig? |
|
|
64
|
+
| :--- | :--- | :--- |
|
|
65
|
+
| **das vollständige Profil eines Politikers abrufen** | `get_parlamentarier` | Nein |
|
|
66
|
+
| **alle Nebenmandate einer Person auflisten** | `list_interessenbindungen` | Nein |
|
|
67
|
+
| **nach Branchen oder Themen suchen** | `search_parlamentarier_nach_branche` | Nein |
|
|
68
|
+
| **das Netzwerk einer Lobbygruppe analysieren** | `get_lobbygruppe` | Nein |
|
|
69
|
+
| **Ranglisten nach Anzahl Mandaten erstellen** | `get_ranking` | Nein |
|
|
70
|
+
| **die Transparenz einer Kommission auswerten** | `get_transparenzquote` | Nein |
|
|
71
|
+
| **den lokalen Datenbestand aktualisieren** | `refresh_dump` / `dump_status` | Nein |
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 malkreide
|
|
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.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
Data notice
|
|
26
|
+
-----------
|
|
27
|
+
|
|
28
|
+
This software accesses and redistributes data from Lobbywatch.ch, which is
|
|
29
|
+
licensed under the Creative Commons Attribution-ShareAlike 4.0 International
|
|
30
|
+
License (CC BY-SA 4.0): https://creativecommons.org/licenses/by-sa/4.0/
|
|
31
|
+
|
|
32
|
+
Users of this software must comply with the CC BY-SA 4.0 terms for the data,
|
|
33
|
+
independently of the MIT licence that covers the code. Every tool response
|
|
34
|
+
produced by this server includes the required attribution string.
|