Diomedes Git commited on
Commit
a041674
·
1 Parent(s): 0b15c28

server, adding some basic. also got rid of gemini mess, and deleted reviews and moved notes out of repo

Browse files
notes_etc/development_guide_01.md DELETED
@@ -1,797 +0,0 @@
1
- # Corvid Council - Development Guide
2
-
3
- ## Project Overview
4
-
5
- **Name**: Corvid Council
6
- **Hackathon**: Gradio Agents & MCP Hackathon - Winter 25
7
- **Track**: Building MCP (Creative)
8
- **Tags**: `building-mcp-track-creative`
9
-
10
- ### Concept
11
- A multi-agent group chat where 4 AI crow experts discuss topics, each using specialized MCP tools. The Gradio app itself also exposes MCP tools, allowing external applications to query the expert panel.
12
-
13
- ### The Hook
14
- "What if you could consult a panel of AI experts? Use it two ways: watch them research and debate in a group chat, or query them as an MCP tool from Claude Desktop or any MCP client."
15
-
16
- ---
17
-
18
- ## Architecture
19
-
20
- ```
21
- External User/App (Claude Desktop, etc.)
22
- ↓ (calls MCP tool)
23
- ask_crow_council(question)
24
- ↓ (hits)
25
- Gradio App (IS an MCP server)
26
- ↓ (internally orchestrates)
27
- CouncilOrchestrator
28
- ↓ (manages)
29
- 4 Character Agents (Corvus, Magpie, Raven, Crow)
30
- ↓ (each uses)
31
- Character-Specific MCP Tools (~12-15 total)
32
- ↓ (which call)
33
- External APIs (Semantic Scholar, eBird, Weather, etc.)
34
- ↓ (returns aggregated response)
35
- Back to User
36
- ```
37
-
38
- ### Two Interfaces, Same Backend
39
-
40
- **Interface 1: Gradio Chat UI** (Direct access)
41
- - Users see the full group chat
42
- - Watch characters use their tools in real-time
43
- - See sources, debates, synthesis
44
- - Can inject questions
45
-
46
- **Interface 2: MCP Tools** (Programmatic access)
47
- - External apps call `ask_crow_council(question)`
48
- - Same orchestration happens internally
49
- - Returns structured, synthesized response
50
- - Fast, clean API integration
51
-
52
- ---
53
-
54
- ## The Characters
55
-
56
- ### Corvus (The Scholar) - Melancholic
57
- **Archetype**: Academic, thorough, perfectionist
58
- **Personality**:
59
- - PhD researcher studying corvid cognition
60
- - Every claim needs citation
61
- - Cautious, analytical, sometimes pedantic
62
- - Posts rarely but substantively
63
- - Always verifies before sharing
64
-
65
- **Communication Style**:
66
- - Long, structured messages
67
- - Includes citations and methodology
68
- - Questions assumptions
69
- - "According to X study (DOI)..."
70
-
71
- **MCP Tools** (4 tools):
72
- - `search_corvid_papers(query, years, min_citations)` - Academic database search
73
- - `get_paper_details(doi)` - Full paper information
74
- - `verify_claim_against_literature(claim, context)` - Fact-checking
75
- - `get_citation_network(doi, depth)` - Related research
76
-
77
- **Behavior Pattern**:
78
- 1. Sees topic → searches papers
79
- 2. Reads abstract/methods → verifies quality
80
- 3. Composes careful response with citations
81
- 4. If others make claims → verifies against literature
82
-
83
- ---
84
-
85
- ### Magpie (The Enthusiast) - Sanguine
86
- **Archetype**: Social, optimistic, storyteller
87
- **Personality**:
88
- - Collects crow stories like magpies collect shiny things
89
- - Loves viral videos, human-interest pieces
90
- - Shares everything excitedly, often unchecked
91
- - Makes connections between unrelated things
92
- - High energy, frequent poster
93
-
94
- **Communication Style**:
95
- - Rapid-fire short messages
96
- - Uses emojis
97
- - "omg you HAVE to see this!"
98
- - Shares first, verifies later (or never)
99
-
100
- **MCP Tools** (3 tools):
101
- - `search_social_media(query, platform, timeframe)` - TikTok, Twitter, Reddit
102
- - `search_web(query)` - General web search
103
- - `find_trending_content(topic)` - What's viral right now
104
-
105
- **Behavior Pattern**:
106
- 1. Sees topic → searches social/web immediately
107
- 2. Shares whatever looks interesting
108
- 3. Sometimes posts old/duplicate content
109
- 4. Creates energy, others moderate
110
-
111
- **Quirk**: Posts old news 30-40% of the time (doesn't check dates)
112
-
113
- ---
114
-
115
- ### Raven (The Activist) - Choleric
116
- **Archetype**: Action-oriented, passionate, confrontational
117
- **Personality**:
118
- - Environmental activist
119
- - Sees crows as ecosystem health indicators
120
- - Aggressive fact-gathering
121
- - Calls out misinformation
122
- - Posts urgent updates
123
-
124
- **Communication Style**:
125
- - Short, urgent messages
126
- - Exclamation points
127
- - "We need to act NOW"
128
- - Challenges others directly
129
- - Facts as weapons
130
-
131
- **MCP Tools** (4 tools):
132
- - `get_environmental_data(location, metric)` - Pollution, habitat data
133
- - `search_news(query, recency)` - Current news monitoring
134
- - `fact_check_claim(claim, source)` - Debunking tool
135
- - `analyze_sentiment(topic, source)` - What are people saying?
136
-
137
- **Behavior Pattern**:
138
- 1. Sees topic → looks for environmental angle
139
- 2. Searches news for threats/problems
140
- 3. Fact-checks others' claims aggressively
141
- 4. Rallies for action
142
-
143
- **Creates drama**: Often disagrees with Corvus (action vs. more research)
144
-
145
- ---
146
-
147
- ### Crow (The Observer) - Phlegmatic
148
- **Archetype**: Calm, patient, contemplative
149
- **Personality**:
150
- - Birdwatcher who observes local populations
151
- - Philosophical, sees long-term patterns
152
- - Rarely posts but reads everything
153
- - When speaks, it's meaningful
154
- - Patience over urgency
155
-
156
- **Communication Style**:
157
- - Rare, brief, profound
158
- - Long silences then sudden insight
159
- - "I've been watching..."
160
- - Ties everything together
161
- - No wasted words
162
-
163
- **MCP Tools** (3 tools):
164
- - `get_bird_sightings(location, species, timeframe)` - eBird data
165
- - `get_weather_patterns(location, duration)` - Weather effects on behavior
166
- - `analyze_temporal_patterns(data, timeframe)` - Long-term trends
167
-
168
- **Behavior Pattern**:
169
- 1. Observes conversation quietly
170
- 2. Uses tools to gather observational data
171
- 3. Waits for pattern to emerge
172
- 4. Posts when has something meaningful
173
- 5. Often provides perspective that settles debates
174
-
175
- **Special role**: The "wise elder" who cuts through noise
176
-
177
- ---
178
-
179
- ## MCP Tool Architecture
180
-
181
- ### Internal MCP Tools (Consumed by App)
182
-
183
- All tools live in one MCP server: `crow-chat-mcp`
184
-
185
- **Tool Categories**:
186
- 1. Academic (Corvus): 4 tools
187
- 2. Social/Web (Magpie): 3 tools
188
- 3. Real-time Data (Raven): 4 tools
189
- 4. Observational (Crow): 3 tools
190
-
191
- **Total**: ~14 core tools
192
-
193
- **Tool Definition Pattern**:
194
- ```python
195
- @mcp.tool()
196
- async def tool_name(
197
- param1: str,
198
- param2: int = default_value
199
- ) -> ReturnType:
200
- """
201
- Clear description of what tool does.
202
-
203
- Args:
204
- param1: Description
205
- param2: Description
206
-
207
- Returns:
208
- Description of return value
209
- """
210
- # Implementation
211
- pass
212
- ```
213
-
214
- ### External MCP Tools (Exposed by App)
215
-
216
- **The Gradio app exposes these tools**:
217
-
218
- ```python
219
- @mcp.tool()
220
- async def ask_crow_council(question: str) -> dict:
221
- """
222
- Consult the Corvid Council expert panel.
223
-
224
- All four experts research using their specialized tools
225
- and provide a synthesized answer with sources.
226
-
227
- Returns:
228
- {
229
- "consensus": str, # Synthesized answer
230
- "expert_opinions": [...], # Individual takes
231
- "sources": [...], # All citations/links
232
- "confidence": float # 0-1
233
- }
234
- """
235
-
236
- @mcp.tool()
237
- async def ask_specific_expert(
238
- question: str,
239
- expert: Literal["corvus", "magpie", "raven", "crow"]
240
- ) -> dict:
241
- """
242
- Query one specific expert.
243
- """
244
-
245
- @mcp.tool()
246
- async def fact_check_claim(claim: str) -> dict:
247
- """
248
- Have the council verify a claim about crows.
249
-
250
- Returns:
251
- {
252
- "verdict": "verified" | "disputed" | "unknown",
253
- "evidence": [...],
254
- "expert_consensus": str
255
- }
256
- """
257
- ```
258
-
259
- ---
260
-
261
- ## Core Components
262
-
263
- ### 1. CouncilOrchestrator
264
- **Purpose**: Coordinates character responses, manages conversation flow
265
-
266
- **Key Methods**:
267
- ```python
268
- class CouncilOrchestrator:
269
- async def process_query(question: str) -> CouncilResult:
270
- """Main entry point for both UI and MCP"""
271
-
272
- async def facilitate_discussion(question, initial_responses) -> Discussion:
273
- """Characters respond to each other"""
274
-
275
- async def synthesize_consensus(responses) -> str:
276
- """Create unified answer for MCP calls"""
277
- ```
278
-
279
- ### 2. Character (Base Class)
280
- **Purpose**: Encapsulates character behavior
281
-
282
- **Key Methods**:
283
- ```python
284
- class Character:
285
- async def research_and_respond(question: str, context: list) -> Response:
286
- """Use tools to research, generate response"""
287
-
288
- async def use_tools(question: str) -> list[ToolResult]:
289
- """Call relevant MCP tools"""
290
-
291
- async def generate_message(question, tool_results, context) -> str:
292
- """Compose message in character voice"""
293
- ```
294
-
295
- ### 3. MCPToolManager
296
- **Purpose**: Handles all MCP tool calls, error handling, rate limiting
297
-
298
- **Key Methods**:
299
- ```python
300
- class MCPToolManager:
301
- async def call_tool(tool_name: str, params: dict) -> ToolResult:
302
- """Execute tool with error handling"""
303
-
304
- async def batch_call_tools(calls: list) -> list[ToolResult]:
305
- """Call multiple tools efficiently"""
306
- ```
307
-
308
- ### 4. GradioInterface
309
- **Purpose**: Chat UI for direct human interaction
310
-
311
- **Key Methods**:
312
- ```python
313
- def create_interface() -> gr.Blocks:
314
- """Build Gradio chat interface"""
315
-
316
- async def handle_user_message(message, history, state):
317
- """Process user input, orchestrate responses"""
318
-
319
- def display_tool_usage(character, tool_name):
320
- """Show 'Character is using tool...' indicators"""
321
- ```
322
-
323
- ---
324
-
325
- ## Development Phases
326
-
327
- ### Week 1: Core System (Days 1-7)
328
-
329
- #### Days 1-2: Foundation
330
- **Goal**: One character working with tools in Gradio
331
-
332
- - [ ] Set up project structure
333
- - [ ] Build `CouncilOrchestrator` skeleton
334
- - [ ] Implement `Character` base class
335
- - [ ] Create Corvus with 2 tools (search_papers, get_details)
336
- - [ ] Basic Gradio interface
337
- - [ ] Test: Corvus responds to question using tools
338
-
339
- **Success Metric**: Ask "Are crows smart?", see Corvus search papers and respond with citation
340
-
341
- #### Days 3-4: Second Character
342
- **Goal**: Two characters interacting
343
-
344
- - [ ] Add Magpie with 2 tools (search_web, search_social)
345
- - [ ] Implement character interaction (they respond to each other)
346
- - [ ] Staggered response timing (2-3 sec apart)
347
- - [ ] Test: Both characters respond differently to same question
348
-
349
- **Success Metric**: Two distinct personalities with different tool usage patterns visible
350
-
351
- #### Days 5-6: Third & Fourth Characters
352
- **Goal**: Full council assembled
353
-
354
- - [ ] Add Raven with 2 tools (get_environmental_data, search_news)
355
- - [ ] Add Crow with 2 tools (get_sightings, get_weather)
356
- - [ ] Four-way interaction patterns
357
- - [ ] Test: All four respond to question, some interact with each other
358
-
359
- **Success Metric**: Rich multi-agent discussion with varied tool usage
360
-
361
- #### Day 7: Week 1 Polish
362
- - [ ] Improve system prompts for distinct personalities
363
- - [ ] Add typing indicators
364
- - [ ] Better error handling for tool failures
365
- - [ ] Test conversation flows
366
- - [ ] Fix bugs
367
-
368
- ---
369
-
370
- ### Week 2: External MCP & Polish (Days 8-14)
371
-
372
- #### Days 8-9: MCP Exposure
373
- **Goal**: Gradio app as MCP server
374
-
375
- - [ ] Implement external MCP tool exposure
376
- - [ ] `ask_crow_council()` tool
377
- - [ ] `ask_specific_expert()` tool
378
- - [ ] Test from Claude Desktop or MCP inspector
379
- - [ ] Ensure both interfaces use same orchestrator
380
-
381
- **Success Metric**: External tool call triggers internal discussion, returns synthesized response
382
-
383
- #### Days 10-11: Additional Tools & Refinement
384
- **Goal**: Complete tool sets
385
-
386
- - [ ] Add remaining tools (3rd-4th tool per character)
387
- - [ ] Implement `fact_check_claim()` external tool
388
- - [ ] Add tool chaining where appropriate
389
- - [ ] Better synthesis algorithm for consensus
390
- - [ ] Rate limiting and caching
391
-
392
- #### Days 12-13: Polish & Demo Prep
393
- **Goal**: Production ready
394
-
395
- - [ ] UI improvements (avatars, timestamps, theming)
396
- - [ ] Tool usage visualization
397
- - [ ] Demo mode with curated scenarios
398
- - [ ] Write comprehensive README
399
- - [ ] Create demo video showing both interfaces
400
- - [ ] Test edge cases
401
-
402
- #### Day 14: Deployment & Documentation
403
- **Goal**: Ship it
404
-
405
- - [ ] Deploy to Hugging Face Spaces
406
- - [ ] Verify both interfaces work publicly
407
- - [ ] Final testing in Claude Desktop
408
- - [ ] Polish documentation
409
- - [ ] Submit to hackathon
410
- - [ ] Buffer for last-minute issues
411
-
412
- ---
413
-
414
- ## Technical Stack
415
-
416
- ### Core
417
- - **Python 3.10+**
418
- - **Gradio 5.x** (with MCP capabilities)
419
- - **MCP SDK** (`pip install mcp`)
420
- - **OpenAI API** (gpt-4o-mini for characters)
421
- - **Anthropic API** (optional, for variety)
422
-
423
- ### External APIs
424
- - **Semantic Scholar API** (academic papers) - Free, no key needed
425
- - **Brave Search API** or **SerpAPI** (web search) - Free tier available
426
- - **eBird API** (bird sightings) - Free with registration
427
- - **OpenWeather API** (weather data) - Free tier
428
- - **News API** (news search) - Free tier
429
-
430
- ### Development Tools
431
- - **MCP Inspector** (for testing MCP tools)
432
- - **Claude Desktop** (for testing external tool usage)
433
- - **Git/GitHub** (version control)
434
- - **HF Spaces** (deployment)
435
-
436
- ---
437
-
438
- ## File Structure
439
-
440
- ```
441
- corvid-council/
442
- ├── README.md
443
- ├── requirements.txt
444
- ├── app.py # Main Gradio app
445
- ├── .env.example # API key template
446
- ├── src/
447
- │ ├── __init__.py
448
- │ ├── orchestrator.py # CouncilOrchestrator
449
- │ ├── character.py # Character base class
450
- │ ├── characters/
451
- │ │ ├── __init__.py
452
- │ │ ├── corvus.py # Corvus implementation
453
- │ │ ├── magpie.py # Magpie implementation
454
- │ │ ├── raven.py # Raven implementation
455
- │ │ └── crow.py # Crow implementation
456
- │ ├── mcp/
457
- │ │ ├── __init__.py
458
- │ │ ├── server.py # Internal MCP server
459
- │ │ ├── tools/
460
- │ │ │ ├── academic.py # Corvus tools
461
- │ │ │ ├── social.py # Magpie tools
462
- │ │ │ ├── realtime.py # Raven tools
463
- │ │ │ └── observational.py # Crow tools
464
- │ │ └── external.py # External MCP tools (exposed by app)
465
- │ ├── ui/
466
- │ │ ├── __init__.py
467
- │ │ ├── interface.py # Gradio interface
468
- │ │ └── components.py # Custom UI components
469
- │ └── utils/
470
- │ ├── __init__.py
471
- │ ├── prompts.py # System prompts for characters
472
- │ └── helpers.py # Utility functions
473
- └── tests/
474
- ├── test_orchestrator.py
475
- ├── test_characters.py
476
- └── test_mcp_tools.py
477
- ```
478
-
479
- ---
480
-
481
- ## Key Implementation Patterns
482
-
483
- ### Pattern 1: Staggered Responses
484
- ```python
485
- async def generate_responses(question, characters):
486
- """Characters respond one at a time with delays"""
487
- responses = []
488
-
489
- for i, character in enumerate(characters):
490
- # Show typing indicator
491
- show_typing(character.name)
492
-
493
- # Delay based on message length
494
- await asyncio.sleep(random.uniform(2, 5))
495
-
496
- # Generate response (includes tool usage)
497
- response = await character.research_and_respond(
498
- question,
499
- context=responses # See previous responses
500
- )
501
-
502
- responses.append(response)
503
-
504
- return responses
505
- ```
506
-
507
- ### Pattern 2: Tool Usage Display
508
- ```python
509
- async def use_tools_with_display(character, tools_to_use):
510
- """Show tool usage in UI"""
511
- results = []
512
-
513
- for tool in tools_to_use:
514
- # Show in UI: "Corvus is searching papers..."
515
- update_ui(f"{character.name} is using {tool.name}...")
516
-
517
- try:
518
- result = await mcp_client.call_tool(tool.name, tool.params)
519
- results.append(result)
520
- except Exception as e:
521
- # Show error gracefully
522
- update_ui(f"⚠️ Tool {tool.name} failed, continuing...")
523
-
524
- return results
525
- ```
526
-
527
- ### Pattern 3: Character Decides Which Tools
528
- ```python
529
- async def research_and_respond(self, question, context):
530
- """Character decides which tools to use"""
531
-
532
- # Ask LLM what tools to use
533
- tool_plan = await self.llm.generate(
534
- system_prompt=self.system_prompt,
535
- user_prompt=f"""
536
- Question: {question}
537
- Available tools: {self.tools}
538
-
539
- Which tools should you use? Return JSON list.
540
- """
541
- )
542
-
543
- # Execute chosen tools
544
- tool_results = await self.execute_tools(tool_plan)
545
-
546
- # Generate response using results
547
- message = await self.llm.generate(
548
- system_prompt=self.system_prompt,
549
- user_prompt=f"""
550
- Question: {question}
551
- Tool results: {tool_results}
552
-
553
- Write your response in character.
554
- """
555
- )
556
-
557
- return message
558
- ```
559
-
560
- ### Pattern 4: Consensus Synthesis
561
- ```python
562
- async def synthesize_consensus(self, responses):
563
- """Create unified answer for MCP calls"""
564
-
565
- # Extract key points from each response
566
- synthesis_prompt = f"""
567
- Four experts answered the same question. Synthesize their responses:
568
-
569
- Corvus (Academic): {responses['corvus']}
570
- Magpie (Social): {responses['magpie']}
571
- Raven (Activist): {responses['raven']}
572
- Crow (Observer): {responses['crow']}
573
-
574
- Create a consensus answer that:
575
- - Combines their insights
576
- - Notes where they agree/disagree
577
- - Includes all sources
578
- - Rates overall confidence
579
- """
580
-
581
- return await llm.generate(synthesis_prompt)
582
- ```
583
-
584
- ---
585
-
586
- ## Common Pitfalls & Solutions
587
-
588
- ### Pitfall 1: Rate Limiting
589
- **Problem**: Hitting API rate limits with multiple characters
590
-
591
- **Solutions**:
592
- - Stagger API calls (2-3 seconds apart)
593
- - Use cheaper models (gpt-4o-mini)
594
- - Cache tool results
595
- - Implement exponential backoff
596
-
597
- ### Pitfall 2: Tool Failures
598
- **Problem**: External API is down or returns error
599
-
600
- **Solutions**:
601
- - Always wrap tool calls in try-except
602
- - Have fallback responses
603
- - Show errors gracefully to user
604
- - Continue conversation even if one tool fails
605
-
606
- ### Pitfall 3: Repetitive Conversations
607
- **Problem**: Characters say similar things
608
-
609
- **Solutions**:
610
- - Strong, distinct system prompts
611
- - Different tool sets force different perspectives
612
- - Include context of previous responses
613
- - Add personality quirks (Magpie posts old news, etc.)
614
-
615
- ### Pitfall 4: Slow Responses
616
- **Problem**: Waiting for multiple LLM + tool calls is slow
617
-
618
- **Solutions**:
619
- - Show typing indicators
620
- - Stream responses where possible
621
- - Call tools in parallel when they don't depend on each other
622
- - Pre-generate demo scenarios for judging
623
-
624
- ### Pitfall 5: MCP Exposure Not Working
625
- **Problem**: External apps can't call your tools
626
-
627
- **Solutions**:
628
- - Test with MCP Inspector first
629
- - Check CORS settings
630
- - Verify tool schemas are valid
631
- - Ensure server is publicly accessible
632
- - Check HF Spaces networking settings
633
-
634
- ---
635
-
636
- ## Testing Strategy
637
-
638
- ### Unit Tests
639
- - Individual tools return expected format
640
- - Characters generate appropriate responses
641
- - Orchestrator handles edge cases
642
-
643
- ### Integration Tests
644
- - Full conversation flow works
645
- - Both interfaces (UI and MCP) work
646
- - Tool failures handled gracefully
647
-
648
- ### Manual Testing Scenarios
649
-
650
- **Scenario 1: Basic Question**
651
- - Ask: "Are crows smart?"
652
- - Expect: All 4 respond, use tools, cite sources
653
-
654
- **Scenario 2: Fact-Checking**
655
- - Magpie posts dubious claim
656
- - Expect: Corvus or Raven fact-checks it
657
-
658
- **Scenario 3: External MCP Call**
659
- - From Claude Desktop: call `ask_crow_council("Do crows use tools?")`
660
- - Expect: Structured response with consensus
661
-
662
- **Scenario 4: Old News**
663
- - Magpie shares old content
664
- - Expect: Others notice and comment (first few times)
665
-
666
- **Scenario 5: Tool Failure**
667
- - Simulate API down
668
- - Expect: Graceful degradation, conversation continues
669
-
670
- ---
671
-
672
- ## Demo Script
673
-
674
- ### Demo 1: Gradio UI (2 minutes)
675
- 1. Open interface, introduce the four experts
676
- 2. Ask: "Can crows recognize individual humans?"
677
- 3. Show:
678
- - Characters using different tools
679
- - Tool usage indicators
680
- - Different personality styles
681
- - Sources and citations
682
- 4. Highlight: Real-time research, transparent process
683
-
684
- ### Demo 2: MCP Tool Usage (2 minutes)
685
- 1. Switch to Claude Desktop
686
- 2. Show available tools: `ask_crow_council`, etc.
687
- 3. Ask same question through MCP tool
688
- 4. Show:
689
- - Same backend process (quick view of Gradio)
690
- - Structured response returned
691
- - Synthesized consensus with sources
692
- 5. Highlight: Same intelligence, two interfaces
693
-
694
- ### Demo 3: Architecture Explanation (1 minute)
695
- 1. Show diagram
696
- 2. Explain: MCP tools consuming MCP tools
697
- 3. Point out: Composability, extensibility
698
- 4. Mention: Could add more experts, more tools
699
-
700
- ---
701
-
702
- ## Success Criteria
703
-
704
- ### Minimum Viable Product (Must Have)
705
- - ✅ 4 characters with distinct personalities
706
- - ✅ 8-10 working MCP tools (2-3 per character)
707
- - ✅ Gradio chat interface
708
- - ✅ External MCP tool exposure (ask_crow_council)
709
- - ✅ Both interfaces work reliably
710
- - ✅ Deployed to HF Spaces
711
- - ✅ Good README and demo
712
-
713
- ### Stretch Goals (Nice to Have)
714
- - ✅ 12-15 tools (full tool sets)
715
- - ✅ Tool chaining (one tool's output → another)
716
- - ✅ Advanced interaction patterns (quirk decay)
717
- - ✅ Memory across sessions
718
- - ✅ Beautiful UI with animations
719
- - ✅ Comprehensive error handling
720
-
721
- ### Judging Criteria (What Matters)
722
- 1. **Technical sophistication**: Multi-tool MCP system
723
- 2. **Creativity**: Novel use of MCP (agents-as-tools)
724
- 3. **Completeness**: Both interfaces work well
725
- 4. **Demo quality**: Clear, impressive, functional
726
- 5. **Documentation**: Easy to understand and extend
727
-
728
- ---
729
-
730
- ## Resources
731
-
732
- ### APIs
733
- - Semantic Scholar: https://api.semanticscholar.org/
734
- - eBird: https://documenter.getpostman.com/view/664302/S1ENwy59
735
- - OpenWeather: https://openweathermap.org/api
736
- - News API: https://newsapi.org/
737
-
738
- ### Documentation
739
- - MCP Spec: https://spec.modelcontextprotocol.io/
740
- - Gradio Docs: https://www.gradio.app/docs
741
- - HF Course: https://huggingface.co/learn/mcp-course/
742
-
743
- ### Tools
744
- - MCP Inspector: (check course for link)
745
- - Claude Desktop: https://claude.ai/download
746
-
747
- ---
748
-
749
- ## Quick Start Commands
750
-
751
- ```bash
752
- # Clone and setup
753
- git clone <your-repo>
754
- cd corvid-council
755
- pip install -r requirements.txt
756
-
757
- # Set up environment
758
- cp .env.example .env
759
- # Edit .env with your API keys
760
-
761
- # Run locally
762
- python app.py
763
-
764
- # Deploy to HF Spaces
765
- git push hf main
766
- ```
767
-
768
- ---
769
-
770
- ## Support Checklist for LLMs
771
-
772
- When asking an LLM for help, provide:
773
- - [ ] This development guide
774
- - [ ] Specific file you're working on
775
- - [ ] Error message if debugging
776
- - [ ] What you've tried already
777
- - [ ] Specific question or task
778
-
779
- **Example prompt**:
780
- ```
781
- I'm building the Corvid Council project (see attached dev guide).
782
- I'm working on [specific component].
783
- I'm trying to [specific task].
784
- I'm getting [error/issue].
785
- I've tried [what you tried].
786
- Can you help me [specific ask]?
787
- ```
788
-
789
- ---
790
-
791
- ## Contact & Resources
792
-
793
- **Hackathon**: Gradio Agents & MCP Hackathon - Winter 25
794
- **Track**: Building MCP (Creative)
795
- **Tag**: `building-mcp-track-creative`
796
-
797
- Good luck! Build something amazing. 🎯
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
notes_etc/development_guide_02.txt DELETED
@@ -1,181 +0,0 @@
1
- Project Overview
2
-
3
- Name: Cluas
4
- Hackathon / Track: Gradio Agents & MCP Hackathon – Winter 25, Creative Track
5
- Concept: A multi-agent MCP system where four AI “crow experts” discuss topics, each using specialized tools. Users can either observe the panel in a Gradio chat interface or query it programmatically via MCP.
6
-
7
- Hook:
8
- “What if you could consult a panel of AI experts that not only debates, researches, and cross-verifies claims but also maintains a shared memory across sessions?”
9
-
10
- Architecture Overview
11
- External User/App (Claude Desktop, etc.)
12
- ↓ (calls MCP tool)
13
- ask_crow_council(question)
14
-
15
- Gradio App (MCP server)
16
-
17
- CouncilOrchestrator
18
-
19
- 4 Character Agents (Corvus, Magpie, Raven, Crow)
20
-
21
- Character-Specific MCP Tools (~12–15 total)
22
-
23
- External APIs (Semantic Scholar, eBird, Weather, News)
24
-
25
- Shared AgentMemory (JSON / DB)
26
-
27
- Synthesized Response → User
28
-
29
-
30
- Key Differences vs. earlier plan:
31
-
32
- AgentMemory is first-class: acts as shared context, not just caching.
33
-
34
- Cache is memory: supports cross-agent recall, deduplication, “didn’t we discuss this?”
35
-
36
- Tool orchestration is separate from UI: backend drives both Gradio interface and MCP endpoint.
37
-
38
- Characters
39
- Character Role Personality Core Tools Behavior
40
- Corvus Scholar Analytical, cautious, thorough search_academic, get_paper_details, verify_claim, citation_network Reads literature, fact-checks, posts rarely but substantively
41
- Magpie Enthusiast Social, rapid-fire, excited search_social, search_web, find_trending Posts frequently, may repeat info, creates conversational energy
42
- Raven Activist Urgent, confrontational, data-driven get_environmental_data, search_news, fact_check_claim, analyze_sentiment Searches real-time threats, challenges others’ claims
43
- Crow Observer Calm, patient, contemplative get_sightings, get_weather_patterns, analyze_temporal_patterns Rarely posts, synthesizes patterns, provides perspective
44
-
45
- Notes:
46
-
47
- Each character writes in a distinct voice.
48
-
49
- AgentMemory is referenced when appropriate to recall past papers, claims, or events.
50
-
51
- Tools are abstracted behind MCP methods for modularity.
52
-
53
- MCP Tool Architecture
54
-
55
- Internal MCP Tools (Character Tools):
56
-
57
- Academic (Corvus): 4 tools
58
-
59
- Social/Web (Magpie): 3 tools
60
-
61
- Real-time Data (Raven): 4 tools
62
-
63
- Observational (Crow): 3 tools
64
-
65
- External MCP Tools (Exposed via Gradio App):
66
-
67
- @mcp.tool()
68
- async def ask_crow_council(question: str) -> dict:
69
- """Consult all 4 characters; return synthesized response with sources."""
70
-
71
- @mcp.tool()
72
- async def ask_specific_expert(question: str, expert: Literal["corvus", "magpie", "raven", "crow"]) -> dict:
73
- """Query a single character."""
74
-
75
- @mcp.tool()
76
- async def fact_check_claim(claim: str) -> dict:
77
- """Have the council verify a claim using AgentMemory and tools."""
78
-
79
-
80
- AgentMemory Integration:
81
-
82
- Logs items with metadata: title, DOI/arXiv link, snippet, timestamp, tags.
83
-
84
- Retrieval methods: get_recent(days), get_by_tag(tag), search_title(query).
85
-
86
- Can prune old memories; supports both short-term and long-term context.
87
-
88
- Project Structure (Updated for Modularity)
89
- cluas/
90
- ├── README.md
91
- ├── .env.example
92
- ├── src/
93
- │ ├── __init__.py
94
- │ ├── orchestrator.py # CouncilOrchestrator
95
- │ ├── character.py # Character base class
96
- │ ├── characters/
97
- │ │ ├── corvus.py
98
- │ │ ├── magpie.py
99
- │ │ ├── raven.py
100
- │ │ └── crow.py
101
- │ ├── mcp/
102
- │ │ ├── server.py # Internal MCP server
103
- │ │ ├── tools/
104
- │ │ │ ├── academic.py
105
- │ │ │ ├── social.py
106
- │ │ │ ├── realtime.py
107
- │ │ │ └── observational.py
108
- │ │ └── external.py # Exposed MCP tools
109
- │ ├── memory/
110
- │ │ └── agent_memory.py # Shared memory / cache
111
- │ ├── ui/
112
- │ │ ├── interface.py
113
- │ │ └── components.py
114
- │ └── utils/
115
- │ ├── prompts.py
116
- │ └── helpers.py
117
- └── tests/
118
- ├── test_orchestrator.py
119
- ├── test_characters.py
120
- └── test_agent_memory.py
121
-
122
- Development Phases (Current Scope)
123
- Phase 1 – Core MVP
124
-
125
- Corvus implemented with 2 academic tools.
126
-
127
- Basic AgentMemory operational (short-term storage).
128
-
129
- Basic Gradio chat interface.
130
-
131
- Tool orchestration scaffolding in place.
132
-
133
- Phase 2 – Additional Characters & Tools
134
-
135
- Add Magpie and Raven with partial tools.
136
-
137
- Implement staggered responses, typing indicators.
138
-
139
- Ensure memory integration across agents.
140
-
141
- Phase 3 – Full Council & MCP Exposure
142
-
143
- Crow added.
144
-
145
- Expose ask_crow_council() and ask_specific_expert().
146
-
147
- Test multi-agent synthesis and external calls.
148
-
149
- Phase 4 – Polish & Deploy
150
-
151
- Error handling, retries, and rate limiting.
152
-
153
- Memory pruning, long-term storage optional (Supabase / MongoDB).
154
-
155
- UI/UX polish for Gradio.
156
-
157
- Demo-ready, hackathon-ready deployment.
158
-
159
- Key Implementation Patterns
160
-
161
- Tool orchestration per character – each agent decides which tools to use, executes asynchronously.
162
-
163
- Staggered responses – enhances human-like timing.
164
-
165
- Memory-driven context – prior discussions inform current responses.
166
-
167
- Consensus synthesis – LLM generates unified response for MCP output.
168
-
169
- Technical Stack
170
-
171
- Python 3.13 (uv-managed)
172
-
173
- Gradio 5.x with MCP
174
-
175
- MCP SDK for server and tools
176
-
177
- LLM: gpt-4o-mini (Anthropic optional)
178
-
179
- APIs: Semantic Scholar, eBird, News API, OpenWeather
180
-
181
- Data/Memory: JSON for now, possible future DB (Supabase or MongoDB)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
notes_etc/exposing_cluas_as_an_MCP.py DELETED
@@ -1,14 +0,0 @@
1
- # figure something a bit like this
2
-
3
-
4
- @mcp.tool()
5
- async def ask_crow_council(question: str) -> dict:
6
- """Consult all 4 characters; return synthesized response with sources."""
7
-
8
- @mcp.tool()
9
- async def ask_specific_expert(question: str, expert: Literal["corvus", "magpie", "raven", "crow"]) -> dict:
10
- """Query a single character."""
11
-
12
- @mcp.tool()
13
- async def fact_check_claim(claim: str) -> dict:
14
- """Have the council verify a claim using AgentMemory and tools."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
notes_etc/possible_architecture_diagram.txt DELETED
@@ -1,48 +0,0 @@
1
- ┌─────────────────────────────────────┐
2
- │ User / Client │
3
- │ (Claude Desktop, App, API caller) │
4
- └────────────────────┬────────────────┘
5
- │ ask_council()
6
-
7
- ┌─────────────────────────────────────┐
8
- │ Corvid Council MCP Server │
9
- │ (Gradio chat + MCP tool handler) │
10
- └────────────────────┬────────────────┘
11
- │ orchestrates
12
-
13
- ┌─────────────────────────────────────┐
14
- │ Council Orchestrator │
15
- │ - Calls individual agents │
16
- │ - Coordinates group chat flow │
17
- └────────────────────┬────────────────┘
18
-
19
- ┌───────────────────────────┼─────────────────────────────┐
20
- ▼ ▼ ▼
21
- ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
22
- │ Corvus │ │ Magpie │ │ Raven │
23
- │ (Academic AI) │ │ (Trends AI) │ │ (Deep-Focus AI)│
24
- │ PubMed/S2 API │ │ Web/Social API │ │ Feeds/Alerts │
25
- └───────┬─────────┘ └───────┬─────────┘ └───────┬─────────┘
26
- │ │ │
27
- └──────────────┬────────────┴────────────┬───────────────┘
28
- ▼ ▼
29
- ┌─────────────────────────────────────┐
30
- │ Agent Memory │
31
- │ (shared JSON/DB knowledge log) │
32
- │ - recent papers, trends, facts │
33
- │ - tags, timestamps, DOIs │
34
- │ - enables recall & “inside jokes” │
35
- └─────────────────────────────────────┘
36
-
37
-
38
- ┌─────────────────────────────────────┐
39
- │ LLM Consensus Layer │
40
- │ - builds final group answer │
41
- │ - personality + expertise mixing │
42
- └─────────────────────────────────────┘
43
-
44
-
45
- ┌─────────────────────────────────────┐
46
- │ Final Output │
47
- │ (chat replay + structured answer) │
48
- └─────────────────────────────────────┘
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
notes_etc/possible_project_structure.txt DELETED
@@ -1,32 +0,0 @@
1
- corvid-council/
2
- ├── README.md
3
- ├── .env.example
4
- ├── src/
5
- │ ├── __init__.py
6
- │ ├── orchestrator.py # CouncilOrchestrator
7
- │ ├── character.py # Character base class
8
- │ ├── characters/
9
- │ │ ├── corvus.py
10
- │ │ ├── magpie.py
11
- │ │ ├── raven.py
12
- │ │ └── crow.py
13
- │ ├── mcp/
14
- │ │ ├── server.py # Internal MCP server
15
- │ │ ├── tools/
16
- │ │ │ ├── academic.py
17
- │ │ │ ├── social.py
18
- │ │ │ ├── realtime.py
19
- │ │ │ └── observational.py
20
- │ │ └── external.py # Exposed MCP tools
21
- │ ├── memory/
22
- │ │ └── agent_memory.py # Shared memory / cache
23
- │ ├── ui/
24
- │ │ ├── interface.py
25
- │ │ └── components.py
26
- │ └── utils/
27
- │ ├── prompts.py
28
- │ └── helpers.py
29
- └── tests/
30
- ├── test_orchestrator.py
31
- ├── test_characters.py
32
- └── test_agent_memory.py
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pyproject.toml CHANGED
@@ -5,6 +5,7 @@ description = "Add your description here"
5
  readme = "README.md"
6
  requires-python = ">=3.13"
7
  dependencies = [
 
8
  "feedparser>=6.0.12",
9
  "gradio[mcp,oauth]==6.0.0.dev4",
10
  "mcp>=1.20.0",
 
5
  readme = "README.md"
6
  requires-python = ">=3.13"
7
  dependencies = [
8
+ "fastmcp>=2.13.1",
9
  "feedparser>=6.0.12",
10
  "gradio[mcp,oauth]==6.0.0.dev4",
11
  "mcp>=1.20.0",
reviews/changes.md DELETED
@@ -1,220 +0,0 @@
1
- # Change Log
2
-
3
- **Date:** 2025-11-15
4
-
5
- This document tracks notable changes in the "Corvid Council" repository.
6
-
7
- ---
8
-
9
- ### November 15, 2025
10
-
11
- #### Summary of Changes
12
-
13
- A code snippet related to a potential PubMed API implementation was added as comments to `src/cluas_mcp/common/api_clients.py`. No other functional or structural changes were observed in the repository. The core logic, file structure, and administrative files (`README.md`, `.gitignore`, `requirements.txt`) remain the same as the last review.
14
-
15
- #### Detailed Changes
16
-
17
- - **Modified `src/cluas_mcp/common/api_clients.py`**:
18
- - A block of commented-out Python code was added. This code demonstrates how to use the `Bio.Entrez` library to search PubMed for articles related to corvids, parse the results, and extract details like title, authors, abstract, and DOI.
19
-
20
- #### Analysis
21
-
22
- - **Significant:**
23
- - The change itself is minor (it's only comments), but it's significant in what it signals: active exploration of how to implement the `PubMedClient`. This is a direct move towards fulfilling one of the key requirements for making the `Corvus` character fully functional.
24
-
25
- - **Good:**
26
- - This is a positive step. It shows that the next phase of development is being actively researched. The example code is relevant and provides a clear path for the real implementation.
27
- - Using a well-known library like `BioPython` is a good choice for interacting with NCBI services.
28
-
29
- - **Concerning:**
30
- - There are no concerns with this change. It's a healthy sign of a project in the early stages of development. The only minor point is that the code is commented out in the main source file rather than being in a separate experimental script, but this is a trivial issue at this stage.
31
-
32
- ---
33
-
34
- ### November 18, 2025
35
-
36
- #### Summary of Changes
37
-
38
- A new `notes_etc/` directory was added, containing detailed development guides and an architecture diagram. Crucially, the previously empty `src/cluas_mcp/common/formatting.py` file was implemented, fixing a critical import error.
39
-
40
- #### Detailed Changes
41
-
42
- - **New Directory `notes_etc/`**:
43
- - `development_guide_01.md`: A comprehensive guide detailing the project's concept, architecture, characters, MCP tools, development phases, and technical stack.
44
- - `development_guide_02.txt`: A more concise version of the guide.
45
- - `possible_lightweight_architecture_diagram.txt`: A text-based visualization of the system architecture.
46
- - **Modified `src/cluas_mcp/common/formatting.py`**:
47
- - Implemented the `snippet_abstract` function to truncate text intelligently.
48
- - Implemented the `format_authors` function to format author lists.
49
-
50
- #### Analysis
51
-
52
- - **Significant:**
53
- - The implementation of the functions in `formatting.py` is the most significant change, as it directly unblocks the `Corvus` agent and makes a core piece of the application runnable for the first time.
54
- - The addition of the development guides provides invaluable context and a clear roadmap for the project's future.
55
-
56
- - **Good:**
57
- - These changes are overwhelmingly positive. The bug fix is a major step forward, and the planning documents demonstrate a clear and well-thought-out vision for the project. The architecture is sound, and the character-based agent design is creative and well-defined.
58
-
59
- - **Concerning:**
60
- - There are no concerning changes. The project is progressing logically and is now in a much better state than before. The next logical step is to implement the placeholder API clients.
61
-
62
- ---
63
-
64
- ### November 18, 2025 (Afternoon)
65
-
66
- #### Summary of Changes
67
-
68
- Significant progress has been made on the API clients. The `ArxivClient` has been fully implemented, and the `PubMedClient` now has a functional search method that retrieves article IDs. More planning documents were also added.
69
-
70
- #### Detailed Changes
71
-
72
- - **Modified `src/cluas_mcp/common/api_clients.py`**:
73
- - **`ArxivClient`**: Now contains a full implementation. It builds a query, fetches data from the arXiv API, parses the Atom feed, and returns a list of structured dictionaries containing paper details.
74
- - **`PubMedClient`**: A `pubmed_search` method has been implemented to perform the `esearch` step of the API interaction. It correctly builds a complex query and uses a helper method `parse_id_list` to extract PubMed IDs from the XML response. The `efetch` step is not yet implemented.
75
- - **`SemanticScholarClient`**: Remains a placeholder.
76
- - **New Files in `notes_etc/`**:
77
- - `possible_architecture_diagram.txt`: A more detailed architecture diagram.
78
- - `possible_project_structure.txt`: A proposed target file structure.
79
- - `exposing_cluas_as_an_MCP.py`: A code snippet showing how the app might expose MCP tools.
80
-
81
- #### Analysis
82
-
83
- - **Significant:**
84
- - The implementation of the `ArxivClient` and the first half of the `PubMedClient` is the most significant change. This represents the first major piece of core feature development, moving the project from planning and bug-fixing into active implementation.
85
-
86
- - **Good:**
87
- - This is a huge step in the right direction. The code is functional and well-structured. The `Corvus` agent now has a working data source (arXiv) and a partially working one (PubMed). This directly addresses the main blocker and builds momentum.
88
-
89
- - **Concerning:**
90
- - There are no concerning changes. The progress is excellent. The clear next step is to complete the `PubMedClient` by adding the `efetch` logic to retrieve full article data using the IDs from `pubmed_search`.
91
-
92
- ---
93
-
94
- ### November 18, 2025 (Late Afternoon)
95
-
96
- #### Summary of Changes
97
-
98
- A significant refactoring has occurred. The `PubMedClient` has been moved to a new `academic` submodule, and a robust, retry-enabled HTTP fetching utility has been created in `src/cluas_mcp/common/http.py`.
99
-
100
- #### Detailed Changes
101
-
102
- - **New Directory `src/cluas_mcp/academic/`**:
103
- - The `PubMedClient` has been moved to `src/cluas_mcp/academic/pubmed_client.py`.
104
- - **New File `src/cluas_mcp/common/http.py`**:
105
- - This file introduces a `fetch_with_retry` function that uses the `tenacity` library to provide exponential backoff for HTTP requests. This makes API calls more resilient.
106
- - **Modified `src/cluas_mcp/academic/pubmed_client.py`**:
107
- - The refactored `PubMedClient` now uses the new `fetch_with_retry` utility for its API calls.
108
- - **Modified `pyproject.toml`**:
109
- - The `tenacity` library has been added as a project dependency.
110
- - **Modified `src/cluas_mcp/common/api_clients.py`**:
111
- - This file still contains the old `PubMedClient` code, creating duplication.
112
-
113
- #### Analysis
114
-
115
- - **Significant:**
116
- - The architectural refactoring is highly significant. It shows a move towards a more organized, maintainable, and robust codebase, aligning with the project's planning documents. The creation of a shared, resilient HTTP utility is a major improvement.
117
-
118
- - **Good:**
119
- - The new `http.py` module is excellent and demonstrates best practices for consuming external APIs.
120
- - The file structure is becoming cleaner and more logical.
121
-
122
- - **Concerning:**
123
- - **Code Duplication:** The most pressing issue is the duplicated `PubMedClient` code. The old implementation in `src/cluas_mcp/common/api_clients.py` is now obsolete and should be removed to prevent confusion and future bugs. The other clients in that file should also be refactored into their own modules.
124
-
125
- ---
126
-
127
- ### November 18, 2025 (Evening)
128
-
129
- #### Summary of Changes
130
-
131
- A massive and highly positive refactoring has been completed. The API clients have been fully separated into their own modules, the `PubMedClient` is now feature-complete, a new `AcademicSearch` facade provides a single point of entry, domain keywords have been separated, and—most importantly—tests have been added for the API clients.
132
-
133
- #### Detailed Changes
134
-
135
- - **Completed Refactoring**:
136
- - The API clients now live in `src/cluas_mcp/academic/` as `pubmed.py`, `arxiv.py`, and `semantic_scholar.py`.
137
- - The old `src/cluas_mcp/common/api_clients.py` file still exists but is now entirely obsolete.
138
- - **Completed `PubMedClient`**:
139
- - The client in `academic/pubmed.py` now includes a `fetch_articles` method, completing the `esearch`/`efetch` workflow.
140
- - **New `AcademicSearch` Facade**:
141
- - `academic/academic_search.py` provides a single `academic_search` function that calls all underlying clients, simplifying future use.
142
- - **Domain Knowledge Separation**:
143
- - `src/cluas_mcp/domain/keywords.py` now stores keyword lists, separating this data from the client logic.
144
- - **New Tests**:
145
- - `tests/test_arxiv.py` and `tests/test_pubmed.py` have been added, providing actual tests for the API clients.
146
-
147
- #### Analysis
148
-
149
- - **Significant:**
150
- - This is a pivotal update. The project has rapidly matured from a proof-of-concept to a well-structured and tested data access layer. The addition of tests is the most significant and important change, as it provides a foundation for reliable future development.
151
-
152
- - **Good:**
153
- - The architecture is now clean, modular, and extensible.
154
- - The separation of concerns (clients, domain data, facades) is excellent.
155
- - The data layer for the `Corvus` agent is now functionally complete and robust.
156
-
157
- - **Concerning:**
158
- - The only remaining issue is the obsolete `src/cluas_mcp/common/api_clients.py` file. It should be deleted to finalize the cleanup.
159
-
160
- ---
161
-
162
- ### November 18, 2025 (Night)
163
-
164
- #### Summary of Changes
165
-
166
- The final placeholder API client, `SemanticScholarClient`, has been implemented and tested. The data access layer for the `Corvus` agent is now 100% complete.
167
-
168
- #### Detailed Changes
169
-
170
- - **`SemanticScholarClient` Implemented**:
171
- - The client in `src/cluas_mcp/academic/semantic_scholar.py` has been fully implemented. It queries the Semantic Scholar API, requests specific fields, and normalizes the response into the project's standard format.
172
- - **New Test for `SemanticScholarClient`**:
173
- - A new test file, `tests/test_semantic_scholar.py`, was added to validate the new client.
174
- - **`PubMedClient` Improved**:
175
- - The `pubmed.py` client was improved to extract more data (DOI, PubMed link) and includes better error handling for parsing individual articles.
176
- - **Facade Renamed**:
177
- - The `academic_search.py` facade was renamed to `academic_search_entry.py`.
178
-
179
- #### Analysis
180
-
181
- - **Significant:**
182
- - The completion of the final API client is a major milestone. The `academic_search` facade is now fully operational, capable of querying and aggregating results from all three target data sources.
183
-
184
- - **Good:**
185
- - The project continues its excellent momentum. The practice of adding tests for all new code is being maintained, which is crucial for long-term stability. The data access layer is now a polished, production-ready component.
186
-
187
- - **Concerning:**
188
- - There are no new concerns. The only outstanding task from the refactoring is to delete the obsolete `src/cluas_mcp/common/api_clients.py` file.
189
-
190
- ---
191
-
192
- ### November 18, 2025 (Final)
193
-
194
- #### Summary of Changes
195
-
196
- This update finalizes the data access layer by improving the test infrastructure and cleaning up the last piece of technical debt.
197
-
198
- #### Detailed Changes
199
-
200
- - **Test Suite Refactoring**:
201
- * Tests for individual API clients have been moved to a dedicated `tests/clients/` directory.
202
- * A new `tests/integration/` directory was created for higher-level tests, starting with a test for the `academic_search_entrypoint`.
203
- * A `tests/conftest.py` file was added to manage the system path for the test suite, a standard best practice.
204
- - **Facade Hardening**:
205
- * The `academic_search_entrypoint` was improved with `try...except` blocks to ensure that a failure in one API client does not prevent the others from returning results.
206
- - **Technical Debt Removed**:
207
- * The obsolete `src/cluas_mcp/common/api_clients.py` file has been deleted.
208
- - **Dependencies Formalized**:
209
- * `pytest` was officially added to the `pyproject.toml` dependencies.
210
-
211
- #### Analysis
212
-
213
- - **Significant:**
214
- - The cleanup of the final piece of technical debt and the formalization of a layered testing strategy mark the official completion of the data access layer. This component is now stable, robust, and maintainable.
215
-
216
- - **Good:**
217
- - All changes are positive and demonstrate a commitment to high-quality, professional software development practices. The separation of client-level tests from integration tests is particularly noteworthy.
218
-
219
- - **Concerning:**
220
- - There are no concerns whatsoever. The project is in an ideal state to begin building the application logic.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
reviews/code_review_2025-11-15_14-30-00.md DELETED
@@ -1,70 +0,0 @@
1
- # Code Review: Cluas MCP
2
-
3
- **Date:** 2025-11-15
4
-
5
- ## a) High-Level Summary
6
-
7
- This repository, "Cluas," contains a Python-based project that appears to be a multi-character or multi-agent system for academic research. The name "Cluas" and the character names (Corvus, Raven, Magpie, Crow) all relate to corvids, a family of intelligent birds, suggesting a theme of intelligence and information gathering.
8
-
9
- The core functionality revolves around searching academic paper databases (arXiv, PubMed, Semantic Scholar) based on user queries. The system is designed with a fallback mechanism, starting with PubMed and progressing to other sources if no results are found. It includes a caching mechanism to speed up repeated searches.
10
-
11
- The project is structured into several components:
12
-
13
- - `characters`: Different agents with specific functionalities. So far, only `CorvusMCP` is implemented.
14
- - `cluas_mcp`: The core logic, including API clients for academic search engines, caching, and data formatting.
15
- - `gradio`: Suggests a web interface for interacting with the system, though it is currently empty.
16
- - `tests`: Unit tests for the `CorvusMCP` character, indicating a commitment to testing.
17
-
18
- ## b) Detailed Component Description
19
-
20
- ### `CorvusMCP` Character
21
-
22
- The `CorvusMCP` class in `src/characters/corvus.py` is the most developed part of the application. Its primary role is to search for academic papers.
23
-
24
- - **Search Priority:** It follows a clear search strategy: PubMed -> Semantic Scholar -> arXiv. This is a sensible approach, as PubMed is a high-quality, curated database for biomedical literature.
25
- - **Caching:** It uses a `CacheManager` to store search results. When a search is requested, it first checks the cache for the given query. If found, it returns the cached results, avoiding redundant API calls. If not, it performs the search, and the new results are cached for future use.
26
- - **API Clients:** It utilizes `PubMedClient`, `SemanticScholarClient`, and `ArxivClient` from `src/cluas_mcp/common/api_clients.py`. Currently, only the `ArxivClient` has a concrete implementation; the others are placeholders.
27
- - **Data Formatting:** After fetching results, it cleans and formats them into a consistent structure, including title, abstract, authors, publication date, DOI, and a link.
28
-
29
- ### API Clients and Caching
30
-
31
- - **`api_clients.py`**: This file defines classes for interacting with external academic APIs. The `ArxivClient` is functional and constructs a search query that combines the user's query with a predefined list of keywords related to corvids. This suggests the system has a specialized focus.
32
- - **`cache.py`**: The `CacheManager` provides a simple file-based JSON cache. It reads and writes to a `cache.json` file, which is effective for a small-scale application.
33
-
34
- ### Testing
35
-
36
- The `tests/test_corvus.py` file contains unit tests for the `CorvusMCP` class. The tests use `unittest.mock` to patch the API clients and cache manager, allowing for isolated testing of the character's logic. The tests cover several scenarios:
37
-
38
- - Cache hits.
39
- - Successful searches with PubMed.
40
- - Fallback to Semantic Scholar when PubMed returns no results.
41
- - Fallback to arXiv when both PubMed and Semantic Scholar fail.
42
-
43
- This demonstrates good software development practices.
44
-
45
- ## c) Opinionated Breakdown and Future Development
46
-
47
- ### What's Good
48
-
49
- - **Clear Structure:** The project is well-organized, with a logical separation of concerns (characters, core logic, web interface, tests).
50
- - **Intelligent Design:** The fallback search strategy and caching mechanism are well-thought-out features that improve both the quality of results and the performance of the system.
51
- - **Thematic Cohesion:** The corvid theme is creative and consistently applied, which can help in building a strong identity for the project.
52
- - **Test Coverage:** The presence of unit tests for the main character is a great sign of a healthy codebase.
53
-
54
- ### Suggestions for Improvement
55
-
56
- - **Implement Placeholder Clients:** The `PubMedClient` and `SemanticScholarClient` are currently placeholders. Implementing these would be the highest priority next step to make `CorvusMCP` fully functional.
57
- - **Expand Character Roles:** The other characters (`Raven`, `Magpie`, `Crow`) are currently empty files. Their roles should be defined and implemented. Based on the `blah.py` file, it seems the intention is for them to have distinct functions:
58
- - `magpie.find_trending("topic")`: Could focus on identifying popular or recent papers.
59
- - `raven.get_environmental_data("location")`: Could specialize in a different type of data, perhaps from different sources.
60
- - `crow.analyze_patterns("dataset")`: Might be geared towards data analysis or trend detection within a set of papers.
61
- - **Develop the Gradio UI:** The `gradio/app.py` is empty. Building a simple web interface would make the system much more accessible and usable.
62
- - **Refine the `ArxivClient`:** The `ArxivClient` currently combines the user's query with a hardcoded list of corvid-related keywords. This should be made more flexible, perhaps by allowing the character or user to specify the topic or domain of interest. The `search_academic_papers` function in `tools.py` seems to be a step in this direction.
63
- - **Configuration Management:** API keys or other configuration settings should not be hardcoded. A configuration file or environment variables should be used.
64
- - **Error Handling:** The API clients should have more robust error handling for network issues or unexpected API responses.
65
-
66
- ### Future Expectations
67
-
68
- I expect the project to continue developing by fleshing out the existing structure. The immediate next steps would be to implement the remaining API clients and then define the unique functionalities of the other characters. Once the core logic is more complete, the focus will likely shift to building the Gradio web interface to allow users to interact with the different characters.
69
-
70
- Overall, this is a promising project with a solid foundation and a clear, creative vision. The code is clean, well-structured, and demonstrates good engineering practices.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
reviews/code_review_2025-11-15_15-00-00.md DELETED
@@ -1,62 +0,0 @@
1
- # Code Review: Cluas MCP (Second Pass)
2
-
3
- **Date:** 2025-11-15
4
-
5
- ## a) High-Level Summary
6
-
7
- This second review of the "Cluas" repository shows some evolution from the initial version, primarily with the introduction of a shared memory system. The project remains a Python-based, multi-agent framework for academic research, themed around corvids.
8
-
9
- The core functionality is still centered on the `CorvusMCP` character, which searches academic databases. The most significant change is the replacement of the simple cache with an `AgentMemory` system. This new system is designed to create a shared context between agents by logging discovered items (papers) and allowing retrieval based on recency, tags, or title.
10
-
11
- The overall structure is largely the same: `characters` for agents, `cluas_mcp` for core logic, a placeholder `gradio` UI, and `tests`. However, many of the previously identified issues, such as placeholder API clients, empty character files, and clutter in the source directory, persist.
12
-
13
- ## b) Detailed Component Description
14
-
15
- ### `CorvusMCP` and `AgentMemory`
16
-
17
- The `CorvusMCP` character in `src/characters/corvus.py` has been updated to integrate with the new `AgentMemory` system.
18
-
19
- - **Memory Integration:** Instead of a simple query cache, `CorvusMCP` now interacts with `AgentMemory`. When `search_papers` finds a paper, it logs it as a memory item, tagging it with "academic_search" and noting that "Corvus" mentioned it. This creates a persistent, shared knowledge base.
20
- - **Contextual Results:** The `search_papers` function now has a `memory_days` parameter. When used, it appends recently mentioned items from the shared memory to the search results. This allows the character to provide contextually aware responses that include not just new findings but also recently discussed topics.
21
- - **`AgentMemory` Class:** Located in `src/cluas_mcp/common/memory.py`, this class provides a more sophisticated storage mechanism than the previous `CacheManager`. It stores items in a JSON file with structured data, including timestamps, snippets, DOIs, and tags. It supports various retrieval methods:
22
- - `get_recent(days)`: Fetches items referenced within a specific timeframe.
23
- - `get_by_tag(tag)`: Retrieves items with a given tag.
24
- - `search_title(query)`: Performs a simple text search on titles.
25
- - `prune_long_term()`: A utility to clean up very old memories.
26
-
27
- ### Persisting Issues
28
-
29
- - **Placeholder API Clients:** The `PubMedClient` and `SemanticScholarClient` in `src/cluas_mcp/common/api_clients.py` are still not implemented. This remains the biggest blocker to the `CorvusMCP`'s full functionality, as it can only search arXiv.
30
- - **Empty Modules:** The `magpie`, `raven`, and `crow` character files are still empty. The `gradio/app.py` file is also empty, so there is no user-facing interface. The `formatting.py` file is still empty, which means the `corvus.py` file is still broken.
31
- - **Cluttered Source Directory:** The `src/cluas_mcp` directory still contains scripts that appear to be for testing or experimentation (`abstract_filtered.py`, `testing_arxiv.py`, `blah.py`).
32
- - **Lack of Tests for New Functionality:** There are no new tests for the `AgentMemory` class or for the updated `CorvusMCP` that uses it. The existing test files are either empty or haven't been updated.
33
-
34
- ## c) Opinionated Breakdown and Future Development
35
-
36
- ### What's Good
37
-
38
- - **Shared Memory is a Strong Concept:** The introduction of `AgentMemory` is a significant conceptual improvement. It moves the system from a simple tool to a collaborative multi-agent system where agents can build on each other's findings. This is a much more interesting and powerful architecture.
39
- - **Improved `CorvusMCP` Logic:** The ability to include recent memories in search results is a smart feature that makes the agent more context-aware.
40
-
41
- ### Room for Improvement
42
-
43
- The project is still in a conceptual phase, and the execution is lagging behind the ideas. The critical feedback from the previous review still applies:
44
-
45
- 1. **Fix the Broken Code:** The `formatting.py` file needs to be implemented so that `corvus.py` can run. This is the most immediate and critical issue.
46
- 2. **Implement the API Clients:** The `PubMedClient` and `SemanticScholarClient` must be implemented for the core feature to work as designed.
47
- 3. **Write Tests for `AgentMemory`:** The new memory system is a critical component and should be thoroughly tested. Tests for adding, retrieving, and pruning items are essential.
48
- 4. **Clean the `src` Directory:** The experimental scripts should be moved out of the main source directory to improve code hygiene.
49
- 5. **Define Other Agent Roles:** The vision for the other agents (`Magpie`, `Raven`, `Crow`) should be translated into code. What do they do? How do they interact with the shared memory? For example:
50
- - `Magpie` could search for trending topics on social media or news sites and add them to memory with a "trending" tag.
51
- - `Raven` could be tasked with a specific, long-term monitoring goal, periodically updating a set of memory items.
52
- 6. **Build the UI:** A simple Gradio interface would provide a much-needed way to interact with the agents and see the shared memory in action.
53
-
54
- ### Future Expectations
55
-
56
- The introduction of `AgentMemory` sets a clear direction for the project. The focus should now be on building out the ecosystem around this shared memory. I expect the next phase of development to involve:
57
-
58
- - Making the existing `CorvusMCP` character fully functional by implementing the remaining API clients.
59
- - Adding at least one more character with a distinct role that reads from or writes to the `AgentMemory`.
60
- - Adding a basic user interface to demonstrate the multi-agent interaction.
61
-
62
- The project has a stronger conceptual foundation now, but it needs a significant implementation effort to realize its potential. The priority should be to make the existing code runnable and tested before adding more features.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
reviews/code_review_2025-11-15_15-30-00.md DELETED
@@ -1,62 +0,0 @@
1
- # Code Review: Corvid Council (Third Pass)
2
-
3
- **Date:** 2025-11-15
4
-
5
- ## a) High-Level Summary
6
-
7
- This review assesses the "Cluas" repository against the detailed "Corvid Council - Development Guide." The document clarifies that the project is a multi-agent system ("Corvid Council") for a hackathon, with a clear vision, architecture, and development plan. The goal is a Gradio-based group chat of four AI crow experts (Corvus, Magpie, Raven, Crow) that also exposes its functionality as a set of external MCP tools.
8
-
9
- The current codebase represents a very early stage of this vision. It has established a foundational piece of the `Corvus` character and the `AgentMemory` system, which aligns with the "Week 1: Days 1-2" goals of the development plan. However, the implementation is still far from the complete architecture described in the guide. The existing code provides a skeleton for one character's tools but does not yet include the orchestration, multi-agent interaction, or the dual Gradio/MCP interface that are central to the project concept.
10
-
11
- ## b) Detailed Description of Components vs. Project Plan
12
-
13
- ### Current Implementation vs. "Week 1: Days 1-2" Goals
14
-
15
- The development guide sets a clear goal for the first two days: "One character working with tools in Gradio." Let's see how the current code stacks up.
16
-
17
- - **`Corvus` Character:** The `src/characters/corvus.py` file lays the groundwork for the "Scholar" archetype. Its `search_papers` function, with its fallback logic (PubMed -> Semantic Scholar -> arXiv), aligns perfectly with the described perfectionist, citation-focused personality. The integration with `AgentMemory` to log findings is a direct implementation of the cross-agent context recall mentioned in the guide.
18
- - **`AgentMemory`:** The `src/cluas_mcp/common/memory.py` is a solid first pass at the "agent-memory subsystem." It supports adding items with metadata (`mentioned_by`, `tags`) and retrieving them, which is the core requirement for enabling cross-agent context.
19
- - **API Clients:** The `src/cluas_mcp/common/api_clients.py` file contains the stubs for the clients Corvus needs. The `ArxivClient` is functional, which allows for some level of testing, while the others are placeholders. This is consistent with the iterative approach of getting one part working first.
20
- - **Missing Components:** Crucially, several key components from the development guide are entirely absent from the current codebase:
21
- - **`CouncilOrchestrator`**: There is no orchestrator to manage conversation flow or facilitate discussion between agents.
22
- - **`Character` Base Class**: The `CorvusMCP` class is standalone; it does not inherit from a common `Character` base class.
23
- - **Gradio Interface**: The `gradio/app.py` is empty. There is no UI to interact with Corvus.
24
- - **MCP Tool Exposure**: There is no code to expose the `ask_crow_council` or other external MCP tools.
25
-
26
- In summary, the current code has started implementing the `Corvus` character's internal logic but has not yet built the framework (orchestrator, base classes, UI) that will allow it to function as part of the "Corvid Council."
27
-
28
- ## c) Opinionated Breakdown, Expectations, and Suggestions
29
-
30
- The development guide is excellent and provides a clear roadmap. The "roughness" of the current code is perfectly understandable in this context. The focus has clearly been on prototyping a single agent's core behavior, which is a sensible way to start.
31
-
32
- ### Alignment with Vision
33
-
34
- - **Good:** The `CorvusMCP` and `AgentMemory` components are well-aligned with the project's vision. The idea of logging findings to a shared memory is a strong foundation for the multi-agent system.
35
- - **Gap:** The current file structure does not match the target structure outlined in the guide. For example, the tools are not yet separated into `src/mcp/tools/`, and there is no `orchestrator.py` or `character.py`.
36
-
37
- ### Actionable Suggestions for Next Steps
38
-
39
- Given the detailed plan, my suggestions are to follow it closely. Here is a prioritized list of actions to bridge the gap between the current code and the "Week 1" goals:
40
-
41
- 1. **Establish the Target File Structure:** Before writing more code, refactor the existing files to match the structure in the development guide. This will make the project much easier to navigate and build upon.
42
- - Create `src/orchestrator.py`, `src/character.py`, and `src/mcp/tools/academic.py`.
43
- - Move the `CorvusMCP` logic into `src/characters/corvus.py` and make it inherit from a new `Character` base class in `src/character.py`.
44
- - Move the API client logic into the appropriate tool files (e.g., `academic.py`).
45
- - Move the experimental scripts (`blah.py`, etc.) to an `/experimental` directory as planned.
46
-
47
- 2. **Implement the `CouncilOrchestrator` Skeleton:** Create the `CouncilOrchestrator` class with placeholder methods as described in the guide. This will be the central hub of the application.
48
-
49
- 3. **Build a Basic Gradio UI:** Implement a minimal version of the Gradio interface. The goal for "Days 1-2" is to be able to ask a question and see Corvus respond. This will involve:
50
- - Creating the chat UI in `src/ui/interface.py`.
51
- - Wiring the UI to the `CouncilOrchestrator`.
52
- - Having the orchestrator call the `Corvus` character.
53
-
54
- 4. **Fix the Broken Import:** The `formatting.py` file is still empty, which blocks `CorvusMCP` from running. Implement the `snippet_abstract` and `format_authors` functions so the character's output can be displayed.
55
-
56
- 5. **Write Tests:** As you build these components, write basic unit tests as planned. A test for the `AgentMemory` and a simple integration test for the orchestrator calling Corvus would be a great start.
57
-
58
- ### Future Expectations
59
-
60
- Following the development guide, I expect to see the project evolve rapidly. After establishing the core framework and getting Corvus to respond in the UI, the next logical steps will be to add the `Magpie` character and implement the staggered, interactive conversation flow described in the guide.
61
-
62
- The project is ambitious for a hackathon, but the detailed plan makes it achievable. The key will be to follow the phased approach, ensuring each component is functional before moving to the next. The current code is a good, albeit small, first step on that path.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
reviews/code_review_2025-11-15_16-00-00.md DELETED
@@ -1,37 +0,0 @@
1
- # Code Review: Corvid Council (Fourth Pass)
2
-
3
- **Date:** 2025-11-15
4
-
5
- ## a) High-Level Summary
6
-
7
- This review covers the latest changes to the "Corvid Council" repository, which are primarily administrative and structural. The updates focus on preparing the project for deployment on Hugging Face Spaces and aligning the repository with standard Python project practices.
8
-
9
- Key changes include the addition of a `README.md`, a `.gitignore` file, and a `requirements.txt` file. The `README.md` now effectively serves as the project's development guide, formalizing the vision and architecture. A placeholder Gradio application has also been added. While there are no major changes to the core agent logic, these updates represent a significant step towards professionalizing the repository and making it ready for collaborative development and deployment.
10
-
11
- ## b) Detailed Description of Changes
12
-
13
- - **`README.md`:** The repository now has a comprehensive `README.md` file. It is a copy of the "Corvid Council - Development Guide," which clearly outlines the project's concept, architecture, character archetypes, and development plan. This is an excellent addition that makes the project's goals clear to any new contributor.
14
- - **`requirements.txt`:** A `requirements.txt` file has been added, listing the project's dependencies: `feedparser`, `requests`, `gradio`, and `huggingface_hub`. This is essential for ensuring a reproducible environment, especially for deployment on Hugging Face Spaces.
15
- - **`.gitignore`:** A standard Python `.gitignore` file has been included. This is a crucial piece of repository hygiene that prevents unnecessary files (like `__pycache__`, `.env`, virtual environment folders) from being committed to version control. It also correctly ignores the `cache.json` and `memory.json` files.
16
- - **`src/gradio/app.py`:** A basic Gradio application has been added. It currently contains boilerplate code for a `ChatInterface` that connects to an OpenAI model on the Hugging Face Hub. It is not yet integrated with the Corvid Council agents, but it provides the entry point for building the user-facing UI.
17
- - **No Core Logic Changes:** The core logic in `src/characters/corvus.py` and `src/cluas_mcp/common/memory.py` remains unchanged from the previous review.
18
-
19
- ## c) Opinionated Breakdown and Suggestions
20
-
21
- These administrative changes were much-needed and have significantly improved the project's structure and readiness. While not feature development, this work is critical for a healthy project.
22
-
23
- ### What's Good
24
-
25
- - **Excellent Documentation:** The `README.md` is now the project's single source of truth for its vision and plan. This is a best practice that was correctly prioritized.
26
- - **Ready for Deployment:** With `requirements.txt` and a basic `app.py`, the project is now technically deployable to Hugging Face Spaces, which is a key part of the hackathon plan.
27
- - **Improved Repository Hygiene:** The `.gitignore` file cleans up the repository and prevents common issues with sensitive data or unnecessary files in version control.
28
-
29
- ### Suggestions for Next Steps
30
-
31
- The project is now well-positioned to continue with the "Week 1" development plan. The next steps should be to:
32
-
33
- 1. **Integrate the Core Logic with Gradio:** The immediate priority is to replace the boilerplate `respond` function in `src/gradio/app.py` with the logic from the `CouncilOrchestrator` (which still needs to be created). The goal is to have user input from the Gradio interface trigger the `Corvus` character's `search_papers` method.
34
- 2. **Continue the Refactoring:** As suggested in the previous review, continue refactoring the code to match the target file structure outlined in the `README.md`. This will involve creating the `CouncilOrchestrator` and the `Character` base class.
35
- 3. **Fix the `formatting.py` file:** This remains a blocker. The `snippet_abstract` and `format_authors` functions need to be implemented for the `Corvus` character to work.
36
-
37
- This round of changes was a successful and necessary step in maturing the project. The focus can now shift back to implementing the core features on this much stronger foundation.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
reviews/code_review_2025-11-18_10-00-00.md DELETED
@@ -1,61 +0,0 @@
1
- # Code Review: Corvid Council - Initial Scaffolding
2
-
3
- **Date:** 2025-11-18
4
-
5
- ## a) High-Level Summary
6
-
7
- The Corvid Council project is in its nascent stages, with the foundational scaffolding for a multi-agent research system. The core concept is strong: a group of specialized AI agents collaborating to research and discuss topics, retaining knowledge over time in a shared memory. The initial code sets up a key character, `Corvus`, responsible for academic searches, and a crucial `AgentMemory` component for persistence. However, the project is not yet functional due to missing implementations and architectural gaps. The immediate blockers are broken imports and placeholder API clients, which prevent the `Corvus` agent from executing its primary function.
8
-
9
- ## b) Detailed Component Description
10
-
11
- ### `src/characters/corvus.py`
12
-
13
- This file defines the `CorvusMCP` class, the first of the AI agents.
14
-
15
- * **Functionality:** Its main purpose is to `search_papers` across multiple academic databases (PubMed, Semantic Scholar, arXiv) in a fallback sequence.
16
- * **Integration:** It correctly initializes and uses the `AgentMemory` system by logging every paper it finds. This is a good early integration, ensuring that the agent's "discoveries" contribute to the collective knowledge base.
17
- * **Issue:** The tool is currently broken. It imports `format_authors` and `snippet_abstract` from `src/cluas_mcp/common/formatting.py`, which is an empty file. This will cause a runtime `ImportError`.
18
- * **Structure:** The class is straightforward, but it could benefit from being derived from a common `Character` base class in the future to standardize agent interfaces.
19
-
20
- ### `src/cluas_mcp/common/memory.py`
21
-
22
- This is arguably the most complete and well-realized component so far.
23
-
24
- * **Functionality:** The `AgentMemory` class provides a simple but effective JSON-backed database for the agents. It allows adding items, retrieving them based on recency or tags, and searching by title.
25
- * **Persistence:** It handles file I/O for reading and writing to a `memory.json` file, ensuring that the council's knowledge persists between sessions.
26
- * **Design:** The use of a dictionary with lowercase titles as keys is a simple but effective way to avoid duplicate entries and update reference timestamps. The methods for adding, retrieving, and searching are clear and well-defined.
27
-
28
- ### `src/gradio/app.py`
29
-
30
- This file contains a boilerplate Gradio `ChatInterface`.
31
-
32
- * **Functionality:** It sets up a basic chat window and connects it to the Hugging Face Inference API.
33
- * **Issue:** It is completely disconnected from the Corvid Council agent system. The `respond` function is a generic chatbot implementation and does not interact with `Corvus` or the `AgentMemory`. This is expected at this early stage but is a key area for future development.
34
-
35
- ## c) Opinionated Breakdown & Future Development
36
-
37
- ### Current State
38
-
39
- The project has a good foundation but is more of an idea sketched in code than a working system. The separation of concerns is logical (characters, common utilities, UI), and the `AgentMemory` component is a solid start.
40
-
41
- The most significant issue is the lack of a central orchestrator. There is no "council," only a single, non-functional agent. The project's `README.md` and `gemini.md` files clearly outline a vision that the code has not yet begun to implement.
42
-
43
- ### Suggestions for Future Development
44
-
45
- 1. **Fix the `Corvus` Agent:** The immediate priority is to implement the missing functions in `formatting.py` (`format_authors` and `snippet_abstract`). These could be simple implementations to start (e.g., `", ".join(authors)` and `abstract[:250] + "..."`).
46
-
47
- 2. **Implement API Clients:** The `gemini.md` file notes that the API clients are stubs. These need to be implemented to make `Corvus` functional. This involves writing the logic to make actual HTTP requests to PubMed, Semantic Scholar, and arXiv and parse their responses.
48
-
49
- 3. **Create a `CouncilOrchestrator`:** This is the most critical missing piece. A central class is needed to:
50
- * Manage the roster of agents (Corvus, Magpie, etc.).
51
- * Receive a user's query.
52
- * Mediate the conversation between agents (e.g., pass the query to Corvus, then pass Corvus's findings to another agent for critique).
53
- * Synthesize the final response for the user.
54
-
55
- 4. **Develop a `Character` Base Class:** To ensure all agents have a consistent interface, a `Character` abstract base class would be beneficial. It could define a common method like `process_query(query: str, context: List[str]) -> str` that each specialized agent would implement.
56
-
57
- 5. **Integrate Logic with Gradio UI:** Once the orchestrator is in place, the `respond` function in `gradio/app.py` should be rewritten to call the orchestrator instead of the generic Inference API. The chat history would represent the ongoing discussion of the council.
58
-
59
- 6. **Flesh out Other Characters:** After establishing the core architecture, the other characters (Magpie, Raven, Crow) can be created, each with their own specialized tools and "personalities."
60
-
61
- In summary, the project has a promising conceptual foundation. The next steps should focus on building the core architectural components that will allow the agents to interact and turning the placeholder code into functional modules.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
reviews/code_review_2025-11-18_10-30-00.md DELETED
@@ -1,50 +0,0 @@
1
- # Code Review: Corvid Council - Path to Functionality
2
-
3
- **Date:** 2025-11-18
4
-
5
- ## a) High-Level Summary
6
-
7
- The Corvid Council project has made a significant leap forward from a conceptual skeleton to a project with a clear and actionable roadmap. The addition of comprehensive development guides (`notes_etc/`) provides an excellent framework for the project's architecture, agent design, and phased implementation. Most importantly, a critical bug has been fixed by implementing the functions in `src/cluas_mcp/common/formatting.py`, which unblocks the `Corvus` agent. While the system is not yet fully operational (pending API client implementations and an orchestrator), it has moved from a state of being blocked to having a clear path toward achieving its "Week 1" goals.
8
-
9
- ## b) Detailed Component Description
10
-
11
- ### `notes_etc/development_guide_01.md`
12
-
13
- This new document is the most important addition to the repository. It serves as the project's bible.
14
-
15
- * **Content:** It lays out the entire vision, from the high-level concept and architecture to the specific personalities and toolsets of the four agents (Corvus, Magpie, Raven, Crow). It also defines the external MCP tools the app will expose, the core software components (`CouncilOrchestrator`, `Character` base class), and a detailed week-by-week development plan.
16
- * **Impact:** This guide provides invaluable clarity. It solidifies the project's goals and gives a strong indication of the intended structure, which was previously only hinted at in the `README.md`. It's an excellent piece of planning documentation.
17
-
18
- ### `src/cluas_mcp/common/formatting.py`
19
-
20
- This file has been changed from an empty placeholder to a functional module.
21
-
22
- * **Functionality:** It now contains `snippet_abstract` and `format_authors`. These functions provide basic but sensible text formatting, ensuring that data retrieved by agents can be presented cleanly.
23
- * **Impact:** This is a critical fix. It resolves the `ImportError` that previously prevented the `CorvusMCP` tool from running. The `Corvus` agent, while still dependent on placeholder API clients, is now executable.
24
-
25
- ### `src/characters/corvus.py`
26
-
27
- The `Corvus` agent is now in a much better state due to the fix in `formatting.py`.
28
-
29
- * **State:** The code is unchanged, but its context has changed. It can now successfully call the formatting functions.
30
- * **Next Blocker:** The primary blocker for `Corvus` is now the placeholder API clients (`PubMedClient`, `SemanticScholarClient`, etc.) defined in `src/cluas_mcp/common/api_clients.py`. Until these are implemented to make real API calls, `Corvus` cannot retrieve any data.
31
-
32
- ## c) Opinionated Breakdown & Future Development
33
-
34
- ### Current State
35
-
36
- The project is now on solid ground. The combination of a detailed plan and a critical bug fix has transformed it from a collection of disconnected files into a project with a clear trajectory. The immediate blockers are well-defined, and the long-term vision is compelling. The core challenge remains the same: building the central nervous system of the application—the `CouncilOrchestrator`—and connecting it to the agents and the UI.
37
-
38
- ### Suggestions for Future Development
39
-
40
- The development plan laid out in `notes_etc/development_guide_01.md` is excellent and should be followed closely. My suggestions are aligned with it:
41
-
42
- 1. **Implement API Clients:** The immediate next step must be to implement the real API call logic in `src/cluas_mcp/common/api_clients.py`. Start with one (e.g., `PubMedClient` or `ArxivClient`) to get `Corvus` working end-to-end, even if it's just one data source.
43
-
44
- 2. **Build the `CouncilOrchestrator` Skeleton:** As per the guide, create `src/orchestrator.py`. It doesn't need to be fully featured at first. A simple version that can take a query, pass it to the `Corvus` agent, and get a result would be a huge step forward.
45
-
46
- 3. **Create the `Character` Base Class:** Create the `src/character.py` file with an abstract base class. Refactor `CorvusMCP` to inherit from this class. This will establish a pattern that will make adding the other three agents much easier.
47
-
48
- 4. **Basic UI Integration:** Wire the `CouncilOrchestrator` to the Gradio UI. The `respond` function in `src/gradio/app.py` should be updated to call `orchestrator.process_query(message)`. This will achieve the "Day 1-2" goal from the development guide: "Ask 'Are crows smart?', see Corvus search papers and respond with citation."
49
-
50
- The project is well-positioned for rapid progress. By focusing on the "Week 1" goals from the new development guide, the core functionality of a single-agent system can be achieved quickly, providing a solid foundation for the more complex multi-agent interactions to come.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
reviews/code_review_2025-11-18_16-00-00.md DELETED
@@ -1,42 +0,0 @@
1
- # Code Review: Corvid Council - Implementation Begins
2
-
3
- **Date:** 2025-11-18
4
-
5
- ## a) High-Level Summary
6
-
7
- The project has successfully transitioned from planning and bug-fixing to active feature development. The most critical blocker—the lack of functional API clients—is being directly addressed. The `ArxivClient` is now fully implemented, and the `PubMedClient` is halfway there. This marks a significant turning point for the project, as the `Corvus` agent now has access to real, structured data from at least one source. While the overall architecture (orchestrator, character base class) is still missing, this concrete progress on the data layer is a crucial and encouraging step towards a functional application.
8
-
9
- ## b) Detailed Component Description
10
-
11
- ### `src/cluas_mcp/common/api_clients.py`
12
-
13
- This file is now the center of the project's momentum. It has evolved from a collection of placeholders and comments into a partially functional module.
14
-
15
- * **`ArxivClient`:** This class is now fully implemented and serves as a great template for the other clients. It correctly formulates a URL for the arXiv API, uses `requests` to get the data, and then uses the `feedparser` library to parse the resulting Atom XML feed. It demonstrates a complete, end-to-end data retrieval process: query -> fetch -> parse -> structure.
16
-
17
- * **`PubMedClient`:** This class has seen significant progress.
18
- * The `pubmed_search` method implements the first half of the required two-step process. It intelligently constructs a search term from keywords and uses the `esearch.fcgi` endpoint to retrieve a list of article IDs.
19
- * The `parse_id_list` helper method correctly uses `xml.etree.ElementTree` to parse the XML response and extract the IDs.
20
- * **Missing Piece:** The client still needs an `efetch` method to take these IDs and retrieve the full article details.
21
-
22
- * **`SemanticScholarClient`:** This remains a placeholder and is the last remaining data source to be implemented for the `Corvus` agent.
23
-
24
- ## c) Opinionated Breakdown & Future Development
25
-
26
- ### Current State
27
-
28
- This is the most positive review yet. The "brutal" assessment from our last exchange has been met with direct action. The project is no longer just a collection of plans; it has a working data pipeline for one of its key components. This is a classic example of "bottom-up" implementation: building the foundational data layers first before wiring them into the higher-level application logic.
29
-
30
- The `Corvus` agent is now theoretically capable of searching arXiv, which is a major milestone. The immediate next steps are very clear and build directly on the work that has just been completed.
31
-
32
- ### Suggestions for Future Development
33
-
34
- 1. **Complete the `PubMedClient`:** The highest priority is to finish what was started. Create a new method, perhaps `fetch_paper_details(ids: List[str])`, that takes the output from `pubmed_search`, calls the `efetch.fcgi` endpoint, and parses the resulting XML to extract the title, abstract, authors, and DOI for each paper. This will bring the `PubMedClient` to parity with the `ArxivClient`.
35
-
36
- 2. **Refactor `corvus.py`:** Modify the `CorvusMCP.search_papers` method to use the newly implemented clients. The fallback logic (`try PubMed -> fallback to arXiv`) can now be implemented with real function calls. This will make the `Corvus` agent fully data-functional.
37
-
38
- 3. **Implement `SemanticScholarClient`:** Tackle the final placeholder client to complete the data-gathering capabilities of `Corvus`.
39
-
40
- 4. **Shift Focus to the Orchestrator:** Once `Corvus` is fully functional, the focus must shift to the application layer. It's time to create the `CouncilOrchestrator` and the `Character` base class as outlined in the development guides. The goal should be to create the simplest possible loop: a query comes in, the orchestrator passes it to `Corvus`, `Corvus` uses its new tools to get real data, and the result is printed or returned.
41
-
42
- The project has gained significant momentum. The key is to maintain it by immediately building on this success to complete the data layer and then moving up the stack to the application logic.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
reviews/code_review_2025-11-18_17-00-00.md DELETED
@@ -1,52 +0,0 @@
1
- # Code Review: Corvid Council - A Move Towards Robustness
2
-
3
- **Date:** 2025-11-18
4
-
5
- ## a) High-Level Summary
6
-
7
- The project has undergone a significant and positive architectural refactoring. The creation of a dedicated `academic` submodule for the `PubMedClient` and the introduction of a shared, resilient HTTP utility (`common/http.py`) show a clear move towards building a more organized, maintainable, and robust application. This addresses not just functionality but also code quality and long-term health. However, this refactoring is incomplete, having introduced significant code duplication that must be resolved.
8
-
9
- ## b) Detailed Component Description
10
-
11
- ### `src/cluas_mcp/common/http.py`
12
-
13
- This new file is a major architectural improvement.
14
-
15
- * **Functionality:** It provides a `fetch_with_retry` function that wraps `requests.get` with a sophisticated retry mechanism using the `tenacity` library. It correctly implements exponential backoff and is configured to retry on transient HTTP errors (5xx status codes) and rate-limiting responses (HTTP 429), which is crucial for consuming public APIs.
16
- * **Impact:** This utility makes the entire system more resilient. By centralizing this logic, any API client that uses it automatically becomes more robust without duplicating code.
17
-
18
- ### `src/cluas_mcp/academic/pubmed_client.py`
19
-
20
- This new file represents a structural improvement, aligning the codebase with the planned architecture.
21
-
22
- * **Functionality:** It contains the `PubMedClient` and its `pubmed_search` method, which is responsible for querying the PubMed `esearch` endpoint.
23
- * **Improvement:** It has been updated to use the new `fetch_with_retry` function, immediately benefiting from the added resilience.
24
-
25
- ### `src/cluas_mcp/common/api_clients.py`
26
-
27
- This file is now a source of technical debt.
28
-
29
- * **State:** It still contains the old `PubMedClient` implementation, which is now obsolete. The `ArxivClient` and the placeholder `SemanticScholarClient` also remain here.
30
- * **Problem:** The presence of the old `PubMedClient` creates direct code duplication. It's unclear to a new developer which client is the correct one to use, and imports could easily point to the wrong one.
31
-
32
- ## c) Opinionated Breakdown & Future Development
33
-
34
- ### Current State
35
-
36
- The project is in a state of healthy, if incomplete, transition. The decision to refactor the API clients into separate modules and create a shared HTTP utility is excellent. It shows that the developer is thinking not just about "what" the code does, but "how" it should be structured for maintainability.
37
-
38
- However, the job is only half-done. The lingering code duplication in `api_clients.py` is a significant problem that negates some of the benefits of the refactoring. It's a textbook example of technical debt that should be paid down immediately before it causes problems.
39
-
40
- ### Suggestions for Future Development
41
-
42
- 1. **Complete the Refactoring (Immediate Priority):**
43
- * **Delete the old `PubMedClient`** from `src/cluas_mcp/common/api_clients.py`.
44
- * **Move the `ArxivClient`** from `api_clients.py` to a new file: `src/cluas_mcp/academic/arxiv_client.py`. Update it to use the `fetch_with_retry` function as well.
45
- * **Move the `SemanticScholarClient`** placeholder to `src/cluas_mcp/academic/semantic_scholar_client.py`.
46
- * **Delete the `api_clients.py` file** entirely, as its purpose has been superseded by the new `academic` submodule.
47
-
48
- 2. **Complete the `PubMedClient` Functionality:** Once the refactoring is clean, return to implementing the `efetch` logic in `src/cluas_mcp/academic/pubmed_client.py`. Create the method that takes the list of IDs and fetches the full paper details.
49
-
50
- 3. **Update `corvus.py` Imports:** After the refactoring, the `import` statements at the top of `src/characters/corvus.py` will be broken. They will need to be updated to point to the new locations of the client classes (e.g., `from ..cluas_mcp.academic.pubmed_client import PubMedClient`).
51
-
52
- The project is making great strides in code quality. The immediate task is to complete the cleanup from this recent refactoring to solidify the architectural gains before moving on to the next feature.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
reviews/code_review_2025-11-18_18-00-00.md DELETED
@@ -1,55 +0,0 @@
1
- # Code Review: Corvid Council - A Milestone Achieved
2
-
3
- **Date:** 2025-11-18
4
-
5
- ## a) High-Level Summary
6
-
7
- The project has undergone a transformative set of changes, evolving from a scattered collection of scripts into a well-structured, tested, and robust data access layer. The previous architectural issues have been fully addressed through a major refactoring. The API clients are now modular, the `PubMedClient` is feature-complete, and the entire system is supported by a resilient HTTP utility. Most importantly, the addition of a test suite marks a pivotal moment in the project's lifecycle, providing a foundation for reliable and scalable development. The data-gathering toolkit for the `Corvus` agent is now effectively complete.
8
-
9
- ## b) Detailed Component Description
10
-
11
- ### `src/cluas_mcp/academic/` (Submodule)
12
-
13
- This new submodule is the heart of the recent changes and represents the project's data access layer.
14
-
15
- * **`pubmed.py` & `arxiv.py`:** These files contain the clean, refactored, and feature-complete clients for their respective services. They are focused, easy to understand, and leverage the shared `http.py` utility. The `PubMedClient` now correctly implements the full `esearch` -> `efetch` workflow.
16
- * **`academic_search.py`:** This file introduces the "Facade" design pattern to the project. The `academic_search` function provides a single, simple interface for querying all underlying academic sources. This decouples the agent logic from the specifics of each API client, which is an excellent architectural choice that will make the `Corvus` agent much simpler to implement.
17
-
18
- ### `src/cluas_mcp/domain/` (Submodule)
19
-
20
- * **`keywords.py`:** The creation of this submodule to hold domain-specific data (like lists of corvid-related keywords) is a sign of a maturing codebase. It properly separates data from logic, making the system easier to configure and maintain.
21
-
22
- ### `tests/` (Directory)
23
-
24
- The addition of `test_arxiv.py` and `test_pubmed.py` is the most significant improvement in this round of changes.
25
-
26
- * **Functionality:** These files provide simple but effective integration tests for the API clients. They make real calls to the APIs and perform basic validation on the results.
27
- * **Impact:** This is a critical best practice. These tests ensure that the data sources—the very foundation of the application—are working as expected. They prevent regressions and provide confidence for future modifications.
28
-
29
- ## c) Opinionated Breakdown & Future Development
30
-
31
- ### Current State
32
-
33
- The project is now in an excellent state. The "brutal" feedback from earlier has been met with a flurry of high-quality work that has not only fixed the identified problems but has exceeded expectations by introducing tests and better architectural patterns. The technical debt from the initial exploratory phase has been paid down, and the foundation is now solid.
34
-
35
- The `Corvus` agent has a complete, robust, and tested toolkit at its disposal. The project has successfully completed the "build the data layer" phase and is now perfectly positioned to move up the stack to the application logic.
36
-
37
- ### Suggestions for Future Development
38
-
39
- The path forward is clearer than ever. The focus should now be entirely on using the new data layer to bring the agents to life.
40
-
41
- 1. **Finalize Cleanup:** Delete the obsolete `src/cluas_mcp/common/api_clients.py` file to complete the refactoring.
42
-
43
- 2. **Integrate the Data Layer into `Corvus`:**
44
- * Modify `src/characters/corvus.py`.
45
- * Remove the direct imports for the old clients.
46
- * Import and use the new `academic_search` facade from `src/cluas_mcp/academic/academic_search.py`.
47
- * The logic inside `CorvusMCP.search_papers` should now be incredibly simple: it just needs to call `academic_search` and process the unified results.
48
-
49
- 3. **Build the `CouncilOrchestrator`:** With `Corvus` now fully functional, it is time to build the central nervous system.
50
- * Create `src/orchestrator.py` as planned in the development guide.
51
- * Implement a basic `process_query` method that initializes `Corvus` and calls its `search_papers` method.
52
-
53
- 4. **Connect to the UI:** Wire the new `CouncilOrchestrator` to the Gradio app. The `respond` function in `src/gradio/app.py` should do nothing more than pass the user's message to the orchestrator and display the result.
54
-
55
- Achieving these next steps will fulfill the project's "Week 1, Day 1-2" goal and create a true end-to-end, functional application for the first time. The project has tremendous momentum; maintaining it through these next steps will bring the vision to life.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
reviews/code_review_2025-11-18_19-00-00.md DELETED
@@ -1,54 +0,0 @@
1
- # Code Review: Corvid Council - Data Layer Complete
2
-
3
- **Date:** 2025-11-18
4
-
5
- ## a) High-Level Summary
6
-
7
- This update marks the completion of the project's data access layer. The final placeholder, the `SemanticScholarClient`, has been expertly implemented and tested, bringing the `academic` submodule to 100% functionality. The `Corvus` agent's entire toolkit for gathering academic information is now in place, is architecturally sound, and is validated by a suite of tests. This is a major milestone that concludes the foundational phase of the project and clears the way for development to move up the stack to the agent and application logic.
8
-
9
- ## b) Detailed Component Description
10
-
11
- ### `src/cluas_mcp/academic/semantic_scholar.py`
12
-
13
- The final piece of the puzzle. This module completes the data access toolkit.
14
-
15
- * **Functionality:** The `SemanticScholarClient` correctly queries the official API, requests a specific set of fields to keep the payload light, and normalizes the JSON response into the project's standard dictionary format. It intelligently determines if a paper is a preprint or peer-reviewed based on the presence of a DOI.
16
- * **Quality:** The implementation is clean, robust (leveraging the shared `fetch_with_retry` utility), and consistent with the other clients.
17
-
18
- ### `tests/test_semantic_scholar.py`
19
-
20
- This new test file demonstrates a continued commitment to quality.
21
-
22
- * **Functionality:** It provides a solid integration test for the new client, asserting that the response format is correct and that key fields are present. This ensures the client is not only working now but will be protected against future regressions.
23
-
24
- ### `src/cluas_mcp/academic/academic_search_entry.py`
25
-
26
- The facade for the `academic` module is now fully operational.
27
-
28
- * **Functionality:** With all underlying clients implemented, this entry point can now successfully query PubMed, ArXiv, and Semantic Scholar in parallel, providing a rich, aggregated set of results from a single function call.
29
-
30
- ## c) Opinionated Breakdown & Future Development
31
-
32
- ### Current State
33
-
34
- The project's data access layer is now a model of good software engineering practices. It is:
35
- * **Modular:** Each client is in its own file.
36
- * **Robust:** It uses a shared, resilient HTTP client with retries.
37
- * **Tested:** Each client has a corresponding integration test.
38
- * **Well-abstracted:** A single facade provides a clean entry point for the rest of the application.
39
-
40
- This part of the project can be considered "done" for the initial MVP. It is a production-ready component. The contrast between the current state and the state of the project just a few hours ago is dramatic and speaks to a period of highly effective development.
41
-
42
- ### Suggestions for Future Development
43
-
44
- The to-do list has become very short and focused. The project is at a clear inflection point.
45
-
46
- 1. **DELETE `src/cluas_mcp/common/api_clients.py` (Immediate):** This is the last piece of technical debt from the refactoring. Deleting this obsolete file is a zero-effort task that will officially close the chapter on the initial, messy structure.
47
-
48
- 2. **Build the Application Layer (The Only Remaining Task):** The entire focus must now shift to using the newly completed data layer. The following steps, outlined in previous reviews and the development guide, are now unlocked and should be pursued in order:
49
- * **Integrate into `Corvus`:** Refactor `src/characters/corvus.py` to use the `academic_search_entry.py` facade.
50
- * **Build `CouncilOrchestrator`:** Create the orchestrator to manage the agent(s).
51
- * **Build `Character` Base Class:** Create a base class to standardize the agent interface.
52
- * **Connect to UI:** Wire the orchestrator to the Gradio app.
53
-
54
- The project has successfully built a powerful and reliable engine. Now it's time to build the car around it.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
reviews/code_review_2025-11-18_20-00-00.md DELETED
@@ -1,47 +0,0 @@
1
- # Code Review: Corvid Council - Data Layer Hardened and Finalized
2
-
3
- **Date:** 2025-11-18
4
-
5
- ## a) High-Level Summary
6
-
7
- This latest update represents the final hardening and cleanup of the project's data access layer. The test suite has been refactored into a more mature, layered structure, the central search facade has been made more resilient, and the last piece of technical debt from the initial refactoring has been eliminated. The `academic` module is now a production-grade component, marking the definitive end of the foundational phase of development. The project is in an ideal state to pivot entirely to building the agent and application logic.
8
-
9
- ## b) Detailed Component Description
10
-
11
- ### Test Suite (`tests/`)
12
-
13
- The testing infrastructure has seen a significant and professional upgrade.
14
-
15
- * **Layered Structure:** The tests are now organized into `tests/clients/` and `tests/integration/`. This is an excellent practice that separates focused, client-level tests from higher-level tests that verify the interaction between multiple components.
16
- * **Integration Test:** The new `test_academic_search_entrypoint.py` is a perfect example of a good integration test. It uses mocking to isolate the search facade and verify two key behaviors: that it correctly calls all underlying clients, and that it gracefully handles an exception from one client without crashing.
17
- * **Configuration:** The addition of `tests/conftest.py` to manage the test environment's system path is a standard and robust solution for making the test suite reliable and easy to run.
18
-
19
- ### `src/cluas_mcp/academic/academic_search_entrypoint.py`
20
-
21
- The search facade has been made more resilient.
22
-
23
- * **Error Handling:** The function now wraps each client call in a `try...except` block. This ensures that a failure from a single source (e.g., the PubMed API is down) does not prevent the function from returning successful results from the other sources. This is a crucial feature for a system that depends on multiple external services.
24
-
25
- ### Code Cleanup
26
-
27
- * **`api_clients.py` Deleted:** The obsolete `src/cluas_mcp/common/api_clients.py` file has been deleted. This small change has a large impact, as it removes ambiguity and finalizes the major refactoring effort, leaving the codebase clean and easy to navigate.
28
-
29
- ## c) Opinionated Breakdown & Future Development
30
-
31
- ### Current State
32
-
33
- The project is in its best state yet. The data access layer is not just "done," it is well-crafted, resilient, and thoroughly tested. The investment in the testing infrastructure will pay dividends throughout the rest of the development cycle, allowing for faster and more confident changes. The developer has successfully navigated the transition from rapid prototyping to building stable, high-quality components.
34
-
35
- There are no remaining issues or concerns with the foundational code. The table is perfectly set for the next course.
36
-
37
- ### Suggestions for Future Development
38
-
39
- The focus must now shift entirely to the application layer. The next steps are clear and build directly upon the solid foundation that has been established.
40
-
41
- 1. **Integrate the Facade into `Corvus`:** This is the immediate next step. Refactor `src/characters/corvus.py` to import and use the `academic_search` function from the entrypoint. The agent's internal logic will become dramatically simpler, as it no longer needs to know about the individual clients.
42
-
43
- 2. **Build the `CouncilOrchestrator`:** With a fully functional `Corvus` agent, the orchestrator can now be built to manage it. Create `src/orchestrator.py` and implement the logic to receive a query and dispatch it to the `Corvus` agent.
44
-
45
- 3. **Connect to the UI:** Wire the orchestrator to the Gradio app in `src/gradio/app.py`. This will create the first end-to-end functional slice of the application, where a user can type a query into the UI and see real, structured data from three academic sources returned.
46
-
47
- The project has excellent momentum and a very clear path forward. Executing on these next steps will finally bring the core vision of the application to life.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/characters/corvus.py CHANGED
@@ -1,100 +0,0 @@
1
- import time
2
- from typing import List, Optional, Dict
3
- from ..cluas_mcp.common.memory import AgentMemory
4
- from ..cluas_mcp.academic.academic_search_entrypoint import academic_search
5
- from ..cluas_mcp.common.formatting import format_authors, snippet_abstract
6
-
7
- # init shared council memory
8
- memory = AgentMemory(memory_file="src/data/memory.json")
9
-
10
- class CorvusMCP:
11
- """
12
- Corvus MCP tool: searches academic literature and returns structured results.
13
- Automatically logs items into shared agent memory for council context.
14
- """
15
-
16
- def search_papers(
17
- self,
18
- query: str,
19
- max_results: int = 5, # Note: max_results is not currently passed to the facade
20
- memory_days: int = 30
21
- ) -> List[Dict]:
22
- """
23
- Search academic papers for a query string.
24
- Returns a list of dicts:
25
- {
26
- "title": str,
27
- "abstract": str,
28
- "authors": str,
29
- "published": str,
30
- "doi": Optional[str],
31
- "link": str
32
- }
33
- """
34
-
35
- # --- 1. Call the academic search facade ---
36
- all_results = academic_search(query)
37
-
38
- # --- 2. Combine results from all sources ---
39
- results = []
40
- for source in all_results.values():
41
- results.extend(source)
42
-
43
- # --- 3. Clean, format, and log results to memory ---
44
- cleaned = []
45
- for r in results:
46
- title = r.get("title", "Untitled")
47
- abstract = snippet_abstract(r.get("abstract", ""))
48
- # The new clients provide 'author_str' directly
49
- authors = r.get("author_str") or format_authors(r.get("authors", []))
50
- doi = r.get("doi")
51
- link = r.get("link", r.get("arxiv_link", r.get("pubmed_link", "")))
52
-
53
- # log into memory
54
- memory.add_item(
55
- title=title,
56
- doi=doi,
57
- snippet=abstract,
58
- mentioned_by="Corvus",
59
- tags=["academic_search", r.get("source", "unknown")]
60
- )
61
-
62
- cleaned.append({
63
- "title": title,
64
- "abstract": abstract,
65
- "authors": authors,
66
- "published": r.get("published", r.get("year", "")),
67
- "doi": doi,
68
- "link": link
69
- })
70
-
71
- # --- 4. Include recent memory items optionally ---
72
- recent_memory = memory.get_recent(days=memory_days)
73
- for item in recent_memory:
74
- if item["title"] not in [c["title"] for c in cleaned]:
75
- cleaned.append({
76
- "title": item["title"],
77
- "abstract": item.get("snippet", ""),
78
- "authors": "", # optional: keep minimal
79
- "published": "",
80
- "doi": item.get("doi"),
81
- "link": item.get("doi") or "",
82
- })
83
-
84
- return cleaned
85
-
86
-
87
-
88
-
89
-
90
- # poss usage example:
91
-
92
- # from src.characters.corvus import CorvusMCP
93
-
94
- # corvus = CorvusMCP()
95
- # papers = corvus.search_papers("corvid cognition", max_results=3)
96
-
97
- # for p in papers:
98
- # print(f"{p['title']} ({p.get('doi')})")
99
- # print(f"Snippet: {p['abstract']}\n")
100
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/cluas_mcp/server.py CHANGED
@@ -1,5 +1,21 @@
 
1
  import logging
2
 
3
  def main():
4
  logging.basicConfig(level=logging.INFO)
5
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import FastMCP
2
  import logging
3
 
4
  def main():
5
  logging.basicConfig(level=logging.INFO)
6
+ pass
7
+
8
+
9
+ @mcp.tool()
10
+ async def search_academic_papers(query: str) -> str:
11
+ """Search academic papers across PubMed, Semantic Scholar, and ArXiv.
12
+
13
+ Args:
14
+ query: Search term for academic papers
15
+
16
+ Returns:
17
+ JSON string with results from all three sources
18
+ """
19
+ # Call the existing academic_search function
20
+ # Format results as clean JSON string
21
+ # Include paper titles, authors, abstracts, DOIs where available
src/orchestrator.py CHANGED
@@ -1,45 +0,0 @@
1
- from typing import Dict, List
2
- from src.characters.corvus import CorvusMCP
3
-
4
- class CouncilOrchestrator:
5
- """
6
- Manages the council of agents and orchestrates the research process.
7
- """
8
-
9
- def __init__(self):
10
- """
11
- Initializes the orchestrator and the council members.
12
- For now, only Corvus is instantiated.
13
- """
14
- self.corvus = CorvusMCP()
15
- # In the future, other agents like Magpie, Raven, etc., will be initialized here.
16
-
17
- def process_query(self, query: str) -> List[Dict]:
18
- """
19
- Processes a user query by dispatching it to the relevant agents.
20
-
21
- For this initial implementation, it only calls Corvus.
22
-
23
- Args:
24
- query: The user's research query.
25
-
26
- Returns:
27
- A list of dictionaries containing the search results.
28
- """
29
- # In the future, this method will involve more complex logic,
30
- # such as selecting which agent to call first, facilitating debate,
31
- # and synthesizing results.
32
-
33
- # For now, we just call Corvus directly.
34
- results = self.corvus.search_papers(query)
35
-
36
- return results
37
-
38
- # Example usage (for testing):
39
- # if __name__ == "__main__":
40
- # orchestrator = CouncilOrchestrator()
41
- # search_results = orchestrator.process_query("corvid cognition")
42
- # for paper in search_results:
43
- # print(f"Title: {paper['title']}")
44
- # print(f"Authors: {paper['authors']}")
45
- # print("-" * 20)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
uv.lock CHANGED
@@ -122,6 +122,15 @@ wheels = [
122
  { url = "https://files.pythonhosted.org/packages/f8/aa/5082412d1ee302e9e7d80b6949bc4d2a8fa1149aaab610c5fc24709605d6/authlib-1.6.5-py2.py3-none-any.whl", hash = "sha256:3e0e0507807f842b02175507bdee8957a1d5707fd4afb17c32fb43fee90b6e3a", size = 243608, upload-time = "2025-10-02T13:36:07.637Z" },
123
  ]
124
 
 
 
 
 
 
 
 
 
 
125
  [[package]]
126
  name = "brotli"
127
  version = "1.2.0"
@@ -150,6 +159,15 @@ wheels = [
150
  { url = "https://files.pythonhosted.org/packages/f5/10/56978295c14794b2c12007b07f3e41ba26acda9257457d7085b0bb3bb90c/brotli-1.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:e7c0af964e0b4e3412a0ebf341ea26ec767fa0b4cf81abb5e897c9338b5ad6a3", size = 375639, upload-time = "2025-11-05T18:38:55.67Z" },
151
  ]
152
 
 
 
 
 
 
 
 
 
 
153
  [[package]]
154
  name = "certifi"
155
  version = "2025.11.12"
@@ -262,6 +280,7 @@ name = "cluas"
262
  version = "0.1.0"
263
  source = { virtual = "." }
264
  dependencies = [
 
265
  { name = "feedparser" },
266
  { name = "gradio", extra = ["mcp", "oauth"] },
267
  { name = "mcp" },
@@ -272,6 +291,7 @@ dependencies = [
272
 
273
  [package.metadata]
274
  requires-dist = [
 
275
  { name = "feedparser", specifier = ">=6.0.12" },
276
  { name = "gradio", extras = ["mcp", "oauth"], specifier = "==6.0.0.dev4" },
277
  { name = "mcp", specifier = ">=1.20.0" },
@@ -345,6 +365,69 @@ wheels = [
345
  { url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" },
346
  ]
347
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
348
  [[package]]
349
  name = "fastapi"
350
  version = "0.121.2"
@@ -360,6 +443,32 @@ wheels = [
360
  { url = "https://files.pythonhosted.org/packages/eb/23/dfb161e91db7c92727db505dc72a384ee79681fe0603f706f9f9f52c2901/fastapi-0.121.2-py3-none-any.whl", hash = "sha256:f2d80b49a86a846b70cc3a03eb5ea6ad2939298bf6a7fe377aa9cd3dd079d358", size = 109201, upload-time = "2025-11-13T17:05:52.718Z" },
361
  ]
362
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
  [[package]]
364
  name = "feedparser"
365
  version = "6.0.12"
@@ -596,6 +705,48 @@ wheels = [
596
  { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" },
597
  ]
598
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
599
  [[package]]
600
  name = "jinja2"
601
  version = "3.1.6"
@@ -623,6 +774,21 @@ wheels = [
623
  { url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040, upload-time = "2025-08-18T17:03:48.373Z" },
624
  ]
625
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
626
  [[package]]
627
  name = "jsonschema-specifications"
628
  version = "2025.9.1"
@@ -635,6 +801,23 @@ wheels = [
635
  { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" },
636
  ]
637
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
638
  [[package]]
639
  name = "markdown-it-py"
640
  version = "4.0.0"
@@ -733,6 +916,15 @@ wheels = [
733
  { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
734
  ]
735
 
 
 
 
 
 
 
 
 
 
736
  [[package]]
737
  name = "numpy"
738
  version = "2.3.5"
@@ -785,6 +977,18 @@ wheels = [
785
  { url = "https://files.pythonhosted.org/packages/2d/fd/4b5eb0b3e888d86aee4d198c23acec7d214baaf17ea93c1adec94c9518b9/numpy-2.3.5-cp314-cp314t-win_arm64.whl", hash = "sha256:6203fdf9f3dc5bdaed7319ad8698e685c7a3be10819f41d32a0723e611733b42", size = 10545459, upload-time = "2025-11-16T22:52:20.55Z" },
786
  ]
787
 
 
 
 
 
 
 
 
 
 
 
 
 
788
  [[package]]
789
  name = "orjson"
790
  version = "3.11.4"
@@ -872,6 +1076,27 @@ wheels = [
872
  { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" },
873
  ]
874
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
875
  [[package]]
876
  name = "pillow"
877
  version = "11.3.0"
@@ -927,6 +1152,15 @@ wheels = [
927
  { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835, upload-time = "2025-07-01T09:15:50.399Z" },
928
  ]
929
 
 
 
 
 
 
 
 
 
 
930
  [[package]]
931
  name = "pluggy"
932
  version = "1.6.0"
@@ -936,6 +1170,44 @@ wheels = [
936
  { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
937
  ]
938
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
939
  [[package]]
940
  name = "pycparser"
941
  version = "2.23"
@@ -960,6 +1232,11 @@ wheels = [
960
  { url = "https://files.pythonhosted.org/packages/82/2f/e68750da9b04856e2a7ec56fc6f034a5a79775e9b9a81882252789873798/pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e", size = 463400, upload-time = "2025-11-05T10:50:06.732Z" },
961
  ]
962
 
 
 
 
 
 
963
  [[package]]
964
  name = "pydantic-core"
965
  version = "2.41.5"
@@ -1059,6 +1336,24 @@ crypto = [
1059
  { name = "cryptography" },
1060
  ]
1061
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1062
  [[package]]
1063
  name = "pytest"
1064
  version = "9.0.1"
@@ -1127,6 +1422,15 @@ wheels = [
1127
  { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" },
1128
  ]
1129
 
 
 
 
 
 
 
 
 
 
1130
  [[package]]
1131
  name = "pyyaml"
1132
  version = "6.0.3"
@@ -1165,15 +1469,15 @@ wheels = [
1165
 
1166
  [[package]]
1167
  name = "referencing"
1168
- version = "0.37.0"
1169
  source = { registry = "https://pypi.org/simple" }
1170
  dependencies = [
1171
  { name = "attrs" },
1172
  { name = "rpds-py" },
1173
  ]
1174
- sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" }
1175
  wheels = [
1176
- { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" },
1177
  ]
1178
 
1179
  [[package]]
@@ -1282,6 +1586,19 @@ wheels = [
1282
  { url = "https://files.pythonhosted.org/packages/2e/a3/0f0b7d78e2f1eb9e8e1afbff1d2bff8d60144aee17aca51c065b516743dd/safehttpx-0.1.7-py3-none-any.whl", hash = "sha256:c4f4a162db6993464d7ca3d7cc4af0ffc6515a606dfd220b9f82c6945d869cde", size = 8959, upload-time = "2025-10-24T18:30:08.733Z" },
1283
  ]
1284
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1285
  [[package]]
1286
  name = "semantic-version"
1287
  version = "2.10.0"
@@ -1457,3 +1774,23 @@ sdist = { url = "https://files.pythonhosted.org/packages/cb/ce/f06b84e2697fef468
1457
  wheels = [
1458
  { url = "https://files.pythonhosted.org/packages/ee/d9/d88e73ca598f4f6ff671fb5fde8a32925c2e08a637303a1d12883c7305fa/uvicorn-0.38.0-py3-none-any.whl", hash = "sha256:48c0afd214ceb59340075b4a052ea1ee91c16fbc2a9b1469cca0e54566977b02", size = 68109, upload-time = "2025-10-18T13:46:42.958Z" },
1459
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  { url = "https://files.pythonhosted.org/packages/f8/aa/5082412d1ee302e9e7d80b6949bc4d2a8fa1149aaab610c5fc24709605d6/authlib-1.6.5-py2.py3-none-any.whl", hash = "sha256:3e0e0507807f842b02175507bdee8957a1d5707fd4afb17c32fb43fee90b6e3a", size = 243608, upload-time = "2025-10-02T13:36:07.637Z" },
123
  ]
124
 
125
+ [[package]]
126
+ name = "beartype"
127
+ version = "0.22.6"
128
+ source = { registry = "https://pypi.org/simple" }
129
+ sdist = { url = "https://files.pythonhosted.org/packages/88/e2/105ceb1704cb80fe4ab3872529ab7b6f365cf7c74f725e6132d0efcf1560/beartype-0.22.6.tar.gz", hash = "sha256:97fbda69c20b48c5780ac2ca60ce3c1bb9af29b3a1a0216898ffabdd523e48f4", size = 1588975, upload-time = "2025-11-20T04:47:14.736Z" }
130
+ wheels = [
131
+ { url = "https://files.pythonhosted.org/packages/98/c9/ceecc71fe2c9495a1d8e08d44f5f31f5bca1350d5b2e27a4b6265424f59e/beartype-0.22.6-py3-none-any.whl", hash = "sha256:0584bc46a2ea2a871509679278cda992eadde676c01356ab0ac77421f3c9a093", size = 1324807, upload-time = "2025-11-20T04:47:11.837Z" },
132
+ ]
133
+
134
  [[package]]
135
  name = "brotli"
136
  version = "1.2.0"
 
159
  { url = "https://files.pythonhosted.org/packages/f5/10/56978295c14794b2c12007b07f3e41ba26acda9257457d7085b0bb3bb90c/brotli-1.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:e7c0af964e0b4e3412a0ebf341ea26ec767fa0b4cf81abb5e897c9338b5ad6a3", size = 375639, upload-time = "2025-11-05T18:38:55.67Z" },
160
  ]
161
 
162
+ [[package]]
163
+ name = "cachetools"
164
+ version = "6.2.2"
165
+ source = { registry = "https://pypi.org/simple" }
166
+ sdist = { url = "https://files.pythonhosted.org/packages/fb/44/ca1675be2a83aeee1886ab745b28cda92093066590233cc501890eb8417a/cachetools-6.2.2.tar.gz", hash = "sha256:8e6d266b25e539df852251cfd6f990b4bc3a141db73b939058d809ebd2590fc6", size = 31571, upload-time = "2025-11-13T17:42:51.465Z" }
167
+ wheels = [
168
+ { url = "https://files.pythonhosted.org/packages/e6/46/eb6eca305c77a4489affe1c5d8f4cae82f285d9addd8de4ec084a7184221/cachetools-6.2.2-py3-none-any.whl", hash = "sha256:6c09c98183bf58560c97b2abfcedcbaf6a896a490f534b031b661d3723b45ace", size = 11503, upload-time = "2025-11-13T17:42:50.232Z" },
169
+ ]
170
+
171
  [[package]]
172
  name = "certifi"
173
  version = "2025.11.12"
 
280
  version = "0.1.0"
281
  source = { virtual = "." }
282
  dependencies = [
283
+ { name = "fastmcp" },
284
  { name = "feedparser" },
285
  { name = "gradio", extra = ["mcp", "oauth"] },
286
  { name = "mcp" },
 
291
 
292
  [package.metadata]
293
  requires-dist = [
294
+ { name = "fastmcp", specifier = ">=2.13.1" },
295
  { name = "feedparser", specifier = ">=6.0.12" },
296
  { name = "gradio", extras = ["mcp", "oauth"], specifier = "==6.0.0.dev4" },
297
  { name = "mcp", specifier = ">=1.20.0" },
 
365
  { url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" },
366
  ]
367
 
368
+ [[package]]
369
+ name = "cyclopts"
370
+ version = "5.0.0a1"
371
+ source = { registry = "https://pypi.org/simple" }
372
+ dependencies = [
373
+ { name = "attrs" },
374
+ { name = "docstring-parser" },
375
+ { name = "rich" },
376
+ ]
377
+ sdist = { url = "https://files.pythonhosted.org/packages/4c/ca/65e020717410cff8916bae89d5b1704ee190d2a345f47ba5e5f903b41d59/cyclopts-5.0.0a1.tar.gz", hash = "sha256:ae24ce6c0c8dbaba3fdfccdb54d2a9d6e2a6270c47ffe101af1de89ff617851a", size = 148291, upload-time = "2025-11-02T19:32:43.176Z" }
378
+ wheels = [
379
+ { url = "https://files.pythonhosted.org/packages/d1/d3/eda07755dffa4ea637a673181934bcd54255def1c71dd1cc0f8ec49f888e/cyclopts-5.0.0a1-py3-none-any.whl", hash = "sha256:731e0c4412d47993202abffd0bfe222353b12347dfef7e874ac769c74c8a162a", size = 183923, upload-time = "2025-11-02T19:32:41.532Z" },
380
+ ]
381
+
382
+ [[package]]
383
+ name = "diskcache"
384
+ version = "5.6.3"
385
+ source = { registry = "https://pypi.org/simple" }
386
+ sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" }
387
+ wheels = [
388
+ { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" },
389
+ ]
390
+
391
+ [[package]]
392
+ name = "dnspython"
393
+ version = "2.8.0"
394
+ source = { registry = "https://pypi.org/simple" }
395
+ sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" }
396
+ wheels = [
397
+ { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" },
398
+ ]
399
+
400
+ [[package]]
401
+ name = "docstring-parser"
402
+ version = "0.17.0"
403
+ source = { registry = "https://pypi.org/simple" }
404
+ sdist = { url = "https://files.pythonhosted.org/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", size = 27442, upload-time = "2025-07-21T07:35:01.868Z" }
405
+ wheels = [
406
+ { url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896, upload-time = "2025-07-21T07:35:00.684Z" },
407
+ ]
408
+
409
+ [[package]]
410
+ name = "email-validator"
411
+ version = "2.3.0"
412
+ source = { registry = "https://pypi.org/simple" }
413
+ dependencies = [
414
+ { name = "dnspython" },
415
+ { name = "idna" },
416
+ ]
417
+ sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" }
418
+ wheels = [
419
+ { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" },
420
+ ]
421
+
422
+ [[package]]
423
+ name = "exceptiongroup"
424
+ version = "1.3.0"
425
+ source = { registry = "https://pypi.org/simple" }
426
+ sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" }
427
+ wheels = [
428
+ { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" },
429
+ ]
430
+
431
  [[package]]
432
  name = "fastapi"
433
  version = "0.121.2"
 
443
  { url = "https://files.pythonhosted.org/packages/eb/23/dfb161e91db7c92727db505dc72a384ee79681fe0603f706f9f9f52c2901/fastapi-0.121.2-py3-none-any.whl", hash = "sha256:f2d80b49a86a846b70cc3a03eb5ea6ad2939298bf6a7fe377aa9cd3dd079d358", size = 109201, upload-time = "2025-11-13T17:05:52.718Z" },
444
  ]
445
 
446
+ [[package]]
447
+ name = "fastmcp"
448
+ version = "2.13.1"
449
+ source = { registry = "https://pypi.org/simple" }
450
+ dependencies = [
451
+ { name = "authlib" },
452
+ { name = "cyclopts" },
453
+ { name = "exceptiongroup" },
454
+ { name = "httpx" },
455
+ { name = "jsonschema-path" },
456
+ { name = "mcp" },
457
+ { name = "openapi-pydantic" },
458
+ { name = "platformdirs" },
459
+ { name = "py-key-value-aio", extra = ["disk", "keyring", "memory"] },
460
+ { name = "pydantic", extra = ["email"] },
461
+ { name = "pyperclip" },
462
+ { name = "python-dotenv" },
463
+ { name = "rich" },
464
+ { name = "uvicorn" },
465
+ { name = "websockets" },
466
+ ]
467
+ sdist = { url = "https://files.pythonhosted.org/packages/d4/a3/c9eb28b5f0b979b0dd8aa9ba56e69298cdb2d72c15592165d042ccb20194/fastmcp-2.13.1.tar.gz", hash = "sha256:b9c664c51f1ff47c698225e7304267ae29a51913f681bd49e442b8682f9a5f90", size = 8170226, upload-time = "2025-11-15T19:02:17.693Z" }
468
+ wheels = [
469
+ { url = "https://files.pythonhosted.org/packages/9b/4b/7e36db0a90044be181319ff025be7cc57089ddb6ba8f3712dea543b9cf97/fastmcp-2.13.1-py3-none-any.whl", hash = "sha256:7a78b19785c4ec04a758d920c312769a497e3f6ab4c80feed504df1ed7de9f3c", size = 376750, upload-time = "2025-11-15T19:02:15.748Z" },
470
+ ]
471
+
472
  [[package]]
473
  name = "feedparser"
474
  version = "6.0.12"
 
705
  { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" },
706
  ]
707
 
708
+ [[package]]
709
+ name = "jaraco-classes"
710
+ version = "3.4.0"
711
+ source = { registry = "https://pypi.org/simple" }
712
+ dependencies = [
713
+ { name = "more-itertools" },
714
+ ]
715
+ sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" }
716
+ wheels = [
717
+ { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" },
718
+ ]
719
+
720
+ [[package]]
721
+ name = "jaraco-context"
722
+ version = "6.0.1"
723
+ source = { registry = "https://pypi.org/simple" }
724
+ sdist = { url = "https://files.pythonhosted.org/packages/df/ad/f3777b81bf0b6e7bc7514a1656d3e637b2e8e15fab2ce3235730b3e7a4e6/jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3", size = 13912, upload-time = "2024-08-20T03:39:27.358Z" }
725
+ wheels = [
726
+ { url = "https://files.pythonhosted.org/packages/ff/db/0c52c4cf5e4bd9f5d7135ec7669a3a767af21b3a308e1ed3674881e52b62/jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4", size = 6825, upload-time = "2024-08-20T03:39:25.966Z" },
727
+ ]
728
+
729
+ [[package]]
730
+ name = "jaraco-functools"
731
+ version = "4.3.0"
732
+ source = { registry = "https://pypi.org/simple" }
733
+ dependencies = [
734
+ { name = "more-itertools" },
735
+ ]
736
+ sdist = { url = "https://files.pythonhosted.org/packages/f7/ed/1aa2d585304ec07262e1a83a9889880701079dde796ac7b1d1826f40c63d/jaraco_functools-4.3.0.tar.gz", hash = "sha256:cfd13ad0dd2c47a3600b439ef72d8615d482cedcff1632930d6f28924d92f294", size = 19755, upload-time = "2025-08-18T20:05:09.91Z" }
737
+ wheels = [
738
+ { url = "https://files.pythonhosted.org/packages/b4/09/726f168acad366b11e420df31bf1c702a54d373a83f968d94141a8c3fde0/jaraco_functools-4.3.0-py3-none-any.whl", hash = "sha256:227ff8ed6f7b8f62c56deff101545fa7543cf2c8e7b82a7c2116e672f29c26e8", size = 10408, upload-time = "2025-08-18T20:05:08.69Z" },
739
+ ]
740
+
741
+ [[package]]
742
+ name = "jeepney"
743
+ version = "0.9.0"
744
+ source = { registry = "https://pypi.org/simple" }
745
+ sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" }
746
+ wheels = [
747
+ { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" },
748
+ ]
749
+
750
  [[package]]
751
  name = "jinja2"
752
  version = "3.1.6"
 
774
  { url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040, upload-time = "2025-08-18T17:03:48.373Z" },
775
  ]
776
 
777
+ [[package]]
778
+ name = "jsonschema-path"
779
+ version = "0.4.0b1"
780
+ source = { registry = "https://pypi.org/simple" }
781
+ dependencies = [
782
+ { name = "pathable" },
783
+ { name = "pyrsistent" },
784
+ { name = "pyyaml" },
785
+ { name = "referencing" },
786
+ ]
787
+ sdist = { url = "https://files.pythonhosted.org/packages/0f/b9/eb99f1367ebcf8d6e2ae4f1c6e3da61eeb0ddd11c2695b1cdad8a9dad631/jsonschema_path-0.4.0b1.tar.gz", hash = "sha256:4678dcb27e5fbc58b39baf7ca8c36a6a7de3bcafacaaafc1b8a1d54038ce8d8a", size = 11807, upload-time = "2025-06-07T20:52:58.9Z" }
788
+ wheels = [
789
+ { url = "https://files.pythonhosted.org/packages/67/f5/24d5b91ed0409989140d1d4217e9c852d81782ced54e46b0582ad5f56dcb/jsonschema_path-0.4.0b1-py3-none-any.whl", hash = "sha256:f0cd42238c1445cecac3b2796fa24dcc62b8426623d1cb4d8d755f3715abf3ba", size = 15293, upload-time = "2025-06-07T20:52:57.029Z" },
790
+ ]
791
+
792
  [[package]]
793
  name = "jsonschema-specifications"
794
  version = "2025.9.1"
 
801
  { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" },
802
  ]
803
 
804
+ [[package]]
805
+ name = "keyring"
806
+ version = "25.7.0"
807
+ source = { registry = "https://pypi.org/simple" }
808
+ dependencies = [
809
+ { name = "jaraco-classes" },
810
+ { name = "jaraco-context" },
811
+ { name = "jaraco-functools" },
812
+ { name = "jeepney", marker = "sys_platform == 'linux'" },
813
+ { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" },
814
+ { name = "secretstorage", marker = "sys_platform == 'linux'" },
815
+ ]
816
+ sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" }
817
+ wheels = [
818
+ { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" },
819
+ ]
820
+
821
  [[package]]
822
  name = "markdown-it-py"
823
  version = "4.0.0"
 
916
  { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
917
  ]
918
 
919
+ [[package]]
920
+ name = "more-itertools"
921
+ version = "10.8.0"
922
+ source = { registry = "https://pypi.org/simple" }
923
+ sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431, upload-time = "2025-09-02T15:23:11.018Z" }
924
+ wheels = [
925
+ { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667, upload-time = "2025-09-02T15:23:09.635Z" },
926
+ ]
927
+
928
  [[package]]
929
  name = "numpy"
930
  version = "2.3.5"
 
977
  { url = "https://files.pythonhosted.org/packages/2d/fd/4b5eb0b3e888d86aee4d198c23acec7d214baaf17ea93c1adec94c9518b9/numpy-2.3.5-cp314-cp314t-win_arm64.whl", hash = "sha256:6203fdf9f3dc5bdaed7319ad8698e685c7a3be10819f41d32a0723e611733b42", size = 10545459, upload-time = "2025-11-16T22:52:20.55Z" },
978
  ]
979
 
980
+ [[package]]
981
+ name = "openapi-pydantic"
982
+ version = "0.5.1"
983
+ source = { registry = "https://pypi.org/simple" }
984
+ dependencies = [
985
+ { name = "pydantic" },
986
+ ]
987
+ sdist = { url = "https://files.pythonhosted.org/packages/02/2e/58d83848dd1a79cb92ed8e63f6ba901ca282c5f09d04af9423ec26c56fd7/openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d", size = 60892, upload-time = "2025-01-08T19:29:27.083Z" }
988
+ wheels = [
989
+ { url = "https://files.pythonhosted.org/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381, upload-time = "2025-01-08T19:29:25.275Z" },
990
+ ]
991
+
992
  [[package]]
993
  name = "orjson"
994
  version = "3.11.4"
 
1076
  { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" },
1077
  ]
1078
 
1079
+ [[package]]
1080
+ name = "pathable"
1081
+ version = "0.5.0b2"
1082
+ source = { registry = "https://pypi.org/simple" }
1083
+ dependencies = [
1084
+ { name = "pyrsistent" },
1085
+ ]
1086
+ sdist = { url = "https://files.pythonhosted.org/packages/10/a0/5ce545bf5d14d3b8925e73f12f67adee7a139a6726c0df75aca25ab6e8b7/pathable-0.5.0b2.tar.gz", hash = "sha256:0005483148dc1991ab32e4cb4cde7e2b8f376f081a1639631e424db0792860e9", size = 9896, upload-time = "2025-06-07T20:45:35.764Z" }
1087
+ wheels = [
1088
+ { url = "https://files.pythonhosted.org/packages/70/9a/8a74be1ab54bb62bd3c1554af8ef431c530f843d713785874207a77229e6/pathable-0.5.0b2-py3-none-any.whl", hash = "sha256:4c78bc5dae0c64a6191b955b7975bab58f0e298e197f591d4f0172886824a52b", size = 11374, upload-time = "2025-06-07T20:45:34.114Z" },
1089
+ ]
1090
+
1091
+ [[package]]
1092
+ name = "pathvalidate"
1093
+ version = "3.3.1"
1094
+ source = { registry = "https://pypi.org/simple" }
1095
+ sdist = { url = "https://files.pythonhosted.org/packages/fa/2a/52a8da6fe965dea6192eb716b357558e103aea0a1e9a8352ad575a8406ca/pathvalidate-3.3.1.tar.gz", hash = "sha256:b18c07212bfead624345bb8e1d6141cdcf15a39736994ea0b94035ad2b1ba177", size = 63262, upload-time = "2025-06-15T09:07:20.736Z" }
1096
+ wheels = [
1097
+ { url = "https://files.pythonhosted.org/packages/9a/70/875f4a23bfc4731703a5835487d0d2fb999031bd415e7d17c0ae615c18b7/pathvalidate-3.3.1-py3-none-any.whl", hash = "sha256:5263baab691f8e1af96092fa5137ee17df5bdfbd6cff1fcac4d6ef4bc2e1735f", size = 24305, upload-time = "2025-06-15T09:07:19.117Z" },
1098
+ ]
1099
+
1100
  [[package]]
1101
  name = "pillow"
1102
  version = "11.3.0"
 
1152
  { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835, upload-time = "2025-07-01T09:15:50.399Z" },
1153
  ]
1154
 
1155
+ [[package]]
1156
+ name = "platformdirs"
1157
+ version = "4.5.0"
1158
+ source = { registry = "https://pypi.org/simple" }
1159
+ sdist = { url = "https://files.pythonhosted.org/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312", size = 21632, upload-time = "2025-10-08T17:44:48.791Z" }
1160
+ wheels = [
1161
+ { url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3", size = 18651, upload-time = "2025-10-08T17:44:47.223Z" },
1162
+ ]
1163
+
1164
  [[package]]
1165
  name = "pluggy"
1166
  version = "1.6.0"
 
1170
  { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
1171
  ]
1172
 
1173
+ [[package]]
1174
+ name = "py-key-value-aio"
1175
+ version = "0.2.8"
1176
+ source = { registry = "https://pypi.org/simple" }
1177
+ dependencies = [
1178
+ { name = "beartype" },
1179
+ { name = "py-key-value-shared" },
1180
+ ]
1181
+ sdist = { url = "https://files.pythonhosted.org/packages/ca/35/65310a4818acec0f87a46e5565e341c5a96fc062a9a03495ad28828ff4d7/py_key_value_aio-0.2.8.tar.gz", hash = "sha256:c0cfbb0bd4e962a3fa1a9fa6db9ba9df812899bd9312fa6368aaea7b26008b36", size = 32853, upload-time = "2025-10-24T13:31:04.688Z" }
1182
+ wheels = [
1183
+ { url = "https://files.pythonhosted.org/packages/cd/5a/e56747d87a97ad2aff0f3700d77f186f0704c90c2da03bfed9e113dae284/py_key_value_aio-0.2.8-py3-none-any.whl", hash = "sha256:561565547ce8162128fd2bd0b9d70ce04a5f4586da8500cce79a54dfac78c46a", size = 69200, upload-time = "2025-10-24T13:31:03.81Z" },
1184
+ ]
1185
+
1186
+ [package.optional-dependencies]
1187
+ disk = [
1188
+ { name = "diskcache" },
1189
+ { name = "pathvalidate" },
1190
+ ]
1191
+ keyring = [
1192
+ { name = "keyring" },
1193
+ ]
1194
+ memory = [
1195
+ { name = "cachetools" },
1196
+ ]
1197
+
1198
+ [[package]]
1199
+ name = "py-key-value-shared"
1200
+ version = "0.2.8"
1201
+ source = { registry = "https://pypi.org/simple" }
1202
+ dependencies = [
1203
+ { name = "beartype" },
1204
+ { name = "typing-extensions" },
1205
+ ]
1206
+ sdist = { url = "https://files.pythonhosted.org/packages/26/79/05a1f9280cfa0709479319cbfd2b1c5beb23d5034624f548c83fb65b0b61/py_key_value_shared-0.2.8.tar.gz", hash = "sha256:703b4d3c61af124f0d528ba85995c3c8d78f8bd3d2b217377bd3278598070cc1", size = 8216, upload-time = "2025-10-24T13:31:03.601Z" }
1207
+ wheels = [
1208
+ { url = "https://files.pythonhosted.org/packages/84/7a/1726ceaa3343874f322dd83c9ec376ad81f533df8422b8b1e1233a59f8ce/py_key_value_shared-0.2.8-py3-none-any.whl", hash = "sha256:aff1bbfd46d065b2d67897d298642e80e5349eae588c6d11b48452b46b8d46ba", size = 14586, upload-time = "2025-10-24T13:31:02.838Z" },
1209
+ ]
1210
+
1211
  [[package]]
1212
  name = "pycparser"
1213
  version = "2.23"
 
1232
  { url = "https://files.pythonhosted.org/packages/82/2f/e68750da9b04856e2a7ec56fc6f034a5a79775e9b9a81882252789873798/pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e", size = 463400, upload-time = "2025-11-05T10:50:06.732Z" },
1233
  ]
1234
 
1235
+ [package.optional-dependencies]
1236
+ email = [
1237
+ { name = "email-validator" },
1238
+ ]
1239
+
1240
  [[package]]
1241
  name = "pydantic-core"
1242
  version = "2.41.5"
 
1336
  { name = "cryptography" },
1337
  ]
1338
 
1339
+ [[package]]
1340
+ name = "pyperclip"
1341
+ version = "1.11.0"
1342
+ source = { registry = "https://pypi.org/simple" }
1343
+ sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" }
1344
+ wheels = [
1345
+ { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" },
1346
+ ]
1347
+
1348
+ [[package]]
1349
+ name = "pyrsistent"
1350
+ version = "0.20.0"
1351
+ source = { registry = "https://pypi.org/simple" }
1352
+ sdist = { url = "https://files.pythonhosted.org/packages/ce/3a/5031723c09068e9c8c2f0bc25c3a9245f2b1d1aea8396c787a408f2b95ca/pyrsistent-0.20.0.tar.gz", hash = "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4", size = 103642, upload-time = "2023-10-25T21:06:56.342Z" }
1353
+ wheels = [
1354
+ { url = "https://files.pythonhosted.org/packages/23/88/0acd180010aaed4987c85700b7cc17f9505f3edb4e5873e4dc67f613e338/pyrsistent-0.20.0-py3-none-any.whl", hash = "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b", size = 58106, upload-time = "2023-10-25T21:06:54.387Z" },
1355
+ ]
1356
+
1357
  [[package]]
1358
  name = "pytest"
1359
  version = "9.0.1"
 
1422
  { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" },
1423
  ]
1424
 
1425
+ [[package]]
1426
+ name = "pywin32-ctypes"
1427
+ version = "0.2.3"
1428
+ source = { registry = "https://pypi.org/simple" }
1429
+ sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" }
1430
+ wheels = [
1431
+ { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" },
1432
+ ]
1433
+
1434
  [[package]]
1435
  name = "pyyaml"
1436
  version = "6.0.3"
 
1469
 
1470
  [[package]]
1471
  name = "referencing"
1472
+ version = "0.36.2"
1473
  source = { registry = "https://pypi.org/simple" }
1474
  dependencies = [
1475
  { name = "attrs" },
1476
  { name = "rpds-py" },
1477
  ]
1478
+ sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" }
1479
  wheels = [
1480
+ { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775, upload-time = "2025-01-25T08:48:14.241Z" },
1481
  ]
1482
 
1483
  [[package]]
 
1586
  { url = "https://files.pythonhosted.org/packages/2e/a3/0f0b7d78e2f1eb9e8e1afbff1d2bff8d60144aee17aca51c065b516743dd/safehttpx-0.1.7-py3-none-any.whl", hash = "sha256:c4f4a162db6993464d7ca3d7cc4af0ffc6515a606dfd220b9f82c6945d869cde", size = 8959, upload-time = "2025-10-24T18:30:08.733Z" },
1587
  ]
1588
 
1589
+ [[package]]
1590
+ name = "secretstorage"
1591
+ version = "3.4.1"
1592
+ source = { registry = "https://pypi.org/simple" }
1593
+ dependencies = [
1594
+ { name = "cryptography" },
1595
+ { name = "jeepney" },
1596
+ ]
1597
+ sdist = { url = "https://files.pythonhosted.org/packages/32/8a/ed6747b1cc723c81f526d4c12c1b1d43d07190e1e8258dbf934392fc850e/secretstorage-3.4.1.tar.gz", hash = "sha256:a799acf5be9fb93db609ebaa4ab6e8f1f3ed5ae640e0fa732bfea59e9c3b50e8", size = 19871, upload-time = "2025-11-11T11:30:23.798Z" }
1598
+ wheels = [
1599
+ { url = "https://files.pythonhosted.org/packages/b0/6d/24ebb101484f1911a6be6695b76ce43219caa110ebbe07d8c3a5f3106cca/secretstorage-3.4.1-py3-none-any.whl", hash = "sha256:c55d57b4da3de568d8c3af89dad244ab24c35ca1da8625fc1b550edf005ebc41", size = 15301, upload-time = "2025-11-11T11:30:22.618Z" },
1600
+ ]
1601
+
1602
  [[package]]
1603
  name = "semantic-version"
1604
  version = "2.10.0"
 
1774
  wheels = [
1775
  { url = "https://files.pythonhosted.org/packages/ee/d9/d88e73ca598f4f6ff671fb5fde8a32925c2e08a637303a1d12883c7305fa/uvicorn-0.38.0-py3-none-any.whl", hash = "sha256:48c0afd214ceb59340075b4a052ea1ee91c16fbc2a9b1469cca0e54566977b02", size = 68109, upload-time = "2025-10-18T13:46:42.958Z" },
1776
  ]
1777
+
1778
+ [[package]]
1779
+ name = "websockets"
1780
+ version = "15.0.1"
1781
+ source = { registry = "https://pypi.org/simple" }
1782
+ sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" }
1783
+ wheels = [
1784
+ { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" },
1785
+ { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" },
1786
+ { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" },
1787
+ { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" },
1788
+ { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" },
1789
+ { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" },
1790
+ { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" },
1791
+ { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" },
1792
+ { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" },
1793
+ { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" },
1794
+ { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" },
1795
+ { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" },
1796
+ ]