Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 1 addition & 3 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
# CODEOWNERS — auto-assigns @heznpc to every PR for review.
# Solo-dev repo; this exists so Dependabot's grouped bump PRs and outside
# contributions land in the right inbox without manual triage.
# Solo-dev: route all PRs to the maintainer.
* @heznpc
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
- [ ] `ruff check .` passes
- [ ] `ruff format --check .` passes
- [ ] `mypy src/` passes
- [ ] `pytest` passes (coverage >= 70%)
- [ ] `pytest` passes locally; CI coverage gate (see `ci.yml`) will enforce the threshold
- [ ] CHANGELOG.md `[Unreleased]` updated if user-visible
- [ ] No `.coverage`, `.env`, secrets, or build artifacts committed

Expand Down
18 changes: 8 additions & 10 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ jobs:
runs-on: ubuntu-latest
environment: pypi
permissions:
contents: write # softprops/action-gh-release needs this to create the Release
id-token: write # OIDC for PyPI trusted publishing + sigstore attestations
attestations: write # actions/attest-build-provenance writes to the repo's attestation store
contents: write
id-token: write
attestations: write
steps:
- uses: actions/checkout@v6
with:
Expand All @@ -27,6 +27,8 @@ jobs:
- uses: actions/setup-python@v6
with:
python-version: "3.13"
cache: pip
cache-dependency-path: pyproject.toml

- name: Extract version
id: pkg
Expand All @@ -48,19 +50,15 @@ jobs:
python -m build

- name: Generate SLSA build provenance attestation
# Produces a sigstore-signed attestation linking the GitHub Actions run
# to the built wheel + sdist. Surfaces on the PyPI release page as
# "Build attestations: verified" once PyPI ingests it.
uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # v3.2.0
with:
subject-path: 'dist/*'

- name: Publish to PyPI
# Pinned to floating `release/v1` per pypa guidance — the action
# self-updates within v1 and pypa rotates secrets behind that ref.
# Floating `release/v1` is pypa's documented pin; the action self-updates within v1.
# OIDC trusted publishing — configure at:
# https://pypi.org/manage/project/my-mcp-server/settings/publishing/
uses: pypa/gh-action-pypi-publish@release/v1
# Uses OIDC trusted publishing — no PYPI_TOKEN secret needed.
# Configure at: https://pypi.org/manage/project/my-mcp-server/settings/publishing/
with:
attestations: true

Expand Down
8 changes: 5 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ jobs:
- uses: actions/setup-python@v6
with:
python-version: "3.13"
cache: pip
cache-dependency-path: pyproject.toml

- name: Install dependencies
run: pip install -e . pip-licenses
Expand All @@ -66,6 +68,8 @@ jobs:
- uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: pip
cache-dependency-path: pyproject.toml

- name: Install dependencies
run: pip install -e ".[dev]"
Expand All @@ -80,6 +84,4 @@ jobs:
run: mypy src/

- name: Test (with coverage)
# --cov / --cov-fail-under live in pyproject.toml [tool.pytest.ini_options]
# so local `pytest` invocations match CI exactly.
run: pytest -v
run: pytest -v --cov=my_mcp_server --cov-report=term-missing --cov-fail-under=70
7 changes: 2 additions & 5 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ jobs:
strategy:
fail-fast: false
matrix:
# `python` scans src/ for code-level issues.
# `actions` scans .github/workflows/ for workflow-level issues
# (untrusted input in `run:`, script injection, missing permissions,
# etc.). Available since CodeQL 2.20.
# (script injection, missing permissions, etc.).
language: [python, actions]
steps:
- uses: actions/checkout@v6
Expand All @@ -32,8 +30,7 @@ jobs:
languages: ${{ matrix.language }}

- name: Autobuild
# `actions` language doesn't need a build step but autobuild is a no-op
# there, so leave it for simplicity.
if: matrix.language == 'python'
uses: github/codeql-action/autobuild@v4

- name: Perform CodeQL Analysis
Expand Down
1 change: 0 additions & 1 deletion .python-version

This file was deleted.

7 changes: 0 additions & 7 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,6 @@

## Reporting a Vulnerability

If you discover a security vulnerability, please report it responsibly:

1. **Do NOT** open a public issue
2. Email the maintainer or use GitHub's private vulnerability reporting

## Reporting

Use [GitHub's private vulnerability reporting](https://github.com/starter-series/python-mcp-server-starter/security/advisories/new)
(Security tab → Report a vulnerability). Do **not** open a public issue.

Expand Down
16 changes: 4 additions & 12 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ dependencies = [
my-mcp-server = "my_mcp_server.server:main"

[project.optional-dependencies]
# Dev tools are major-bounded so a future breaking release of ruff/mypy/pytest
# can't silently turn CI red. Dependabot's `lint` / `test` groups bump the
# upper bound when a new major ships and we've reviewed the changelog.
# Major-bounded so a breaking ruff/mypy/pytest release can't turn CI red overnight.
dev = [
"pytest>=8,<10",
"pytest-asyncio>=1,<2",
Expand All @@ -45,15 +43,10 @@ target-version = "py311"
line-length = 100

[tool.ruff.lint]
# E/F/W/I/N/UP — standard correctness + style + import order + pep8-naming + pyupgrade.
# B — flake8-bugbear: common Python bugs (mutable defaults, etc.).
# S — flake8-bandit: security antipatterns (subprocess shell=True, etc.).
# ASYNC — async-specific antipatterns (blocking calls in async funcs).
# RUF, SIM — ruff-specific + simplifications.
select = ["E", "F", "I", "N", "W", "UP", "B", "S", "ASYNC", "RUF", "SIM"]

[tool.ruff.lint.per-file-ignores]
# pytest's primary API is `assert` — S101 fires on every test line otherwise.
# pytest's primary API is `assert`.
"tests/**" = ["S101"]

[tool.mypy]
Expand All @@ -72,9 +65,8 @@ disallow_untyped_defs = false
[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]
# --cov-fail-under matches the baseline measured at sweep time (~71%). Bump
# this number when coverage improves — never lower it without justification.
addopts = "--cov=my_mcp_server --cov-report=term-missing --cov-fail-under=70"
# Coverage flags live in ci.yml, not here — so `pytest tests/test_one.py` for a
# quick local check doesn't pay the instrumentation tax or fail on --cov-fail-under.

[tool.coverage.run]
branch = true
Expand Down