Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cd3e60ba4f | |||
| 360299f5f6 | |||
| d61e33113c | |||
| 5faf7cf45f | |||
| cd922fa750 |
@@ -1,3 +1,9 @@
|
|||||||
|
## v0.50.2 (2025-12-13)
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
|
||||||
|
- **news**: revert get_item() to use get_items() + filter
|
||||||
|
|
||||||
## v0.50.1 (2025-12-12)
|
## v0.50.1 (2025-12-12)
|
||||||
|
|
||||||
### Fix
|
### Fix
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
FROM docker.io/library/python:3.12-slim-trixie@sha256:590cad70271b6c1795c6a11fb5c110efca593adbd0d4883cd19c36df6a56467b
|
FROM docker.io/library/python:3.12-slim-trixie@sha256:fa48eefe2146644c2308b909d6bb7651a768178f84fc9550dcd495e4d6d84d01
|
||||||
|
|
||||||
COPY --from=ghcr.io/astral-sh/uv:0.9.17@sha256:5cb6b54d2bc3fe2eb9a8483db958a0b9eebf9edff68adedb369df8e7b98711a2 /uv /uvx /bin/
|
COPY --from=ghcr.io/astral-sh/uv:0.9.17@sha256:5cb6b54d2bc3fe2eb9a8483db958a0b9eebf9edff68adedb369df8e7b98711a2 /uv /uvx /bin/
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@
|
|||||||
# - Per-session app password authentication
|
# - Per-session app password authentication
|
||||||
# - Multi-user support via Smithery session config
|
# - Multi-user support via Smithery session config
|
||||||
|
|
||||||
FROM docker.io/library/python:3.12-slim-trixie@sha256:590cad70271b6c1795c6a11fb5c110efca593adbd0d4883cd19c36df6a56467b
|
FROM docker.io/library/python:3.12-slim-trixie@sha256:fa48eefe2146644c2308b909d6bb7651a768178f84fc9550dcd495e4d6d84d01
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ apiVersion: v2
|
|||||||
name: nextcloud-mcp-server
|
name: nextcloud-mcp-server
|
||||||
description: A Helm chart for Nextcloud MCP Server - enables AI assistants to interact with Nextcloud
|
description: A Helm chart for Nextcloud MCP Server - enables AI assistants to interact with Nextcloud
|
||||||
type: application
|
type: application
|
||||||
version: 0.50.1
|
version: 0.50.2
|
||||||
appVersion: "0.50.1"
|
appVersion: "0.50.2"
|
||||||
keywords:
|
keywords:
|
||||||
- nextcloud
|
- nextcloud
|
||||||
- mcp
|
- mcp
|
||||||
|
|||||||
@@ -228,6 +228,10 @@ class NewsClient(BaseNextcloudClient):
|
|||||||
async def get_item(self, item_id: int) -> dict[str, Any]:
|
async def get_item(self, item_id: int) -> dict[str, Any]:
|
||||||
"""Get a specific item by ID.
|
"""Get a specific item by ID.
|
||||||
|
|
||||||
|
Note: The News API doesn't have a direct single-item endpoint,
|
||||||
|
so we fetch all items and filter. For efficiency, consider
|
||||||
|
caching or using get_items with specific feed if known.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
item_id: Item ID
|
item_id: Item ID
|
||||||
|
|
||||||
@@ -235,10 +239,15 @@ class NewsClient(BaseNextcloudClient):
|
|||||||
Item data
|
Item data
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
HTTPStatusError: 404 if item not found
|
ValueError: If item not found
|
||||||
"""
|
"""
|
||||||
response = await self._make_request("GET", f"{self.API_BASE}/items/{item_id}")
|
# Fetch all items and find the one we need
|
||||||
return response.json()
|
# This is inefficient but the API doesn't provide a direct endpoint
|
||||||
|
items = await self.get_items(batch_size=-1, get_read=True)
|
||||||
|
for item in items:
|
||||||
|
if item.get("id") == item_id:
|
||||||
|
return item
|
||||||
|
raise ValueError(f"Item {item_id} not found")
|
||||||
|
|
||||||
async def get_updated_items(
|
async def get_updated_items(
|
||||||
self,
|
self,
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "nextcloud-mcp-server"
|
name = "nextcloud-mcp-server"
|
||||||
version = "0.50.1"
|
version = "0.50.2"
|
||||||
description = "Model Context Protocol (MCP) server for Nextcloud integration - enables AI assistants to interact with Nextcloud data"
|
description = "Model Context Protocol (MCP) server for Nextcloud integration - enables AI assistants to interact with Nextcloud data"
|
||||||
authors = [
|
authors = [
|
||||||
{name = "Chris Coutinho", email = "chris@coutinho.io"}
|
{name = "Chris Coutinho", email = "chris@coutinho.io"}
|
||||||
|
|||||||
@@ -310,14 +310,16 @@ async def test_news_api_get_items_unread_only(mocker):
|
|||||||
|
|
||||||
|
|
||||||
async def test_news_api_get_item(mocker):
|
async def test_news_api_get_item(mocker):
|
||||||
"""Test that get_item fetches a single item by ID."""
|
"""Test that get_item fetches all items and filters for the requested ID."""
|
||||||
item = create_mock_news_item(item_id=123, title="Single Item")
|
# Create multiple items, only one should be returned
|
||||||
mock_response = create_mock_response(status_code=200, json_data=item)
|
items = [
|
||||||
|
create_mock_news_item(item_id=100, title="Other Item 1"),
|
||||||
|
create_mock_news_item(item_id=123, title="Single Item"),
|
||||||
|
create_mock_news_item(item_id=200, title="Other Item 2"),
|
||||||
|
]
|
||||||
|
|
||||||
mock_client = mocker.AsyncMock(spec=httpx.AsyncClient)
|
mock_client = mocker.AsyncMock(spec=httpx.AsyncClient)
|
||||||
mock_make_request = mocker.patch.object(
|
mock_get_items = mocker.patch.object(NewsClient, "get_items", return_value=items)
|
||||||
NewsClient, "_make_request", return_value=mock_response
|
|
||||||
)
|
|
||||||
|
|
||||||
client = NewsClient(mock_client, "testuser")
|
client = NewsClient(mock_client, "testuser")
|
||||||
result = await client.get_item(item_id=123)
|
result = await client.get_item(item_id=123)
|
||||||
@@ -325,7 +327,24 @@ async def test_news_api_get_item(mocker):
|
|||||||
assert result["id"] == 123
|
assert result["id"] == 123
|
||||||
assert result["title"] == "Single Item"
|
assert result["title"] == "Single Item"
|
||||||
|
|
||||||
mock_make_request.assert_called_once_with("GET", "/apps/news/api/v1-3/items/123")
|
# Verify it fetched all items with correct params
|
||||||
|
mock_get_items.assert_called_once_with(batch_size=-1, get_read=True)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_news_api_get_item_not_found(mocker):
|
||||||
|
"""Test that get_item raises ValueError when item not found."""
|
||||||
|
items = [
|
||||||
|
create_mock_news_item(item_id=100, title="Item 1"),
|
||||||
|
create_mock_news_item(item_id=200, title="Item 2"),
|
||||||
|
]
|
||||||
|
|
||||||
|
mock_client = mocker.AsyncMock(spec=httpx.AsyncClient)
|
||||||
|
mocker.patch.object(NewsClient, "get_items", return_value=items)
|
||||||
|
|
||||||
|
client = NewsClient(mock_client, "testuser")
|
||||||
|
|
||||||
|
with pytest.raises(ValueError, match="Item 999 not found"):
|
||||||
|
await client.get_item(item_id=999)
|
||||||
|
|
||||||
|
|
||||||
async def test_news_api_get_updated_items(mocker):
|
async def test_news_api_get_updated_items(mocker):
|
||||||
|
|||||||
@@ -1962,7 +1962,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nextcloud-mcp-server"
|
name = "nextcloud-mcp-server"
|
||||||
version = "0.50.1"
|
version = "0.50.2"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "aiosqlite" },
|
{ name = "aiosqlite" },
|
||||||
|
|||||||
Reference in New Issue
Block a user