diff --git a/server.py b/server.py index e14a742..502b4cb 100644 --- a/server.py +++ b/server.py @@ -209,8 +209,8 @@ async def list_tools() -> list[types.Tool]: "properties": { "group_by": { "type": "string", - "enum": ["stage", "industry", "deal_type", "year", "firm"], - "description": "Dimension to group by. Default: 'stage'.", + "enum": ["stage", "industry", "deal_type", "year", "month", "firm", "country"], + "description": "Dimension to group by. 'month' = per month (YYYY-MM). 'country' = target country. Default: 'stage'.", "default": "stage", }, "year_from": { @@ -305,6 +305,123 @@ async def list_tools() -> list[types.Tool]: ), inputSchema={"type": "object", "properties": {}, "required": []}, ), + + types.Tool( + name="list_members", + description=( + "Lists people (professionals) visible to the current user: members of their own firm " + "plus members of other firms that have at least one shared deal in the network.\n\n" + "Useful for questions like:\n" + "- 'Who covers Automotive deals in Germany?'\n" + "- 'List all partners in the network'\n" + "- 'Who are the directors at [FirmName]?'\n" + "- 'Find me someone who covers Healthcare in Italy'\n\n" + "Each member includes their firm, job title, city, sectors they cover, and countries." + ), + inputSchema={ + "type": "object", + "properties": { + "search": { + "type": "string", + "description": "Free-text search on name or email.", + }, + "firm_id": { + "type": "integer", + "description": "Filter by firm ID (use list_firms to get IDs).", + }, + "job_title": { + "type": "string", + "description": "Partial match on job title. E.g. 'Partner', 'Director', 'Associate'.", + }, + "staff_category": { + "type": "string", + "description": "Partial match on staff category. E.g. 'partners', 'directors', 'associates'.", + }, + "sector": { + "type": "string", + "description": "Filter by industry sector covered. E.g. 'Automotive', 'Healthcare', 'Technology'.", + }, + "country": { + "type": "string", + "description": "Filter by country of activity. E.g. 'Italy', 'Germany', 'France'.", + }, + "limit": { + "type": "integer", + "description": "Max results (1–100, default 30).", + "default": 30, + }, + "offset": { + "type": "integer", + "description": "Pagination offset (default 0).", + "default": 0, + }, + }, + }, + ), + + types.Tool( + name="get_member_detail", + description=( + "Returns the full profile of a single network member by their ID, including " + "job title, city, phone numbers, firm, all sectors covered, countries of activity, " + "and their professional summary." + ), + inputSchema={ + "type": "object", + "properties": { + "member_id": { + "type": "integer", + "description": "The numeric ID of the member (from list_members).", + } + }, + "required": ["member_id"], + }, + ), + + types.Tool( + name="list_rfh_suggestions", + description=( + "Returns all suggestions (buy-side leads / candidates) submitted for a specific " + "Request for Help (RFH).\n\n" + "If the RFH was created by YOUR firm: you see all suggestions from all responding firms.\n" + "If it's an incoming RFH: you see only your firm's own submissions.\n\n" + "Each suggestion includes: suggesting firm, status (New/Go/No Go/Read), " + "whether it was selected, company info, financials (Turnover, EBITDA), and reasoning.\n\n" + "Use list_my_requests_for_help or list_incoming_requests to find RFH IDs first." + ), + inputSchema={ + "type": "object", + "properties": { + "rfh_id": { + "type": "integer", + "description": "The ID of the Request for Help (IdRequestForHelp).", + } + }, + "required": ["rfh_id"], + }, + ), + + types.Tool( + name="rfh_statistics", + description=( + "Returns aggregated statistics on Requests for Help (RFH) activity for the current firm.\n\n" + "Covers two dimensions:\n" + "1. **Outgoing RFH** (requests my firm sent): how many, open vs closed, " + "how many received no response, average suggestions received, " + "breakdown by suggestion status (Go/No Go/New/Read), selected suggestions, " + "and which firms in the network respond most actively.\n\n" + "2. **Incoming RFH** (requests from other firms targeting my firm): " + "how many we've been asked, how many we haven't answered yet, " + "our response rate, average days to respond, " + "and breakdown of our suggestions by status.\n\n" + "Example questions:\n" + "- 'How are our Requests for Help performing?'\n" + "- 'Which firms send us the most suggestions?'\n" + "- 'How many incoming RFH have we not answered yet?'\n" + "- 'What is our average response time on incoming requests?'" + ), + inputSchema={"type": "object", "properties": {}, "required": []}, + ), ] @@ -410,6 +527,35 @@ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]: result = await _get("/firms") return await respond(result) + # ------------------------------------------------------------------ + elif name == "list_members": + params: dict[str, Any] = {} + for key in ("search", "job_title", "staff_category", "sector", "country"): + if arguments.get(key): + params[key] = arguments[key] + for key in ("firm_id", "limit", "offset"): + if arguments.get(key) is not None: + params[key] = int(arguments[key]) + result = await _get("/members", params) + return await respond(result) + + # ------------------------------------------------------------------ + elif name == "get_member_detail": + member_id = int(arguments["member_id"]) + result = await _get(f"/member/{member_id}") + return await respond(result) + + # ------------------------------------------------------------------ + elif name == "list_rfh_suggestions": + rfh_id = int(arguments["rfh_id"]) + result = await _get(f"/rfh/{rfh_id}/suggestions") + return await respond(result) + + # ------------------------------------------------------------------ + elif name == "rfh_statistics": + result = await _get("/rfh-stats") + return await respond(result) + # ------------------------------------------------------------------ else: return [types.TextContent(type="text", text=f"Unknown tool: {name}")]