diff --git a/sandbox/pyproject.toml b/sandbox/pyproject.toml index 029ae86..2d0a5e7 100644 --- a/sandbox/pyproject.toml +++ b/sandbox/pyproject.toml @@ -22,9 +22,9 @@ dependencies = [ [project.scripts] mcp-server-plaid = "mcp_server_plaid:main" -[tool.pytest] +[tool.pytest.ini_options] testpaths = ["src/mcp_server_plaid/test"] -python_files = "test_*.py" +python_files = ["test_*.py"] [project.optional-dependencies] test = [ diff --git a/sandbox/src/mcp_server_plaid/tools/tool_search_documentation.py b/sandbox/src/mcp_server_plaid/tools/tool_search_documentation.py index 4d1e526..47156de 100644 --- a/sandbox/src/mcp_server_plaid/tools/tool_search_documentation.py +++ b/sandbox/src/mcp_server_plaid/tools/tool_search_documentation.py @@ -33,12 +33,42 @@ ) +def _escape_md_brackets(text: str) -> str: + """Escape characters that would break a markdown link's title text.""" + return text.replace("\\", "\\\\").replace("[", "\\[").replace("]", "\\]") + + +def _format_sources(sources: List[Dict[str, Any]]) -> str: + """Format AskBill source metadata as a deduplicated markdown bullet list.""" + seen_urls = set() + lines = [] + for source in sources: + url = source.get("url", "") + title = source.get("title", "") or url + if url and url in seen_urls: + continue + if url: + seen_urls.add(url) + if title != url: + lines.append(f"- [{_escape_md_brackets(title)}](<{url}>)") + else: + lines.append(f"- <{url}>") + elif title: + lines.append(f"- {_escape_md_brackets(title)}") + return "\n".join(lines) + + # Tool handler async def handle_search_documentation( arguments: Dict[str, Any], *, bill_client: AskBillClient, **_ ) -> List[types.TextContent | types.ImageContent | types.EmbeddedResource]: response = await bill_client.ask_question(question=arguments["question"]) - return [types.TextContent(type="text", text=str(response["answer"]))] + answer = str(response["answer"]) + sources = response.get("sources") or [] + formatted_sources = _format_sources(sources) + if formatted_sources: + answer = f"{answer.rstrip()}\n\n## Sources\n{formatted_sources}" + return [types.TextContent(type="text", text=answer)] registry.register(SEARCH_DOCUMENTATION_TOOL, handle_search_documentation)