diff --git a/.dxtignore b/.dxtignore new file mode 100644 index 0000000..3480de5 --- /dev/null +++ b/.dxtignore @@ -0,0 +1,8 @@ +__pycache__/ +.claude/ +.ruff_cache/ +.venv/ +build/ +lib/ +scripts/ +*.egg-info/ diff --git a/README.md b/README.md index 8bee4b9..916589a 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,16 @@ # Netwrix Privilege Secure MCP Server -The Netwrix Privilege Secure MCP Server is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) server that provides seamless integration with Netwrix Privilege Secure (NPS), enabling AI agents to monitor and manage privileged access across your environment. + +This [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) server enables AI assistants to interface directly with Netwrix Privilege Secure (NPS), enhancing your organization's Privileged Access Management (PAM) capabilities. ## Use Cases -- Monitor active privileged sessions and view live screenshots -- Search command history to identify suspicious activity -- Identify administrators with outdated passwords -- Track managed credentials that require rotation -- Automate security audits and compliance reporting +- **Real-time Monitoring:** View active privileged sessions with live screenshots +- **Security Investigation:** Search command history to identify suspicious activity +- **Credential Management:** Identify outdated passwords and credentials needing rotation ## Tools ### Session Monitoring + - **get_active_sessions** - Get active activity sessions - **get_active_session_image** - Get a live image for an active session @@ -20,6 +20,7 @@ The Netwrix Privilege Secure MCP Server is a [Model Context Protocol (MCP)](http - `search_term`: Term to search for in command history (string, required) ### Credential Management + - **get_admin_credentials_older_than** - Find administrators with outdated passwords - `days`: Password age threshold in days (number, required) @@ -27,6 +28,7 @@ The Netwrix Privilege Secure MCP Server is a [Model Context Protocol (MCP)](http - `days`: Password age threshold in days (number, required) ### Event Search + - **get_events_from_server** - Find latest 10 events that contain the search text - `search_text`: Text to search for (str, required) @@ -35,24 +37,26 @@ The Netwrix Privilege Secure MCP Server is a [Model Context Protocol (MCP)](http ## Installation -Clone the repository: +Prequisites: + +- Install [uv](https://docs.astral.sh/uv/getting-started/installation/), a Python project manager + +- Clone the repository (or download it as a zip) + +To clone the repository: ```bash git clone https://github.com/netwrix/mcp-server-nps.git ``` ### Install in Claude Desktop via Desktop Extensions -**Requirements:** Python 3.12+ installed - -- File -> Settings -> Extensions -> Advanced settings -> Install Extension... -- Select the `mcp-server-nps.dxt` file to upload -- Fill out required fields and select Install +- Navigate `File -> Settings -> Extensions -> Advanced settings -> Install Extension...` +- Select the `mcp-server-nps.dxt` file to upload (found in releases) +- Fill out the required fields, select Install and Enable ### Install in Claude Desktop via Configuration File -**Requirements:** uv installed - -- File -> Settings -> Developer -> Edit Config +- Navigate `File -> Settings -> Developer -> Edit Config` Open the `claude_desktop_config.json` file and add the following: @@ -60,12 +64,15 @@ Open the `claude_desktop_config.json` file and add the following: { "mcpServers": { "mcp-server-nps": { - "command": "uv", - "args": ["path/to/src/mcp_server_nps/server.py"], + "command": "uv", + "args": [ + "run", + "path/to/src/mcp_server_nps/server.py" + ], "env": { "NPS_URL": "https://example.com", - "NPS_USERNAME": "my_username", - "NPS_PASSWORD": "mypassword" + "NPS_USERNAME": "your_nps_username", + "NPS_PASSWORD": "your_nps_password" } } } @@ -78,6 +85,6 @@ Then, restart Claude Desktop. This project is licensed under the terms of the MIT open source license. Please refer to [MIT](./LICENSE) for the full terms. -## Questions & Support +## Connect with Us -If you need help using this MCP server or understanding your results, just visit the [Netwrix Community](https://community.netwrix.com/) - we’re here to help! \ No newline at end of file +If you need help using this MCP server, want to better understand your results, or would like to share feedback, visit the [Netwrix Community](https://community.netwrix.com/) - we’re here to help and eager to hear about your experience! diff --git a/manifest.json b/manifest.json index ba9601d..7aecb17 100644 --- a/manifest.json +++ b/manifest.json @@ -4,7 +4,7 @@ "version": "0.1.0", "display_name": "Netwrix Privilege Secure MCP Server", "description": "Monitor privileged sessions, search history, manage credentials, and track security events", - "long_description": "This MCP server provides an integration with Netwrix Privilege Secure. It enables AI assistants to monitor active privileged sessions in real-time while searching command history and metadata from past sessions. Security teams can identify credentials that need rotation based on age and retrieve live session screenshots for enhanced monitoring. The system supports querying security events and audit logs as well as checking system version.", + "long_description": "This MCP server provides an integration with Netwrix Privilege Secure, enhancing your Privileged Access Management (PAM) capabilities. It enables AI assistants to monitor active privileged sessions with live screenshots, search command history to identify suspicious activity, and detect credentials requiring rotation.", "author": { "name": "Netwrix Corporation" }, @@ -12,24 +12,28 @@ "netwrix", "security", "privilege", + "access", + "management", "monitoring" ], "license": "MIT", "icon": "assets/icon.png", "server": { "type": "python", - "entry_point": "src/mcp_server_nps/server.py", + "entry_point": "server.py", "mcp_config": { - "command": "python", + "command": "uv", "args": [ - "${__dirname}/src/mcp_server_nps/server.py" + "run", + "--directory", + "${__dirname}", + "python", + "${__dirname}/server.py" ], "env": { - "PYTHONPATH": "${__dirname}/lib", "NPS_URL": "${user_config.nps_url}", "NPS_USERNAME": "${user_config.nps_username}", - "NPS_PASSWORD": "${user_config.nps_password}", - "NPS_VERIFY_TRUST": "false" + "NPS_PASSWORD": "${user_config.nps_password}" } } }, diff --git a/mcp-server-nps.dxt b/mcp-server-nps.dxt index 437920d..d02ed17 100644 Binary files a/mcp-server-nps.dxt and b/mcp-server-nps.dxt differ diff --git a/src/mcp_server_nps/__init__.py b/mcp_server_nps/__init__.py similarity index 100% rename from src/mcp_server_nps/__init__.py rename to mcp_server_nps/__init__.py diff --git a/src/mcp_server_nps/client.py b/mcp_server_nps/client.py similarity index 100% rename from src/mcp_server_nps/client.py rename to mcp_server_nps/client.py diff --git a/pyproject.toml b/pyproject.toml index e53c685..d7fb1d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,3 @@ select = [ "SIM", # flake8-simplify "I", # isort ] - -[tool.uv] -package = true diff --git a/scripts/Bundle-DXT.ps1 b/scripts/Bundle-DXT.ps1 deleted file mode 100644 index 4a6cf77..0000000 --- a/scripts/Bundle-DXT.ps1 +++ /dev/null @@ -1,6 +0,0 @@ -# Bundle the project dependencies -Remove-Item -Path ./lib/ -Recurse -ErrorAction Ignore -& uv pip install . --target ./lib/ - -# Build the DXT extension -& npx dxt pack \ No newline at end of file diff --git a/src/mcp_server_nps/server.py b/server.py similarity index 100% rename from src/mcp_server_nps/server.py rename to server.py diff --git a/uv.lock b/uv.lock index de9db66..bc33996 100644 --- a/uv.lock +++ b/uv.lock @@ -36,11 +36,11 @@ wheels = [ [[package]] name = "certifi" -version = "2025.7.9" +version = "2025.7.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/de/8a/c729b6b60c66a38f590c4e774decc4b2ec7b0576be8f1aa984a53ffa812a/certifi-2025.7.9.tar.gz", hash = "sha256:c1d2ec05395148ee10cf672ffc28cd37ea0ab0d99f9cc74c43e588cbd111b079", size = 160386 } +sdist = { url = "https://files.pythonhosted.org/packages/b3/76/52c535bcebe74590f296d6c77c86dabf761c41980e1347a2422e4aa2ae41/certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995", size = 163981 } wheels = [ - { url = "https://files.pythonhosted.org/packages/66/f3/80a3f974c8b535d394ff960a11ac20368e06b736da395b551a49ce950cce/certifi-2025.7.9-py3-none-any.whl", hash = "sha256:d842783a14f8fdd646895ac26f719a061408834473cfc10203f6a575beb15d39", size = 159230 }, + { url = "https://files.pythonhosted.org/packages/4f/52/34c6cf5bb9285074dc3531c437b3919e825d976fde097a7a73f79e726d03/certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2", size = 162722 }, ] [[package]] @@ -274,6 +274,7 @@ dependencies = [ { name = "pydantic-settings" }, { name = "pyjwt" }, { name = "pyotp" }, + { name = "pywin32" }, ] [package.optional-dependencies] @@ -294,6 +295,7 @@ requires-dist = [ { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0.0" }, { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.21.0" }, { name = "pytest-mock", marker = "extra == 'dev'", specifier = ">=3.10.0" }, + { name = "pywin32", specifier = ">=310" }, ] provides-extras = ["dev"] @@ -491,15 +493,18 @@ wheels = [ [[package]] name = "pywin32" -version = "310" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/ec/4fdbe47932f671d6e348474ea35ed94227fb5df56a7c30cbbb42cd396ed0/pywin32-310-cp312-cp312-win32.whl", hash = "sha256:8a75a5cc3893e83a108c05d82198880704c44bbaee4d06e442e471d3c9ea4f3d", size = 8796239 }, - { url = "https://files.pythonhosted.org/packages/e3/e5/b0627f8bb84e06991bea89ad8153a9e50ace40b2e1195d68e9dff6b03d0f/pywin32-310-cp312-cp312-win_amd64.whl", hash = "sha256:bf5c397c9a9a19a6f62f3fb821fbf36cac08f03770056711f765ec1503972060", size = 9503839 }, - { url = "https://files.pythonhosted.org/packages/1f/32/9ccf53748df72301a89713936645a664ec001abd35ecc8578beda593d37d/pywin32-310-cp312-cp312-win_arm64.whl", hash = "sha256:2349cc906eae872d0663d4d6290d13b90621eaf78964bb1578632ff20e152966", size = 8459470 }, - { url = "https://files.pythonhosted.org/packages/1c/09/9c1b978ffc4ae53999e89c19c77ba882d9fce476729f23ef55211ea1c034/pywin32-310-cp313-cp313-win32.whl", hash = "sha256:5d241a659c496ada3253cd01cfaa779b048e90ce4b2b38cd44168ad555ce74ab", size = 8794384 }, - { url = "https://files.pythonhosted.org/packages/45/3c/b4640f740ffebadd5d34df35fecba0e1cfef8fde9f3e594df91c28ad9b50/pywin32-310-cp313-cp313-win_amd64.whl", hash = "sha256:667827eb3a90208ddbdcc9e860c81bde63a135710e21e4cb3348968e4bd5249e", size = 9503039 }, - { url = "https://files.pythonhosted.org/packages/b4/f4/f785020090fb050e7fb6d34b780f2231f302609dc964672f72bfaeb59a28/pywin32-310-cp313-cp313-win_arm64.whl", hash = "sha256:e308f831de771482b7cf692a1f308f8fca701b2d8f9dde6cc440c7da17e47b33", size = 8458152 }, +version = "311" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543 }, + { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040 }, + { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102 }, + { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700 }, + { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700 }, + { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318 }, + { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714 }, + { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800 }, + { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540 }, ] [[package]]