diff --git a/services/library-desk/src/models/tools.py b/services/library-desk/src/models/tools.py new file mode 100644 index 0000000..42079bf --- /dev/null +++ b/services/library-desk/src/models/tools.py @@ -0,0 +1,61 @@ +""" +Tool catalog models for Library Desk. + +Provides simplified tool definitions optimized for AI agent consumption. +""" + +from pydantic import BaseModel, Field +from typing import List, Dict, Any, Optional +from enum import Enum + + +class ParameterType(str, Enum): + """Parameter data types.""" + STRING = "string" + INTEGER = "integer" + BOOLEAN = "boolean" + ARRAY = "array" + OBJECT = "object" + + +class ToolParameter(BaseModel): + """Tool parameter definition.""" + name: str = Field(..., description="Parameter name") + type: ParameterType = Field(..., description="Parameter type") + description: str = Field(..., description="Parameter description") + required: bool = Field(default=False, description="Whether parameter is required") + default: Optional[Any] = Field(default=None, description="Default value if not required") + example: Optional[Any] = Field(default=None, description="Example value") + + +class ToolDefinition(BaseModel): + """Individual tool definition.""" + name: str = Field(..., description="Tool identifier (e.g., 'wiki_create_page')") + category: str = Field(..., description="Tool category (e.g., 'wiki', 'graph')") + description: str = Field(..., description="What this tool does") + method: str = Field(..., description="HTTP method (GET, POST, PUT, DELETE)") + endpoint: str = Field(..., description="API endpoint path") + parameters: List[ToolParameter] = Field(default_factory=list, description="Tool parameters") + returns: str = Field(..., description="What the tool returns") + example: Optional[Dict[str, Any]] = Field(default=None, description="Example request") + fast: bool = Field(default=True, description="Whether operation completes quickly (<5s)") + + +class CategoryInfo(BaseModel): + """Tool category information.""" + name: str = Field(..., description="Category name") + description: str = Field(..., description="Category description") + tool_count: int = Field(..., description="Number of tools in category") + + +class ToolCatalog(BaseModel): + """Complete tool catalog response.""" + service: str = Field(default="library-desk", description="Service name") + version: str = Field(default="1.0.0", description="API version") + base_url: str = Field(..., description="Base URL for API") + categories: List[CategoryInfo] = Field(..., description="Available categories") + tools: List[ToolDefinition] = Field(..., description="All available tools") + authentication: str = Field( + default="Bearer token via Authorization header", + description="Authentication method" + ) diff --git a/services/library-desk/src/routers/tools.py b/services/library-desk/src/routers/tools.py new file mode 100644 index 0000000..bc7cd8a --- /dev/null +++ b/services/library-desk/src/routers/tools.py @@ -0,0 +1,334 @@ +""" +Tools router for Library Desk API. + +Provides tool discovery endpoint for AI agents. +""" + +from fastapi import APIRouter +from src.models.tools import ( + ToolCatalog, ToolDefinition, ToolParameter, CategoryInfo, ParameterType +) + +router = APIRouter(prefix="/tools", tags=["Tools"]) + + +def get_wiki_tools() -> list[ToolDefinition]: + """Get wiki tool definitions.""" + return [ + ToolDefinition( + name="wiki_list_pages", + category="wiki", + description="List wiki pages for a user with optional tag filtering", + method="GET", + endpoint="/wiki/pages", + parameters=[ + ToolParameter( + name="user", + type=ParameterType.STRING, + description="User identifier", + required=False, + default="jpmschweitzer", + example="jpmschweitzer" + ), + ToolParameter( + name="tag", + type=ParameterType.STRING, + description="Filter by tag (dossier)", + required=False, + example="projects" + ), + ToolParameter( + name="limit", + type=ParameterType.INTEGER, + description="Maximum pages to return", + required=False, + default=50, + example=20 + ), + ], + returns="List of pages with summaries", + fast=True + ), + ToolDefinition( + name="wiki_get_page", + category="wiki", + description="Get a single wiki page by ID with full content", + method="GET", + endpoint="/wiki/pages/{page_id}", + parameters=[ + ToolParameter( + name="page_id", + type=ParameterType.INTEGER, + description="Page ID to retrieve", + required=True, + example=123 + ), + ToolParameter( + name="user", + type=ParameterType.STRING, + description="User identifier for access control", + required=False, + default="jpmschweitzer" + ), + ], + returns="Complete page object with content", + fast=True + ), + ToolDefinition( + name="wiki_create_page", + category="wiki", + description="Create a new wiki page in user's namespace", + method="POST", + endpoint="/wiki/pages", + parameters=[ + ToolParameter( + name="title", + type=ParameterType.STRING, + description="Page title", + required=True, + example="Project Documentation" + ), + ToolParameter( + name="path", + type=ParameterType.STRING, + description="Page path (will be prefixed with user namespace)", + required=True, + example="/projects/my-project" + ), + ToolParameter( + name="content", + type=ParameterType.STRING, + description="Page content in Markdown format", + required=True, + example="# Overview\n\nThis is the content." + ), + ToolParameter( + name="description", + type=ParameterType.STRING, + description="Short page description", + required=False, + example="Documentation for my project" + ), + ToolParameter( + name="tags", + type=ParameterType.ARRAY, + description="Tags for categorization (dossiers)", + required=False, + example=["projects", "documentation"] + ), + ToolParameter( + name="user", + type=ParameterType.STRING, + description="User identifier", + required=False, + default="jpmschweitzer" + ), + ], + returns="Created page object", + example={ + "title": "My Project", + "path": "/projects/my-project", + "content": "# My Project\n\nProject description here.", + "tags": ["projects"], + "user": "jpmschweitzer" + }, + fast=True + ), + ToolDefinition( + name="wiki_update_page", + category="wiki", + description="Update an existing wiki page", + method="PUT", + endpoint="/wiki/pages/{page_id}", + parameters=[ + ToolParameter( + name="page_id", + type=ParameterType.INTEGER, + description="Page ID to update", + required=True, + example=123 + ), + ToolParameter( + name="title", + type=ParameterType.STRING, + description="New page title", + required=False + ), + ToolParameter( + name="content", + type=ParameterType.STRING, + description="New page content", + required=False + ), + ToolParameter( + name="description", + type=ParameterType.STRING, + description="New description", + required=False + ), + ToolParameter( + name="tags", + type=ParameterType.ARRAY, + description="New tags", + required=False + ), + ToolParameter( + name="user", + type=ParameterType.STRING, + description="User identifier", + required=False, + default="jpmschweitzer" + ), + ], + returns="Updated page object", + fast=True + ), + ToolDefinition( + name="wiki_delete_page", + category="wiki", + description="Delete a wiki page", + method="DELETE", + endpoint="/wiki/pages/{page_id}", + parameters=[ + ToolParameter( + name="page_id", + type=ParameterType.INTEGER, + description="Page ID to delete", + required=True, + example=123 + ), + ToolParameter( + name="user", + type=ParameterType.STRING, + description="User identifier", + required=False, + default="jpmschweitzer" + ), + ], + returns="Success confirmation", + fast=True + ), + ToolDefinition( + name="wiki_search_pages", + category="wiki", + description="Search wiki pages by content within user's namespace", + method="GET", + endpoint="/wiki/search", + parameters=[ + ToolParameter( + name="q", + type=ParameterType.STRING, + description="Search query", + required=True, + example="docker configuration" + ), + ToolParameter( + name="user", + type=ParameterType.STRING, + description="User identifier", + required=False, + default="jpmschweitzer" + ), + ToolParameter( + name="limit", + type=ParameterType.INTEGER, + description="Maximum results", + required=False, + default=20 + ), + ], + returns="List of matching pages", + fast=True + ), + ToolDefinition( + name="wiki_list_dossiers", + category="wiki", + description="List all dossiers (unique tags) for a user with page counts", + method="GET", + endpoint="/wiki/dossiers", + parameters=[ + ToolParameter( + name="user", + type=ParameterType.STRING, + description="User identifier", + required=False, + default="jpmschweitzer" + ), + ], + returns="List of dossiers with page counts", + fast=True + ), + ToolDefinition( + name="wiki_get_dossier_pages", + category="wiki", + description="Get all pages in a specific dossier", + method="GET", + endpoint="/wiki/dossiers/{dossier_name}/pages", + parameters=[ + ToolParameter( + name="dossier_name", + type=ParameterType.STRING, + description="Dossier name (tag)", + required=True, + example="projects" + ), + ToolParameter( + name="user", + type=ParameterType.STRING, + description="User identifier", + required=False, + default="jpmschweitzer" + ), + ToolParameter( + name="limit", + type=ParameterType.INTEGER, + description="Maximum pages", + required=False, + default=100 + ), + ], + returns="List of pages in dossier", + fast=True + ), + ] + + +@router.get("", response_model=ToolCatalog) +async def get_tool_catalog() -> ToolCatalog: + """ + Get simplified tool catalog for AI agent consumption. + + This endpoint provides a machine-readable catalog of all Library Desk tools, + optimized for discovery and use by AI agents like The Librarian. + + Returns tool definitions with: + - Clear descriptions + - Parameter specifications + - Usage examples + - Performance characteristics + """ + wiki_tools = get_wiki_tools() + + # Calculate category stats + categories = {} + for tool in wiki_tools: + if tool.category not in categories: + categories[tool.category] = 0 + categories[tool.category] += 1 + + category_info = [ + CategoryInfo( + name="wiki", + description="Wiki.js page and dossier management operations", + tool_count=categories.get("wiki", 0) + ) + ] + + return ToolCatalog( + service="library-desk", + version="1.0.0", + base_url="http://library-desk:8089", + categories=category_info, + tools=wiki_tools, + authentication="Bearer token via Authorization header (LIBRARY_API_KEY)" + )