feat: Complete Phase 5 - Instrument all 93 MCP tools
Applied @instrument_tool decorator to all 86 remaining tools across 8 server files. Instrumented files: - calendar.py: 16 tools - contacts.py: 7 tools - deck.py: 25 tools - webdav.py: 11 tools - tables.py: 6 tools - sharing.py: 5 tools - cookbook.py: 13 tools - semantic.py: 3 tools Total: 93 tools instrumented (7 in notes.py + 86 in other files) These metrics populate: - MCP Tool Calls panel (by tool name and status) - MCP Tool Duration panel (histogram) - MCP Tool Errors panel (by tool name and error type) This completes PR #295 - All 5 phases of metrics instrumentation done: ✅ Phase 1: Queue size metrics (2 locations) ✅ Phase 2: Health checks (1 location) ✅ Phase 3: Database operations (3 methods) ✅ Phase 4: OAuth token metrics (3 locations) ✅ Phase 5: MCP tool metrics (93 tools) All 34 dashboard panels now have data sources.
This commit is contained in:
@@ -12,6 +12,7 @@ from nextcloud_mcp_server.models.calendar import (
|
||||
ListTodosResponse,
|
||||
Todo,
|
||||
)
|
||||
from nextcloud_mcp_server.observability.metrics import instrument_tool
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -20,6 +21,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
# Calendar tools
|
||||
@mcp.tool()
|
||||
@require_scopes("calendar:read")
|
||||
@instrument_tool
|
||||
async def nc_calendar_list_calendars(ctx: Context) -> ListCalendarsResponse:
|
||||
"""List all available calendars for the user"""
|
||||
client = await get_client(ctx)
|
||||
@@ -30,6 +32,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("calendar:write")
|
||||
@instrument_tool
|
||||
async def nc_calendar_create_event(
|
||||
calendar_name: str,
|
||||
title: str,
|
||||
@@ -106,6 +109,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("calendar:read")
|
||||
@instrument_tool
|
||||
async def nc_calendar_list_events(
|
||||
calendar_name: str,
|
||||
ctx: Context,
|
||||
@@ -208,6 +212,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("calendar:read")
|
||||
@instrument_tool
|
||||
async def nc_calendar_get_event(
|
||||
calendar_name: str,
|
||||
event_uid: str,
|
||||
@@ -220,6 +225,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("calendar:write")
|
||||
@instrument_tool
|
||||
async def nc_calendar_update_event(
|
||||
calendar_name: str,
|
||||
event_uid: str,
|
||||
@@ -293,6 +299,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("calendar:write")
|
||||
@instrument_tool
|
||||
async def nc_calendar_delete_event(
|
||||
calendar_name: str,
|
||||
event_uid: str,
|
||||
@@ -304,6 +311,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("calendar:write")
|
||||
@instrument_tool
|
||||
async def nc_calendar_create_meeting(
|
||||
title: str,
|
||||
date: str,
|
||||
@@ -370,6 +378,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("calendar:read")
|
||||
@instrument_tool
|
||||
async def nc_calendar_get_upcoming_events(
|
||||
ctx: Context,
|
||||
calendar_name: str = "", # Empty = all calendars
|
||||
@@ -420,6 +429,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("calendar:read")
|
||||
@instrument_tool
|
||||
async def nc_calendar_find_availability(
|
||||
duration_minutes: int,
|
||||
ctx: Context,
|
||||
@@ -500,6 +510,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("calendar:write")
|
||||
@instrument_tool
|
||||
async def nc_calendar_bulk_operations(
|
||||
operation: str, # "update", "delete", "move"
|
||||
ctx: Context,
|
||||
@@ -749,6 +760,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("calendar:write")
|
||||
@instrument_tool
|
||||
async def nc_calendar_manage_calendar(
|
||||
action: str, # "create", "delete", "update", "list"
|
||||
ctx: Context,
|
||||
@@ -818,6 +830,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("todo:read", "calendar:read")
|
||||
@instrument_tool
|
||||
async def nc_calendar_list_todos(
|
||||
calendar_name: str,
|
||||
ctx: Context,
|
||||
@@ -863,6 +876,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("todo:write", "calendar:read")
|
||||
@instrument_tool
|
||||
async def nc_calendar_create_todo(
|
||||
calendar_name: str,
|
||||
summary: str,
|
||||
@@ -906,6 +920,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("todo:write", "calendar:read")
|
||||
@instrument_tool
|
||||
async def nc_calendar_update_todo(
|
||||
calendar_name: str,
|
||||
todo_uid: str,
|
||||
@@ -966,6 +981,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("todo:write", "calendar:read")
|
||||
@instrument_tool
|
||||
async def nc_calendar_delete_todo(
|
||||
calendar_name: str,
|
||||
todo_uid: str,
|
||||
@@ -986,6 +1002,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("todo:read", "calendar:read")
|
||||
@instrument_tool
|
||||
async def nc_calendar_search_todos(
|
||||
ctx: Context,
|
||||
status: Optional[str] = None,
|
||||
|
||||
@@ -4,6 +4,7 @@ from mcp.server.fastmcp import Context, FastMCP
|
||||
|
||||
from nextcloud_mcp_server.auth import require_scopes
|
||||
from nextcloud_mcp_server.context import get_client
|
||||
from nextcloud_mcp_server.observability.metrics import instrument_tool
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -12,6 +13,7 @@ def configure_contacts_tools(mcp: FastMCP):
|
||||
# Contacts tools
|
||||
@mcp.tool()
|
||||
@require_scopes("contacts:read")
|
||||
@instrument_tool
|
||||
async def nc_contacts_list_addressbooks(ctx: Context):
|
||||
"""List all addressbooks for the user."""
|
||||
client = await get_client(ctx)
|
||||
@@ -19,6 +21,7 @@ def configure_contacts_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("contacts:read")
|
||||
@instrument_tool
|
||||
async def nc_contacts_list_contacts(ctx: Context, *, addressbook: str):
|
||||
"""List all contacts in the specified addressbook."""
|
||||
client = await get_client(ctx)
|
||||
@@ -26,6 +29,7 @@ def configure_contacts_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("contacts:write")
|
||||
@instrument_tool
|
||||
async def nc_contacts_create_addressbook(
|
||||
ctx: Context, *, name: str, display_name: str
|
||||
):
|
||||
@@ -42,6 +46,7 @@ def configure_contacts_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("contacts:write")
|
||||
@instrument_tool
|
||||
async def nc_contacts_delete_addressbook(ctx: Context, *, name: str):
|
||||
"""Delete an addressbook."""
|
||||
client = await get_client(ctx)
|
||||
@@ -49,6 +54,7 @@ def configure_contacts_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("contacts:write")
|
||||
@instrument_tool
|
||||
async def nc_contacts_create_contact(
|
||||
ctx: Context, *, addressbook: str, uid: str, contact_data: dict
|
||||
):
|
||||
@@ -66,6 +72,7 @@ def configure_contacts_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("contacts:write")
|
||||
@instrument_tool
|
||||
async def nc_contacts_delete_contact(ctx: Context, *, addressbook: str, uid: str):
|
||||
"""Delete a contact."""
|
||||
client = await get_client(ctx)
|
||||
@@ -73,6 +80,7 @@ def configure_contacts_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("contacts:write")
|
||||
@instrument_tool
|
||||
async def nc_contacts_update_contact(
|
||||
ctx: Context, *, addressbook: str, uid: str, contact_data: dict, etag: str = ""
|
||||
):
|
||||
|
||||
@@ -24,6 +24,7 @@ from nextcloud_mcp_server.models.cookbook import (
|
||||
UpdateRecipeResponse,
|
||||
Version,
|
||||
)
|
||||
from nextcloud_mcp_server.observability.metrics import instrument_tool
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -72,6 +73,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("cookbook:write")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_import_recipe(url: str, ctx: Context) -> ImportRecipeResponse:
|
||||
"""Import a recipe from a URL using schema.org metadata.
|
||||
|
||||
@@ -129,6 +131,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("cookbook:read")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_list_recipes(ctx: Context) -> ListRecipesResponse:
|
||||
"""Get all recipes in the database"""
|
||||
client = await get_client(ctx)
|
||||
@@ -154,6 +157,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("cookbook:read")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_get_recipe(recipe_id: int, ctx: Context) -> Recipe:
|
||||
"""Get a specific recipe by its ID"""
|
||||
client = await get_client(ctx)
|
||||
@@ -179,6 +183,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("cookbook:write")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_create_recipe(
|
||||
name: str,
|
||||
description: str | None = None,
|
||||
@@ -258,6 +263,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("cookbook:write")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_update_recipe(
|
||||
recipe_id: int,
|
||||
name: str | None = None,
|
||||
@@ -347,6 +353,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("cookbook:write")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_delete_recipe(
|
||||
recipe_id: int, ctx: Context
|
||||
) -> DeleteRecipeResponse:
|
||||
@@ -382,6 +389,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("cookbook:read")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_search_recipes(
|
||||
query: str, ctx: Context
|
||||
) -> SearchRecipesResponse:
|
||||
@@ -418,6 +426,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("cookbook:read")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_list_categories(ctx: Context) -> ListCategoriesResponse:
|
||||
"""Get all known categories.
|
||||
|
||||
@@ -445,6 +454,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("cookbook:read")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_get_recipes_in_category(
|
||||
category: str, ctx: Context
|
||||
) -> ListRecipesResponse:
|
||||
@@ -481,6 +491,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("cookbook:read")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_list_keywords(ctx: Context) -> ListKeywordsResponse:
|
||||
"""Get all known keywords/tags"""
|
||||
client = await get_client(ctx)
|
||||
@@ -506,6 +517,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("cookbook:read")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_get_recipes_with_keywords(
|
||||
keywords: list[str], ctx: Context
|
||||
) -> ListRecipesResponse:
|
||||
@@ -540,6 +552,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("cookbook:write")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_set_config(
|
||||
folder: str | None = None,
|
||||
update_interval: int | None = None,
|
||||
@@ -583,6 +596,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("cookbook:write")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_reindex(ctx: Context) -> ReindexResponse:
|
||||
"""Trigger a rescan of all recipes into the caching database.
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ from nextcloud_mcp_server.models.deck import (
|
||||
LabelOperationResponse,
|
||||
StackOperationResponse,
|
||||
)
|
||||
from nextcloud_mcp_server.observability.metrics import instrument_tool
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -118,6 +119,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:read")
|
||||
@instrument_tool
|
||||
async def deck_get_boards(ctx: Context) -> list[DeckBoard]:
|
||||
"""Get all Nextcloud Deck boards"""
|
||||
client = await get_client(ctx)
|
||||
@@ -126,6 +128,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:read")
|
||||
@instrument_tool
|
||||
async def deck_get_board(ctx: Context, board_id: int) -> DeckBoard:
|
||||
"""Get details of a specific Nextcloud Deck board"""
|
||||
client = await get_client(ctx)
|
||||
@@ -134,6 +137,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:read")
|
||||
@instrument_tool
|
||||
async def deck_get_stacks(ctx: Context, board_id: int) -> list[DeckStack]:
|
||||
"""Get all stacks in a Nextcloud Deck board"""
|
||||
client = await get_client(ctx)
|
||||
@@ -142,6 +146,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:read")
|
||||
@instrument_tool
|
||||
async def deck_get_stack(ctx: Context, board_id: int, stack_id: int) -> DeckStack:
|
||||
"""Get details of a specific Nextcloud Deck stack"""
|
||||
client = await get_client(ctx)
|
||||
@@ -150,6 +155,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:read")
|
||||
@instrument_tool
|
||||
async def deck_get_cards(
|
||||
ctx: Context, board_id: int, stack_id: int
|
||||
) -> list[DeckCard]:
|
||||
@@ -162,6 +168,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:read")
|
||||
@instrument_tool
|
||||
async def deck_get_card(
|
||||
ctx: Context, board_id: int, stack_id: int, card_id: int
|
||||
) -> DeckCard:
|
||||
@@ -172,6 +179,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:read")
|
||||
@instrument_tool
|
||||
async def deck_get_labels(ctx: Context, board_id: int) -> list[DeckLabel]:
|
||||
"""Get all labels in a Nextcloud Deck board"""
|
||||
client = await get_client(ctx)
|
||||
@@ -180,6 +188,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:read")
|
||||
@instrument_tool
|
||||
async def deck_get_label(ctx: Context, board_id: int, label_id: int) -> DeckLabel:
|
||||
"""Get details of a specific Nextcloud Deck label"""
|
||||
client = await get_client(ctx)
|
||||
@@ -190,6 +199,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:write")
|
||||
@instrument_tool
|
||||
async def deck_create_board(
|
||||
ctx: Context, title: str, color: str
|
||||
) -> CreateBoardResponse:
|
||||
@@ -207,6 +217,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:write")
|
||||
@instrument_tool
|
||||
async def deck_create_stack(
|
||||
ctx: Context, board_id: int, title: str, order: int
|
||||
) -> CreateStackResponse:
|
||||
@@ -223,6 +234,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:write")
|
||||
@instrument_tool
|
||||
async def deck_update_stack(
|
||||
ctx: Context,
|
||||
board_id: int,
|
||||
@@ -249,6 +261,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:write")
|
||||
@instrument_tool
|
||||
async def deck_delete_stack(
|
||||
ctx: Context, board_id: int, stack_id: int
|
||||
) -> StackOperationResponse:
|
||||
@@ -270,6 +283,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
# Card Tools
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:write")
|
||||
@instrument_tool
|
||||
async def deck_create_card(
|
||||
ctx: Context,
|
||||
board_id: int,
|
||||
@@ -304,6 +318,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:write")
|
||||
@instrument_tool
|
||||
async def deck_update_card(
|
||||
ctx: Context,
|
||||
board_id: int,
|
||||
@@ -357,6 +372,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:write")
|
||||
@instrument_tool
|
||||
async def deck_delete_card(
|
||||
ctx: Context, board_id: int, stack_id: int, card_id: int
|
||||
) -> CardOperationResponse:
|
||||
@@ -379,6 +395,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:write")
|
||||
@instrument_tool
|
||||
async def deck_archive_card(
|
||||
ctx: Context, board_id: int, stack_id: int, card_id: int
|
||||
) -> CardOperationResponse:
|
||||
@@ -401,6 +418,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:write")
|
||||
@instrument_tool
|
||||
async def deck_unarchive_card(
|
||||
ctx: Context, board_id: int, stack_id: int, card_id: int
|
||||
) -> CardOperationResponse:
|
||||
@@ -423,6 +441,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:write")
|
||||
@instrument_tool
|
||||
async def deck_reorder_card(
|
||||
ctx: Context,
|
||||
board_id: int,
|
||||
@@ -455,6 +474,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
# Label Tools
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:write")
|
||||
@instrument_tool
|
||||
async def deck_create_label(
|
||||
ctx: Context, board_id: int, title: str, color: str
|
||||
) -> CreateLabelResponse:
|
||||
@@ -471,6 +491,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:write")
|
||||
@instrument_tool
|
||||
async def deck_update_label(
|
||||
ctx: Context,
|
||||
board_id: int,
|
||||
@@ -497,6 +518,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:write")
|
||||
@instrument_tool
|
||||
async def deck_delete_label(
|
||||
ctx: Context, board_id: int, label_id: int
|
||||
) -> LabelOperationResponse:
|
||||
@@ -518,6 +540,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
# Card-Label Assignment Tools
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:write")
|
||||
@instrument_tool
|
||||
async def deck_assign_label_to_card(
|
||||
ctx: Context, board_id: int, stack_id: int, card_id: int, label_id: int
|
||||
) -> CardOperationResponse:
|
||||
@@ -541,6 +564,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:write")
|
||||
@instrument_tool
|
||||
async def deck_remove_label_from_card(
|
||||
ctx: Context, board_id: int, stack_id: int, card_id: int, label_id: int
|
||||
) -> CardOperationResponse:
|
||||
@@ -565,6 +589,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
# Card-User Assignment Tools
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:write")
|
||||
@instrument_tool
|
||||
async def deck_assign_user_to_card(
|
||||
ctx: Context, board_id: int, stack_id: int, card_id: int, user_id: str
|
||||
) -> CardOperationResponse:
|
||||
@@ -588,6 +613,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("deck:write")
|
||||
@instrument_tool
|
||||
async def deck_unassign_user_from_card(
|
||||
ctx: Context, board_id: int, stack_id: int, card_id: int, user_id: str
|
||||
) -> CardOperationResponse:
|
||||
|
||||
@@ -21,7 +21,10 @@ from nextcloud_mcp_server.models.semantic import (
|
||||
SemanticSearchResult,
|
||||
VectorSyncStatusResponse,
|
||||
)
|
||||
from nextcloud_mcp_server.observability.metrics import record_qdrant_operation
|
||||
from nextcloud_mcp_server.observability.metrics import (
|
||||
instrument_tool,
|
||||
record_qdrant_operation,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -31,6 +34,7 @@ def configure_semantic_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("semantic:read")
|
||||
@instrument_tool
|
||||
async def nc_semantic_search(
|
||||
query: str, ctx: Context, limit: int = 10, score_threshold: float = 0.7
|
||||
) -> SemanticSearchResponse:
|
||||
@@ -216,6 +220,7 @@ def configure_semantic_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("semantic:read")
|
||||
@instrument_tool
|
||||
async def nc_semantic_search_answer(
|
||||
query: str,
|
||||
ctx: Context,
|
||||
@@ -544,6 +549,7 @@ def configure_semantic_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("semantic:read")
|
||||
@instrument_tool
|
||||
async def nc_get_vector_sync_status(ctx: Context) -> VectorSyncStatusResponse:
|
||||
"""Get the current vector sync status.
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ from mcp.server.fastmcp import Context, FastMCP
|
||||
|
||||
from nextcloud_mcp_server.auth import require_scopes
|
||||
from nextcloud_mcp_server.context import get_client
|
||||
from nextcloud_mcp_server.observability.metrics import instrument_tool
|
||||
|
||||
|
||||
def configure_sharing_tools(mcp: FastMCP):
|
||||
@@ -17,6 +18,7 @@ def configure_sharing_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("sharing:write")
|
||||
@instrument_tool
|
||||
async def nc_share_create(
|
||||
path: str,
|
||||
share_with: str,
|
||||
@@ -56,6 +58,7 @@ def configure_sharing_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("sharing:write")
|
||||
@instrument_tool
|
||||
async def nc_share_delete(share_id: int, ctx: Context) -> str:
|
||||
"""Delete a share by its ID.
|
||||
|
||||
@@ -75,6 +78,7 @@ def configure_sharing_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("sharing:write")
|
||||
@instrument_tool
|
||||
async def nc_share_get(share_id: int, ctx: Context) -> str:
|
||||
"""Get information about a specific share.
|
||||
|
||||
@@ -93,6 +97,7 @@ def configure_sharing_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("sharing:write")
|
||||
@instrument_tool
|
||||
async def nc_share_list(
|
||||
ctx: Context, path: str | None = None, shared_with_me: bool = False
|
||||
) -> str:
|
||||
@@ -114,6 +119,7 @@ def configure_sharing_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("sharing:write")
|
||||
@instrument_tool
|
||||
async def nc_share_update(share_id: int, permissions: int, ctx: Context) -> str:
|
||||
"""Update the permissions of an existing share.
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ from mcp.server.fastmcp import Context, FastMCP
|
||||
|
||||
from nextcloud_mcp_server.auth import require_scopes
|
||||
from nextcloud_mcp_server.context import get_client
|
||||
from nextcloud_mcp_server.observability.metrics import instrument_tool
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -12,6 +13,7 @@ def configure_tables_tools(mcp: FastMCP):
|
||||
# Tables tools
|
||||
@mcp.tool()
|
||||
@require_scopes("tables:read")
|
||||
@instrument_tool
|
||||
async def nc_tables_list_tables(ctx: Context):
|
||||
"""List all tables available to the user"""
|
||||
client = await get_client(ctx)
|
||||
@@ -19,6 +21,7 @@ def configure_tables_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("tables:read")
|
||||
@instrument_tool
|
||||
async def nc_tables_get_schema(table_id: int, ctx: Context):
|
||||
"""Get the schema/structure of a specific table including columns and views"""
|
||||
client = await get_client(ctx)
|
||||
@@ -26,6 +29,7 @@ def configure_tables_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("tables:read")
|
||||
@instrument_tool
|
||||
async def nc_tables_read_table(
|
||||
table_id: int,
|
||||
ctx: Context,
|
||||
@@ -38,6 +42,7 @@ def configure_tables_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("tables:write")
|
||||
@instrument_tool
|
||||
async def nc_tables_insert_row(table_id: int, data: dict, ctx: Context):
|
||||
"""Insert a new row into a table.
|
||||
|
||||
@@ -48,6 +53,7 @@ def configure_tables_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("tables:write")
|
||||
@instrument_tool
|
||||
async def nc_tables_update_row(row_id: int, data: dict, ctx: Context):
|
||||
"""Update an existing row in a table.
|
||||
|
||||
@@ -58,6 +64,7 @@ def configure_tables_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("tables:write")
|
||||
@instrument_tool
|
||||
async def nc_tables_delete_row(row_id: int, ctx: Context):
|
||||
"""Delete a row from a table"""
|
||||
client = await get_client(ctx)
|
||||
|
||||
@@ -5,6 +5,7 @@ from mcp.server.fastmcp import Context, FastMCP
|
||||
from nextcloud_mcp_server.auth import require_scopes
|
||||
from nextcloud_mcp_server.context import get_client
|
||||
from nextcloud_mcp_server.models import DirectoryListing, FileInfo, SearchFilesResponse
|
||||
from nextcloud_mcp_server.observability.metrics import instrument_tool
|
||||
from nextcloud_mcp_server.utils.document_parser import (
|
||||
is_parseable_document,
|
||||
parse_document,
|
||||
@@ -17,6 +18,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
# WebDAV file system tools
|
||||
@mcp.tool()
|
||||
@require_scopes("files:read")
|
||||
@instrument_tool
|
||||
async def nc_webdav_list_directory(
|
||||
ctx: Context, path: str = ""
|
||||
) -> DirectoryListing:
|
||||
@@ -50,6 +52,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("files:read")
|
||||
@instrument_tool
|
||||
async def nc_webdav_read_file(path: str, ctx: Context):
|
||||
"""Read the content of a file from NextCloud.
|
||||
|
||||
@@ -130,6 +133,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("files:write")
|
||||
@instrument_tool
|
||||
async def nc_webdav_write_file(
|
||||
path: str, content: str, ctx: Context, content_type: str | None = None
|
||||
):
|
||||
@@ -158,6 +162,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("files:write")
|
||||
@instrument_tool
|
||||
async def nc_webdav_create_directory(path: str, ctx: Context):
|
||||
"""Create a directory in NextCloud.
|
||||
|
||||
@@ -172,6 +177,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("files:write")
|
||||
@instrument_tool
|
||||
async def nc_webdav_delete_resource(path: str, ctx: Context):
|
||||
"""Delete a file or directory in NextCloud.
|
||||
|
||||
@@ -186,6 +192,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("files:write")
|
||||
@instrument_tool
|
||||
async def nc_webdav_move_resource(
|
||||
source_path: str, destination_path: str, ctx: Context, overwrite: bool = False
|
||||
):
|
||||
@@ -206,6 +213,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("files:write")
|
||||
@instrument_tool
|
||||
async def nc_webdav_copy_resource(
|
||||
source_path: str, destination_path: str, ctx: Context, overwrite: bool = False
|
||||
):
|
||||
@@ -226,6 +234,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("files:read")
|
||||
@instrument_tool
|
||||
async def nc_webdav_search_files(
|
||||
ctx: Context,
|
||||
scope: str = "",
|
||||
@@ -342,6 +351,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("files:read")
|
||||
@instrument_tool
|
||||
async def nc_webdav_find_by_name(
|
||||
pattern: str, ctx: Context, scope: str = "", limit: int | None = None
|
||||
) -> SearchFilesResponse:
|
||||
@@ -369,6 +379,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("files:read")
|
||||
@instrument_tool
|
||||
async def nc_webdav_find_by_type(
|
||||
mime_type: str, ctx: Context, scope: str = "", limit: int | None = None
|
||||
) -> SearchFilesResponse:
|
||||
@@ -396,6 +407,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
|
||||
@mcp.tool()
|
||||
@require_scopes("files:read")
|
||||
@instrument_tool
|
||||
async def nc_webdav_list_favorites(
|
||||
ctx: Context, scope: str = "", limit: int | None = None
|
||||
) -> SearchFilesResponse:
|
||||
|
||||
Reference in New Issue
Block a user