From 6c13b9c04709edd7cedde8b367fc314fa818b429 Mon Sep 17 00:00:00 2001 From: Filip Lajszczak Date: Tue, 8 Jul 2025 13:27:50 +0000 Subject: [PATCH 01/17] Adds missing spaces in multiline command example. Co-authored-by: Lee Cartwright --- README.md | 6 +++--- manifest.json | 2 +- pyproject.toml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8911976..80bad0c 100644 --- a/README.md +++ b/README.md @@ -47,9 +47,9 @@ the [desktop extension](https://github.com/anthropics/dxt/) for Claude Desktop. ### Claude Code Run: ```bash - claude mcp add pythonanywhere-mcp-server\ - -e API_TOKEN=yourpythonanywhereapitoken\ - -e LOGNAME=yourpythonanywhereusername\ + claude mcp add pythonanywhere-mcp-server \ + -e API_TOKEN=yourpythonanywhereapitoken \ + -e LOGNAME=yourpythonanywhereusername \ -- uvx pythonanywhere-mcp-server ``` diff --git a/manifest.json b/manifest.json index d051de9..64a8090 100644 --- a/manifest.json +++ b/manifest.json @@ -3,7 +3,7 @@ "name": "PythonAnywhere MCP Server", "description": "Manage files, websites, and scheduled tasks on PythonAnywhere via the Model Context Protocol.", "icon": "icon.png", - "version": "0.0.1", + "version": "0.0.2", "author": { "name": "PythonAnywhere Developers", "email": "developers@pythonanywhere.com" diff --git a/pyproject.toml b/pyproject.toml index 200c2ef..6505d32 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "pythonanywhere-mcp-server" -version = "0.0.1" +version = "0.0.2" description = "PythonAnywhere Model Context Protocol Server" authors = [ {name = "PythonAnywhere Developers", email = "developers@pythonanywhere.com"} From 3c068bfe4098d8473e4c50117748cada9b47444b Mon Sep 17 00:00:00 2001 From: Filip Lajszczak Date: Tue, 8 Jul 2025 13:35:49 +0000 Subject: [PATCH 02/17] Removes rogue space after backslash. --- README.md | 2 +- manifest.json | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 80bad0c..27c9cca 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ the [desktop extension](https://github.com/anthropics/dxt/) for Claude Desktop. Run: ```bash claude mcp add pythonanywhere-mcp-server \ - -e API_TOKEN=yourpythonanywhereapitoken \ + -e API_TOKEN=yourpythonanywhereapitoken \ -e LOGNAME=yourpythonanywhereusername \ -- uvx pythonanywhere-mcp-server ``` diff --git a/manifest.json b/manifest.json index 64a8090..da4fef2 100644 --- a/manifest.json +++ b/manifest.json @@ -3,7 +3,7 @@ "name": "PythonAnywhere MCP Server", "description": "Manage files, websites, and scheduled tasks on PythonAnywhere via the Model Context Protocol.", "icon": "icon.png", - "version": "0.0.2", + "version": "0.0.3", "author": { "name": "PythonAnywhere Developers", "email": "developers@pythonanywhere.com" diff --git a/pyproject.toml b/pyproject.toml index 6505d32..2d27663 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "pythonanywhere-mcp-server" -version = "0.0.2" +version = "0.0.3" description = "PythonAnywhere Model Context Protocol Server" authors = [ {name = "PythonAnywhere Developers", email = "developers@pythonanywhere.com"} From e5598da5299bb168a3636bb2b921343e468ed9e3 Mon Sep 17 00:00:00 2001 From: Filip Lajszczak Date: Tue, 8 Jul 2025 13:44:45 +0000 Subject: [PATCH 03/17] Removes another rogue space after another backslash. --- README.md | 2 +- manifest.json | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 27c9cca..1975eed 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Run: ```bash claude mcp add pythonanywhere-mcp-server \ -e API_TOKEN=yourpythonanywhereapitoken \ - -e LOGNAME=yourpythonanywhereusername \ + -e LOGNAME=yourpythonanywhereusername \ -- uvx pythonanywhere-mcp-server ``` diff --git a/manifest.json b/manifest.json index da4fef2..9bd9134 100644 --- a/manifest.json +++ b/manifest.json @@ -3,7 +3,7 @@ "name": "PythonAnywhere MCP Server", "description": "Manage files, websites, and scheduled tasks on PythonAnywhere via the Model Context Protocol.", "icon": "icon.png", - "version": "0.0.3", + "version": "0.0.4", "author": { "name": "PythonAnywhere Developers", "email": "developers@pythonanywhere.com" diff --git a/pyproject.toml b/pyproject.toml index 2d27663..f732ec3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "pythonanywhere-mcp-server" -version = "0.0.3" +version = "0.0.4" description = "PythonAnywhere Model Context Protocol Server" authors = [ {name = "PythonAnywhere Developers", email = "developers@pythonanywhere.com"} From d7d526ac59d4d597517dd5e0b72b7d568dba53a1 Mon Sep 17 00:00:00 2001 From: Filip Lajszczak Date: Tue, 24 Jun 2025 19:54:38 +0000 Subject: [PATCH 04/17] chore(deps): add renovate.json --- renovate.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 renovate.json diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..20f49f3 --- /dev/null +++ b/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "github>anaconda/renovate-config" + ] +} From 56b2ad59fc041550f2cb925fe0796ac46f48a0ae Mon Sep 17 00:00:00 2001 From: Giles Thomas Date: Tue, 8 Jul 2025 15:17:27 +0100 Subject: [PATCH 05/17] Fixed a couple of typos/formatting issues in README.md --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 1975eed..3f8429b 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ server acts as a bridge between AI-powered tools and your programmatic management of files, websites, webapps, and scheduled tasks. By exposing a standardized interface, it allows language models and automation clients to perform operations—such as editing files, deploying web apps, or -scheduling jobs—on your behalf, all while maintaining fine-grained control +scheduling jobs -- on your behalf, all while maintaining fine-grained control and auditability. ## Features @@ -18,13 +18,13 @@ and auditability. documentation](https://help.pythonanywhere.com/pages/ASGICommandLine))_ - **WSGI Web app management**: Reload only _(at the moment)_. - **Scheduled task management**: List, create, update, and delete. - _(Npote that it enables LLMs to execute arbitrary commands if a task is - scheduled to soon after creation and deleted after execution. For that we + _(Note that this enables LLMs to execute arbitrary commands if a task is + scheduled to soon after creation and deleted after execution. For that we would suggest running it with [mcp-server-time](https://pypi.org/project/mcp-server-time/) as models easily get confused about time.)_ ## Installation -MCP protocol is well-defined and supported by various clients, but +The MCP protocol is well-defined and supported by various clients, but installation is different depending on the client you are using. We will cover cases that we tried and tested. @@ -100,14 +100,14 @@ maintaining human oversight, especially for sensitive actions such as modifying or deleting files. If you are running multiple MCP servers simultaneously, be -cautious—particularly if any server can access external resources you do not +cautious -- particularly if any server can access external resources you do not control, such as GitHub issues. These can become attack vectors. For more details, see [this story](https://simonwillison.net/2025/Jul/6/supabase-mcp-lethal-trifecta/). ## Implementation -Server uses [python mcp sdk](https://github.com/modelcontextprotocol/python-sdk) -in connection with [pythonanywhere-core](https://github.com/pythonanywhere/pythonanywhere-core) -package ([docs](https://core.pythonanywhere.com/)) that wraps subset of [PythonAnywhere -API](https://help.pythonanywhere.com/pages/API/) and would be expanded in -the future as needed. \ No newline at end of file +The server uses the [python mcp sdk](https://github.com/modelcontextprotocol/python-sdk) +in connection with the [pythonanywhere-core](https://github.com/pythonanywhere/pythonanywhere-core) +package ([docs](https://core.pythonanywhere.com/)), which wraps a subset of the [PythonAnywhere +API](https://help.pythonanywhere.com/pages/API/) and may be expanded in +the future as needed. From a737643928bf903bc61b91484809b3bb8d7ac3ac Mon Sep 17 00:00:00 2001 From: Giles Thomas Date: Tue, 8 Jul 2025 15:25:13 +0100 Subject: [PATCH 06/17] Missed a typo -- fixed --- README.md | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 3f8429b..b986d1f 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,41 @@ # PythonAnywhere Model Context Protocol Server -A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) -server acts as a bridge between AI-powered tools and your -[PythonAnywhere](https://www.pythonanywhere.com/) account, enabling secure, -programmatic management of files, websites, webapps, and scheduled tasks. By -exposing a standardized interface, it allows language models and automation -clients to perform operations—such as editing files, deploying web apps, or +A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) +server acts as a bridge between AI-powered tools and your +[PythonAnywhere](https://www.pythonanywhere.com/) account, enabling secure, +programmatic management of files, websites, webapps, and scheduled tasks. By +exposing a standardized interface, it allows language models and automation +clients to perform operations—such as editing files, deploying web apps, or scheduling jobs -- on your behalf, all while maintaining fine-grained control and auditability. ## Features -- **File management**: Read, upload, delete files and list directory trees. +- **File management**: Read, upload, delete files and list directory trees. _(also enables debugging with direct access to log files, which are just files on PythonAnywhere)_ - **ASGI Web app management**: Create, delete, reload, and list. - _(as described in the [PythonAnywhere ASGI + _(as described in the [PythonAnywhere ASGI documentation](https://help.pythonanywhere.com/pages/ASGICommandLine))_ - **WSGI Web app management**: Reload only _(at the moment)_. - **Scheduled task management**: List, create, update, and delete. - _(Note that this enables LLMs to execute arbitrary commands if a task is - scheduled to soon after creation and deleted after execution. For that we + _(Note that this enables LLMs to execute arbitrary commands if a task is + scheduled too soon after creation and deleted after execution. For that we would suggest running it with [mcp-server-time](https://pypi.org/project/mcp-server-time/) as models easily get confused about time.)_ ## Installation -The MCP protocol is well-defined and supported by various clients, but -installation is different depending on the client you are using. We will +The MCP protocol is well-defined and supported by various clients, but +installation is different depending on the client you are using. We will cover cases that we tried and tested. In all cases, you need to have `uv` installed and available in your `PATH`. -Have your PythonAnywhere API token and username ready. You can find (or -generate) your API token in the [API section of your PythonAnywhere +Have your PythonAnywhere API token and username ready. You can find (or +generate) your API token in the [API section of your PythonAnywhere account](https://www.pythonanywhere.com/account/#api_token). ### Desktop Extension - works with Claude Desktop -Probably the most straightforward way to install the MCP server is to use +Probably the most straightforward way to install the MCP server is to use the [desktop extension](https://github.com/anthropics/dxt/) for Claude Desktop. 1. Open Claude Desktop. @@ -73,7 +73,7 @@ Add it to your `mcp.json`. ``` ### Claude Desktop (manual setup) and Cursor: -Add it to `claude_desktop_config.json` (for Claude Desktop) or (`mcp.json` +Add it to `claude_desktop_config.json` (for Claude Desktop) or (`mcp.json` for Cursor). ```json @@ -94,20 +94,20 @@ for Cursor). ## Caveats -Direct integration of an LLM with your PythonAnywhere account offers -significant capabilities, but also introduces risks. We strongly advise -maintaining human oversight, especially for sensitive actions such as +Direct integration of an LLM with your PythonAnywhere account offers +significant capabilities, but also introduces risks. We strongly advise +maintaining human oversight, especially for sensitive actions such as modifying or deleting files. -If you are running multiple MCP servers simultaneously, be +If you are running multiple MCP servers simultaneously, be cautious -- particularly if any server can access external resources you do not -control, such as GitHub issues. These can become attack vectors. For more +control, such as GitHub issues. These can become attack vectors. For more details, see [this story](https://simonwillison.net/2025/Jul/6/supabase-mcp-lethal-trifecta/). ## Implementation -The server uses the [python mcp sdk](https://github.com/modelcontextprotocol/python-sdk) -in connection with the [pythonanywhere-core](https://github.com/pythonanywhere/pythonanywhere-core) -package ([docs](https://core.pythonanywhere.com/)), which wraps a subset of the [PythonAnywhere -API](https://help.pythonanywhere.com/pages/API/) and may be expanded in +The server uses the [python mcp sdk](https://github.com/modelcontextprotocol/python-sdk) +in connection with the [pythonanywhere-core](https://github.com/pythonanywhere/pythonanywhere-core) +package ([docs](https://core.pythonanywhere.com/)), which wraps a subset of the [PythonAnywhere +API](https://help.pythonanywhere.com/pages/API/) and may be expanded in the future as needed. From c3177ca899dee23ac862e5ede599f0120e8ea8f5 Mon Sep 17 00:00:00 2001 From: "anaconda-renovate[bot]" <117830771+anaconda-renovate[bot]@users.noreply.github.com> Date: Tue, 8 Jul 2025 14:36:13 +0000 Subject: [PATCH 07/17] chore(deps): update astral-sh/setup-uv action to v6.3.1 --- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c287ab2..29cf114 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,7 +23,7 @@ jobs: python-version: '3.13' - name: Install uv - uses: astral-sh/setup-uv@v3 + uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v6.3.1 - name: Check tag matches pyproject.toml version run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dd89d95..6c8a5b0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install the latest version of uv and set the python version - uses: astral-sh/setup-uv@v6 + uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v6.3.1 with: python-version: ${{ matrix.python-version }} activate-environment: true From aea8668c8a0f64e90e9fbb167715271fc885bb39 Mon Sep 17 00:00:00 2001 From: "anaconda-renovate[bot]" <117830771+anaconda-renovate[bot]@users.noreply.github.com> Date: Tue, 8 Jul 2025 14:35:43 +0000 Subject: [PATCH 08/17] chore(deps): update actions/checkout action to v4.2.2 --- .github/workflows/release.yml | 4 ++-- .github/workflows/test.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 29cf114..cb0cdae 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,7 +15,7 @@ jobs: needs: test steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Python uses: actions/setup-python@v5 @@ -58,7 +58,7 @@ jobs: needs: test steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Node.js uses: actions/setup-node@v4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6c8a5b0..772e065 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: matrix: python-version: ["3.13"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Install the latest version of uv and set the python version uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v6.3.1 with: From 19f7220712d6f98e6b64fe5c9d5531a3c935561f Mon Sep 17 00:00:00 2001 From: "anaconda-renovate[bot]" <117830771+anaconda-renovate[bot]@users.noreply.github.com> Date: Tue, 8 Jul 2025 14:35:49 +0000 Subject: [PATCH 09/17] chore(deps): update actions/download-artifact action to v4.3.0 --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cb0cdae..ffccfce 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -95,13 +95,13 @@ jobs: needs: [build_package, build_extension] steps: - name: Download Python artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: python-dist path: dist/ - name: Download DXT artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: dxt-dist path: ./ From e08dda79d1ab4230dd84c12f95dbe0cfa537abc3 Mon Sep 17 00:00:00 2001 From: "anaconda-renovate[bot]" <117830771+anaconda-renovate[bot]@users.noreply.github.com> Date: Tue, 8 Jul 2025 14:35:54 +0000 Subject: [PATCH 10/17] chore(deps): update actions/setup-node action to v4.4.0 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ffccfce..7e89e63 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -61,7 +61,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '18' From 912b62bf552336567c23b3a3fae421e4368bf292 Mon Sep 17 00:00:00 2001 From: "anaconda-renovate[bot]" <117830771+anaconda-renovate[bot]@users.noreply.github.com> Date: Tue, 8 Jul 2025 14:35:58 +0000 Subject: [PATCH 11/17] chore(deps): update actions/setup-python action to v5.6.0 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7e89e63..71bbc4c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: '3.13' From fa7180bc8fb7ef3e9f73075c45ec4175b44700e2 Mon Sep 17 00:00:00 2001 From: "anaconda-renovate[bot]" <117830771+anaconda-renovate[bot]@users.noreply.github.com> Date: Tue, 8 Jul 2025 14:36:03 +0000 Subject: [PATCH 12/17] chore(deps): update actions/upload-artifact action to v4.6.2 --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 71bbc4c..df90e9e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -48,7 +48,7 @@ jobs: password: ${{ secrets.PYPI_API_TOKEN }} - name: Upload Python artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: python-dist path: dist/ @@ -85,7 +85,7 @@ jobs: run: dxt pack - name: Upload DXT artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: dxt-dist path: '*.dxt' From 70f9c4cf7b60952a01c88d9e7ddf8e1abe51d5c0 Mon Sep 17 00:00:00 2001 From: "anaconda-renovate[bot]" <117830771+anaconda-renovate[bot]@users.noreply.github.com> Date: Tue, 8 Jul 2025 14:36:18 +0000 Subject: [PATCH 13/17] chore(deps): update dependency node to v22 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index df90e9e..902c92c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -63,7 +63,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: - node-version: '18' + node-version: '22' - name: Check tag matches manifest.json version run: | From 603fa7f674c8a02f5511239953b3d6d436d5ed1c Mon Sep 17 00:00:00 2001 From: "anaconda-renovate[bot]" <117830771+anaconda-renovate[bot]@users.noreply.github.com> Date: Tue, 8 Jul 2025 14:36:24 +0000 Subject: [PATCH 14/17] chore(deps): update softprops/action-gh-release action to v2 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 902c92c..b6ee8bb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -107,7 +107,7 @@ jobs: path: ./ - name: Create Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8 # v2.3.2 with: files: | *.dxt From 1fa20884c07266f26511ea9d1551e0a2aac79bf7 Mon Sep 17 00:00:00 2001 From: Filip Lajszczak Date: Tue, 8 Jul 2025 15:10:49 +0000 Subject: [PATCH 15/17] Version bump. --- manifest.json | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/manifest.json b/manifest.json index 9bd9134..fa8ca8b 100644 --- a/manifest.json +++ b/manifest.json @@ -3,7 +3,7 @@ "name": "PythonAnywhere MCP Server", "description": "Manage files, websites, and scheduled tasks on PythonAnywhere via the Model Context Protocol.", "icon": "icon.png", - "version": "0.0.4", + "version": "0.0.5", "author": { "name": "PythonAnywhere Developers", "email": "developers@pythonanywhere.com" diff --git a/pyproject.toml b/pyproject.toml index f732ec3..7a0dedd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "pythonanywhere-mcp-server" -version = "0.0.4" +version = "0.0.5" description = "PythonAnywhere Model Context Protocol Server" authors = [ {name = "PythonAnywhere Developers", email = "developers@pythonanywhere.com"} From ca139cdc07552a2be9874c19b8659aa35f8244e0 Mon Sep 17 00:00:00 2001 From: Lee Cartwright Date: Mon, 21 Jul 2025 09:55:07 +0100 Subject: [PATCH 16/17] Adds more tools for handling WSGI web apps. Co-authored-by: Piotr Kaznowski Co-authored-by: Lee Cartwright Added a test for the create_webapp functionality. Updated some dependencies, most importantly pythonanywhere-core. Added a bunch of webapp functionality. --- manifest.json | 9 - src/pythonanywhere_mcp_server/tools/webapp.py | 207 ++++++++++++++++++ .../tools/website.py | 5 +- tests/test_webapp_tools.py | 122 +++++++++-- uv.lock | 164 ++++++++++++-- 5 files changed, 462 insertions(+), 45 deletions(-) diff --git a/manifest.json b/manifest.json index fa8ca8b..8911d6d 100644 --- a/manifest.json +++ b/manifest.json @@ -8,16 +8,7 @@ "name": "PythonAnywhere Developers", "email": "developers@pythonanywhere.com" }, - "main": "pythonanywhere_mcp_server.py", - "categories": [ - "developer-tools", - "utilities" - ], "license": "MIT", - "permissions": [ - "mcp", - "filesystem" - ], "user_config": { "pa_api_token": { "type": "string", diff --git a/src/pythonanywhere_mcp_server/tools/webapp.py b/src/pythonanywhere_mcp_server/tools/webapp.py index a6a225f..7af9692 100644 --- a/src/pythonanywhere_mcp_server/tools/webapp.py +++ b/src/pythonanywhere_mcp_server/tools/webapp.py @@ -1,8 +1,12 @@ +from pathlib import Path + from mcp.server.fastmcp import FastMCP from pythonanywhere_core.webapp import Webapp from pythonanywhere_core.base import AuthenticationError, NoTokenError +# ToDo: Add the log file functions once pythonanywhere-core webapp log file functions +# have been improved def register_webapp_tools(mcp: FastMCP) -> None: @mcp.tool() @@ -28,3 +32,206 @@ def reload_webapp(domain: str) -> str: raise RuntimeError("Authentication failed — check API_TOKEN and domain.") except Exception as exc: raise RuntimeError(str(exc)) from exc + + @mcp.tool() + def create_webapp(domain: str, python_version: str, virtualenv_path: str, project_path: str) -> str: + """ + Create a new uWSGI-based web application for the given domain. + + This creates a new webapp on PythonAnywhere with the specified configuration. + The webapp will be created using the specified Python version, virtual environment, + and project path. If a webapp already exists for this domain, it will fail unless + the nuke parameter is set to True. + + The WSGI configuration file can be found in /var/www. The file is of the format: + domain.replace('.', '_') + '_wsgi.py' It would be automatically created as side effect of running this tool. + + Note: + Virtual environments should be configured in the PythonAnywhere web app + configuration, not in the WSGI file itself. + + Args: + domain (str): The domain name for the new webapp (e.g., 'alice.pythonanywhere.com'). + python_version (str): Python version to use (e.g., '3.11', '3.10', '3.9'). + virtualenv_path (str | None): Path to the virtual environment to use. (or None for + no virtualenv when using one of pre-installed Pythons with batteries included packages) + project_path (str): Path to the project directory containing the webapp code. + + Returns: + str: Status message indicating creation result. + + Raises: + RuntimeError: If authentication fails, webapp already exists (and nuke=False), + or other API errors occur. If 403 error is raised, it may mean that there + is already a non-uwsgi-based website. `list_websites` tool can be used to check that. + """ + try: + webapp = Webapp(domain) + webapp.create( + python_version=python_version, + virtualenv_path=Path(virtualenv_path), + project_path=Path(project_path), + nuke=False + ) + return f"Webapp '{domain}' created successfully." + except (AuthenticationError, NoTokenError): + raise RuntimeError("Authentication failed — check API_TOKEN and domain.") + except Exception as exc: + raise RuntimeError(str(exc)) from exc + + @mcp.tool() + def delete_webapp(domain: str) -> str: + """ + Delete a uWSGI-based web application for the given domain. + + This permanently deletes the webapp configuration from PythonAnywhere. The actual + files in your file system are not deleted, only the webapp configuration that + serves them. This action cannot be undone. + + Args: + domain (str): The domain name of the webapp to delete + (e.g., 'alice.pythonanywhere.com'). + + Returns: + str: Status message indicating deletion result. + + Raises: + RuntimeError: If authentication fails, webapp doesn't exist, or other API errors occur. + """ + try: + Webapp(domain).delete() + return f"Webapp '{domain}' deleted successfully." + except (AuthenticationError, NoTokenError): + raise RuntimeError("Authentication failed — check API_TOKEN and domain.") + except Exception as exc: + raise RuntimeError(str(exc)) from exc + + @mcp.tool() + def patch_webapp(domain: str, data: dict) -> dict: + """ + Update configuration settings for a uWSGI-based web application. + + This allows you to modify various webapp settings such as the Python version, + virtual environment path, source directory, and other configuration options. + Only the fields provided in the data dictionary will be updated. + + In order for any changes to take effect you must reload the webapp. + + If you provide an invalid Python version you will receive a 400 error with an error + message that the version you used is not a valid choice. You will need to choose a + different Python version if you get this error. + + Args: + domain (str): The domain name of the webapp to update + (e.g., 'alice.pythonanywhere.com'). + data (dict): Dictionary containing the configuration updates. Supported keys are: + - 'python_version': Python version (e.g., '3.11') + - 'virtualenv_path': Path to virtual environment + - 'source_directory': Path to source code directory + - 'working_directory': Working directory for the webapp + - 'force_https': Force the use of HTTPS when accessing the webapp + - 'password_protection_enabled': Enable basic HTTP password to your webapp, provided via PythonAnywhere not in the webapp code + - 'password_protection_username': The username used for HTTP password protection + - 'password_protection_password': The password used for HTTP password protection + + Returns: + dict: Updated webapp configuration information. + Example: { + "id": 2097234, + "user": username, + "domain_name": domain, + "python_version": "3.10", + "source_directory": f"/home/{username}/mysite", + "working_directory": f"/home/{username}/", + "virtualenv_path": "", + "expiry": "2025-10-16", + "force_https": False, + "password_protection_enabled": False, + "password_protection_username": "foo", + "password_protection_password": "bar" + } + + Raises: + RuntimeError: If authentication fails, webapp doesn't exist, or other API errors occur. + """ + try: + result = Webapp(domain).patch(data) + return result + except (AuthenticationError, NoTokenError): + raise RuntimeError("Authentication failed — check API_TOKEN and domain.") + except Exception as exc: + raise RuntimeError(str(exc)) from exc + + @mcp.tool() + def list_webapps() -> list: + """ + List all uWSGI-based web applications for the current user. + + This retrieves information about all webapps configured in your PythonAnywhere + account. The returned list contains dictionaries with detailed information about + each webapp including domain, Python version, paths, and status. + + On PythonAnywhere one may also have non-uWSGI-based websites (usually ASGI-based), + which are not included in this list. For those, use the `list_websites` tool. + + Returns: + list: List of dictionaries containing webapp information. Empty list means + no WSGI-based webapps are deployed. That still could mean that there are + non-WSGI-based apps that can be listed with the `list_websites` tool. + + See also: + get_webapp_info: Get detailed information about a specific webapp. + + Raises: + RuntimeError: If authentication fails or other API errors occur. + """ + try: + result = Webapp.list_webapps() + return result + except (AuthenticationError, NoTokenError): + raise RuntimeError("Authentication failed — check API_TOKEN.") + except Exception as exc: + raise RuntimeError(str(exc)) from exc + + @mcp.tool() + def get_webapp_info(domain: str) -> dict: + """ + Get detailed information about a specific uWSGI-based web application. + + This retrieves comprehensive configuration and status information for the + specified webapp, including paths, Python version, enabled status, and + other configuration details. + + Args: + domain (str): The domain name of the webapp to get information for + (e.g., 'alice.pythonanywhere.com'). + + Returns: + dict: Dictionary containing detailed webapp information including: + - "id": int, # Unique identifier for the site or user session + - "user": str, # Username associated with the deployment + - "domain_name": str, # Domain name for the deployed site + - "python_version": str, # Python version used, e.g., "3.10" + - "source_directory": str, # Absolute path to the site's source directory + - "working_directory": str, # Absolute path to the working directory + - "virtualenv_path": str, # Path to the Python virtual environment (can be empty) + - "expiry": str, # Expiration date in ISO format, e.g., "2025-10-16" + - "force_https": bool, # Whether HTTPS is enforced + - "password_protection_enabled": bool, # Whether password protection is enabled + - "password_protection_username": str, # Username for password-protected access + - "password_protection_password": str # Password for password-protected access + + + Raises: + RuntimeError: If authentication fails, webapp doesn't exist, or other API errors occur. + """ + try: + result = Webapp(domain).get() + return result + except (AuthenticationError, NoTokenError): + raise RuntimeError("Authentication failed — check API_TOKEN and domain.") + except Exception as exc: + raise RuntimeError(str(exc)) from exc + + + diff --git a/src/pythonanywhere_mcp_server/tools/website.py b/src/pythonanywhere_mcp_server/tools/website.py index fd0254b..3006c7d 100644 --- a/src/pythonanywhere_mcp_server/tools/website.py +++ b/src/pythonanywhere_mcp_server/tools/website.py @@ -37,11 +37,14 @@ def list_websites() -> list[dict[str, Any]]: Return info dictionaries for every ASGI website configured for the current user. Empty list means that there are no websites deployed. That would not include WSGI-based web applications, - which could be only reloaded with `reload_webapp` tool. + which could be only listed with the `list_webapps` tool. Returns: List[dict[str, Any]]: List of dictionaries with website information. + Empty list if no websites are configured for the user, but there could be + still WSGI-based web applications configured that qould be listed with + the `list_webapps` tool. """ try: diff --git a/tests/test_webapp_tools.py b/tests/test_webapp_tools.py index 063dca6..57cd2ce 100644 --- a/tests/test_webapp_tools.py +++ b/tests/test_webapp_tools.py @@ -1,30 +1,120 @@ import pytest +from pathlib import Path +from pythonanywhere_core.base import AuthenticationError import tools.webapp as webapp_tools -def test_reload_webapp(mcp, mocker): + +@pytest.fixture +def setup_webapp_tools(mcp): webapp_tools.register_webapp_tools(mcp) + return mcp + + +@pytest.mark.parametrize("tool_name,method_name,params,expected_params,expected_result", [ + ("reload_webapp", "reload", {"domain": "test.com"}, {}, "Webapp 'test.com' reloaded."), + ("delete_webapp", "delete", {"domain": "test.com"}, {}, "Webapp 'test.com' deleted successfully."), + ("get_webapp_info", "get", {"domain": "test.com"}, {}, {"domain_name": "test.com", "python_version": "3.10"}), + ("patch_webapp", "patch", {"domain": "test.com", "data": {"python_version": "3.10"}}, {"python_version": "3.10"}, {"domain_name": "test.com", "python_version": "3.10"}), +]) +def test_webapp_tools_success(setup_webapp_tools, mocker, tool_name, method_name, params, expected_params, expected_result): mock_webapp = mocker.patch("tools.webapp.Webapp", autospec=True) - result = mcp.call_tool("reload_webapp", {"domain": "test.com"}) - mock_webapp.assert_called_with("test.com") - mock_webapp.return_value.reload.assert_called_once() - assert result == "Webapp 'test.com' reloaded." + + if tool_name == "get_webapp_info" or tool_name == "patch_webapp": + getattr(mock_webapp.return_value, method_name).return_value = expected_result + + result = setup_webapp_tools.call_tool(tool_name, params) + + if "domain" in params: + mock_webapp.assert_called_with(params["domain"]) + if expected_params: + getattr(mock_webapp.return_value, method_name).assert_called_with(expected_params) + else: + getattr(mock_webapp.return_value, method_name).assert_called_once() + + assert result == expected_result -def test_reload_webapp_auth_error(mcp, mocker): - webapp_tools.register_webapp_tools(mcp) +@pytest.mark.parametrize("tool_name,method_name,params,side_effect,expected_error", [ + ("reload_webapp", "reload", {"domain": "test.com"}, AuthenticationError(), "Authentication failed"), + ("reload_webapp", "reload", {"domain": "test.com"}, Exception("webapp reload error"), "webapp reload error"), + ("delete_webapp", "delete", {"domain": "test.com"}, AuthenticationError(), "Authentication failed"), + ("delete_webapp", "delete", {"domain": "test.com"}, Exception("delete error"), "delete error"), + ("get_webapp_info", "get", {"domain": "test.com"}, AuthenticationError(), "Authentication failed"), + ("get_webapp_info", "get", {"domain": "test.com"}, Exception("info error"), "info error"), + ("patch_webapp", "patch", {"domain": "test.com", "data": {"python_version": "3.10"}}, AuthenticationError(), "Authentication failed"), + ("patch_webapp", "patch", {"domain": "test.com", "data": {"python_version": "3.10"}}, Exception("patch error"), "patch error"), +]) +def test_webapp_tools_errors(setup_webapp_tools, mocker, tool_name, method_name, params, side_effect, expected_error): mock_webapp = mocker.patch("tools.webapp.Webapp", autospec=True) - from pythonanywhere_core.base import AuthenticationError - mock_webapp.return_value.reload.side_effect = AuthenticationError() + getattr(mock_webapp.return_value, method_name).side_effect = side_effect + with pytest.raises(RuntimeError) as exc: - mcp.call_tool("reload_webapp", {"domain": "test.com"}) - assert "Authentication failed" in str(exc) + setup_webapp_tools.call_tool(tool_name, params) + assert expected_error in str(exc) -def test_reload_webapp_other_error(mcp, mocker): - webapp_tools.register_webapp_tools(mcp) +@pytest.mark.parametrize("side_effect,expected_error", [ + (AuthenticationError(), "Authentication failed"), + (Exception("list error"), "list error"), +]) +def test_list_webapps_errors(setup_webapp_tools, mocker, side_effect, expected_error): + mocker.patch("tools.webapp.Webapp", autospec=True) + mocker.patch("tools.webapp.Webapp.list_webapps", side_effect=side_effect) + + with pytest.raises(RuntimeError) as exc: + setup_webapp_tools.call_tool("list_webapps", {}) + assert expected_error in str(exc) + + +def test_create_webapp(setup_webapp_tools, mocker): + webapp_domain = 'test.com' + python_version = '3.10' + virtualenv_path = Path('/test/venv/path') + project_path = Path('/project/path') + mock_webapp = mocker.patch("tools.webapp.Webapp", autospec=True) - mock_webapp.return_value.reload.side_effect = Exception("webapp reload error") + result = setup_webapp_tools.call_tool( + "create_webapp", + { + "domain": webapp_domain, + "python_version": python_version, + "virtualenv_path": virtualenv_path, + "project_path": project_path, + } + ) + mock_webapp.assert_called_with(webapp_domain) + mock_webapp.return_value.create.assert_called_with( + python_version=python_version, + virtualenv_path=virtualenv_path, + project_path=project_path, + nuke=False + ) + mock_webapp.return_value.create.assert_called_once() + assert result == f"Webapp 'test.com' created successfully." + + +@pytest.mark.parametrize("side_effect,expected_error", [ + (AuthenticationError(), "Authentication failed"), + (Exception("webapp create error"), "webapp create error"), +]) +def test_create_webapp_errors(setup_webapp_tools, mocker, side_effect, expected_error): + mock_webapp = mocker.patch("tools.webapp.Webapp", autospec=True) + mock_webapp.return_value.create.side_effect = side_effect + params = { + "domain": "test.com", + "python_version": "3.10", + "virtualenv_path": "/test/venv/path", + "project_path": "/project/path", + } with pytest.raises(RuntimeError) as exc: - mcp.call_tool("reload_webapp", {"domain": "test.com"}) - assert "webapp reload error" in str(exc) + setup_webapp_tools.call_tool("create_webapp", params) + assert expected_error in str(exc) + + +def test_list_webapps(setup_webapp_tools, mocker): + mocker.patch("tools.webapp.Webapp", autospec=True) + expected = [{"domain_name": "test.com", "python_version": "3.10"}] + mocker.patch("tools.webapp.Webapp.list_webapps", return_value=expected) + result = setup_webapp_tools.call_tool("list_webapps", {}) + assert result == expected diff --git a/uv.lock b/uv.lock index b89691f..576b2ca 100644 --- a/uv.lock +++ b/uv.lock @@ -24,13 +24,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, ] +[[package]] +name = "attrs" +version = "25.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload-time = "2025-03-13T11:10:22.779Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" }, +] + [[package]] name = "certifi" -version = "2025.6.15" +version = "2025.7.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/73/f7/f14b46d4bcd21092d7d3ccef689615220d8a08fb25e564b65d20738e672e/certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b", size = 158753, upload-time = "2025-06-15T02:45:51.329Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/76/52c535bcebe74590f296d6c77c86dabf761c41980e1347a2422e4aa2ae41/certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995", size = 163981, upload-time = "2025-07-14T03:29:28.449Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/ae/320161bd181fc06471eed047ecce67b693fd7515b16d495d8932db763426/certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057", size = 157650, upload-time = "2025-06-15T02:45:49.977Z" }, + { url = "https://files.pythonhosted.org/packages/4f/52/34c6cf5bb9285074dc3531c437b3919e825d976fde097a7a73f79e726d03/certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2", size = 162722, upload-time = "2025-07-14T03:29:26.863Z" }, ] [[package]] @@ -140,6 +149,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, ] +[[package]] +name = "jsonschema" +version = "4.24.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bf/d3/1cf5326b923a53515d8f3a2cd442e6d7e94fcc444716e879ea70a0ce3177/jsonschema-4.24.0.tar.gz", hash = "sha256:0b4e8069eb12aedfa881333004bccaec24ecef5a8a6a4b6df142b2cc9599d196", size = 353480, upload-time = "2025-05-26T18:48:10.459Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/3d/023389198f69c722d039351050738d6755376c8fd343e91dc493ea485905/jsonschema-4.24.0-py3-none-any.whl", hash = "sha256:a462455f19f5faf404a7902952b6f0e3ce868f3ee09a359b05eca6673bd8412d", size = 88709, upload-time = "2025-05-26T18:48:08.417Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bf/ce/46fbd9c8119cfc3581ee5643ea49464d168028cfb5caff5fc0596d0cf914/jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608", size = 15513, upload-time = "2025-04-23T12:34:07.418Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size = 18437, upload-time = "2025-04-23T12:34:05.422Z" }, +] + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -154,22 +190,24 @@ wheels = [ [[package]] name = "mcp" -version = "1.9.4" +version = "1.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "httpx" }, { name = "httpx-sse" }, + { name = "jsonschema" }, { name = "pydantic" }, { name = "pydantic-settings" }, { name = "python-multipart" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, { name = "sse-starlette" }, { name = "starlette" }, { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/06/f2/dc2450e566eeccf92d89a00c3e813234ad58e2ba1e31d11467a09ac4f3b9/mcp-1.9.4.tar.gz", hash = "sha256:cfb0bcd1a9535b42edaef89947b9e18a8feb49362e1cc059d6e7fc636f2cb09f", size = 333294, upload-time = "2025-06-12T08:20:30.158Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/f5/9506eb5578d5bbe9819ee8ba3198d0ad0e2fbe3bab8b257e4131ceb7dfb6/mcp-1.11.0.tar.gz", hash = "sha256:49a213df56bb9472ff83b3132a4825f5c8f5b120a90246f08b0dac6bedac44c8", size = 406907, upload-time = "2025-07-10T16:41:09.388Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/97/fc/80e655c955137393c443842ffcc4feccab5b12fa7cb8de9ced90f90e6998/mcp-1.9.4-py3-none-any.whl", hash = "sha256:7fcf36b62936adb8e63f89346bccca1268eeca9bf6dfb562ee10b1dfbda9dac0", size = 130232, upload-time = "2025-06-12T08:20:28.551Z" }, + { url = "https://files.pythonhosted.org/packages/92/9c/c9ca79f9c512e4113a5d07043013110bb3369fc7770040c61378c7fbcf70/mcp-1.11.0-py3-none-any.whl", hash = "sha256:58deac37f7483e4b338524b98bc949b7c2b7c33d978f5fafab5bde041c5e2595", size = 155880, upload-time = "2025-07-10T16:41:07.935Z" }, ] [package.optional-dependencies] @@ -331,7 +369,7 @@ wheels = [ [[package]] name = "pythonanywhere-core" -version = "0.2.4" +version = "0.2.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "python-dateutil" }, @@ -339,14 +377,14 @@ dependencies = [ { name = "snakesay" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/89/14/1571c175067213895b64ff667c91197547ba4f5f008863b6904cc189cc01/pythonanywhere_core-0.2.4.tar.gz", hash = "sha256:40fcef8efc31ccc5478a41305ce96abec2eabe532d549bc04787328d46519a3a", size = 9077, upload-time = "2024-11-28T16:50:25.669Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/8e/863cb1d09b2850b19913023d4b18d635f4e281fcd25bb4cd77c97a3edcf4/pythonanywhere_core-0.2.5.tar.gz", hash = "sha256:2b43d726c18e5972a01bdfe09482c72dad20325fc3196c7504f4d90acaf642c0", size = 9169, upload-time = "2025-07-16T16:42:16.585Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/80/b8/1ac4f5aae2772bb8de079841575838ca7e3cc48ed4884bd5d21b11f54473/pythonanywhere_core-0.2.4-py3-none-any.whl", hash = "sha256:ad36f041d10d7bebcedbe77ddca64604a500c344671b801c6703cd0cdb1afdc0", size = 12119, upload-time = "2024-11-28T16:50:23.974Z" }, + { url = "https://files.pythonhosted.org/packages/af/22/7f8e30f2ff3bcc46af95e717e5167b12e9c5fa9681a089801c08582227c9/pythonanywhere_core-0.2.5-py3-none-any.whl", hash = "sha256:076aca292b7fddeb276a37a7f25a58223cf249dee8c180b384ee12a5270836e1", size = 12594, upload-time = "2025-07-16T16:42:15.595Z" }, ] [[package]] name = "pythonanywhere-mcp-server" -version = "0.0.1" +version = "0.0.5" source = { editable = "." } dependencies = [ { name = "mcp", extra = ["cli"] }, @@ -368,6 +406,32 @@ requires-dist = [ ] provides-extras = ["test"] +[[package]] +name = "pywin32" +version = "311" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" }, + { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" }, + { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" }, + { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" }, + { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" }, + { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, +] + +[[package]] +name = "referencing" +version = "0.36.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775, upload-time = "2025-01-25T08:48:14.241Z" }, +] + [[package]] name = "requests" version = "2.32.4" @@ -396,6 +460,68 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229, upload-time = "2025-03-30T14:15:12.283Z" }, ] +[[package]] +name = "rpds-py" +version = "0.26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/aa/4456d84bbb54adc6a916fb10c9b374f78ac840337644e4a5eda229c81275/rpds_py-0.26.0.tar.gz", hash = "sha256:20dae58a859b0906f0685642e591056f1e787f3a8b39c8e8749a45dc7d26bdb0", size = 27385, upload-time = "2025-07-01T15:57:13.958Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/67/bb62d0109493b12b1c6ab00de7a5566aa84c0e44217c2d94bee1bd370da9/rpds_py-0.26.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:696764a5be111b036256c0b18cd29783fab22154690fc698062fc1b0084b511d", size = 363917, upload-time = "2025-07-01T15:54:34.755Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f3/34e6ae1925a5706c0f002a8d2d7f172373b855768149796af87bd65dcdb9/rpds_py-0.26.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1e6c15d2080a63aaed876e228efe4f814bc7889c63b1e112ad46fdc8b368b9e1", size = 350073, upload-time = "2025-07-01T15:54:36.292Z" }, + { url = "https://files.pythonhosted.org/packages/75/83/1953a9d4f4e4de7fd0533733e041c28135f3c21485faaef56a8aadbd96b5/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390e3170babf42462739a93321e657444f0862c6d722a291accc46f9d21ed04e", size = 384214, upload-time = "2025-07-01T15:54:37.469Z" }, + { url = "https://files.pythonhosted.org/packages/48/0e/983ed1b792b3322ea1d065e67f4b230f3b96025f5ce3878cc40af09b7533/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7da84c2c74c0f5bc97d853d9e17bb83e2dcafcff0dc48286916001cc114379a1", size = 400113, upload-time = "2025-07-01T15:54:38.954Z" }, + { url = "https://files.pythonhosted.org/packages/69/7f/36c0925fff6f660a80be259c5b4f5e53a16851f946eb080351d057698528/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c5fe114a6dd480a510b6d3661d09d67d1622c4bf20660a474507aaee7eeeee9", size = 515189, upload-time = "2025-07-01T15:54:40.57Z" }, + { url = "https://files.pythonhosted.org/packages/13/45/cbf07fc03ba7a9b54662c9badb58294ecfb24f828b9732970bd1a431ed5c/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3100b3090269f3a7ea727b06a6080d4eb7439dca4c0e91a07c5d133bb1727ea7", size = 406998, upload-time = "2025-07-01T15:54:43.025Z" }, + { url = "https://files.pythonhosted.org/packages/6c/b0/8fa5e36e58657997873fd6a1cf621285ca822ca75b4b3434ead047daa307/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c03c9b0c64afd0320ae57de4c982801271c0c211aa2d37f3003ff5feb75bb04", size = 385903, upload-time = "2025-07-01T15:54:44.752Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f7/b25437772f9f57d7a9fbd73ed86d0dcd76b4c7c6998348c070d90f23e315/rpds_py-0.26.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5963b72ccd199ade6ee493723d18a3f21ba7d5b957017607f815788cef50eaf1", size = 419785, upload-time = "2025-07-01T15:54:46.043Z" }, + { url = "https://files.pythonhosted.org/packages/a7/6b/63ffa55743dfcb4baf2e9e77a0b11f7f97ed96a54558fcb5717a4b2cd732/rpds_py-0.26.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9da4e873860ad5bab3291438525cae80169daecbfafe5657f7f5fb4d6b3f96b9", size = 561329, upload-time = "2025-07-01T15:54:47.64Z" }, + { url = "https://files.pythonhosted.org/packages/2f/07/1f4f5e2886c480a2346b1e6759c00278b8a69e697ae952d82ae2e6ee5db0/rpds_py-0.26.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5afaddaa8e8c7f1f7b4c5c725c0070b6eed0228f705b90a1732a48e84350f4e9", size = 590875, upload-time = "2025-07-01T15:54:48.9Z" }, + { url = "https://files.pythonhosted.org/packages/cc/bc/e6639f1b91c3a55f8c41b47d73e6307051b6e246254a827ede730624c0f8/rpds_py-0.26.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4916dc96489616a6f9667e7526af8fa693c0fdb4f3acb0e5d9f4400eb06a47ba", size = 556636, upload-time = "2025-07-01T15:54:50.619Z" }, + { url = "https://files.pythonhosted.org/packages/05/4c/b3917c45566f9f9a209d38d9b54a1833f2bb1032a3e04c66f75726f28876/rpds_py-0.26.0-cp313-cp313-win32.whl", hash = "sha256:2a343f91b17097c546b93f7999976fd6c9d5900617aa848c81d794e062ab302b", size = 222663, upload-time = "2025-07-01T15:54:52.023Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0b/0851bdd6025775aaa2365bb8de0697ee2558184c800bfef8d7aef5ccde58/rpds_py-0.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:0a0b60701f2300c81b2ac88a5fb893ccfa408e1c4a555a77f908a2596eb875a5", size = 234428, upload-time = "2025-07-01T15:54:53.692Z" }, + { url = "https://files.pythonhosted.org/packages/ed/e8/a47c64ed53149c75fb581e14a237b7b7cd18217e969c30d474d335105622/rpds_py-0.26.0-cp313-cp313-win_arm64.whl", hash = "sha256:257d011919f133a4746958257f2c75238e3ff54255acd5e3e11f3ff41fd14256", size = 222571, upload-time = "2025-07-01T15:54:54.822Z" }, + { url = "https://files.pythonhosted.org/packages/89/bf/3d970ba2e2bcd17d2912cb42874107390f72873e38e79267224110de5e61/rpds_py-0.26.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:529c8156d7506fba5740e05da8795688f87119cce330c244519cf706a4a3d618", size = 360475, upload-time = "2025-07-01T15:54:56.228Z" }, + { url = "https://files.pythonhosted.org/packages/82/9f/283e7e2979fc4ec2d8ecee506d5a3675fce5ed9b4b7cb387ea5d37c2f18d/rpds_py-0.26.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f53ec51f9d24e9638a40cabb95078ade8c99251945dad8d57bf4aabe86ecee35", size = 346692, upload-time = "2025-07-01T15:54:58.561Z" }, + { url = "https://files.pythonhosted.org/packages/e3/03/7e50423c04d78daf391da3cc4330bdb97042fc192a58b186f2d5deb7befd/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab504c4d654e4a29558eaa5bb8cea5fdc1703ea60a8099ffd9c758472cf913f", size = 379415, upload-time = "2025-07-01T15:54:59.751Z" }, + { url = "https://files.pythonhosted.org/packages/57/00/d11ee60d4d3b16808432417951c63df803afb0e0fc672b5e8d07e9edaaae/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd0641abca296bc1a00183fe44f7fced8807ed49d501f188faa642d0e4975b83", size = 391783, upload-time = "2025-07-01T15:55:00.898Z" }, + { url = "https://files.pythonhosted.org/packages/08/b3/1069c394d9c0d6d23c5b522e1f6546b65793a22950f6e0210adcc6f97c3e/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b312fecc1d017b5327afa81d4da1480f51c68810963a7336d92203dbb3d4f1", size = 512844, upload-time = "2025-07-01T15:55:02.201Z" }, + { url = "https://files.pythonhosted.org/packages/08/3b/c4fbf0926800ed70b2c245ceca99c49f066456755f5d6eb8863c2c51e6d0/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c741107203954f6fc34d3066d213d0a0c40f7bb5aafd698fb39888af277c70d8", size = 402105, upload-time = "2025-07-01T15:55:03.698Z" }, + { url = "https://files.pythonhosted.org/packages/1c/b0/db69b52ca07413e568dae9dc674627a22297abb144c4d6022c6d78f1e5cc/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3e55a7db08dc9a6ed5fb7103019d2c1a38a349ac41901f9f66d7f95750942f", size = 383440, upload-time = "2025-07-01T15:55:05.398Z" }, + { url = "https://files.pythonhosted.org/packages/4c/e1/c65255ad5b63903e56b3bb3ff9dcc3f4f5c3badde5d08c741ee03903e951/rpds_py-0.26.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e851920caab2dbcae311fd28f4313c6953993893eb5c1bb367ec69d9a39e7ed", size = 412759, upload-time = "2025-07-01T15:55:08.316Z" }, + { url = "https://files.pythonhosted.org/packages/e4/22/bb731077872377a93c6e93b8a9487d0406c70208985831034ccdeed39c8e/rpds_py-0.26.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dfbf280da5f876d0b00c81f26bedce274e72a678c28845453885a9b3c22ae632", size = 556032, upload-time = "2025-07-01T15:55:09.52Z" }, + { url = "https://files.pythonhosted.org/packages/e0/8b/393322ce7bac5c4530fb96fc79cc9ea2f83e968ff5f6e873f905c493e1c4/rpds_py-0.26.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1cc81d14ddfa53d7f3906694d35d54d9d3f850ef8e4e99ee68bc0d1e5fed9a9c", size = 585416, upload-time = "2025-07-01T15:55:11.216Z" }, + { url = "https://files.pythonhosted.org/packages/49/ae/769dc372211835bf759319a7aae70525c6eb523e3371842c65b7ef41c9c6/rpds_py-0.26.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dca83c498b4650a91efcf7b88d669b170256bf8017a5db6f3e06c2bf031f57e0", size = 554049, upload-time = "2025-07-01T15:55:13.004Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f9/4c43f9cc203d6ba44ce3146246cdc38619d92c7bd7bad4946a3491bd5b70/rpds_py-0.26.0-cp313-cp313t-win32.whl", hash = "sha256:4d11382bcaf12f80b51d790dee295c56a159633a8e81e6323b16e55d81ae37e9", size = 218428, upload-time = "2025-07-01T15:55:14.486Z" }, + { url = "https://files.pythonhosted.org/packages/7e/8b/9286b7e822036a4a977f2f1e851c7345c20528dbd56b687bb67ed68a8ede/rpds_py-0.26.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff110acded3c22c033e637dd8896e411c7d3a11289b2edf041f86663dbc791e9", size = 231524, upload-time = "2025-07-01T15:55:15.745Z" }, + { url = "https://files.pythonhosted.org/packages/55/07/029b7c45db910c74e182de626dfdae0ad489a949d84a468465cd0ca36355/rpds_py-0.26.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:da619979df60a940cd434084355c514c25cf8eb4cf9a508510682f6c851a4f7a", size = 364292, upload-time = "2025-07-01T15:55:17.001Z" }, + { url = "https://files.pythonhosted.org/packages/13/d1/9b3d3f986216b4d1f584878dca15ce4797aaf5d372d738974ba737bf68d6/rpds_py-0.26.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ea89a2458a1a75f87caabefe789c87539ea4e43b40f18cff526052e35bbb4fdf", size = 350334, upload-time = "2025-07-01T15:55:18.922Z" }, + { url = "https://files.pythonhosted.org/packages/18/98/16d5e7bc9ec715fa9668731d0cf97f6b032724e61696e2db3d47aeb89214/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feac1045b3327a45944e7dcbeb57530339f6b17baff154df51ef8b0da34c8c12", size = 384875, upload-time = "2025-07-01T15:55:20.399Z" }, + { url = "https://files.pythonhosted.org/packages/f9/13/aa5e2b1ec5ab0e86a5c464d53514c0467bec6ba2507027d35fc81818358e/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b818a592bd69bfe437ee8368603d4a2d928c34cffcdf77c2e761a759ffd17d20", size = 399993, upload-time = "2025-07-01T15:55:21.729Z" }, + { url = "https://files.pythonhosted.org/packages/17/03/8021810b0e97923abdbab6474c8b77c69bcb4b2c58330777df9ff69dc559/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a8b0dd8648709b62d9372fc00a57466f5fdeefed666afe3fea5a6c9539a0331", size = 516683, upload-time = "2025-07-01T15:55:22.918Z" }, + { url = "https://files.pythonhosted.org/packages/dc/b1/da8e61c87c2f3d836954239fdbbfb477bb7b54d74974d8f6fcb34342d166/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d3498ad0df07d81112aa6ec6c95a7e7b1ae00929fb73e7ebee0f3faaeabad2f", size = 408825, upload-time = "2025-07-01T15:55:24.207Z" }, + { url = "https://files.pythonhosted.org/packages/38/bc/1fc173edaaa0e52c94b02a655db20697cb5fa954ad5a8e15a2c784c5cbdd/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24a4146ccb15be237fdef10f331c568e1b0e505f8c8c9ed5d67759dac58ac246", size = 387292, upload-time = "2025-07-01T15:55:25.554Z" }, + { url = "https://files.pythonhosted.org/packages/7c/eb/3a9bb4bd90867d21916f253caf4f0d0be7098671b6715ad1cead9fe7bab9/rpds_py-0.26.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a9a63785467b2d73635957d32a4f6e73d5e4df497a16a6392fa066b753e87387", size = 420435, upload-time = "2025-07-01T15:55:27.798Z" }, + { url = "https://files.pythonhosted.org/packages/cd/16/e066dcdb56f5632713445271a3f8d3d0b426d51ae9c0cca387799df58b02/rpds_py-0.26.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:de4ed93a8c91debfd5a047be327b7cc8b0cc6afe32a716bbbc4aedca9e2a83af", size = 562410, upload-time = "2025-07-01T15:55:29.057Z" }, + { url = "https://files.pythonhosted.org/packages/60/22/ddbdec7eb82a0dc2e455be44c97c71c232983e21349836ce9f272e8a3c29/rpds_py-0.26.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:caf51943715b12af827696ec395bfa68f090a4c1a1d2509eb4e2cb69abbbdb33", size = 590724, upload-time = "2025-07-01T15:55:30.719Z" }, + { url = "https://files.pythonhosted.org/packages/2c/b4/95744085e65b7187d83f2fcb0bef70716a1ea0a9e5d8f7f39a86e5d83424/rpds_py-0.26.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4a59e5bc386de021f56337f757301b337d7ab58baa40174fb150accd480bc953", size = 558285, upload-time = "2025-07-01T15:55:31.981Z" }, + { url = "https://files.pythonhosted.org/packages/37/37/6309a75e464d1da2559446f9c811aa4d16343cebe3dbb73701e63f760caa/rpds_py-0.26.0-cp314-cp314-win32.whl", hash = "sha256:92c8db839367ef16a662478f0a2fe13e15f2227da3c1430a782ad0f6ee009ec9", size = 223459, upload-time = "2025-07-01T15:55:33.312Z" }, + { url = "https://files.pythonhosted.org/packages/d9/6f/8e9c11214c46098b1d1391b7e02b70bb689ab963db3b19540cba17315291/rpds_py-0.26.0-cp314-cp314-win_amd64.whl", hash = "sha256:b0afb8cdd034150d4d9f53926226ed27ad15b7f465e93d7468caaf5eafae0d37", size = 236083, upload-time = "2025-07-01T15:55:34.933Z" }, + { url = "https://files.pythonhosted.org/packages/47/af/9c4638994dd623d51c39892edd9d08e8be8220a4b7e874fa02c2d6e91955/rpds_py-0.26.0-cp314-cp314-win_arm64.whl", hash = "sha256:ca3f059f4ba485d90c8dc75cb5ca897e15325e4e609812ce57f896607c1c0867", size = 223291, upload-time = "2025-07-01T15:55:36.202Z" }, + { url = "https://files.pythonhosted.org/packages/4d/db/669a241144460474aab03e254326b32c42def83eb23458a10d163cb9b5ce/rpds_py-0.26.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:5afea17ab3a126006dc2f293b14ffc7ef3c85336cf451564a0515ed7648033da", size = 361445, upload-time = "2025-07-01T15:55:37.483Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2d/133f61cc5807c6c2fd086a46df0eb8f63a23f5df8306ff9f6d0fd168fecc/rpds_py-0.26.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:69f0c0a3df7fd3a7eec50a00396104bb9a843ea6d45fcc31c2d5243446ffd7a7", size = 347206, upload-time = "2025-07-01T15:55:38.828Z" }, + { url = "https://files.pythonhosted.org/packages/05/bf/0e8fb4c05f70273469eecf82f6ccf37248558526a45321644826555db31b/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:801a71f70f9813e82d2513c9a96532551fce1e278ec0c64610992c49c04c2dad", size = 380330, upload-time = "2025-07-01T15:55:40.175Z" }, + { url = "https://files.pythonhosted.org/packages/d4/a8/060d24185d8b24d3923322f8d0ede16df4ade226a74e747b8c7c978e3dd3/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df52098cde6d5e02fa75c1f6244f07971773adb4a26625edd5c18fee906fa84d", size = 392254, upload-time = "2025-07-01T15:55:42.015Z" }, + { url = "https://files.pythonhosted.org/packages/b9/7b/7c2e8a9ee3e6bc0bae26bf29f5219955ca2fbb761dca996a83f5d2f773fe/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bc596b30f86dc6f0929499c9e574601679d0341a0108c25b9b358a042f51bca", size = 516094, upload-time = "2025-07-01T15:55:43.603Z" }, + { url = "https://files.pythonhosted.org/packages/75/d6/f61cafbed8ba1499b9af9f1777a2a199cd888f74a96133d8833ce5eaa9c5/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9dfbe56b299cf5875b68eb6f0ebaadc9cac520a1989cac0db0765abfb3709c19", size = 402889, upload-time = "2025-07-01T15:55:45.275Z" }, + { url = "https://files.pythonhosted.org/packages/92/19/c8ac0a8a8df2dd30cdec27f69298a5c13e9029500d6d76718130f5e5be10/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac64f4b2bdb4ea622175c9ab7cf09444e412e22c0e02e906978b3b488af5fde8", size = 384301, upload-time = "2025-07-01T15:55:47.098Z" }, + { url = "https://files.pythonhosted.org/packages/41/e1/6b1859898bc292a9ce5776016c7312b672da00e25cec74d7beced1027286/rpds_py-0.26.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:181ef9b6bbf9845a264f9aa45c31836e9f3c1f13be565d0d010e964c661d1e2b", size = 412891, upload-time = "2025-07-01T15:55:48.412Z" }, + { url = "https://files.pythonhosted.org/packages/ef/b9/ceb39af29913c07966a61367b3c08b4f71fad841e32c6b59a129d5974698/rpds_py-0.26.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:49028aa684c144ea502a8e847d23aed5e4c2ef7cadfa7d5eaafcb40864844b7a", size = 557044, upload-time = "2025-07-01T15:55:49.816Z" }, + { url = "https://files.pythonhosted.org/packages/2f/27/35637b98380731a521f8ec4f3fd94e477964f04f6b2f8f7af8a2d889a4af/rpds_py-0.26.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e5d524d68a474a9688336045bbf76cb0def88549c1b2ad9dbfec1fb7cfbe9170", size = 585774, upload-time = "2025-07-01T15:55:51.192Z" }, + { url = "https://files.pythonhosted.org/packages/52/d9/3f0f105420fecd18551b678c9a6ce60bd23986098b252a56d35781b3e7e9/rpds_py-0.26.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c1851f429b822831bd2edcbe0cfd12ee9ea77868f8d3daf267b189371671c80e", size = 554886, upload-time = "2025-07-01T15:55:52.541Z" }, + { url = "https://files.pythonhosted.org/packages/6b/c5/347c056a90dc8dd9bc240a08c527315008e1b5042e7a4cf4ac027be9d38a/rpds_py-0.26.0-cp314-cp314t-win32.whl", hash = "sha256:7bdb17009696214c3b66bb3590c6d62e14ac5935e53e929bcdbc5a495987a84f", size = 219027, upload-time = "2025-07-01T15:55:53.874Z" }, + { url = "https://files.pythonhosted.org/packages/75/04/5302cea1aa26d886d34cadbf2dc77d90d7737e576c0065f357b96dc7a1a6/rpds_py-0.26.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f14440b9573a6f76b4ee4770c13f0b5921f71dde3b6fcb8dabbefd13b7fe05d7", size = 232821, upload-time = "2025-07-01T15:55:55.167Z" }, +] + [[package]] name = "shellingham" version = "1.5.4" @@ -434,14 +560,14 @@ wheels = [ [[package]] name = "sse-starlette" -version = "2.3.6" +version = "2.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8c/f4/989bc70cb8091eda43a9034ef969b25145291f3601703b82766e5172dfed/sse_starlette-2.3.6.tar.gz", hash = "sha256:0382336f7d4ec30160cf9ca0518962905e1b69b72d6c1c995131e0a703b436e3", size = 18284, upload-time = "2025-05-30T13:34:12.914Z" } +sdist = { url = "https://files.pythonhosted.org/packages/07/3e/eae74d8d33e3262bae0a7e023bb43d8bdd27980aa3557333f4632611151f/sse_starlette-2.4.1.tar.gz", hash = "sha256:7c8a800a1ca343e9165fc06bbda45c78e4c6166320707ae30b416c42da070926", size = 18635, upload-time = "2025-07-06T09:41:33.631Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/05/78850ac6e79af5b9508f8841b0f26aa9fd329a1ba00bf65453c2d312bcc8/sse_starlette-2.3.6-py3-none-any.whl", hash = "sha256:d49a8285b182f6e2228e2609c350398b2ca2c36216c2675d875f81e93548f760", size = 10606, upload-time = "2025-05-30T13:34:11.703Z" }, + { url = "https://files.pythonhosted.org/packages/e4/f1/6c7eaa8187ba789a6dd6d74430307478d2a91c23a5452ab339b6fbe15a08/sse_starlette-2.4.1-py3-none-any.whl", hash = "sha256:08b77ea898ab1a13a428b2b6f73cfe6d0e607a7b4e15b9bb23e4a37b087fd39a", size = 10824, upload-time = "2025-07-06T09:41:32.321Z" }, ] [[package]] @@ -473,11 +599,11 @@ wheels = [ [[package]] name = "typing-extensions" -version = "4.14.0" +version = "4.14.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423, upload-time = "2025-06-02T14:52:11.399Z" } +sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839, upload-time = "2025-06-02T14:52:10.026Z" }, + { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" }, ] [[package]] @@ -503,13 +629,13 @@ wheels = [ [[package]] name = "uvicorn" -version = "0.34.3" +version = "0.35.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/de/ad/713be230bcda622eaa35c28f0d328c3675c371238470abdea52417f17a8e/uvicorn-0.34.3.tar.gz", hash = "sha256:35919a9a979d7a59334b6b10e05d77c1d0d574c50e0fc98b8b1a0f165708b55a", size = 76631, upload-time = "2025-06-01T07:48:17.531Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/0d/8adfeaa62945f90d19ddc461c55f4a50c258af7662d34b6a3d5d1f8646f6/uvicorn-0.34.3-py3-none-any.whl", hash = "sha256:16246631db62bdfbf069b0645177d6e8a77ba950cfedbfd093acef9444e4d885", size = 62431, upload-time = "2025-06-01T07:48:15.664Z" }, + { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" }, ] From 36a773f6b08798aa84dc81f3f0bdac5200260684 Mon Sep 17 00:00:00 2001 From: Filip Lajszczak Date: Mon, 21 Jul 2025 15:29:41 +0000 Subject: [PATCH 17/17] Bump up version Co-authored-by: Piotr Kaznowski --- manifest.json | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/manifest.json b/manifest.json index 8911d6d..9684c6b 100644 --- a/manifest.json +++ b/manifest.json @@ -3,7 +3,7 @@ "name": "PythonAnywhere MCP Server", "description": "Manage files, websites, and scheduled tasks on PythonAnywhere via the Model Context Protocol.", "icon": "icon.png", - "version": "0.0.5", + "version": "0.0.6", "author": { "name": "PythonAnywhere Developers", "email": "developers@pythonanywhere.com" diff --git a/pyproject.toml b/pyproject.toml index 7a0dedd..c91cbb5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "pythonanywhere-mcp-server" -version = "0.0.5" +version = "0.0.6" description = "PythonAnywhere Model Context Protocol Server" authors = [ {name = "PythonAnywhere Developers", email = "developers@pythonanywhere.com"} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy