universal-mcp-applications 0.1.13__tar.gz → 0.1.14__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of universal-mcp-applications might be problematic. Click here for more details.
- universal_mcp_applications-0.1.14/GEMINI.md +125 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/PKG-INFO +1 -1
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/bump_and_release.sh +3 -5
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/pyproject.toml +1 -1
- universal_mcp_applications-0.1.14/src/scripts/docgen.py +516 -0
- universal_mcp_applications-0.1.14/src/scripts/prompts.py +28 -0
- universal_mcp_applications-0.1.14/src/scripts/run_docgen.py +65 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/aws_s3/app.py +71 -71
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/calendly/app.py +199 -199
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/canva/app.py +189 -189
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/domain_checker/app.py +31 -24
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/e2b/app.py +6 -7
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/elevenlabs/app.py +24 -20
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/exa/app.py +25 -20
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/falai/app.py +44 -41
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/file_system/app.py +20 -12
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/firecrawl/app.py +46 -47
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/fireflies/app.py +79 -79
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/fpl/app.py +83 -74
- universal_mcp_applications-0.1.14/src/universal_mcp/applications/github/README.md +21 -0
- universal_mcp_applications-0.1.14/src/universal_mcp/applications/github/app.py +429 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_calendar/app.py +63 -65
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_docs/app.py +78 -78
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_drive/app.py +361 -440
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_gemini/app.py +34 -17
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_mail/app.py +117 -117
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_searchconsole/app.py +41 -47
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_sheet/app.py +157 -164
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/http_tools/app.py +16 -16
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/linkedin/app.py +26 -31
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/ms_teams/app.py +190 -190
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/openai/app.py +55 -56
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/outlook/app.py +57 -57
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/perplexity/app.py +17 -17
- universal_mcp_applications-0.1.14/src/universal_mcp/applications/reddit/app.py +836 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/replicate/app.py +40 -42
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/resend/app.py +157 -154
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/scraper/app.py +24 -24
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/serpapi/app.py +18 -20
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/sharepoint/app.py +46 -36
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/slack/app.py +66 -66
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/tavily/app.py +7 -7
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/twitter/api_segments/compliance_api.py +17 -20
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +35 -40
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/twitter/api_segments/dm_events_api.py +18 -21
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/twitter/api_segments/likes_api.py +19 -22
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/twitter/api_segments/lists_api.py +59 -68
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/twitter/api_segments/spaces_api.py +36 -42
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/twitter/api_segments/trends_api.py +7 -8
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/twitter/api_segments/tweets_api.py +159 -185
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/twitter/api_segments/usage_api.py +5 -6
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/twitter/api_segments/users_api.py +230 -264
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/unipile/app.py +99 -105
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/whatsapp/app.py +86 -82
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/whatsapp_business/app.py +147 -147
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/youtube/app.py +290 -290
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/zenquotes/app.py +6 -6
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/uv.lock +1 -1
- universal_mcp_applications-0.1.13/src/universal_mcp/applications/github/README.md +0 -1049
- universal_mcp_applications-0.1.13/src/universal_mcp/applications/github/app.py +0 -50601
- universal_mcp_applications-0.1.13/src/universal_mcp/applications/reddit/app.py +0 -4664
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/.gitignore +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/LICENSE +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/examples/children_story_builder.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/tests/test_applications.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/ahrefs/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/ahrefs/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/ahrefs/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/airtable/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/airtable/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/airtable/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/apollo/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/apollo/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/apollo/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/asana/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/asana/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/asana/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/aws_s3/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/aws_s3/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/bill/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/bill/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/bill/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/box/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/box/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/box/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/braze/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/braze/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/braze/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/cal_com_v2/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/cal_com_v2/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/cal_com_v2/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/calendly/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/calendly/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/canva/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/canva/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/clickup/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/clickup/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/clickup/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/coda/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/coda/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/coda/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/confluence/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/confluence/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/confluence/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/contentful/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/contentful/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/contentful/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/crustdata/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/crustdata/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/crustdata/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/dialpad/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/dialpad/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/dialpad/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/digitalocean/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/digitalocean/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/digitalocean/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/domain_checker/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/domain_checker/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/e2b/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/e2b/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/elevenlabs/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/elevenlabs/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/exa/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/exa/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/falai/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/falai/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/figma/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/figma/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/figma/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/file_system/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/firecrawl/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/firecrawl/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/fireflies/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/fireflies/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/fpl/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/fpl/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/fpl/utils/api.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/fpl/utils/fixtures.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/fpl/utils/helper.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/fpl/utils/league_utils.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/fpl/utils/position_utils.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/ghost_content/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/ghost_content/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/ghost_content/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/github/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/gong/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/gong/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/gong/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_calendar/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_calendar/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_docs/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_docs/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_drive/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_drive/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_gemini/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_gemini/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_mail/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_mail/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_searchconsole/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_searchconsole/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_sheet/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_sheet/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/google_sheet/helper.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/hashnode/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/hashnode/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/hashnode/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/heygen/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/heygen/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/heygen/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/http_tools/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/http_tools/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/hubspot/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/hubspot/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/hubspot/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/jira/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/jira/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/jira/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/klaviyo/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/klaviyo/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/klaviyo/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/linkedin/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/linkedin/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/mailchimp/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/mailchimp/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/mailchimp/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/markitdown/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/markitdown/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/markitdown/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/miro/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/miro/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/miro/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/ms_teams/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/ms_teams/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/neon/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/neon/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/neon/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/notion/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/notion/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/notion/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/openai/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/openai/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/outlook/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/outlook/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/perplexity/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/perplexity/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/pipedrive/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/pipedrive/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/pipedrive/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/posthog/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/posthog/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/posthog/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/reddit/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/reddit/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/replicate/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/replicate/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/resend/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/resend/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/retell/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/retell/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/retell/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/rocketlane/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/rocketlane/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/rocketlane/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/scraper/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/scraper/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/semanticscholar/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/semanticscholar/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/semanticscholar/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/semrush/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/semrush/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/semrush/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/sendgrid/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/sendgrid/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/sendgrid/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/sentry/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/sentry/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/sentry/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/serpapi/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/serpapi/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/sharepoint/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/sharepoint/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/shopify/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/shopify/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/shopify/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/shortcut/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/shortcut/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/shortcut/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/slack/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/slack/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/spotify/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/spotify/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/spotify/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/supabase/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/supabase/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/supabase/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/tavily/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/tavily/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/trello/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/trello/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/trello/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/twilio/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/twilio/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/twilio/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/twitter/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/twitter/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/twitter/api_segments/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/twitter/api_segments/api_segment_base.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/twitter/api_segments/openapi_json_api.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/twitter/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/unipile/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/unipile/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/whatsapp/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/whatsapp/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/whatsapp/audio.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/whatsapp/whatsapp.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/whatsapp_business/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/whatsapp_business/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/wrike/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/wrike/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/wrike/app.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/youtube/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/youtube/__init__.py +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/zenquotes/README.md +0 -0
- {universal_mcp_applications-0.1.13 → universal_mcp_applications-0.1.14}/src/universal_mcp/applications/zenquotes/__init__.py +0 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Agent.md — Python + uv
|
|
2
|
+
|
|
3
|
+
Purpose
|
|
4
|
+
- This repository uses Python managed by uv for dependency resolution, virtual environments, locking, and execution. Always prefer uv subcommands (add/remove/run/sync/export) over raw pip/venv commands.
|
|
5
|
+
|
|
6
|
+
Core rules
|
|
7
|
+
- Use `uv add` to add or upgrade dependencies so that both `pyproject.toml` and `uv.lock` stay in sync; do not use `pip install` directly.
|
|
8
|
+
- Keep runtime dependencies in `[project.dependencies]` and development-only tools in the `dev` group via `uv add --dev ...`.
|
|
9
|
+
- Use `uv run` to execute Python, test, and tooling commands without manually activating a virtual environment.
|
|
10
|
+
|
|
11
|
+
Project bootstrap
|
|
12
|
+
- New project (scaffold files): `uv init`
|
|
13
|
+
- First install or clean install: `uv sync`
|
|
14
|
+
- Run the app: `uv run python -m <your_module>` or `uv run main.py`
|
|
15
|
+
- REPL: `uv run python`
|
|
16
|
+
- Scripts in pyproject: prefer `uv run <command>` to ensure the correct environment is used
|
|
17
|
+
|
|
18
|
+
Managing dependencies
|
|
19
|
+
- Add runtime dependency: `uv add <name>` (e.g., `uv add httpx`)
|
|
20
|
+
- Add dev dependencies: `uv add --dev pytest ruff`
|
|
21
|
+
- Pin/upgrade by constraint: `uv add "httpx>=0.27"` or adjust `pyproject.toml` and then `uv sync`
|
|
22
|
+
- Remove dependency: `uv remove <name>`
|
|
23
|
+
- Export lock for external tooling: `uv export --format requirements-txt --output-file requirements.txt`
|
|
24
|
+
|
|
25
|
+
Locking and environments
|
|
26
|
+
- `uv run` and `uv sync` will ensure the environment matches `pyproject.toml` and `uv.lock`
|
|
27
|
+
- Avoid manual `pip install` or manual `venv` activation; let uv manage the environment
|
|
28
|
+
- Commit `uv.lock` to version control for reproducible installs
|
|
29
|
+
|
|
30
|
+
pyproject guidance
|
|
31
|
+
- Dependencies live under `[project]` → `dependencies = [...]`
|
|
32
|
+
- Development-only tooling should go under a dev group (e.g., `uv add --dev ruff pytest`) for clean separation
|
|
33
|
+
- Keep `requires-python` current (e.g., `>=3.12`) to match the team’s baseline
|
|
34
|
+
|
|
35
|
+
Usage in this repo
|
|
36
|
+
- When adding libraries or changing versions, propose `uv add ...` changes that update both `pyproject.toml` and `uv.lock`, then run `uv run pytest -q` to validate
|
|
37
|
+
- Prefer minimal diffs, explain the plan, apply changes, and run tests/tooling via `uv run`
|
|
38
|
+
- If build/test fails, inspect error context, adjust constraints or code, and re-run via `uv run`
|
|
39
|
+
|
|
40
|
+
Common commands (copy/paste)
|
|
41
|
+
- Initialize: `uv init` | Install deps: `uv sync`
|
|
42
|
+
- Add runtime: `uv add <pkg>` | Add dev: `uv add --dev <pkg>`
|
|
43
|
+
- Remove: `uv remove <pkg>`
|
|
44
|
+
- Run app: `uv run python -m <your_module>` or `uv run main.py`
|
|
45
|
+
- Tests: `uv run pytest -q`
|
|
46
|
+
- Lint/format: `uv run ruff check .` and/or `uv run ruff format .`
|
|
47
|
+
- Export: `uv export --format requirements-txt --output-file requirements.txt`
|
|
48
|
+
|
|
49
|
+
Of course. Based on your request and the example `app.py` you provided, here is a new section that you can append to your `gemini.md` file. This section provides clear guidance on how to write effective tool names and descriptions, using the Perplexity `chat` function as a concrete example.
|
|
50
|
+
|
|
51
|
+
The proposed changes will make the function's purpose much clearer, especially its ability to access and cite real-time web information, which is a key feature.
|
|
52
|
+
|
|
53
|
+
***
|
|
54
|
+
|
|
55
|
+
### Tool Function Naming and Descriptions
|
|
56
|
+
|
|
57
|
+
To ensure the AI agent can effectively understand and use the tools available, function names and their descriptions (docstrings) must be clear, descriptive, and accurate. A good description outlines the tool's purpose, its key capabilities, and what it returns.
|
|
58
|
+
|
|
59
|
+
**Core Principles:**
|
|
60
|
+
- **Use Action-Oriented Names:** Function names should be verbs that clearly state what the tool *does* (e.g., `search`, `create_file`, `list_users`).
|
|
61
|
+
- **Be Specific in Descriptions:** The docstring is the primary source of context for the AI. It should explicitly mention the service it interacts with (e.g., "Perplexity AI") and its unique features (e.g., "performs real-time web searches," "provides citations").
|
|
62
|
+
- **Detail Inputs and Outputs:** Clearly describe the purpose of each argument and what the function returns, as this helps the agent formulate correct calls.
|
|
63
|
+
|
|
64
|
+
**Example: Improving the Perplexity Tool**
|
|
65
|
+
|
|
66
|
+
Let's refine the tool in `app.py` to be more descriptive and unlock its full potential. The original `chat` function is too generic. We will rename it to `search` and significantly improve its docstring.
|
|
67
|
+
|
|
68
|
+
**BEFORE:** The original function name and description are too simple.
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
# from app.py
|
|
72
|
+
def chat(
|
|
73
|
+
self,
|
|
74
|
+
query: str,
|
|
75
|
+
model: Literal[...] = "sonar",
|
|
76
|
+
temperature: float = 1,
|
|
77
|
+
system_prompt: str = "Be precise and concise.",
|
|
78
|
+
) -> dict[str, Any] | str:
|
|
79
|
+
"""
|
|
80
|
+
Initiates a chat completion request to generate AI responses using various models with customizable parameters.
|
|
81
|
+
...
|
|
82
|
+
Tags:
|
|
83
|
+
chat, generate, ai, completion, important
|
|
84
|
+
"""
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**AFTER:** The updated name and description are highly specific, highlighting the core web search and citation capabilities. Do not leave any empty lines between the descriptions.
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
# from app.py
|
|
91
|
+
def search(
|
|
92
|
+
self,
|
|
93
|
+
query: str,
|
|
94
|
+
model: Literal[
|
|
95
|
+
"r1-1776",
|
|
96
|
+
"sonar",
|
|
97
|
+
"sonar-pro",
|
|
98
|
+
"sonar-reasoning",
|
|
99
|
+
"sonar-reasoning-pro",
|
|
100
|
+
"sonar-deep-research",
|
|
101
|
+
] = "sonar-pro",
|
|
102
|
+
temperature: float = 1,
|
|
103
|
+
system_prompt: str = "You are a helpful AI assistant that answers questions using real-time information from the web.",
|
|
104
|
+
) -> dict[str, Any] | str:
|
|
105
|
+
"""
|
|
106
|
+
Performs a real-time web search using Perplexity AI to answer a query with up-to-date information and citations.
|
|
107
|
+
This tool is ideal for questions about current events, facts, or any topic that requires access to the latest information from the internet. It returns a natural language answer along with the sources used.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
query: The search query or question to ask. For example: "What are the latest developments in AI regulation?"
|
|
111
|
+
model: The Perplexity model to use. 'sonar-pro' is recommended for comprehensive, up-to-date answers.
|
|
112
|
+
temperature: Controls randomness in the model's output. Higher values make the output more random, lower values make it more deterministic. Defaults to 1.
|
|
113
|
+
system_prompt: The initial system message to guide the model's behavior.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
A dictionary containing the generated 'content' (str) and a list of 'citations' (list) from the web search.
|
|
117
|
+
|
|
118
|
+
Raises:
|
|
119
|
+
AuthenticationError: Raised when API authentication fails due to missing or invalid credentials.
|
|
120
|
+
HTTPError: Raised when the API request fails or returns an error status.
|
|
121
|
+
|
|
122
|
+
Tags:
|
|
123
|
+
search, web, research, citations, current events, important
|
|
124
|
+
"""
|
|
125
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: universal-mcp-applications
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.14
|
|
4
4
|
Summary: A Universal MCP Application: universal_mcp_applications
|
|
5
5
|
Project-URL: Homepage, https://github.com/universal-mcp/applications
|
|
6
6
|
Project-URL: Repository, https://github.com/universal-mcp/applications
|
|
@@ -20,9 +20,8 @@ BRANCH=$(git branch --show-current)
|
|
|
20
20
|
CURRENT_VERSION=$(grep -E '^version = "[0-9]+\.[0-9]+\.[0-9]+.*"' pyproject.toml | cut -d'"' -f2)
|
|
21
21
|
|
|
22
22
|
# Split version into major, minor, patch
|
|
23
|
-
MAJOR
|
|
24
|
-
|
|
25
|
-
PATCH=$(echo "$CURRENT_VERSION" | cut -d'.' -f3)
|
|
23
|
+
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
|
|
24
|
+
|
|
26
25
|
# Remove any rc suffix from PATCH if it exists
|
|
27
26
|
PATCH_NUM=$(echo $PATCH | sed 's/-rc[0-9]*//')
|
|
28
27
|
|
|
@@ -51,7 +50,7 @@ else
|
|
|
51
50
|
fi
|
|
52
51
|
|
|
53
52
|
# Update version in pyproject.toml
|
|
54
|
-
sed -i "s/^version =
|
|
53
|
+
sed -i '' "s/^version = ".*"/version = \"$NEW_VERSION\"/" pyproject.toml
|
|
55
54
|
|
|
56
55
|
echo "Version bumped from $CURRENT_VERSION to $NEW_VERSION"
|
|
57
56
|
|
|
@@ -82,7 +81,6 @@ if [ "$1" = "release" ]; then
|
|
|
82
81
|
rm -rf .pytest_cache
|
|
83
82
|
rm -rf .ruff_cache
|
|
84
83
|
rm -rf .mypy_cache
|
|
85
|
-
rm -rf .venv
|
|
86
84
|
rm -rf .cache
|
|
87
85
|
rm -rf .DS_Store
|
|
88
86
|
rm -rf .idea
|
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
"""Docstring generator using litellm with structured output.
|
|
2
|
+
|
|
3
|
+
This module provides a simple way to generate docstrings and suggest improved
|
|
4
|
+
names for Python functions using LLMs with structured output.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import ast
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
import re
|
|
12
|
+
import sys
|
|
13
|
+
import time
|
|
14
|
+
import traceback
|
|
15
|
+
from typing import Optional
|
|
16
|
+
|
|
17
|
+
import typer
|
|
18
|
+
from rich.console import Console
|
|
19
|
+
|
|
20
|
+
console = Console()
|
|
21
|
+
|
|
22
|
+
import litellm
|
|
23
|
+
from pydantic import BaseModel, Field
|
|
24
|
+
|
|
25
|
+
# --- CHANGE 1: Import prompts from the new prompts.py file ---
|
|
26
|
+
from src.scripts.prompts import SYSTEM_PROMPT_TEMPLATE, USER_PROMPT_TEMPLATE
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class DescriptionOutput(BaseModel):
|
|
30
|
+
"""Structure for the generated description output."""
|
|
31
|
+
|
|
32
|
+
description: str = Field(description="A clear, detailed description of what the function does")
|
|
33
|
+
suggested_name: Optional[str] = Field(None, description="A better name for the function, if applicable")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class FunctionExtractor(ast.NodeVisitor):
|
|
37
|
+
"""
|
|
38
|
+
An AST node visitor that collects the source code of all function
|
|
39
|
+
and method definitions within a Python script.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(self, source_code: str):
|
|
43
|
+
self.source_lines = source_code.splitlines(keepends=True)
|
|
44
|
+
self.functions: list[tuple[str, str]] = [] # Store tuples of (function_name, function_source)
|
|
45
|
+
|
|
46
|
+
def _get_source_segment(self, node: ast.AST) -> str | None:
|
|
47
|
+
"""Safely extracts the source segment for a node using ast.get_source_segment."""
|
|
48
|
+
try:
|
|
49
|
+
source_segment = ast.get_source_segment("".join(self.source_lines), node)
|
|
50
|
+
return source_segment
|
|
51
|
+
except Exception as e:
|
|
52
|
+
print(
|
|
53
|
+
f"Warning: Could not retrieve source for node {getattr(node, 'name', 'unknown')} at line {getattr(node, 'lineno', 'unknown')}: {e}"
|
|
54
|
+
)
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
def visit_FunctionDef(self, node: ast.FunctionDef):
|
|
58
|
+
"""Visits a regular function definition and collects it if not excluded."""
|
|
59
|
+
if not node.name.startswith("_") and node.name != "list_tools":
|
|
60
|
+
source_code = self._get_source_segment(node)
|
|
61
|
+
if source_code:
|
|
62
|
+
self.functions.append((node.name, source_code))
|
|
63
|
+
self.generic_visit(node)
|
|
64
|
+
|
|
65
|
+
def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef):
|
|
66
|
+
"""Visits an asynchronous function definition and collects it if not excluded."""
|
|
67
|
+
if not node.name.startswith("_") and node.name != "list_tools":
|
|
68
|
+
source_code = self._get_source_segment(node)
|
|
69
|
+
if source_code:
|
|
70
|
+
self.functions.append((node.name, source_code))
|
|
71
|
+
self.generic_visit(node)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def extract_functions_from_script(file_path: str) -> list[tuple[str, str]]:
|
|
75
|
+
"""
|
|
76
|
+
Reads a Python script and extracts the source code of all functions.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
file_path: The path to the Python (.py) script.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
A list of tuples, where each tuple contains the function name (str)
|
|
83
|
+
and its full source code (str), including decorators.
|
|
84
|
+
Returns an empty list if the file cannot be read, parsed, or contains no functions.
|
|
85
|
+
|
|
86
|
+
Raises:
|
|
87
|
+
FileNotFoundError: If the file_path does not exist.
|
|
88
|
+
SyntaxError: If the file contains invalid Python syntax.
|
|
89
|
+
Exception: For other potential I/O or AST processing errors.
|
|
90
|
+
"""
|
|
91
|
+
try:
|
|
92
|
+
with open(file_path, encoding="utf-8") as f:
|
|
93
|
+
source_code = f.read()
|
|
94
|
+
except FileNotFoundError:
|
|
95
|
+
print(f"Error: File not found at {file_path}")
|
|
96
|
+
raise
|
|
97
|
+
except Exception as e:
|
|
98
|
+
print(f"Error reading file {file_path}: {e}")
|
|
99
|
+
raise
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
tree = ast.parse(source_code, filename=file_path)
|
|
103
|
+
except SyntaxError as e:
|
|
104
|
+
print(f"Error: Invalid Python syntax in {file_path} at line {e.lineno}, offset {e.offset}: {e.msg}")
|
|
105
|
+
raise
|
|
106
|
+
except Exception as e:
|
|
107
|
+
print(f"Error parsing {file_path} into AST: {e}")
|
|
108
|
+
raise
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
extractor = FunctionExtractor(source_code)
|
|
112
|
+
extractor.visit(tree)
|
|
113
|
+
|
|
114
|
+
if not extractor.functions:
|
|
115
|
+
print("Warning: No functions found in the file.")
|
|
116
|
+
|
|
117
|
+
return extractor.functions
|
|
118
|
+
except Exception as e:
|
|
119
|
+
print(f"Error during function extraction: {e}")
|
|
120
|
+
import traceback
|
|
121
|
+
|
|
122
|
+
traceback.print_exc()
|
|
123
|
+
return []
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def extract_json_from_text(text):
|
|
127
|
+
"""Extract valid JSON from text that might contain additional content.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
text: Raw text response from the model
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Dict containing the extracted JSON data
|
|
134
|
+
|
|
135
|
+
Raises:
|
|
136
|
+
ValueError: If no valid JSON could be extracted
|
|
137
|
+
"""
|
|
138
|
+
json_match = re.search(r"```(?:json)?\s*([\s\S]*?)\s*```", text)
|
|
139
|
+
if json_match:
|
|
140
|
+
try:
|
|
141
|
+
return json.loads(json_match.group(1))
|
|
142
|
+
except json.JSONDecodeError:
|
|
143
|
+
pass
|
|
144
|
+
|
|
145
|
+
try:
|
|
146
|
+
start = text.find("{")
|
|
147
|
+
if start >= 0:
|
|
148
|
+
brace_count = 0
|
|
149
|
+
for i in range(start, len(text)):
|
|
150
|
+
if text[i] == "{":
|
|
151
|
+
brace_count += 1
|
|
152
|
+
elif text[i] == "}":
|
|
153
|
+
brace_count -= 1
|
|
154
|
+
if brace_count == 0:
|
|
155
|
+
return json.loads(text[start : i + 1])
|
|
156
|
+
except json.JSONDecodeError:
|
|
157
|
+
pass
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
return json.loads(text)
|
|
161
|
+
except json.JSONDecodeError as e:
|
|
162
|
+
raise ValueError("Could not extract valid JSON from the response") from e
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def parse_existing_docstring(docstring: str | None) -> tuple[str, str]:
|
|
166
|
+
"""
|
|
167
|
+
Parses an existing docstring to separate the summary from the rest.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
docstring: The existing docstring string.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
A tuple containing (summary, rest_of_docstring).
|
|
174
|
+
'rest_of_docstring' will include Args, Returns, etc.
|
|
175
|
+
"""
|
|
176
|
+
if not docstring:
|
|
177
|
+
return "", ""
|
|
178
|
+
|
|
179
|
+
lines = docstring.strip().split('\n')
|
|
180
|
+
summary_lines = []
|
|
181
|
+
rest_lines = []
|
|
182
|
+
summary_done = False
|
|
183
|
+
|
|
184
|
+
# Sections that mark the end of the summary
|
|
185
|
+
section_markers = ("Args:", "Returns:", "Raises:", "Tags:", "Example:", "Examples:")
|
|
186
|
+
|
|
187
|
+
for i, line in enumerate(lines):
|
|
188
|
+
stripped_line = line.strip()
|
|
189
|
+
if not summary_done and stripped_line and not stripped_line.startswith(section_markers):
|
|
190
|
+
summary_lines.append(line.strip())
|
|
191
|
+
elif not summary_done and not stripped_line and summary_lines:
|
|
192
|
+
# This blank line marks the end of the summary
|
|
193
|
+
summary_done = True
|
|
194
|
+
rest_lines = lines[i+1:]
|
|
195
|
+
break
|
|
196
|
+
elif stripped_line.startswith(section_markers):
|
|
197
|
+
# A section starts immediately after the summary
|
|
198
|
+
summary_done = True
|
|
199
|
+
rest_lines = lines[i:]
|
|
200
|
+
break
|
|
201
|
+
else:
|
|
202
|
+
# We are already in the rest of the docstring
|
|
203
|
+
rest_lines.append(line)
|
|
204
|
+
|
|
205
|
+
summary = " ".join(summary_lines)
|
|
206
|
+
# Reconstruct the rest of the docstring, preserving original indentation
|
|
207
|
+
rest_of_docstring = "\n".join(rest_lines)
|
|
208
|
+
|
|
209
|
+
return summary, rest_of_docstring
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def generate_description(
|
|
213
|
+
function_code: str, file_content: str, model: str = "perplexity/sonar"
|
|
214
|
+
) -> DescriptionOutput:
|
|
215
|
+
"""
|
|
216
|
+
Generate a high-quality description and suggest a name for a Python function.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
function_code: The source code of the function to document.
|
|
220
|
+
file_content: The entire content of the Python file for context.
|
|
221
|
+
model: The model to use for generating the description.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
A DescriptionOutput object containing the description and optional name.
|
|
225
|
+
"""
|
|
226
|
+
system_prompt = SYSTEM_PROMPT_TEMPLATE.format(file_content=file_content)
|
|
227
|
+
user_prompt = USER_PROMPT_TEMPLATE.format(function_code=function_code)
|
|
228
|
+
|
|
229
|
+
# --- NEW: Retry logic with exponential backoff ---
|
|
230
|
+
max_retries = 3
|
|
231
|
+
base_delay = 2 # in seconds
|
|
232
|
+
|
|
233
|
+
for attempt in range(max_retries):
|
|
234
|
+
try:
|
|
235
|
+
response = litellm.completion(
|
|
236
|
+
model=model,
|
|
237
|
+
messages=[
|
|
238
|
+
{"role": "system", "content": system_prompt},
|
|
239
|
+
{"role": "user", "content": user_prompt},
|
|
240
|
+
],
|
|
241
|
+
)
|
|
242
|
+
response_text = response.choices[0].message.content
|
|
243
|
+
try:
|
|
244
|
+
parsed_data = extract_json_from_text(response_text)
|
|
245
|
+
return DescriptionOutput(
|
|
246
|
+
description=parsed_data.get("description", "No description available."),
|
|
247
|
+
suggested_name=parsed_data.get("suggested_name"),
|
|
248
|
+
)
|
|
249
|
+
except ValueError as e:
|
|
250
|
+
print(f" - JSON extraction failed: {e}")
|
|
251
|
+
return DescriptionOutput(description="Failed to extract description.")
|
|
252
|
+
|
|
253
|
+
except litellm.InternalServerError as e:
|
|
254
|
+
if attempt < max_retries - 1:
|
|
255
|
+
delay = base_delay * (2**attempt)
|
|
256
|
+
print(
|
|
257
|
+
f" - Server error occurred: {e}. Retrying in {delay} seconds... (Attempt {attempt + 1}/{max_retries})",
|
|
258
|
+
file=sys.stderr,
|
|
259
|
+
)
|
|
260
|
+
time.sleep(delay)
|
|
261
|
+
else:
|
|
262
|
+
print(f" - Max retries reached. Error generating description: {e}", file=sys.stderr)
|
|
263
|
+
return DescriptionOutput(description=f"Error generating description after {max_retries} retries: {e}")
|
|
264
|
+
|
|
265
|
+
except Exception as e:
|
|
266
|
+
print(f" - An unexpected error occurred: {e}", file=sys.stderr)
|
|
267
|
+
return DescriptionOutput(description=f"An unexpected error occurred: {e}")
|
|
268
|
+
|
|
269
|
+
return DescriptionOutput(description="Failed to generate description after all retries.")
|
|
270
|
+
|
|
271
|
+
def insert_docstring_into_function(function_code: str, docstring: str) -> str:
|
|
272
|
+
"""
|
|
273
|
+
Insert a docstring into a function's code, replacing an existing one if present.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
function_code: The source code of the function snippet.
|
|
277
|
+
docstring: The formatted docstring string content.
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
The updated function code with the docstring inserted.
|
|
281
|
+
"""
|
|
282
|
+
try:
|
|
283
|
+
lines = function_code.splitlines(keepends=True)
|
|
284
|
+
tree = ast.parse(function_code)
|
|
285
|
+
if not tree.body or not isinstance(tree.body[0], (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
286
|
+
return function_code
|
|
287
|
+
|
|
288
|
+
func_node = tree.body[0]
|
|
289
|
+
|
|
290
|
+
# --- REVISED INDENTATION LOGIC ---
|
|
291
|
+
# Determine the correct indentation from the function's existing body.
|
|
292
|
+
if func_node.body:
|
|
293
|
+
# Use the indentation of the first statement in the body (e.g., the old docstring).
|
|
294
|
+
first_body_line_str = lines[func_node.body[0].lineno - 1]
|
|
295
|
+
body_indent = first_body_line_str[: len(first_body_line_str) - len(first_body_line_str.lstrip())]
|
|
296
|
+
else:
|
|
297
|
+
# Fallback for empty functions: calculate from the 'def' line.
|
|
298
|
+
def_line = lines[func_node.lineno - 1]
|
|
299
|
+
def_indent = def_line[: len(def_line) - len(def_line.lstrip())]
|
|
300
|
+
body_indent = def_indent + " "
|
|
301
|
+
# --- END OF REVISED LOGIC ---
|
|
302
|
+
|
|
303
|
+
# Format the new docstring with the determined indentation
|
|
304
|
+
new_docstring_lines_formatted = [f'{body_indent}"""\n']
|
|
305
|
+
new_docstring_lines_formatted.extend([f"{body_indent}{line}\n" for line in docstring.splitlines()])
|
|
306
|
+
new_docstring_lines_formatted.append(f'{body_indent}"""\n')
|
|
307
|
+
|
|
308
|
+
# Check if the first statement is an existing docstring
|
|
309
|
+
existing_docstring_node = None
|
|
310
|
+
if (
|
|
311
|
+
func_node.body and
|
|
312
|
+
isinstance(func_node.body[0], ast.Expr) and
|
|
313
|
+
isinstance(func_node.body[0].value, ast.Constant) and
|
|
314
|
+
isinstance(func_node.body[0].value.value, str)
|
|
315
|
+
):
|
|
316
|
+
existing_docstring_node = func_node.body[0]
|
|
317
|
+
|
|
318
|
+
# Splice the code
|
|
319
|
+
insert_idx = func_node.body[0].lineno - 1 if func_node.body else func_node.lineno
|
|
320
|
+
pre_insertion_lines = lines[:insert_idx]
|
|
321
|
+
|
|
322
|
+
if existing_docstring_node:
|
|
323
|
+
post_insertion_lines = lines[existing_docstring_node.end_lineno:]
|
|
324
|
+
else:
|
|
325
|
+
post_insertion_lines = lines[insert_idx:]
|
|
326
|
+
|
|
327
|
+
output_lines = pre_insertion_lines + new_docstring_lines_formatted + post_insertion_lines
|
|
328
|
+
|
|
329
|
+
final_code = "".join(output_lines)
|
|
330
|
+
ast.parse(final_code) # Validate syntax
|
|
331
|
+
return final_code
|
|
332
|
+
|
|
333
|
+
except Exception as e:
|
|
334
|
+
print(f"Error processing function snippet for insertion: {e}", file=sys.stderr)
|
|
335
|
+
traceback.print_exc(file=sys.stderr)
|
|
336
|
+
return function_code
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def rename_function_in_code(function_code: str, old_name: str, new_name: str) -> str:
|
|
340
|
+
"""
|
|
341
|
+
Safely renames a function within its source code snippet.
|
|
342
|
+
|
|
343
|
+
Args:
|
|
344
|
+
function_code: The source code of the function.
|
|
345
|
+
old_name: The original name of the function.
|
|
346
|
+
new_name: The new name for the function.
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
The function code with the updated name.
|
|
350
|
+
"""
|
|
351
|
+
# This regex looks for 'def' or 'async def' followed by the old name
|
|
352
|
+
pattern = r"(async\s+def|def)\s+" + re.escape(old_name) + r"(\s*\()"
|
|
353
|
+
replacement = r"\1 " + new_name + r"\2"
|
|
354
|
+
|
|
355
|
+
new_function_code, num_replacements = re.subn(pattern, replacement, function_code, 1)
|
|
356
|
+
|
|
357
|
+
if num_replacements == 0:
|
|
358
|
+
print(f" - Warning: Could not rename function '{old_name}'. Name not found in definition.")
|
|
359
|
+
return function_code
|
|
360
|
+
|
|
361
|
+
return new_function_code
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def update_list_tools_method(content: str, old_name: str, new_name: str) -> str:
|
|
365
|
+
"""
|
|
366
|
+
Updates the list_tools method by renaming a function reference.
|
|
367
|
+
|
|
368
|
+
This function uses regex to find the `list_tools` method and replace
|
|
369
|
+
`self.old_name` with `self.new_name` within its return list, preserving
|
|
370
|
+
surrounding code and formatting.
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
content: The full source code of the file as a string.
|
|
374
|
+
old_name: The original name of the function to replace.
|
|
375
|
+
new_name: The new name for the function.
|
|
376
|
+
|
|
377
|
+
Returns:
|
|
378
|
+
The updated file content with the function renamed in list_tools.
|
|
379
|
+
"""
|
|
380
|
+
# Regex to find `self.old_name` followed by a comma, newline, or closing bracket
|
|
381
|
+
# This ensures we only replace the specific tool reference in the list.
|
|
382
|
+
pattern = r"(self\.)" + re.escape(old_name) + r"(?=\s*[,\]])"
|
|
383
|
+
replacement = r"\1" + new_name
|
|
384
|
+
|
|
385
|
+
# First, find the list_tools method definition to narrow the search area
|
|
386
|
+
list_tools_match = re.search(r"def\s+list_tools\s*\([^)]*\):\s*return\s*\[[^\]]*\]", content, re.DOTALL)
|
|
387
|
+
|
|
388
|
+
if not list_tools_match:
|
|
389
|
+
print(" - Warning: `list_tools` method not found or has an unexpected format. Cannot update tool list.")
|
|
390
|
+
return content
|
|
391
|
+
|
|
392
|
+
list_tools_code = list_tools_match.group(0)
|
|
393
|
+
|
|
394
|
+
# Perform the replacement only within the found method block
|
|
395
|
+
updated_list_tools_code, num_replacements = re.subn(pattern, replacement, list_tools_code)
|
|
396
|
+
|
|
397
|
+
if num_replacements > 0:
|
|
398
|
+
print(f" - Renamed '{old_name}' to '{new_name}' in `list_tools` method.")
|
|
399
|
+
return content.replace(list_tools_code, updated_list_tools_code)
|
|
400
|
+
else:
|
|
401
|
+
print(f" - Warning: Function '{old_name}' not found in `list_tools` method.")
|
|
402
|
+
return content
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def process_file(file_path: str, model: str = "perplexity/sonar") -> int:
|
|
406
|
+
"""
|
|
407
|
+
Process a Python file to update docstrings and optionally rename functions.
|
|
408
|
+
|
|
409
|
+
Args:
|
|
410
|
+
file_path: Path to the Python file to process.
|
|
411
|
+
model: The model to use for generating descriptions.
|
|
412
|
+
|
|
413
|
+
Returns:
|
|
414
|
+
Number of functions processed.
|
|
415
|
+
"""
|
|
416
|
+
if not os.path.exists(file_path):
|
|
417
|
+
raise FileNotFoundError(f"File not found: {file_path}")
|
|
418
|
+
|
|
419
|
+
with open(file_path, encoding="utf-8") as f:
|
|
420
|
+
original_content = f.read()
|
|
421
|
+
|
|
422
|
+
functions = extract_functions_from_script(file_path)
|
|
423
|
+
if not functions:
|
|
424
|
+
print(f"No functions found in {file_path}")
|
|
425
|
+
return 0
|
|
426
|
+
|
|
427
|
+
updated_content = original_content
|
|
428
|
+
count = 0
|
|
429
|
+
|
|
430
|
+
for function_name, function_code in functions:
|
|
431
|
+
print(f"Processing function: {function_name}")
|
|
432
|
+
|
|
433
|
+
try:
|
|
434
|
+
func_tree = ast.parse(function_code)
|
|
435
|
+
func_node = func_tree.body[0]
|
|
436
|
+
existing_docstring = ast.get_docstring(func_node, clean=True)
|
|
437
|
+
_, rest_of_docstring = parse_existing_docstring(existing_docstring)
|
|
438
|
+
except (SyntaxError, IndexError):
|
|
439
|
+
print(f" - Could not parse function '{function_name}', skipping.")
|
|
440
|
+
continue
|
|
441
|
+
|
|
442
|
+
# 1. Generate new description and check for suggested name
|
|
443
|
+
output = generate_description(function_code, original_content, model)
|
|
444
|
+
new_description = output.description.strip()
|
|
445
|
+
suggested_name = output.suggested_name
|
|
446
|
+
|
|
447
|
+
if not new_description or "Error generating description" in new_description:
|
|
448
|
+
print(f" - Failed to generate description for '{function_name}', skipping.")
|
|
449
|
+
continue
|
|
450
|
+
|
|
451
|
+
# 2. Reconstruct the full docstring content
|
|
452
|
+
reconstructed_docstring = new_description
|
|
453
|
+
if rest_of_docstring:
|
|
454
|
+
reconstructed_docstring += "\n\n" + rest_of_docstring
|
|
455
|
+
|
|
456
|
+
# 3. Handle function renaming if suggested
|
|
457
|
+
code_to_update = function_code
|
|
458
|
+
is_renamed = False
|
|
459
|
+
if suggested_name and suggested_name != function_name:
|
|
460
|
+
print(f" - Renaming function '{function_name}' to '{suggested_name}'")
|
|
461
|
+
code_to_update = rename_function_in_code(code_to_update, function_name, suggested_name)
|
|
462
|
+
is_renamed = True
|
|
463
|
+
|
|
464
|
+
# 4. Insert the new docstring back into the (potentially renamed) function code
|
|
465
|
+
updated_function_block = insert_docstring_into_function(code_to_update, reconstructed_docstring)
|
|
466
|
+
|
|
467
|
+
# 5. If any changes were made, update the main content
|
|
468
|
+
if updated_function_block != function_code:
|
|
469
|
+
updated_content = updated_content.replace(function_code, updated_function_block)
|
|
470
|
+
count += 1
|
|
471
|
+
print(f" - Updated function block for '{suggested_name or function_name}'.")
|
|
472
|
+
|
|
473
|
+
# 6. If the function was renamed, also update the list_tools method
|
|
474
|
+
if is_renamed:
|
|
475
|
+
updated_content = update_list_tools_method(updated_content, function_name, suggested_name)
|
|
476
|
+
|
|
477
|
+
if updated_content != original_content:
|
|
478
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
479
|
+
f.write(updated_content)
|
|
480
|
+
print(f"\nUpdated {count} functions in {file_path}")
|
|
481
|
+
else:
|
|
482
|
+
print("\nNo changes made to the file.")
|
|
483
|
+
|
|
484
|
+
return count
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
app = typer.Typer()
|
|
488
|
+
@app.command()
|
|
489
|
+
def docgen(
|
|
490
|
+
file_path: Path = typer.Argument(..., help="Path to the Python file to process"),
|
|
491
|
+
model: str = typer.Option(
|
|
492
|
+
"gemini/gemini-2.5-pro",
|
|
493
|
+
"--model",
|
|
494
|
+
"-m",
|
|
495
|
+
help="Model to use for generating docstrings",
|
|
496
|
+
),
|
|
497
|
+
):
|
|
498
|
+
"""
|
|
499
|
+
Generate/update docstrings and function names in Python files using LLMs.
|
|
500
|
+
|
|
501
|
+
This command uses litellm to generate high-quality docstring descriptions
|
|
502
|
+
and suggest better function names, updating the file accordingly.
|
|
503
|
+
"""
|
|
504
|
+
if not file_path.exists():
|
|
505
|
+
console.print(f"[red]Error: File not found: {file_path}[/red]")
|
|
506
|
+
raise typer.Exit(1)
|
|
507
|
+
|
|
508
|
+
try:
|
|
509
|
+
processed = process_file(str(file_path), model)
|
|
510
|
+
console.print(f"[green]Successfully processed {processed} functions[/green]")
|
|
511
|
+
except Exception as e:
|
|
512
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
513
|
+
raise typer.Exit(1) from e
|
|
514
|
+
|
|
515
|
+
if __name__ == "__main__":
|
|
516
|
+
app()
|