c3023d2cc3
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.
99 lines
3.5 KiB
Python
99 lines
3.5 KiB
Python
import logging
|
|
|
|
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__)
|
|
|
|
|
|
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)
|
|
return await client.contacts.list_addressbooks()
|
|
|
|
@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)
|
|
return await client.contacts.list_contacts(addressbook=addressbook)
|
|
|
|
@mcp.tool()
|
|
@require_scopes("contacts:write")
|
|
@instrument_tool
|
|
async def nc_contacts_create_addressbook(
|
|
ctx: Context, *, name: str, display_name: str
|
|
):
|
|
"""Create a new addressbook.
|
|
|
|
Args:
|
|
name: The name of the addressbook.
|
|
display_name: The display name of the addressbook.
|
|
"""
|
|
client = await get_client(ctx)
|
|
return await client.contacts.create_addressbook(
|
|
name=name, display_name=display_name
|
|
)
|
|
|
|
@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)
|
|
return await client.contacts.delete_addressbook(name=name)
|
|
|
|
@mcp.tool()
|
|
@require_scopes("contacts:write")
|
|
@instrument_tool
|
|
async def nc_contacts_create_contact(
|
|
ctx: Context, *, addressbook: str, uid: str, contact_data: dict
|
|
):
|
|
"""Create a new contact.
|
|
|
|
Args:
|
|
addressbook: The name of the addressbook to create the contact in.
|
|
uid: The unique ID for the contact.
|
|
contact_data: A dictionary with the contact's details, e.g. {"fn": "John Doe", "email": "john.doe@example.com"}.
|
|
"""
|
|
client = await get_client(ctx)
|
|
return await client.contacts.create_contact(
|
|
addressbook=addressbook, uid=uid, contact_data=contact_data
|
|
)
|
|
|
|
@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)
|
|
return await client.contacts.delete_contact(addressbook=addressbook, uid=uid)
|
|
|
|
@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 = ""
|
|
):
|
|
"""Update an existing contact while preserving all existing properties.
|
|
|
|
Args:
|
|
addressbook: The name of the addressbook containing the contact.
|
|
uid: The unique ID of the contact to update.
|
|
contact_data: A dictionary with the contact's updated details, e.g. {"fn": "Jane Doe", "email": "jane.doe@example.com"}.
|
|
etag: Optional ETag for optimistic concurrency control.
|
|
"""
|
|
client = await get_client(ctx)
|
|
return await client.contacts.update_contact(
|
|
addressbook=addressbook, uid=uid, contact_data=contact_data, etag=etag
|
|
)
|