search for standard sites pub-search.waow.tech
search zig blog atproto
11
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: correct API URL and document content extraction

- fix backend URL to leaflet-search-backend.fly.dev
- update pdsx to latest for target_repo support
- extract content from leaflet's pages[].blocks[].block.plaintext structure
- handle DotDict by converting to dict before accessing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

zzstoatzz 78a4ddb3 89251682

+26 -29
+3
mcp/pyproject.toml
··· 69 69 70 70 [tool.ruff.lint.per-file-ignores] 71 71 "__init__.py" = ["F401", "I001"] 72 + 73 + [tool.uv.sources] 74 + pdsx = { git = "https://github.com/zzstoatzz/pdsx.git" }
+20 -8
mcp/src/leaflet_mcp/server.py
··· 141 141 """ 142 142 # use pdsx to fetch the actual record from ATProto 143 143 try: 144 - from pdsx._internal.client import create_client 145 144 from pdsx._internal.operations import get_record 145 + from pdsx.mcp.client import get_atproto_client 146 146 except ImportError as e: 147 147 raise RuntimeError( 148 148 "pdsx is required for fetching full documents. install with: uv add pdsx" ··· 156 156 157 157 repo = parts[0] 158 158 159 - async with create_client(repo=repo) as client: 159 + async with get_atproto_client(target_repo=repo) as client: 160 160 record = await get_record(client, uri) 161 161 162 162 value = record.value 163 - # handle both dict and model responses 164 - if hasattr(value, "model_dump"): 165 - value = value.model_dump() 166 - elif hasattr(value, "to_dict"): 163 + # DotDict doesn't have a working .get(), convert to dict first 164 + if hasattr(value, "to_dict") and callable(value.to_dict): 167 165 value = value.to_dict() 166 + elif not isinstance(value, dict): 167 + value = dict(value) 168 + 169 + # extract content from leaflet's block structure 170 + # pages[].blocks[].block.plaintext 171 + content_parts = [] 172 + for page in value.get("pages", []): 173 + for block_wrapper in page.get("blocks", []): 174 + block = block_wrapper.get("block", {}) 175 + plaintext = block.get("plaintext", "") 176 + if plaintext: 177 + content_parts.append(plaintext) 178 + 179 + content = "\n\n".join(content_parts) 168 180 169 181 return Document( 170 182 uri=record.uri, 171 183 title=value.get("title", ""), 172 - content=value.get("content", ""), 173 - createdAt=value.get("createdAt", ""), 184 + content=content, 185 + createdAt=value.get("publishedAt", "") or value.get("createdAt", ""), 174 186 tags=value.get("tags", []), 175 187 publicationUri=value.get("publication", ""), 176 188 )
+3 -21
mcp/uv.lock
··· 704 704 { name = "pytest" }, 705 705 { name = "pytest-asyncio" }, 706 706 { name = "pytest-sugar" }, 707 - { name = "respx" }, 708 707 { name = "ruff" }, 709 708 ] 710 709 ··· 712 711 requires-dist = [ 713 712 { name = "fastmcp", specifier = ">=2.0" }, 714 713 { name = "httpx", specifier = ">=0.28" }, 715 - { name = "pdsx" }, 714 + { name = "pdsx", git = "https://github.com/zzstoatzz/pdsx.git" }, 716 715 ] 717 716 718 717 [package.metadata.requires-dev] ··· 720 719 { name = "pytest", specifier = ">=8.3.0" }, 721 720 { name = "pytest-asyncio", specifier = ">=0.25.0" }, 722 721 { name = "pytest-sugar" }, 723 - { name = "respx", specifier = ">=0.22" }, 724 722 { name = "ruff", specifier = ">=0.12.0" }, 725 723 ] 726 724 ··· 1043 1041 1044 1042 [[package]] 1045 1043 name = "pdsx" 1046 - version = "0.0.1a12" 1047 - source = { registry = "https://pypi.org/simple" } 1044 + version = "0.0.1a16" 1045 + source = { git = "https://github.com/zzstoatzz/pdsx.git#0ddb54107895408e6e3b0a8049c2eb4377f53fc0" } 1048 1046 dependencies = [ 1049 1047 { name = "atproto" }, 1050 1048 { name = "pydantic-settings" }, 1051 1049 { name = "pyyaml" }, 1052 1050 { name = "rich" }, 1053 - ] 1054 - sdist = { url = "https://files.pythonhosted.org/packages/4a/09/ad53b8d8a7f987ac835e600f86aec68a30b71a70ce1ae54ce1269231791f/pdsx-0.0.1a12.tar.gz", hash = "sha256:1475b4533b63386b82819f6fe7f1d9835fb0fdc199d4a11ae0bff53424dabbad", size = 140194, upload-time = "2025-12-07T07:27:39.757Z" } 1055 - wheels = [ 1056 - { url = "https://files.pythonhosted.org/packages/0b/39/0b12ff07d04ebf0c495a7e92bd8b5d2d6611d04d5500bf7ec14b997cc125/pdsx-0.0.1a12-py3-none-any.whl", hash = "sha256:8a9df926f88e0aa88c956335c4cd2925ee47dae064d65e7686727efb46af8dc8", size = 28384, upload-time = "2025-12-07T07:27:38.68Z" }, 1057 1051 ] 1058 1052 1059 1053 [[package]] ··· 1547 1541 sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } 1548 1542 wheels = [ 1549 1543 { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, 1550 - ] 1551 - 1552 - [[package]] 1553 - name = "respx" 1554 - version = "0.22.0" 1555 - source = { registry = "https://pypi.org/simple" } 1556 - dependencies = [ 1557 - { name = "httpx" }, 1558 - ] 1559 - sdist = { url = "https://files.pythonhosted.org/packages/f4/7c/96bd0bc759cf009675ad1ee1f96535edcb11e9666b985717eb8c87192a95/respx-0.22.0.tar.gz", hash = "sha256:3c8924caa2a50bd71aefc07aa812f2466ff489f1848c96e954a5362d17095d91", size = 28439, upload-time = "2024-12-19T22:33:59.374Z" } 1560 - wheels = [ 1561 - { url = "https://files.pythonhosted.org/packages/8e/67/afbb0978d5399bc9ea200f1d4489a23c9a1dad4eee6376242b8182389c79/respx-0.22.0-py2.py3-none-any.whl", hash = "sha256:631128d4c9aba15e56903fb5f66fb1eff412ce28dd387ca3a81339e52dbd3ad0", size = 25127, upload-time = "2024-12-19T22:33:57.837Z" }, 1562 1544 ] 1563 1545 1564 1546 [[package]]