Add members, member detail, RFH suggestions and RFH stats tools

- list_members: search network professionals by sector, country, job title
- get_member_detail: full profile with sectors and countries
- list_rfh_suggestions: suggestions for a RFH (requester sees all, recipient sees own)
- rfh_statistics: aggregated outgoing/incoming RFH metrics + top responding firms
- deal_statistics: add country and month group_by options
This commit is contained in:
Spaike 2026-05-07 23:07:30 +02:00
parent 20e241f925
commit b598338e3e
1 changed files with 148 additions and 2 deletions

150
server.py
View File

@ -209,8 +209,8 @@ async def list_tools() -> list[types.Tool]:
"properties": { "properties": {
"group_by": { "group_by": {
"type": "string", "type": "string",
"enum": ["stage", "industry", "deal_type", "year", "firm"], "enum": ["stage", "industry", "deal_type", "year", "month", "firm", "country"],
"description": "Dimension to group by. Default: 'stage'.", "description": "Dimension to group by. 'month' = per month (YYYY-MM). 'country' = target country. Default: 'stage'.",
"default": "stage", "default": "stage",
}, },
"year_from": { "year_from": {
@ -305,6 +305,123 @@ async def list_tools() -> list[types.Tool]:
), ),
inputSchema={"type": "object", "properties": {}, "required": []}, 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 (1100, 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") result = await _get("/firms")
return await respond(result) 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: else:
return [types.TextContent(type="text", text=f"Unknown tool: {name}")] return [types.TextContent(type="text", text=f"Unknown tool: {name}")]