Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8dc0809
Merge pull request #1 from vinaayakh-aot/feature/multiple-mcps
vinaayakh-aot Mar 31, 2026
93cb253
Updated Cerner and FHIR connection
vinaayakh-aot Mar 31, 2026
018ba62
cleanup and update mcp
vinaayakh-aot Mar 31, 2026
cc803d2
SDK connector added
vinaayakh-aot Apr 1, 2026
cab653b
Updated architecture
vinaayakh-aot Apr 1, 2026
2701380
Update Playground to work with the new architecture
vinaayakh-aot Apr 1, 2026
00e3fbd
Register full MCP tools for FHIR, Drive, SMTP
kesav-aot Apr 1, 2026
eabb762
Merge pull request #3 from kesav-aot/CNP-27-expose-all-actions-define…
vinaayakh-aot Apr 1, 2026
31feb45
Google drive connector has
vinaayakh-aot Apr 1, 2026
88de542
Updated Docs and UI
vinaayakh-aot Apr 1, 2026
4e0fa60
Merge pull request #4 from vinaayakh-aot/feature/ui-doc-updates
vinaayakh-aot Apr 1, 2026
77cdb13
Merge branch 'main' into feature/architecture-fix
vinaayakh-aot Apr 1, 2026
e5b8b56
Added Test coverage
vinaayakh-aot Apr 1, 2026
7302910
Add Documentation
vinaayakh-aot Apr 1, 2026
bc8dbbb
Updated Connector Class
vinaayakh-aot Apr 2, 2026
a225586
Fix inconsistency in MCP server responses
vinaayakh-aot Apr 2, 2026
8e10feb
Merge pull request #6 from vinaayakh-aot/feature/architecture-fix
vinaayakh-aot Apr 5, 2026
0e26561
Feature/python packages (#7)
vinaayakh-aot Apr 5, 2026
d678511
Feature/python packages (#8)
vinaayakh-aot Apr 6, 2026
9b28582
Implemented Vulnerability detection and Code quality analysis
Apr 14, 2026
d270590
Identified issues in Google Drive Connector resolved
Apr 16, 2026
3fec9f0
Identified issues in HTTP Connector resolved
Apr 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/docker-policy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Enforce baseline container hygiene: digest-pinned base image and non-root USER.
name: Docker policy

on:
push:
branches: [main, master]
paths:
- "Dockerfile"
- "docker/**"
pull_request:
paths:
- "Dockerfile"
- "docker/**"

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Verify Dockerfiles use digest-pinned FROM and non-root USER
shell: bash
run: |
set -euo pipefail
shopt -s nullglob
files=(Dockerfile docker/*/Dockerfile)
for f in "${files[@]}"; do
echo "Checking $f"
if ! grep -qE '^FROM [^[:space:]]+@sha256:[a-f0-9]{64}' "$f"; then
echo "ERROR: $f must use FROM image@sha256:<64-hex-digest>" >&2
exit 1
fi
if ! grep -qE '^USER[[:space:]]' "$f"; then
echo "ERROR: $f must end with a non-root USER directive" >&2
exit 1
fi
done
echo "PASS: all Dockerfiles pinned and non-root"
228 changes: 228 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
# Pinned action SHAs are immutable; update intentionally when upgrading.
# yamllint disable rule:line-length
name: Publish Node Wire package

# Manual trigger: go to Actions → "Publish Node Wire package" → Run workflow.
# package_path must match the allowlist below (prevents confused-deputy path abuse).
#
# Examples:
# package_path: packages/runtime
# package_path: packages/connectors/fhir_epic
# package_path: packages/connectors/google_drive
on:
workflow_dispatch:
inputs:
package_path:
description: |
Relative path to the package directory (must be allowlisted).
Examples: packages/runtime | packages/connectors/fhir_epic
required: true
type: string
version:
description: "Semver version to publish (e.g. 0.2.0)"
required: true
type: string

env:
PIP_AUDIT_VERSION: "2.7.3"
CYCLONEDX_BOM_VERSION: "4.6.1"

jobs:
# ─────────────────────────────────────────────────────────────────────────────
# Build a binary wheel for each platform (Linux / macOS / Windows).
# cibuildwheel compiles Cython extensions and produces manylinux / macosx /
# win_amd64 wheels. The NoPyBuild override in setup.py ensures .py source
# files are excluded from all wheels.
# ─────────────────────────────────────────────────────────────────────────────
build-wheels:
name: Build (${{ matrix.os }})
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
permissions:
contents: read

steps:
- name: Validate package path (allowlist)
shell: python
run: |
import os
import sys

raw = "${{ inputs.package_path }}".strip().replace("\\", "/")
norm = os.path.normpath(raw).replace("\\", "/")
# Reject traversal / absolute paths
if norm.startswith("..") or os.path.isabs(raw):
print("ERROR: invalid package_path", file=sys.stderr)
sys.exit(1)
allowed = {
"packages/runtime",
"packages/connectors/http_generic",
"packages/connectors/stripe",
"packages/connectors/smtp",
"packages/connectors/google_drive",
"packages/connectors/fhir_cerner",
"packages/connectors/fhir_epic",
}
if norm not in allowed:
print(f"ERROR: package_path {norm!r} is not allowlisted.", file=sys.stderr)
print("Allowed:", sorted(allowed), file=sys.stderr)
sys.exit(1)
print(f"PASS: package_path {norm!r} is allowlisted")

- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Set up Python
uses: actions/setup-python@0b93645e9fea7318ecaed2b3599a311e5c0f207c # v5.3.0
with:
python-version: "3.11"

- name: Install build tools
run: python -m pip install --upgrade pip "cython>=3.0" "cibuildwheel>=2.16.0"

- name: Build platform wheel(s)
run: |
cd "${{ inputs.package_path }}"
python -m cibuildwheel --output-dir dist
env:
# Build only Python 3.11+ wheels (matches requires-python in pyproject.toml)
CIBW_BUILD: "cp311-* cp312-*"
# Skip 32-bit targets and PyPy — not supported
CIBW_SKIP: "*-win32 *-manylinux_i686 pp*"

# ── Security gate: verify no .py source files leaked into any wheel ──────
- name: Verify binary-only wheel (no .py source)
shell: python
run: |
import glob, sys, zipfile

wheels = glob.glob("${{ inputs.package_path }}/dist/*.whl")
if not wheels:
print("ERROR: No wheels produced", file=sys.stderr)
sys.exit(1)

leaked: dict[str, list[str]] = {}
for whl in wheels:
with zipfile.ZipFile(whl) as zf:
bad = [n for n in zf.namelist() if n.endswith(".py")]
if bad:
leaked[whl] = bad

if leaked:
print("SECURITY FAIL: .py files found in wheel(s):", file=sys.stderr)
for whl, files in leaked.items():
print(f" {whl}:", file=sys.stderr)
for f in files:
print(f" {f}", file=sys.stderr)
sys.exit(1)

print(f"PASS: {len(wheels)} wheel(s) verified — no .py source files")

- name: Record wheel SHA256 (artifact integrity)
shell: python
run: |
import glob, hashlib, pathlib, sys
dist = pathlib.Path("${{ inputs.package_path }}") / "dist"
wheels = sorted(dist.glob("*.whl"))
if not wheels:
print("ERROR: no wheels to hash", file=sys.stderr)
sys.exit(1)
lines = []
for w in wheels:
h = hashlib.sha256(w.read_bytes()).hexdigest()
line = f"{h} {w.name}"
print(line)
lines.append(line)
(dist / "sha256sums.txt").write_text("\n".join(lines) + "\n", encoding="utf-8")

- name: Upload wheel artifacts
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
with:
name: wheels-${{ matrix.os }}
path: ${{ inputs.package_path }}/dist/*.whl
if-no-files-found: error

# ─────────────────────────────────────────────────────────────────────────────
# Collect all platform wheels, run supply-chain checks, publish to PyPI.
# Uses Trusted Publisher (OIDC) — no long-lived PyPI API tokens needed.
# Configure on PyPI: Settings → Publishing → Add Publisher (GitHub, this repo,
# workflow name = "Publish Node Wire package").
# ─────────────────────────────────────────────────────────────────────────────
publish:
name: Publish to PyPI
needs: build-wheels
runs-on: ubuntu-latest
permissions:
id-token: write # Required for Trusted Publisher OIDC
contents: read

steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Set up Python
uses: actions/setup-python@0b93645e9fea7318ecaed2b3599a311e5c0f207c # v5.3.0
with:
python-version: "3.11"

- name: Download all wheel artifacts
uses: actions/download-artifact@fa0a91b85d4f404e442e0f254de474a18c628458 # v4.1.8
with:
path: dist-all

- name: Flatten into dist/ directory
run: |
mkdir -p dist
find dist-all -name "*.whl" -exec cp {} dist/ \;
echo "Wheels collected:"
ls dist/
sha256sum dist/*.whl | tee dist/sha256sums.txt

- name: Validate wheel version matches input
shell: python
run: |
import glob, sys

expected = "${{ inputs.version }}"
wheels = glob.glob("dist/*.whl")
if not wheels:
print("ERROR: No wheels found for publish", file=sys.stderr)
sys.exit(1)

bad = [w for w in wheels if f"-{expected}-" not in w and f"-{expected.replace('.', '_')}-" not in w]
if bad:
print(f"ERROR: Version mismatch. Expected {expected!r} in filename.", file=sys.stderr)
for w in bad:
print(f" {w}", file=sys.stderr)
sys.exit(1)

print(f"PASS: {len(wheels)} wheel(s) match version {expected!r}")

- name: Install built wheels for CVE scan
run: pip install dist/*.whl

- name: Vulnerability scan (CVE gate — blocks publish on HIGH or higher)
run: |
pip install "pip-audit==${{ env.PIP_AUDIT_VERSION }}"
pip-audit --fail-on HIGH

- name: Generate SBOM
run: |
pip install "cyclonedx-bom==${{ env.CYCLONEDX_BOM_VERSION }}"
cyclonedx-py environment -o sbom.json
echo "SBOM generated: sbom.json"

- name: Upload SBOM as release artifact
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
with:
name: sbom-${{ inputs.version }}
path: sbom.json

- name: Publish to PyPI (Trusted Publisher / OIDC + Sigstore attestations)
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
with:
packages-dir: dist/
# attestations: true generates a Sigstore attestation automatically.
# Clients can verify with: pip download <pkg> && python -m pypi_attestation_viewer <whl>
attestations: true
73 changes: 73 additions & 0 deletions .github/workflows/quality-gates.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: Quality gates

on:
pull_request:
push:
branches: [main, master]

# This workflow enforces Bandit, tests/coverage, and SonarQube.

jobs:
bandit:
name: Bandit security scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Python
uses: actions/setup-python@0b93645e9fea7318ecaed2b3599a311e5c0f207c # v5.3.0
with:
python-version: "3.11"
- name: Install dependencies
run: python -m pip install --upgrade pip && pip install -e ".[dev,agents]"
- name: Generate Bandit JSON report
run: bandit -c pyproject.toml -r src -f json -o bandit-report.json
- name: Upload Bandit report artifact
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
with:
name: bandit-report
path: bandit-report.json
- name: Enforce high-severity Bandit gate
run: bandit -c pyproject.toml -r src --severity-level high

test:
name: Tests and coverage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Python
uses: actions/setup-python@0b93645e9fea7318ecaed2b3599a311e5c0f207c # v5.3.0
with:
python-version: "3.11"
- name: Install dependencies
run: python -m pip install --upgrade pip && pip install -e ".[dev,agents]"
- name: Run tests (coverage.xml generated via pyproject addopts)
run: pytest tests/ -v
- name: Upload coverage artifact
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
with:
name: coverage-xml
path: coverage.xml
if-no-files-found: error

sonar:
name: SonarQube analysis
runs-on: ubuntu-latest
needs: [bandit, test]
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: Download coverage artifact
uses: actions/download-artifact@fa0a91b85d4f404e442e0f254de474a18c628458 # v4.1.8
with:
name: coverage-xml
path: .
- name: SonarQube scan (wait for quality gate)
uses: SonarSource/sonarqube-scan-action@v5.3.1
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
with:
args: >
-Dsonar.qualitygate.wait=true
-Dsonar.qualitygate.timeout=300
21 changes: 20 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,23 @@ __pycache__/
dist/
.env
.DS_Store
**/.DS_Store
**/.DS_Store
.coverage
.coverage.*
htmlcov/

# GCP / cloud credentials
connectorplatform-*.json
*-service-account.json
*credentials*.json

# Grafana exports (auto-generated)
grafana/*.json

# Python lock files
uv.lock

# Temporary test/upload files
upload-test.txt
*-test.txt
upload-*.txt
9 changes: 9 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Bandit pre-commit hook.

repos:
- repo: https://github.com/PyCQA/bandit
rev: 1.8.6
hooks:
- id: bandit
args: ["-c", "pyproject.toml", "-r", "src"]
pass_filenames: false
Loading
Loading