feat(mcp): add MCP server with availability tool
parent
bb5ce5e71a
commit
1b23493167
@ -0,0 +1,3 @@
|
||||
from app.mcp.server import mcp
|
||||
|
||||
__all__ = ["mcp"]
|
||||
@ -0,0 +1,19 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
from app.mcp.tools import check_availability as check_availability_impl
|
||||
|
||||
mcp = FastMCP(
|
||||
"Personal Agent MCP",
|
||||
streamable_http_path="/",
|
||||
)
|
||||
|
||||
|
||||
@mcp.tool(description="Check Google Calendar availability for a time range.")
|
||||
def check_availability(
|
||||
start: str,
|
||||
end: str,
|
||||
calendar_ids: list[str] | None = None,
|
||||
) -> dict[str, object]:
|
||||
return check_availability_impl(start=start, end=end, calendar_ids=calendar_ids)
|
||||
@ -0,0 +1,31 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from app.config import get_settings
|
||||
from app.core.service import CoreAgentService
|
||||
|
||||
settings = get_settings()
|
||||
core_service = CoreAgentService(settings=settings, logger=logging.getLogger("personal-agent.mcp"))
|
||||
|
||||
|
||||
def check_availability(
|
||||
start: str, end: str, calendar_ids: list[str] | None = None
|
||||
) -> dict[str, Any]:
|
||||
"""Return free/busy availability for a time range on one or more calendars."""
|
||||
result = core_service.check_availability(start=start, end=end, calendar_ids=calendar_ids)
|
||||
return {
|
||||
"start": result.start,
|
||||
"end": result.end,
|
||||
"available": result.available,
|
||||
"busy_slots": [
|
||||
{
|
||||
"calendar_id": slot.calendar_id,
|
||||
"start": slot.start,
|
||||
"end": slot.end,
|
||||
}
|
||||
for slot in result.busy_slots
|
||||
],
|
||||
"checked_calendars": result.checked_calendars,
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from starlette.applications import Starlette
|
||||
from starlette.routing import Mount
|
||||
|
||||
from app.mcp import mcp
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(_: Starlette):
|
||||
async with mcp.session_manager.run():
|
||||
yield
|
||||
|
||||
|
||||
app = Starlette(
|
||||
routes=[
|
||||
Mount("/mcp", app=mcp.streamable_http_app()),
|
||||
],
|
||||
lifespan=lifespan,
|
||||
)
|
||||
Loading…
Reference in New Issue