diff --git a/.bumpver.toml b/.bumpver.toml new file mode 100644 index 0000000..db6fe6f --- /dev/null +++ b/.bumpver.toml @@ -0,0 +1,24 @@ +[bumpver] +current_version = "0.1.1" +version_pattern = "MAJOR.MINOR.PATCH" +commit_message = "bump: version {old_version} → {new_version}" +tag_message = "v{new_version}" +tag_scope = "default" + +[bumpver.file_patterns] +# pyproject.toml +"pyproject.toml" = [ + 'version = "{version}"' +] +# Python package __init__ +"stackhawk_mcp/__init__.py" = [ + '__version__ = "{version}"' +] +# README version badge or text +"README.md" = [ + '**Current Version: {version}**' +] +# DXT file version +"stackhawk-mcp.dxt" = [ + '"version": "{version}"' +] \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..595903f --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @stackhawk/engineering \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..9deb86b --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,37 @@ +## StackHawk Pull Request + +> Description here + + +![replace me](giphy-url.gif) + +### Type of change + +- [x] Code +- [ ] Documentation +- [ ] Release +- [ ] Other _(please explain)_ + +### Related Issues + + +> [NGNRNG-XXX](https://stackhawk.atlassian.net/browse/NGNRNG-XXX) + +### Code + +- [ ] This PR has a descriptive title, attached issue id, and information useful to a reviewer +- [ ] The "Ready for Review" label attached to the PR, and reviewers have been informed +- [ ] All lint rules and tests related to the changed code pass locally + +Did you add or update tests to cover this change? +- [ ] Yes +- [ ] No: _(please explain)_ + +### Documentation + +- [ ] Documentation has been updated, with attention to spelling, grammar and clarity + +### Release + +- [ ] This software deployment has been communicated to the team +- [ ] Relevant changes are reflected in the product changelog diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0f0a651..504bf55 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,4 +25,48 @@ jobs: pip install . - name: Run tests run: | - pytest --maxfail=1 --disable-warnings \ No newline at end of file + pytest --maxfail=1 --disable-warnings + + tag-version: + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + needs: test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Get version from pyproject.toml + id: get_version + run: | + VERSION=$(grep '^version = ' pyproject.toml | head -1 | cut -d '"' -f2) + echo "::set-output name=version::$VERSION" + - name: Create and push tag for current version + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git tag v${{ steps.get_version.outputs.version }} + git push origin v${{ steps.get_version.outputs.version }} + + bump-version: + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + needs: [test, tag-version] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: Install bumpver + run: | + python -m pip install --upgrade pip + pip install bumpver + - name: Bump patch version with bumpver + run: bumpver update --patch --no-tag --commit + - name: Push version bump commit + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git push \ No newline at end of file diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index d07e48c..a8c18c7 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -16,6 +16,8 @@ jobs: environment: pypi steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Set up Python uses: actions/setup-python@v5 with: @@ -23,7 +25,28 @@ jobs: - name: Install build dependencies run: | python -m pip install --upgrade pip - pip install build + pip install build bumpver + - name: Bump minor version with bumpver + if: github.ref == 'refs/heads/main' + run: bumpver update --minor --commit + - name: Get version from pyproject.toml + id: get_version + run: | + VERSION=$(grep '^version = ' pyproject.toml | head -1 | cut -d '"' -f2) + echo "version=$VERSION" >> $GITHUB_OUTPUT + - name: Create and push tag for new version + if: github.ref == 'refs/heads/main' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git tag v${{ steps.get_version.outputs.version }} + git push origin v${{ steps.get_version.outputs.version }} + - name: Push version bump commit + if: github.ref == 'refs/heads/main' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git push - name: Build package run: | python -m build diff --git a/README.md b/README.md index 99daddb..9cf0920 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # StackHawk MCP Server -**Current Version: 0.1.0** +**Current Version: 0.1.1** _Requires Python 3.10 or higher_ A Model Context Protocol (MCP) server for integrating with StackHawk's security scanning platform. Provides security analytics, YAML configuration management, sensitive data/threat surface analysis, and anti-hallucination tools for LLMs. @@ -39,7 +39,12 @@ A Model Context Protocol (MCP) server for integrating with StackHawk's security pip install stackhawk-mcp # Requires Python 3.10 or higher ``` -2. **Set your StackHawk API key:** +2. **Install locally from the repo:** + ```bash + pip install --user . + # Run this command from the root of the cloned repository + ``` +3. **Set your StackHawk API key:** ```bash export STACKHAWK_API_KEY="your-api-key-here" ``` diff --git a/pyproject.toml b/pyproject.toml index 1a45d7c..37c2973 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta" [project] name = "stackhawk-mcp" -version = "0.1.0" +version = "0.1.1" description = "StackHawk MCP Server for Security Analytics and Developer Integration" authors = [{name = "StackHawk, Inc.", email = "support@stackhawk.com"}] -license = {text = "Apache-2.0"} +license = "Apache-2.0" readme = "README.md" requires-python = ">=3.10" dependencies = [ @@ -27,7 +27,7 @@ dev = [ ] [project.scripts] -stackhawk-mcp = "stackhawk_mcp.server:main" +stackhawk-mcp = "stackhawk_mcp.__main__:cli" [project.urls] Homepage = "https://github.com/stackhawk/stackhawk-mcp" @@ -45,4 +45,7 @@ disallow_untyped_defs = true [tool.pytest.ini_options] asyncio_mode = "auto" -testpaths = ["tests"] \ No newline at end of file +testpaths = ["tests"] + +[tool.setuptools.packages.find] +include = ["stackhawk_mcp*"] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 0dfee06..0c525c8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,6 +12,7 @@ pytest>=7.0.0 pytest-asyncio>=0.21.0 black>=23.0.0 mypy>=1.0.0 +bumpver>=2023.1129 # FastAPI dependencies fastapi diff --git a/stackhawk-mcp.dxt b/stackhawk-mcp.dxt index e0cf4ff..e56dda6 100644 --- a/stackhawk-mcp.dxt +++ b/stackhawk-mcp.dxt @@ -1,7 +1,7 @@ { "name": "stackhawk-mcp", "description": "StackHawk MCP Server for security testing, analytics, YAML validation, and vulnerability management.", - "version": "1.0.0", + "version": "0.1.1", "author": "StackHawk", "license": "MIT", "homepage": "https://github.com/stackhawk/stackhawk-mcp", diff --git a/stackhawk_mcp/__init__.py b/stackhawk_mcp/__init__.py index 4102c2d..694686e 100644 --- a/stackhawk_mcp/__init__.py +++ b/stackhawk_mcp/__init__.py @@ -5,6 +5,6 @@ with the StackHawk API through the Model Context Protocol (MCP). """ -__version__ = "0.1.0" +__version__ = "0.1.1" __author__ = "StackHawk MCP Team" __email__ = "support@stackhawk.com" \ No newline at end of file diff --git a/stackhawk_mcp/__main__.py b/stackhawk_mcp/__main__.py new file mode 100644 index 0000000..a77ad57 --- /dev/null +++ b/stackhawk_mcp/__main__.py @@ -0,0 +1,8 @@ +import asyncio +from .server import main + +def cli(): + asyncio.run(main()) + +if __name__ == "__main__": + cli() \ No newline at end of file diff --git a/stackhawk_mcp/http_server.py b/stackhawk_mcp/http_server.py index 8e9dc1c..f630037 100644 --- a/stackhawk_mcp/http_server.py +++ b/stackhawk_mcp/http_server.py @@ -7,6 +7,7 @@ import uvicorn import asyncio from stackhawk_mcp.server import StackHawkMCPServer +from stackhawk_mcp import __version__ app = FastAPI() @@ -21,9 +22,10 @@ # Get API key from environment API_KEY = os.environ.get("STACKHAWK_API_KEY", "changeme") +BASE_URL = os.environ.get("STACKHAWK_BASE_URL", "https://api.stackhawk.com") # Create the MCP server instance -mcp_server = StackHawkMCPServer(api_key=API_KEY) +mcp_server = StackHawkMCPServer(api_key=API_KEY, base_url=BASE_URL) # Store active SSE connections active_connections = {} @@ -70,7 +72,7 @@ async def handle_initialize_request(request_data): }, "serverInfo": { "name": "StackHawk MCP", - "version": "0.1.0" + "version": __version__ } } ) @@ -236,7 +238,7 @@ async def root(): "id": 1, "result": { "serverName": "StackHawk MCP", - "serverVersion": "0.1.0", + "serverVersion": __version__, "protocolVersion": "v1" } }) @@ -248,7 +250,7 @@ async def root_post(): "id": 1, "result": { "serverName": "StackHawk MCP", - "serverVersion": "0.1.0", + "serverVersion": __version__, "protocolVersion": "v1" } }) diff --git a/stackhawk_mcp/server.py b/stackhawk_mcp/server.py index 1140236..5810a8f 100644 --- a/stackhawk_mcp/server.py +++ b/stackhawk_mcp/server.py @@ -35,6 +35,7 @@ ) import mcp.server.stdio import mcp.types as types +from stackhawk_mcp import __version__ # Configure logging to stderr so Claude Desktop can see it logging.basicConfig( @@ -44,7 +45,7 @@ ) logger = logging.getLogger("stackhawk-mcp") -STACKHAWK_MCP_VERSION = "0.1.0" +STACKHAWK_MCP_VERSION = __version__ def debug_print(message): @@ -597,9 +598,9 @@ async def _get_application_vulnerabilities(self, app_id: str = None, severity_fi class StackHawkMCPServer: """StackHawk MCP Server implementation""" - def __init__(self, api_key: str): + def __init__(self, api_key: str, base_url: str = "https://api.stackhawk.com"): debug_print("Initializing StackHawkMCPServer...") - self.client = StackHawkClient(api_key) + self.client = StackHawkClient(api_key, base_url) self.server = Server("stackhawk-mcp") self._schema_cache: Optional[Dict[str, Any]] = None self._schema_cache_time: Optional[datetime] = None @@ -2380,16 +2381,18 @@ async def main(): import os api_key = os.environ.get("STACKHAWK_API_KEY") + base_url = os.environ.get("STACKHAWK_BASE_URL", "https://api.stackhawk.com") if not api_key: debug_print("ERROR: STACKHAWK_API_KEY environment variable is required") sys.exit(1) debug_print(f"API key found: {api_key[:20]}...") + debug_print(f"Base URL: {base_url}") server = None try: debug_print("Creating StackHawkMCPServer...") - server = StackHawkMCPServer(api_key) + server = StackHawkMCPServer(api_key, base_url) debug_print("Running server...") await server.run() except KeyboardInterrupt: