Skip to content

Commit 49779e6

Browse files
ivolnistovclaude
andauthored
chore: apply ruff format (#4)
* chore: apply ruff format CI's `ruff format --check packages/` was failing on three files in the limited-mode PR (#2) because I ran `ruff check --fix` locally but not `ruff format`. Mechanically re-running `uv run ruff format packages/` fixes them: client.py, config.py, tools/posts.py. No behavior changes — purely formatting. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(types): None-check Post/Chat lookups in new limited-mode tools CI's mypy job flagged 19 union-attr errors on the limited-mode tools that fetch a Post/Chat by ID and then dereference attributes without checking for None: get_chat, get_reply_thread, get_replies_to_post, edit_post, delete_post, create_thread_chat. Add explicit `if X is None: return error_response(...)` guards before each first dereference, returning a clean ToolResponse error to the caller instead of an AttributeError at runtime. Also fixes create_thread_chat: Chat.create_branch returns a Branch wrapper, not a Chat. The new chat id is `branch.chat.id`, not `branch.id`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 45a92dc commit 49779e6

4 files changed

Lines changed: 42 additions & 21 deletions

File tree

packages/pararam-nexus-mcp/src/pararam_nexus_mcp/client.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@ def __init__(self) -> None:
3232
# Cookie manager is only used in full mode; limited (token) mode never
3333
# persists session data.
3434
self._cookie_manager: AsyncFileCookieManager | None = (
35-
None
36-
if config.mode == 'limited'
37-
else AsyncFileCookieManager(str(config.pararam_cookie_file))
35+
None if config.mode == 'limited' else AsyncFileCookieManager(str(config.pararam_cookie_file))
3836
)
3937
self._initialized: bool = True
4038

packages/pararam-nexus-mcp/src/pararam_nexus_mcp/config.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,11 @@ def validate_credentials(self) -> None:
8282

8383
if not self.pararam_login:
8484
raise ValueError(
85-
'PARARAM_LOGIN environment variable is required '
86-
'(or set PARARAM_USER_TOKEN for limited mode)'
85+
'PARARAM_LOGIN environment variable is required (or set PARARAM_USER_TOKEN for limited mode)'
8786
)
8887
if not self.pararam_password:
8988
raise ValueError(
90-
'PARARAM_PASSWORD environment variable is required '
91-
'(or set PARARAM_USER_TOKEN for limited mode)'
89+
'PARARAM_PASSWORD environment variable is required (or set PARARAM_USER_TOKEN for limited mode)'
9290
)
9391

9492

packages/pararam-nexus-mcp/src/pararam_nexus_mcp/tools/chats.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,11 @@ async def get_chat(chat_id: int) -> ToolResponse[GetChatPayload | None]:
143143
client = await get_client()
144144
logger.info('Fetching chat %s', chat_id)
145145
chat = await client.client.get_chat_by_id(chat_id)
146+
if chat is None:
147+
return error_response(
148+
message=f'Chat {chat_id} not found',
149+
error='not found',
150+
)
146151
await chat.load()
147152
info = ChatInfo(
148153
id=chat.id,
@@ -276,10 +281,18 @@ async def create_thread_chat(
276281
title,
277282
)
278283
parent = await client.client.get_chat_by_id(parent_chat_id)
284+
if parent is None:
285+
return error_response(
286+
message=f'Parent chat {parent_chat_id} not found',
287+
error='not found',
288+
)
279289
branch = await parent.create_branch(post_no=post_no, title=title or '')
290+
# Branch wraps the new child Chat plus parent linkage; the actual
291+
# chat id is `branch.chat.id`.
292+
branch_chat_id = int(branch.chat.id)
280293
return success_response(
281-
message=f'Branch chat created (id={branch.id})',
282-
payload=CreateChatPayload(chat_id=branch.id, title=title, type='thread'),
294+
message=f'Branch chat created (id={branch_chat_id})',
295+
payload=CreateChatPayload(chat_id=branch_chat_id, title=title, type='thread'),
283296
)
284297
except (
285298
PararamioAuthenticationError,

packages/pararam-nexus-mcp/src/pararam_nexus_mcp/tools/posts.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1520,13 +1520,16 @@ async def get_reply_thread(
15201520
client = await get_client()
15211521
logger.info('Fetching reply thread for chat=%s post=%s', chat_id, post_no)
15221522
post = await client.client.get_post(chat_id, post_no)
1523+
if post is None:
1524+
return error_response(
1525+
message=f'Post {chat_id}/{post_no} not found',
1526+
error='not found',
1527+
)
15231528
posts = await post.rerere()
15241529
post_nos = [p.post_no for p in posts]
15251530
return success_response(
15261531
message=f'Thread for {chat_id}/{post_no} has {len(post_nos)} posts',
1527-
payload=ReplyThreadPayload(
1528-
chat_id=chat_id, root_post_no=post_no, post_nos=post_nos
1529-
),
1532+
payload=ReplyThreadPayload(chat_id=chat_id, root_post_no=post_no, post_nos=post_nos),
15301533
)
15311534
except (
15321535
PararamioAuthenticationError,
@@ -1538,9 +1541,7 @@ async def get_reply_thread(
15381541
logger.error('Failed to fetch reply thread %s/%s: %s', chat_id, post_no, e)
15391542
return error_response(message='Could not fetch reply thread', error=str(e))
15401543
except Exception as e:
1541-
logger.error(
1542-
'Unexpected error fetching reply thread %s/%s: %s', chat_id, post_no, e, exc_info=True
1543-
)
1544+
logger.error('Unexpected error fetching reply thread %s/%s: %s', chat_id, post_no, e, exc_info=True)
15441545
return error_response(message='Unexpected error', error=str(e))
15451546

15461547
@mcp.tool()
@@ -1561,6 +1562,11 @@ async def get_replies_to_post(
15611562
client = await get_client()
15621563
logger.info('Fetching replies to chat=%s post=%s', chat_id, post_no)
15631564
post = await client.client.get_post(chat_id, post_no)
1565+
if post is None:
1566+
return error_response(
1567+
message=f'Post {chat_id}/{post_no} not found',
1568+
error='not found',
1569+
)
15641570
replies = await post.load_reply_posts()
15651571
formatted: list[PostInfo] = []
15661572
for reply in replies:
@@ -1600,9 +1606,7 @@ async def get_replies_to_post(
16001606
logger.error('Failed to fetch replies %s/%s: %s', chat_id, post_no, e)
16011607
return error_response(message='Could not fetch replies', error=str(e))
16021608
except Exception as e:
1603-
logger.error(
1604-
'Unexpected error fetching replies %s/%s: %s', chat_id, post_no, e, exc_info=True
1605-
)
1609+
logger.error('Unexpected error fetching replies %s/%s: %s', chat_id, post_no, e, exc_info=True)
16061610
return error_response(message='Unexpected error', error=str(e))
16071611

16081612
@mcp.tool()
@@ -1629,6 +1633,11 @@ async def edit_post(
16291633
client = await get_client()
16301634
logger.info('Editing chat=%s post=%s', chat_id, post_no)
16311635
post = await client.client.get_post(chat_id, post_no)
1636+
if post is None:
1637+
return error_response(
1638+
message=f'Post {chat_id}/{post_no} not found',
1639+
error='not found',
1640+
)
16321641
await post.edit(text=text, quote=quote, reply_no=reply_no)
16331642
return success_response(
16341643
message=f'Edited post {chat_id}/{post_no}',
@@ -1665,13 +1674,16 @@ async def delete_post(
16651674
client = await get_client()
16661675
logger.info('Deleting chat=%s post=%s', chat_id, post_no)
16671676
post = await client.client.get_post(chat_id, post_no)
1677+
if post is None:
1678+
return error_response(
1679+
message=f'Post {chat_id}/{post_no} not found',
1680+
error='not found',
1681+
)
16681682
await post.delete()
16691683
is_deleted = bool(getattr(post, 'is_deleted', True))
16701684
return success_response(
16711685
message=f'Deleted post {chat_id}/{post_no}',
1672-
payload=DeletePostPayload(
1673-
chat_id=chat_id, post_no=post_no, is_deleted=is_deleted
1674-
),
1686+
payload=DeletePostPayload(chat_id=chat_id, post_no=post_no, is_deleted=is_deleted),
16751687
)
16761688
except (
16771689
PararamioAuthenticationError,

0 commit comments

Comments
 (0)