refactor: change OAuth scope separator from colon to dot for IDP compatibility
Many identity providers (AWS Cognito, Okta, Azure AD) reject or mishandle colons in OAuth scope names. This migrates all custom scopes from `resource:action` to `resource.action` format (e.g., `notes:read` → `notes.read`), which is universally accepted and aligns with industry conventions (Microsoft, Google). Includes Alembic migration 004 for stored scope strings and ADR-024 documenting the rationale and RFC references. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
899b9c7191
commit
29fd0486c9
@@ -56,7 +56,7 @@ def register_auth_tools(mcp: FastMCP) -> None:
|
||||
|
||||
Args:
|
||||
ctx: MCP context
|
||||
scopes: Requested application scopes (e.g. ["notes:read", "calendar:write"]).
|
||||
scopes: Requested application scopes (e.g. ["notes.read", "calendar.write"]).
|
||||
If not specified, all available scopes are requested.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -55,7 +55,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
title="List Calendars",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("calendar:read")
|
||||
@require_scopes("calendar.read")
|
||||
@instrument_tool
|
||||
async def nc_calendar_list_calendars(ctx: Context) -> ListCalendarsResponse:
|
||||
"""List all available calendars for the user"""
|
||||
@@ -69,7 +69,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
title="Create Calendar Event",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("calendar:write")
|
||||
@require_scopes("calendar.write")
|
||||
@instrument_tool
|
||||
async def nc_calendar_create_event(
|
||||
calendar_name: str,
|
||||
@@ -149,7 +149,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
title="List Calendar Events",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("calendar:read")
|
||||
@require_scopes("calendar.read")
|
||||
@instrument_tool
|
||||
async def nc_calendar_list_events(
|
||||
calendar_name: str,
|
||||
@@ -269,7 +269,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
title="Get Calendar Event",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("calendar:read")
|
||||
@require_scopes("calendar.read")
|
||||
@instrument_tool
|
||||
async def nc_calendar_get_event(
|
||||
calendar_name: str,
|
||||
@@ -285,7 +285,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
title="Update Calendar Event",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("calendar:write")
|
||||
@require_scopes("calendar.write")
|
||||
@instrument_tool
|
||||
async def nc_calendar_update_event(
|
||||
calendar_name: str,
|
||||
@@ -364,7 +364,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
destructiveHint=True, idempotentHint=True, openWorldHint=True
|
||||
),
|
||||
)
|
||||
@require_scopes("calendar:write")
|
||||
@require_scopes("calendar.write")
|
||||
@instrument_tool
|
||||
async def nc_calendar_delete_event(
|
||||
calendar_name: str,
|
||||
@@ -379,7 +379,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
title="Create Meeting",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("calendar:write")
|
||||
@require_scopes("calendar.write")
|
||||
@instrument_tool
|
||||
async def nc_calendar_create_meeting(
|
||||
title: str,
|
||||
@@ -449,7 +449,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
title="Get Upcoming Events",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("calendar:read")
|
||||
@require_scopes("calendar.read")
|
||||
@instrument_tool
|
||||
async def nc_calendar_get_upcoming_events(
|
||||
ctx: Context,
|
||||
@@ -512,7 +512,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
title="Find Availability",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("calendar:read")
|
||||
@require_scopes("calendar.read")
|
||||
@instrument_tool
|
||||
async def nc_calendar_find_availability(
|
||||
duration_minutes: int,
|
||||
@@ -596,7 +596,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
title="Bulk Calendar Operations",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("calendar:write")
|
||||
@require_scopes("calendar.write")
|
||||
@instrument_tool
|
||||
async def nc_calendar_bulk_operations(
|
||||
operation: str, # "update", "delete", "move"
|
||||
@@ -849,7 +849,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
title="Manage Calendar",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("calendar:write")
|
||||
@require_scopes("calendar.write")
|
||||
@instrument_tool
|
||||
async def nc_calendar_manage_calendar(
|
||||
action: str, # "create", "delete", "update", "list"
|
||||
@@ -922,7 +922,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
title="List Todo Tasks",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("todo:read", "calendar:read")
|
||||
@require_scopes("todo.read", "calendar.read")
|
||||
@instrument_tool
|
||||
async def nc_calendar_list_todos(
|
||||
calendar_name: str,
|
||||
@@ -971,7 +971,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
title="Create Todo Task",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("todo:write", "calendar:read")
|
||||
@require_scopes("todo.write", "calendar.read")
|
||||
@instrument_tool
|
||||
async def nc_calendar_create_todo(
|
||||
calendar_name: str,
|
||||
@@ -1018,7 +1018,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
title="Update Todo Task",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("todo:write", "calendar:read")
|
||||
@require_scopes("todo.write", "calendar.read")
|
||||
@instrument_tool
|
||||
async def nc_calendar_update_todo(
|
||||
calendar_name: str,
|
||||
@@ -1084,7 +1084,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
destructiveHint=True, idempotentHint=True, openWorldHint=True
|
||||
),
|
||||
)
|
||||
@require_scopes("todo:write", "calendar:read")
|
||||
@require_scopes("todo.write", "calendar.read")
|
||||
@instrument_tool
|
||||
async def nc_calendar_delete_todo(
|
||||
calendar_name: str,
|
||||
@@ -1108,7 +1108,7 @@ def configure_calendar_tools(mcp: FastMCP):
|
||||
title="Search Todo Tasks",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("todo:read", "calendar:read")
|
||||
@require_scopes("todo.read", "calendar.read")
|
||||
@instrument_tool
|
||||
async def nc_calendar_search_todos(
|
||||
ctx: Context,
|
||||
|
||||
@@ -48,7 +48,7 @@ def configure_collectives_tools(mcp: FastMCP):
|
||||
title="List Collectives",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("collectives:read")
|
||||
@require_scopes("collectives.read")
|
||||
@instrument_tool
|
||||
async def collectives_get_collectives(
|
||||
ctx: Context,
|
||||
@@ -66,7 +66,7 @@ def configure_collectives_tools(mcp: FastMCP):
|
||||
title="List Collective Pages",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("collectives:read")
|
||||
@require_scopes("collectives.read")
|
||||
@instrument_tool
|
||||
async def collectives_get_pages(
|
||||
ctx: Context, collective_id: int
|
||||
@@ -90,7 +90,7 @@ def configure_collectives_tools(mcp: FastMCP):
|
||||
title="Get Collective Page",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("collectives:read")
|
||||
@require_scopes("collectives.read")
|
||||
@instrument_tool
|
||||
async def collectives_get_page(
|
||||
ctx: Context, collective_id: int, page_id: int
|
||||
@@ -138,7 +138,7 @@ def configure_collectives_tools(mcp: FastMCP):
|
||||
title="Search Collective Pages",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("collectives:read")
|
||||
@require_scopes("collectives.read")
|
||||
@instrument_tool
|
||||
async def collectives_search_pages(
|
||||
ctx: Context, collective_id: int, query: str
|
||||
@@ -166,7 +166,7 @@ def configure_collectives_tools(mcp: FastMCP):
|
||||
title="List Collective Tags",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("collectives:read")
|
||||
@require_scopes("collectives.read")
|
||||
@instrument_tool
|
||||
async def collectives_get_tags(
|
||||
ctx: Context, collective_id: int
|
||||
@@ -188,7 +188,7 @@ def configure_collectives_tools(mcp: FastMCP):
|
||||
title="List Trashed Collective Pages",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("collectives:read")
|
||||
@require_scopes("collectives.read")
|
||||
@instrument_tool
|
||||
async def collectives_get_trashed_pages(
|
||||
ctx: Context, collective_id: int
|
||||
@@ -212,7 +212,7 @@ def configure_collectives_tools(mcp: FastMCP):
|
||||
title="List Trashed Collectives",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("collectives:read")
|
||||
@require_scopes("collectives.read")
|
||||
@instrument_tool
|
||||
async def collectives_get_trashed_collectives(
|
||||
ctx: Context,
|
||||
@@ -238,7 +238,7 @@ def configure_collectives_tools(mcp: FastMCP):
|
||||
title="Create Collective",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("collectives:write")
|
||||
@require_scopes("collectives.write")
|
||||
@instrument_tool
|
||||
async def collectives_create_collective(
|
||||
ctx: Context, name: str, emoji: str | None = None
|
||||
@@ -263,7 +263,7 @@ def configure_collectives_tools(mcp: FastMCP):
|
||||
title="Set Collective Emoji",
|
||||
annotations=ToolAnnotations(idempotentHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("collectives:write")
|
||||
@require_scopes("collectives.write")
|
||||
@instrument_tool
|
||||
async def collectives_set_collective_emoji(
|
||||
ctx: Context, collective_id: int, emoji: str | None = None
|
||||
@@ -295,7 +295,7 @@ def configure_collectives_tools(mcp: FastMCP):
|
||||
title="Trash Collective",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("collectives:write")
|
||||
@require_scopes("collectives.write")
|
||||
@instrument_tool
|
||||
async def collectives_trash_collective(
|
||||
ctx: Context, collective_id: int
|
||||
@@ -324,7 +324,7 @@ def configure_collectives_tools(mcp: FastMCP):
|
||||
destructiveHint=True, idempotentHint=False, openWorldHint=True
|
||||
),
|
||||
)
|
||||
@require_scopes("collectives:write")
|
||||
@require_scopes("collectives.write")
|
||||
@instrument_tool
|
||||
async def collectives_delete_collective(
|
||||
ctx: Context, collective_id: int
|
||||
@@ -353,7 +353,7 @@ def configure_collectives_tools(mcp: FastMCP):
|
||||
title="Restore Collective",
|
||||
annotations=ToolAnnotations(idempotentHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("collectives:write")
|
||||
@require_scopes("collectives.write")
|
||||
@instrument_tool
|
||||
async def collectives_restore_collective(
|
||||
ctx: Context, collective_id: int
|
||||
@@ -379,7 +379,7 @@ def configure_collectives_tools(mcp: FastMCP):
|
||||
title="Create Collective Page",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("collectives:write")
|
||||
@require_scopes("collectives.write")
|
||||
@instrument_tool
|
||||
async def collectives_create_page(
|
||||
ctx: Context, collective_id: int, parent_id: int, title: str
|
||||
@@ -412,7 +412,7 @@ def configure_collectives_tools(mcp: FastMCP):
|
||||
title="Move Collective Page",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("collectives:write")
|
||||
@require_scopes("collectives.write")
|
||||
@instrument_tool
|
||||
async def collectives_move_page(
|
||||
ctx: Context,
|
||||
@@ -453,7 +453,7 @@ def configure_collectives_tools(mcp: FastMCP):
|
||||
title="Trash Collective Page",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("collectives:write")
|
||||
@require_scopes("collectives.write")
|
||||
@instrument_tool
|
||||
async def collectives_trash_page(
|
||||
ctx: Context, collective_id: int, page_id: int
|
||||
@@ -484,7 +484,7 @@ def configure_collectives_tools(mcp: FastMCP):
|
||||
title="Restore Collective Page",
|
||||
annotations=ToolAnnotations(idempotentHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("collectives:write")
|
||||
@require_scopes("collectives.write")
|
||||
@instrument_tool
|
||||
async def collectives_restore_page(
|
||||
ctx: Context, collective_id: int, page_id: int
|
||||
@@ -512,7 +512,7 @@ def configure_collectives_tools(mcp: FastMCP):
|
||||
title="Set Collective Page Emoji",
|
||||
annotations=ToolAnnotations(idempotentHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("collectives:write")
|
||||
@require_scopes("collectives.write")
|
||||
@instrument_tool
|
||||
async def collectives_set_page_emoji(
|
||||
ctx: Context,
|
||||
@@ -544,7 +544,7 @@ def configure_collectives_tools(mcp: FastMCP):
|
||||
title="Create Collective Tag",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("collectives:write")
|
||||
@require_scopes("collectives.write")
|
||||
@instrument_tool
|
||||
async def collectives_create_tag(
|
||||
ctx: Context, collective_id: int, name: str, color: str
|
||||
@@ -568,7 +568,7 @@ def configure_collectives_tools(mcp: FastMCP):
|
||||
title="Assign Tag to Collective Page",
|
||||
annotations=ToolAnnotations(idempotentHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("collectives:write")
|
||||
@require_scopes("collectives.write")
|
||||
@instrument_tool
|
||||
async def collectives_assign_tag(
|
||||
ctx: Context, collective_id: int, page_id: int, tag_id: int
|
||||
@@ -596,7 +596,7 @@ def configure_collectives_tools(mcp: FastMCP):
|
||||
title="Remove Tag from Collective Page",
|
||||
annotations=ToolAnnotations(idempotentHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("collectives:write")
|
||||
@require_scopes("collectives.write")
|
||||
@instrument_tool
|
||||
async def collectives_remove_tag(
|
||||
ctx: Context, collective_id: int, page_id: int, tag_id: int
|
||||
|
||||
@@ -99,7 +99,7 @@ def configure_contacts_tools(mcp: FastMCP):
|
||||
title="List Address Books",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("contacts:read")
|
||||
@require_scopes("contacts.read")
|
||||
@instrument_tool
|
||||
async def nc_contacts_list_addressbooks(ctx: Context) -> ListAddressBooksResponse:
|
||||
"""List all addressbooks for the user."""
|
||||
@@ -123,7 +123,7 @@ def configure_contacts_tools(mcp: FastMCP):
|
||||
title="List Contacts",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("contacts:read")
|
||||
@require_scopes("contacts.read")
|
||||
@instrument_tool
|
||||
async def nc_contacts_list_contacts(
|
||||
ctx: Context, *, addressbook: str
|
||||
@@ -146,7 +146,7 @@ def configure_contacts_tools(mcp: FastMCP):
|
||||
title="Create Address Book",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("contacts:write")
|
||||
@require_scopes("contacts.write")
|
||||
@instrument_tool
|
||||
async def nc_contacts_create_addressbook(
|
||||
ctx: Context, *, name: str, display_name: str
|
||||
@@ -168,7 +168,7 @@ def configure_contacts_tools(mcp: FastMCP):
|
||||
destructiveHint=True, idempotentHint=True, openWorldHint=True
|
||||
),
|
||||
)
|
||||
@require_scopes("contacts:write")
|
||||
@require_scopes("contacts.write")
|
||||
@instrument_tool
|
||||
async def nc_contacts_delete_addressbook(ctx: Context, *, name: str):
|
||||
"""Delete an addressbook."""
|
||||
@@ -179,7 +179,7 @@ def configure_contacts_tools(mcp: FastMCP):
|
||||
title="Create Contact",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("contacts:write")
|
||||
@require_scopes("contacts.write")
|
||||
@instrument_tool
|
||||
async def nc_contacts_create_contact(
|
||||
ctx: Context, *, addressbook: str, uid: str, contact_data: dict
|
||||
@@ -204,7 +204,7 @@ def configure_contacts_tools(mcp: FastMCP):
|
||||
destructiveHint=True, idempotentHint=True, openWorldHint=True
|
||||
),
|
||||
)
|
||||
@require_scopes("contacts:write")
|
||||
@require_scopes("contacts.write")
|
||||
@instrument_tool
|
||||
async def nc_contacts_delete_contact(ctx: Context, *, addressbook: str, uid: str):
|
||||
"""Delete a contact.
|
||||
@@ -222,7 +222,7 @@ def configure_contacts_tools(mcp: FastMCP):
|
||||
title="Update Contact",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("contacts:write")
|
||||
@require_scopes("contacts.write")
|
||||
@instrument_tool
|
||||
async def nc_contacts_update_contact(
|
||||
ctx: Context, *, addressbook: str, uid: str, contact_data: dict, etag: str = ""
|
||||
|
||||
@@ -75,7 +75,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
title="Import Recipe from URL",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("cookbook:write")
|
||||
@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.
|
||||
@@ -136,7 +136,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
title="List Recipes",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("cookbook:read")
|
||||
@require_scopes("cookbook.read")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_list_recipes(ctx: Context) -> ListRecipesResponse:
|
||||
"""Get all recipes in the database"""
|
||||
@@ -165,7 +165,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
title="Get Recipe",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("cookbook:read")
|
||||
@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"""
|
||||
@@ -194,7 +194,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
title="Create Recipe",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("cookbook:write")
|
||||
@require_scopes("cookbook.write")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_create_recipe(
|
||||
name: str,
|
||||
@@ -277,7 +277,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
title="Update Recipe",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("cookbook:write")
|
||||
@require_scopes("cookbook.write")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_update_recipe(
|
||||
recipe_id: int,
|
||||
@@ -372,7 +372,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
destructiveHint=True, idempotentHint=True, openWorldHint=True
|
||||
),
|
||||
)
|
||||
@require_scopes("cookbook:write")
|
||||
@require_scopes("cookbook.write")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_delete_recipe(
|
||||
recipe_id: int, ctx: Context
|
||||
@@ -411,7 +411,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
title="Search Recipes",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("cookbook:read")
|
||||
@require_scopes("cookbook.read")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_search_recipes(
|
||||
query: str, ctx: Context
|
||||
@@ -451,7 +451,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
title="List Recipe Categories",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("cookbook:read")
|
||||
@require_scopes("cookbook.read")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_list_categories(ctx: Context) -> ListCategoriesResponse:
|
||||
"""Get all known categories.
|
||||
@@ -482,7 +482,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
title="Get Recipes in Category",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("cookbook:read")
|
||||
@require_scopes("cookbook.read")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_get_recipes_in_category(
|
||||
category: str, ctx: Context
|
||||
@@ -522,7 +522,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
title="List Recipe Keywords",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("cookbook:read")
|
||||
@require_scopes("cookbook.read")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_list_keywords(ctx: Context) -> ListKeywordsResponse:
|
||||
"""Get all known keywords/tags"""
|
||||
@@ -551,7 +551,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
title="Get Recipes with Keywords",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("cookbook:read")
|
||||
@require_scopes("cookbook.read")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_get_recipes_with_keywords(
|
||||
keywords: list[str], ctx: Context
|
||||
@@ -589,7 +589,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
title="Set Cookbook Configuration",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("cookbook:write")
|
||||
@require_scopes("cookbook.write")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_set_config(
|
||||
folder: str | None = None,
|
||||
@@ -636,7 +636,7 @@ def configure_cookbook_tools(mcp: FastMCP):
|
||||
title="Reindex Recipes",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("cookbook:write")
|
||||
@require_scopes("cookbook.write")
|
||||
@instrument_tool
|
||||
async def nc_cookbook_reindex(ctx: Context) -> ReindexResponse:
|
||||
"""Trigger a rescan of all recipes into the caching database.
|
||||
|
||||
@@ -126,7 +126,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
title="List Deck Boards",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("deck:read")
|
||||
@require_scopes("deck.read")
|
||||
@instrument_tool
|
||||
async def deck_get_boards(ctx: Context) -> ListBoardsResponse:
|
||||
"""Get all Nextcloud Deck boards"""
|
||||
@@ -138,7 +138,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
title="Get Deck Board",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("deck:read")
|
||||
@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"""
|
||||
@@ -150,7 +150,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
title="List Deck Stacks",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("deck:read")
|
||||
@require_scopes("deck.read")
|
||||
@instrument_tool
|
||||
async def deck_get_stacks(ctx: Context, board_id: int) -> ListStacksResponse:
|
||||
"""Get all stacks in a Nextcloud Deck board"""
|
||||
@@ -162,7 +162,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
title="Get Deck Stack",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("deck:read")
|
||||
@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"""
|
||||
@@ -174,7 +174,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
title="List Deck Cards",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("deck:read")
|
||||
@require_scopes("deck.read")
|
||||
@instrument_tool
|
||||
async def deck_get_cards(
|
||||
ctx: Context, board_id: int, stack_id: int
|
||||
@@ -189,7 +189,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
title="Get Deck Card",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("deck:read")
|
||||
@require_scopes("deck.read")
|
||||
@instrument_tool
|
||||
async def deck_get_card(
|
||||
ctx: Context, board_id: int, stack_id: int, card_id: int
|
||||
@@ -203,7 +203,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
title="List Deck Labels",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("deck:read")
|
||||
@require_scopes("deck.read")
|
||||
@instrument_tool
|
||||
async def deck_get_labels(ctx: Context, board_id: int) -> ListLabelsResponse:
|
||||
"""Get all labels in a Nextcloud Deck board"""
|
||||
@@ -216,7 +216,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
title="Get Deck Label",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("deck:read")
|
||||
@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"""
|
||||
@@ -230,7 +230,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
title="Create Deck Board",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("deck:write")
|
||||
@require_scopes("deck.write")
|
||||
@instrument_tool
|
||||
async def deck_create_board(
|
||||
ctx: Context, title: str, color: str
|
||||
@@ -251,7 +251,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
title="Create Deck Stack",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("deck:write")
|
||||
@require_scopes("deck.write")
|
||||
@instrument_tool
|
||||
async def deck_create_stack(
|
||||
ctx: Context, board_id: int, title: str, order: int
|
||||
@@ -271,7 +271,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
title="Update Deck Stack",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("deck:write")
|
||||
@require_scopes("deck.write")
|
||||
@instrument_tool
|
||||
async def deck_update_stack(
|
||||
ctx: Context,
|
||||
@@ -303,7 +303,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
destructiveHint=True, idempotentHint=True, openWorldHint=True
|
||||
),
|
||||
)
|
||||
@require_scopes("deck:write")
|
||||
@require_scopes("deck.write")
|
||||
@instrument_tool
|
||||
async def deck_delete_stack(
|
||||
ctx: Context, board_id: int, stack_id: int
|
||||
@@ -328,7 +328,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
title="Create Deck Card",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("deck:write")
|
||||
@require_scopes("deck.write")
|
||||
@instrument_tool
|
||||
async def deck_create_card(
|
||||
ctx: Context,
|
||||
@@ -366,7 +366,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
title="Update Deck Card",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("deck:write")
|
||||
@require_scopes("deck.write")
|
||||
@instrument_tool
|
||||
async def deck_update_card(
|
||||
ctx: Context,
|
||||
@@ -425,7 +425,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
destructiveHint=True, idempotentHint=True, openWorldHint=True
|
||||
),
|
||||
)
|
||||
@require_scopes("deck:write")
|
||||
@require_scopes("deck.write")
|
||||
@instrument_tool
|
||||
async def deck_delete_card(
|
||||
ctx: Context, board_id: int, stack_id: int, card_id: int
|
||||
@@ -451,7 +451,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
title="Archive Deck Card",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("deck:write")
|
||||
@require_scopes("deck.write")
|
||||
@instrument_tool
|
||||
async def deck_archive_card(
|
||||
ctx: Context, board_id: int, stack_id: int, card_id: int
|
||||
@@ -477,7 +477,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
title="Unarchive Deck Card",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("deck:write")
|
||||
@require_scopes("deck.write")
|
||||
@instrument_tool
|
||||
async def deck_unarchive_card(
|
||||
ctx: Context, board_id: int, stack_id: int, card_id: int
|
||||
@@ -503,7 +503,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
title="Reorder/Move Deck Card",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("deck:write")
|
||||
@require_scopes("deck.write")
|
||||
@instrument_tool
|
||||
async def deck_reorder_card(
|
||||
ctx: Context,
|
||||
@@ -539,7 +539,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
title="Create Deck Label",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("deck:write")
|
||||
@require_scopes("deck.write")
|
||||
@instrument_tool
|
||||
async def deck_create_label(
|
||||
ctx: Context, board_id: int, title: str, color: str
|
||||
@@ -559,7 +559,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
title="Update Deck Label",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("deck:write")
|
||||
@require_scopes("deck.write")
|
||||
@instrument_tool
|
||||
async def deck_update_label(
|
||||
ctx: Context,
|
||||
@@ -591,7 +591,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
destructiveHint=True, idempotentHint=True, openWorldHint=True
|
||||
),
|
||||
)
|
||||
@require_scopes("deck:write")
|
||||
@require_scopes("deck.write")
|
||||
@instrument_tool
|
||||
async def deck_delete_label(
|
||||
ctx: Context, board_id: int, label_id: int
|
||||
@@ -616,7 +616,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
title="Assign Label to Deck Card",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("deck:write")
|
||||
@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
|
||||
@@ -643,7 +643,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
title="Remove Label from Deck Card",
|
||||
annotations=ToolAnnotations(idempotentHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("deck:write")
|
||||
@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
|
||||
@@ -671,7 +671,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
title="Assign User to Deck Card",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("deck:write")
|
||||
@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
|
||||
@@ -700,7 +700,7 @@ def configure_deck_tools(mcp: FastMCP):
|
||||
destructiveHint=True, idempotentHint=True, openWorldHint=True
|
||||
),
|
||||
)
|
||||
@require_scopes("deck:write")
|
||||
@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
|
||||
|
||||
@@ -34,10 +34,10 @@ def configure_news_tools(mcp: FastMCP):
|
||||
title="List News Folders",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("news:read")
|
||||
@require_scopes("news.read")
|
||||
@instrument_tool
|
||||
async def nc_news_list_folders(ctx: Context) -> ListFoldersResponse:
|
||||
"""List all News folders (requires news:read scope)."""
|
||||
"""List all News folders (requires news.read scope)."""
|
||||
client = await get_client(ctx)
|
||||
try:
|
||||
folders_data = await client.news.get_folders()
|
||||
@@ -59,10 +59,10 @@ def configure_news_tools(mcp: FastMCP):
|
||||
title="List News Feeds",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("news:read")
|
||||
@require_scopes("news.read")
|
||||
@instrument_tool
|
||||
async def nc_news_list_feeds(ctx: Context) -> ListFeedsResponse:
|
||||
"""List all News feeds with metadata (requires news:read scope).
|
||||
"""List all News feeds with metadata (requires news.read scope).
|
||||
|
||||
Returns feeds with unread counts, error status, and overall starred count.
|
||||
"""
|
||||
@@ -92,7 +92,7 @@ def configure_news_tools(mcp: FastMCP):
|
||||
title="List News Items",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("news:read")
|
||||
@require_scopes("news.read")
|
||||
@instrument_tool
|
||||
async def nc_news_list_items(
|
||||
ctx: Context,
|
||||
@@ -103,7 +103,7 @@ def configure_news_tools(mcp: FastMCP):
|
||||
limit: int = 50,
|
||||
offset: int = 0,
|
||||
) -> ListItemsResponse:
|
||||
"""List News items (articles) with optional filtering (requires news:read scope).
|
||||
"""List News items (articles) with optional filtering (requires news.read scope).
|
||||
|
||||
Args:
|
||||
feed_id: Filter by specific feed ID
|
||||
@@ -166,10 +166,10 @@ def configure_news_tools(mcp: FastMCP):
|
||||
title="Get News Item",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("news:read")
|
||||
@require_scopes("news.read")
|
||||
@instrument_tool
|
||||
async def nc_news_get_item(item_id: int, ctx: Context) -> GetItemResponse:
|
||||
"""Get a specific News item by ID with full content (requires news:read scope).
|
||||
"""Get a specific News item by ID with full content (requires news.read scope).
|
||||
|
||||
Args:
|
||||
item_id: Item ID
|
||||
@@ -204,12 +204,12 @@ def configure_news_tools(mcp: FastMCP):
|
||||
title="Get Starred News Items",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("news:read")
|
||||
@require_scopes("news.read")
|
||||
@instrument_tool
|
||||
async def nc_news_get_starred_items(
|
||||
ctx: Context, limit: int = 50, offset: int = 0
|
||||
) -> ListItemsResponse:
|
||||
"""Get starred (favorited) News items (requires news:read scope).
|
||||
"""Get starred (favorited) News items (requires news.read scope).
|
||||
|
||||
Convenience method for retrieving user's starred articles.
|
||||
|
||||
@@ -257,12 +257,12 @@ def configure_news_tools(mcp: FastMCP):
|
||||
title="Get Unread News Items",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("news:read")
|
||||
@require_scopes("news.read")
|
||||
@instrument_tool
|
||||
async def nc_news_get_unread_items(
|
||||
ctx: Context, limit: int = 50, offset: int = 0
|
||||
) -> ListItemsResponse:
|
||||
"""Get unread News items (requires news:read scope).
|
||||
"""Get unread News items (requires news.read scope).
|
||||
|
||||
Convenience method for retrieving unread articles across all feeds.
|
||||
|
||||
@@ -310,10 +310,10 @@ def configure_news_tools(mcp: FastMCP):
|
||||
title="Get News Feed Health",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("news:read")
|
||||
@require_scopes("news.read")
|
||||
@instrument_tool
|
||||
async def nc_news_get_feed_health(feed_id: int, ctx: Context) -> FeedHealthResponse:
|
||||
"""Get health status for a specific feed (requires news:read scope).
|
||||
"""Get health status for a specific feed (requires news.read scope).
|
||||
|
||||
Returns error count and last error message if the feed has update issues.
|
||||
|
||||
@@ -357,10 +357,10 @@ def configure_news_tools(mcp: FastMCP):
|
||||
title="Get News App Status",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("news:read")
|
||||
@require_scopes("news.read")
|
||||
@instrument_tool
|
||||
async def nc_news_get_status(ctx: Context) -> GetStatusResponse:
|
||||
"""Get News app status and version (requires news:read scope).
|
||||
"""Get News app status and version (requires news.read scope).
|
||||
|
||||
Returns version information and any configuration warnings.
|
||||
"""
|
||||
|
||||
@@ -92,12 +92,12 @@ def configure_notes_tools(mcp: FastMCP):
|
||||
openWorldHint=True,
|
||||
),
|
||||
)
|
||||
@require_scopes("notes:write")
|
||||
@require_scopes("notes.write")
|
||||
@instrument_tool
|
||||
async def nc_notes_create_note(
|
||||
title: str, content: str, category: str, ctx: Context
|
||||
) -> CreateNoteResponse:
|
||||
"""Create a new note (requires notes:write scope)"""
|
||||
"""Create a new note (requires notes.write scope)"""
|
||||
client = await get_client(ctx)
|
||||
try:
|
||||
note_data = await client.notes.create_note(
|
||||
@@ -145,7 +145,7 @@ def configure_notes_tools(mcp: FastMCP):
|
||||
openWorldHint=True,
|
||||
),
|
||||
)
|
||||
@require_scopes("notes:write")
|
||||
@require_scopes("notes.write")
|
||||
@instrument_tool
|
||||
async def nc_notes_update_note(
|
||||
note_id: int,
|
||||
@@ -155,7 +155,7 @@ def configure_notes_tools(mcp: FastMCP):
|
||||
category: str | None,
|
||||
ctx: Context,
|
||||
) -> UpdateNoteResponse:
|
||||
"""Update an existing note's title, content, or category (requires notes:write scope).
|
||||
"""Update an existing note's title, content, or category (requires notes.write scope).
|
||||
|
||||
REQUIRED: etag parameter must be provided to prevent overwriting concurrent changes.
|
||||
Get the current ETag by first retrieving the note using nc_notes_get_note tool.
|
||||
@@ -217,7 +217,7 @@ def configure_notes_tools(mcp: FastMCP):
|
||||
openWorldHint=True,
|
||||
),
|
||||
)
|
||||
@require_scopes("notes:write")
|
||||
@require_scopes("notes.write")
|
||||
@instrument_tool
|
||||
async def nc_notes_append_content(
|
||||
note_id: int, content: str, ctx: Context
|
||||
@@ -274,10 +274,10 @@ def configure_notes_tools(mcp: FastMCP):
|
||||
openWorldHint=True,
|
||||
),
|
||||
)
|
||||
@require_scopes("notes:read")
|
||||
@require_scopes("notes.read")
|
||||
@instrument_tool
|
||||
async def nc_notes_search_notes(query: str, ctx: Context) -> SearchNotesResponse:
|
||||
"""Search notes by title or content, returning only id, title, and category (requires notes:read scope)."""
|
||||
"""Search notes by title or content, returning only id, title, and category (requires notes.read scope)."""
|
||||
client = await get_client(ctx)
|
||||
try:
|
||||
search_results_raw = await client.notes_search_notes(query=query)
|
||||
@@ -327,10 +327,10 @@ def configure_notes_tools(mcp: FastMCP):
|
||||
openWorldHint=True,
|
||||
),
|
||||
)
|
||||
@require_scopes("notes:read")
|
||||
@require_scopes("notes.read")
|
||||
@instrument_tool
|
||||
async def nc_notes_get_note(note_id: int, ctx: Context) -> Note:
|
||||
"""Get a specific note by its ID (requires notes:read scope)"""
|
||||
"""Get a specific note by its ID (requires notes.read scope)"""
|
||||
client = await get_client(ctx)
|
||||
try:
|
||||
note_data = await client.notes.get_note(note_id)
|
||||
@@ -363,7 +363,7 @@ def configure_notes_tools(mcp: FastMCP):
|
||||
openWorldHint=True,
|
||||
),
|
||||
)
|
||||
@require_scopes("notes:read")
|
||||
@require_scopes("notes.read")
|
||||
@instrument_tool
|
||||
async def nc_notes_get_attachment(
|
||||
note_id: int, attachment_filename: str, ctx: Context
|
||||
@@ -417,7 +417,7 @@ def configure_notes_tools(mcp: FastMCP):
|
||||
openWorldHint=True,
|
||||
),
|
||||
)
|
||||
@require_scopes("notes:write")
|
||||
@require_scopes("notes.write")
|
||||
@instrument_tool
|
||||
async def nc_notes_delete_note(note_id: int, ctx: Context) -> DeleteNoteResponse:
|
||||
"""Delete a note permanently"""
|
||||
|
||||
@@ -473,14 +473,14 @@ async def check_logged_in(ctx: Context, user_id: Optional[str] = None) -> str:
|
||||
"profile",
|
||||
"email",
|
||||
"offline_access", # Critical for background operations
|
||||
"notes:read",
|
||||
"notes:write",
|
||||
"calendar:read",
|
||||
"calendar:write",
|
||||
"contacts:read",
|
||||
"contacts:write",
|
||||
"files:read",
|
||||
"files:write",
|
||||
"notes.read",
|
||||
"notes.write",
|
||||
"calendar.read",
|
||||
"calendar.write",
|
||||
"contacts.read",
|
||||
"contacts.write",
|
||||
"files.read",
|
||||
"files.write",
|
||||
]
|
||||
|
||||
# Generate authorization URL
|
||||
|
||||
@@ -48,7 +48,7 @@ def configure_semantic_tools(mcp: FastMCP):
|
||||
openWorldHint=True, # Queries external Nextcloud service
|
||||
),
|
||||
)
|
||||
@require_scopes("semantic:read")
|
||||
@require_scopes("semantic.read")
|
||||
@instrument_tool
|
||||
async def nc_semantic_search(
|
||||
query: str,
|
||||
@@ -303,7 +303,7 @@ def configure_semantic_tools(mcp: FastMCP):
|
||||
openWorldHint=False, # Searches only indexed Nextcloud data
|
||||
),
|
||||
)
|
||||
@require_scopes("semantic:read")
|
||||
@require_scopes("semantic.read")
|
||||
@instrument_tool
|
||||
async def nc_semantic_search_answer(
|
||||
query: str,
|
||||
@@ -645,7 +645,7 @@ def configure_semantic_tools(mcp: FastMCP):
|
||||
openWorldHint=True,
|
||||
),
|
||||
)
|
||||
@require_scopes("semantic:read")
|
||||
@require_scopes("semantic.read")
|
||||
@instrument_tool
|
||||
async def nc_get_vector_sync_status(ctx: Context) -> VectorSyncStatusResponse:
|
||||
"""Get the current vector sync status.
|
||||
|
||||
@@ -21,7 +21,7 @@ def configure_sharing_tools(mcp: FastMCP):
|
||||
title="Create Share",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("sharing:write")
|
||||
@require_scopes("sharing.write")
|
||||
@instrument_tool
|
||||
async def nc_share_create(
|
||||
path: str,
|
||||
@@ -66,7 +66,7 @@ def configure_sharing_tools(mcp: FastMCP):
|
||||
destructiveHint=True, idempotentHint=True, openWorldHint=True
|
||||
),
|
||||
)
|
||||
@require_scopes("sharing:write")
|
||||
@require_scopes("sharing.write")
|
||||
@instrument_tool
|
||||
async def nc_share_delete(share_id: int, ctx: Context) -> str:
|
||||
"""Delete a share by its ID.
|
||||
@@ -89,7 +89,7 @@ def configure_sharing_tools(mcp: FastMCP):
|
||||
title="Get Share Details",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("sharing:write")
|
||||
@require_scopes("sharing.write")
|
||||
@instrument_tool
|
||||
async def nc_share_get(share_id: int, ctx: Context) -> str:
|
||||
"""Get information about a specific share.
|
||||
@@ -111,7 +111,7 @@ def configure_sharing_tools(mcp: FastMCP):
|
||||
title="List Shares",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("sharing:write")
|
||||
@require_scopes("sharing.write")
|
||||
@instrument_tool
|
||||
async def nc_share_list(
|
||||
ctx: Context, path: str | None = None, shared_with_me: bool = False
|
||||
@@ -136,7 +136,7 @@ def configure_sharing_tools(mcp: FastMCP):
|
||||
title="Update Share",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("sharing:write")
|
||||
@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.
|
||||
|
||||
@@ -17,7 +17,7 @@ def configure_tables_tools(mcp: FastMCP):
|
||||
title="List Tables",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("tables:read")
|
||||
@require_scopes("tables.read")
|
||||
@instrument_tool
|
||||
async def nc_tables_list_tables(ctx: Context) -> ListTablesResponse:
|
||||
"""List all tables available to the user"""
|
||||
@@ -30,7 +30,7 @@ def configure_tables_tools(mcp: FastMCP):
|
||||
title="Get Table Schema",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("tables:read")
|
||||
@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"""
|
||||
@@ -41,7 +41,7 @@ def configure_tables_tools(mcp: FastMCP):
|
||||
title="Read Table Rows",
|
||||
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("tables:read")
|
||||
@require_scopes("tables.read")
|
||||
@instrument_tool
|
||||
async def nc_tables_read_table(
|
||||
table_id: int,
|
||||
@@ -57,7 +57,7 @@ def configure_tables_tools(mcp: FastMCP):
|
||||
title="Insert Table Row",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("tables:write")
|
||||
@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.
|
||||
@@ -71,7 +71,7 @@ def configure_tables_tools(mcp: FastMCP):
|
||||
title="Update Table Row",
|
||||
annotations=ToolAnnotations(idempotentHint=False, openWorldHint=True),
|
||||
)
|
||||
@require_scopes("tables:write")
|
||||
@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.
|
||||
@@ -87,7 +87,7 @@ def configure_tables_tools(mcp: FastMCP):
|
||||
destructiveHint=True, idempotentHint=True, openWorldHint=True
|
||||
),
|
||||
)
|
||||
@require_scopes("tables:write")
|
||||
@require_scopes("tables.write")
|
||||
@instrument_tool
|
||||
async def nc_tables_delete_row(row_id: int, ctx: Context):
|
||||
"""Delete a row from a table"""
|
||||
|
||||
@@ -25,7 +25,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
openWorldHint=True,
|
||||
),
|
||||
)
|
||||
@require_scopes("files:read")
|
||||
@require_scopes("files.read")
|
||||
@instrument_tool
|
||||
async def nc_webdav_list_directory(
|
||||
ctx: Context, path: str = ""
|
||||
@@ -65,7 +65,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
openWorldHint=True,
|
||||
),
|
||||
)
|
||||
@require_scopes("files:read")
|
||||
@require_scopes("files.read")
|
||||
@instrument_tool
|
||||
async def nc_webdav_read_file(path: str, ctx: Context):
|
||||
"""Read the content of a file from NextCloud.
|
||||
@@ -137,7 +137,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
openWorldHint=True,
|
||||
),
|
||||
)
|
||||
@require_scopes("files:write")
|
||||
@require_scopes("files.write")
|
||||
@instrument_tool
|
||||
async def nc_webdav_write_file(
|
||||
path: str, content: str, ctx: Context, content_type: str | None = None
|
||||
@@ -170,7 +170,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
openWorldHint=True,
|
||||
),
|
||||
)
|
||||
@require_scopes("files:write")
|
||||
@require_scopes("files.write")
|
||||
@instrument_tool
|
||||
async def nc_webdav_create_directory(path: str, ctx: Context):
|
||||
"""Create a directory in NextCloud.
|
||||
@@ -192,7 +192,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
openWorldHint=True,
|
||||
),
|
||||
)
|
||||
@require_scopes("files:write")
|
||||
@require_scopes("files.write")
|
||||
@instrument_tool
|
||||
async def nc_webdav_delete_resource(path: str, ctx: Context):
|
||||
"""Delete a file or directory in NextCloud.
|
||||
@@ -213,7 +213,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
openWorldHint=True,
|
||||
),
|
||||
)
|
||||
@require_scopes("files:write")
|
||||
@require_scopes("files.write")
|
||||
@instrument_tool
|
||||
async def nc_webdav_move_resource(
|
||||
source_path: str, destination_path: str, ctx: Context, overwrite: bool = False
|
||||
@@ -240,7 +240,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
openWorldHint=True,
|
||||
),
|
||||
)
|
||||
@require_scopes("files:write")
|
||||
@require_scopes("files.write")
|
||||
@instrument_tool
|
||||
async def nc_webdav_copy_resource(
|
||||
source_path: str, destination_path: str, ctx: Context, overwrite: bool = False
|
||||
@@ -267,7 +267,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
openWorldHint=True,
|
||||
),
|
||||
)
|
||||
@require_scopes("files:read")
|
||||
@require_scopes("files.read")
|
||||
@instrument_tool
|
||||
async def nc_webdav_search_files(
|
||||
ctx: Context,
|
||||
@@ -390,7 +390,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
openWorldHint=True,
|
||||
),
|
||||
)
|
||||
@require_scopes("files:read")
|
||||
@require_scopes("files.read")
|
||||
@instrument_tool
|
||||
async def nc_webdav_find_by_name(
|
||||
pattern: str, ctx: Context, scope: str = "", limit: int | None = None
|
||||
@@ -424,7 +424,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
openWorldHint=True,
|
||||
),
|
||||
)
|
||||
@require_scopes("files:read")
|
||||
@require_scopes("files.read")
|
||||
@instrument_tool
|
||||
async def nc_webdav_find_by_type(
|
||||
mime_type: str, ctx: Context, scope: str = "", limit: int | None = None
|
||||
@@ -458,7 +458,7 @@ def configure_webdav_tools(mcp: FastMCP):
|
||||
openWorldHint=True,
|
||||
),
|
||||
)
|
||||
@require_scopes("files:read")
|
||||
@require_scopes("files.read")
|
||||
@instrument_tool
|
||||
async def nc_webdav_list_favorites(
|
||||
ctx: Context, scope: str = "", limit: int | None = None
|
||||
|
||||
Reference in New Issue
Block a user