universal-mcp-applications 0.1.30__py3-none-any.whl → 0.1.36rc1__py3-none-any.whl

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.

Files changed (105) hide show
  1. universal_mcp/applications/ahrefs/app.py +52 -198
  2. universal_mcp/applications/airtable/app.py +23 -122
  3. universal_mcp/applications/apollo/app.py +111 -464
  4. universal_mcp/applications/asana/app.py +417 -1567
  5. universal_mcp/applications/aws_s3/app.py +33 -100
  6. universal_mcp/applications/bill/app.py +546 -1957
  7. universal_mcp/applications/box/app.py +1068 -3981
  8. universal_mcp/applications/braze/app.py +364 -1430
  9. universal_mcp/applications/browser_use/app.py +2 -8
  10. universal_mcp/applications/cal_com_v2/app.py +207 -625
  11. universal_mcp/applications/calendly/app.py +61 -200
  12. universal_mcp/applications/canva/app.py +45 -110
  13. universal_mcp/applications/clickup/app.py +207 -674
  14. universal_mcp/applications/coda/app.py +146 -426
  15. universal_mcp/applications/confluence/app.py +310 -1098
  16. universal_mcp/applications/contentful/app.py +36 -151
  17. universal_mcp/applications/crustdata/app.py +28 -107
  18. universal_mcp/applications/dialpad/app.py +283 -756
  19. universal_mcp/applications/digitalocean/app.py +1766 -5777
  20. universal_mcp/applications/domain_checker/app.py +3 -54
  21. universal_mcp/applications/e2b/app.py +14 -64
  22. universal_mcp/applications/elevenlabs/app.py +9 -47
  23. universal_mcp/applications/exa/app.py +6 -17
  24. universal_mcp/applications/falai/app.py +23 -100
  25. universal_mcp/applications/figma/app.py +53 -137
  26. universal_mcp/applications/file_system/app.py +2 -13
  27. universal_mcp/applications/firecrawl/app.py +51 -152
  28. universal_mcp/applications/fireflies/app.py +59 -281
  29. universal_mcp/applications/fpl/app.py +91 -528
  30. universal_mcp/applications/fpl/utils/fixtures.py +15 -49
  31. universal_mcp/applications/fpl/utils/helper.py +25 -89
  32. universal_mcp/applications/fpl/utils/league_utils.py +20 -64
  33. universal_mcp/applications/ghost_content/app.py +52 -161
  34. universal_mcp/applications/github/app.py +19 -56
  35. universal_mcp/applications/gong/app.py +88 -248
  36. universal_mcp/applications/google_calendar/app.py +16 -68
  37. universal_mcp/applications/google_docs/app.py +88 -188
  38. universal_mcp/applications/google_drive/app.py +140 -462
  39. universal_mcp/applications/google_gemini/app.py +12 -64
  40. universal_mcp/applications/google_mail/app.py +28 -157
  41. universal_mcp/applications/google_searchconsole/app.py +15 -48
  42. universal_mcp/applications/google_sheet/app.py +101 -578
  43. universal_mcp/applications/google_sheet/helper.py +10 -37
  44. universal_mcp/applications/hashnode/app.py +57 -269
  45. universal_mcp/applications/heygen/app.py +44 -122
  46. universal_mcp/applications/http_tools/app.py +10 -32
  47. universal_mcp/applications/hubspot/api_segments/crm_api.py +460 -1573
  48. universal_mcp/applications/hubspot/api_segments/marketing_api.py +74 -262
  49. universal_mcp/applications/hubspot/app.py +23 -87
  50. universal_mcp/applications/jira/app.py +2071 -7986
  51. universal_mcp/applications/klaviyo/app.py +494 -1376
  52. universal_mcp/applications/linkedin/README.md +9 -2
  53. universal_mcp/applications/linkedin/app.py +392 -212
  54. universal_mcp/applications/mailchimp/app.py +450 -1605
  55. universal_mcp/applications/markitdown/app.py +8 -20
  56. universal_mcp/applications/miro/app.py +217 -699
  57. universal_mcp/applications/ms_teams/app.py +64 -186
  58. universal_mcp/applications/neon/app.py +86 -192
  59. universal_mcp/applications/notion/app.py +21 -36
  60. universal_mcp/applications/onedrive/app.py +14 -36
  61. universal_mcp/applications/openai/app.py +42 -165
  62. universal_mcp/applications/outlook/app.py +16 -76
  63. universal_mcp/applications/perplexity/app.py +4 -19
  64. universal_mcp/applications/pipedrive/app.py +832 -3142
  65. universal_mcp/applications/posthog/app.py +163 -432
  66. universal_mcp/applications/reddit/app.py +40 -139
  67. universal_mcp/applications/resend/app.py +41 -107
  68. universal_mcp/applications/retell/app.py +14 -41
  69. universal_mcp/applications/rocketlane/app.py +221 -934
  70. universal_mcp/applications/scraper/README.md +7 -4
  71. universal_mcp/applications/scraper/app.py +216 -102
  72. universal_mcp/applications/semanticscholar/app.py +22 -64
  73. universal_mcp/applications/semrush/app.py +43 -77
  74. universal_mcp/applications/sendgrid/app.py +512 -1262
  75. universal_mcp/applications/sentry/app.py +271 -906
  76. universal_mcp/applications/serpapi/app.py +40 -143
  77. universal_mcp/applications/sharepoint/app.py +15 -37
  78. universal_mcp/applications/shopify/app.py +1551 -4287
  79. universal_mcp/applications/shortcut/app.py +155 -417
  80. universal_mcp/applications/slack/app.py +50 -101
  81. universal_mcp/applications/spotify/app.py +126 -325
  82. universal_mcp/applications/supabase/app.py +104 -213
  83. universal_mcp/applications/tavily/app.py +1 -1
  84. universal_mcp/applications/trello/app.py +693 -2656
  85. universal_mcp/applications/twilio/app.py +14 -50
  86. universal_mcp/applications/twitter/api_segments/compliance_api.py +4 -14
  87. universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +6 -18
  88. universal_mcp/applications/twitter/api_segments/likes_api.py +1 -3
  89. universal_mcp/applications/twitter/api_segments/lists_api.py +5 -15
  90. universal_mcp/applications/twitter/api_segments/trends_api.py +1 -3
  91. universal_mcp/applications/twitter/api_segments/tweets_api.py +9 -31
  92. universal_mcp/applications/twitter/api_segments/usage_api.py +1 -5
  93. universal_mcp/applications/twitter/api_segments/users_api.py +14 -42
  94. universal_mcp/applications/whatsapp/app.py +35 -186
  95. universal_mcp/applications/whatsapp/audio.py +2 -6
  96. universal_mcp/applications/whatsapp/whatsapp.py +17 -51
  97. universal_mcp/applications/whatsapp_business/app.py +70 -283
  98. universal_mcp/applications/wrike/app.py +45 -118
  99. universal_mcp/applications/yahoo_finance/app.py +19 -65
  100. universal_mcp/applications/youtube/app.py +75 -261
  101. universal_mcp/applications/zenquotes/app.py +2 -2
  102. {universal_mcp_applications-0.1.30.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/METADATA +2 -2
  103. {universal_mcp_applications-0.1.30.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/RECORD +105 -105
  104. {universal_mcp_applications-0.1.30.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/WHEEL +0 -0
  105. {universal_mcp_applications-0.1.30.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/licenses/LICENSE +0 -0
@@ -1,5 +1,4 @@
1
1
  from typing import Any
2
-
3
2
  from universal_mcp.applications.application import APIApplication
4
3
  from universal_mcp.integrations import Integration
5
4
 
@@ -19,7 +18,7 @@ class WrikeApp(APIApplication):
19
18
  super().__init__(name="wrike", integration=integration, **kwargs)
20
19
  self.base_url = "https://www.wrike.com/api/v4"
21
20
 
22
- def get_contacts(self, deleted=None, fields=None, metadata=None) -> Any:
21
+ async def get_contacts(self, deleted=None, fields=None, metadata=None) -> Any:
23
22
  """
24
23
  Retrieves contacts from the server with optional deleted status filtering, field selection, and metadata inclusion.
25
24
 
@@ -38,20 +37,12 @@ class WrikeApp(APIApplication):
38
37
  retrieve, contacts, filter, api, important
39
38
  """
40
39
  url = f"{self.base_url}/contacts"
41
- query_params = {
42
- k: v
43
- for k, v in [
44
- ("deleted", deleted),
45
- ("fields", fields),
46
- ("metadata", metadata),
47
- ]
48
- if v is not None
49
- }
40
+ query_params = {k: v for k, v in [("deleted", deleted), ("fields", fields), ("metadata", metadata)] if v is not None}
50
41
  response = self._get(url, params=query_params)
51
42
  response.raise_for_status()
52
43
  return response.json()
53
44
 
54
- def get_contacts_by_contactid(self, contactId, fields=None) -> Any:
45
+ async def get_contacts_by_contactid(self, contactId, fields=None) -> Any:
55
46
  """
56
47
  Retrieves contact information for a specific contact ID, optionally returning only specified fields.
57
48
 
@@ -76,15 +67,8 @@ class WrikeApp(APIApplication):
76
67
  response.raise_for_status()
77
68
  return response.json()
78
69
 
79
- def put_contacts_by_contactid(
80
- self,
81
- contactId,
82
- metadata=None,
83
- currentBillRate=None,
84
- currentCostRate=None,
85
- jobRoleId=None,
86
- customFields=None,
87
- fields=None,
70
+ async def put_contacts_by_contactid(
71
+ self, contactId, metadata=None, currentBillRate=None, currentCostRate=None, jobRoleId=None, customFields=None, fields=None
88
72
  ) -> Any:
89
73
  """
90
74
  Updates an existing contact using the specified contact ID with provided details including metadata, billing/cost rates, job role, and custom fields.
@@ -125,7 +109,7 @@ class WrikeApp(APIApplication):
125
109
  response.raise_for_status()
126
110
  return response.json()
127
111
 
128
- def get_users_by_userid(self, userId) -> Any:
112
+ async def get_users_by_userid(self, userId) -> Any:
129
113
  """
130
114
  Retrieves user information by ID from the API endpoint.
131
115
 
@@ -150,7 +134,7 @@ class WrikeApp(APIApplication):
150
134
  response.raise_for_status()
151
135
  return response.json()
152
136
 
153
- def put_users_by_userid(self, userId, profile=None) -> Any:
137
+ async def put_users_by_userid(self, userId, profile=None) -> Any:
154
138
  """
155
139
  Updates a user's profile information by user ID using a PUT request.
156
140
 
@@ -170,9 +154,7 @@ class WrikeApp(APIApplication):
170
154
  """
171
155
  if userId is None:
172
156
  raise ValueError("Missing required parameter 'userId'")
173
- request_body = {
174
- "profile": profile,
175
- }
157
+ request_body = {"profile": profile}
176
158
  request_body = {k: v for k, v in request_body.items() if v is not None}
177
159
  url = f"{self.base_url}/users/{userId}"
178
160
  query_params = {}
@@ -180,9 +162,7 @@ class WrikeApp(APIApplication):
180
162
  response.raise_for_status()
181
163
  return response.json()
182
164
 
183
- def get_groups(
184
- self, metadata=None, pageSize=None, pageToken=None, fields=None
185
- ) -> Any:
165
+ async def get_groups(self, metadata=None, pageSize=None, pageToken=None, fields=None) -> Any:
186
166
  """
187
167
  Retrieves a list of groups from the API, applying optional filtering and pagination parameters.
188
168
 
@@ -203,22 +183,13 @@ class WrikeApp(APIApplication):
203
183
  """
204
184
  url = f"{self.base_url}/groups"
205
185
  query_params = {
206
- k: v
207
- for k, v in [
208
- ("metadata", metadata),
209
- ("pageSize", pageSize),
210
- ("pageToken", pageToken),
211
- ("fields", fields),
212
- ]
213
- if v is not None
186
+ k: v for k, v in [("metadata", metadata), ("pageSize", pageSize), ("pageToken", pageToken), ("fields", fields)] if v is not None
214
187
  }
215
188
  response = self._get(url, params=query_params)
216
189
  response.raise_for_status()
217
190
  return response.json()
218
191
 
219
- def post_groups(
220
- self, title, members=None, parent=None, avatar=None, metadata=None
221
- ) -> Any:
192
+ async def post_groups(self, title, members=None, parent=None, avatar=None, metadata=None) -> Any:
222
193
  """
223
194
  Creates a new group with the specified title and optional details via a POST request to the groups endpoint.
224
195
 
@@ -240,13 +211,7 @@ class WrikeApp(APIApplication):
240
211
  """
241
212
  if title is None:
242
213
  raise ValueError("Missing required parameter 'title'")
243
- request_body = {
244
- "title": title,
245
- "members": members,
246
- "parent": parent,
247
- "avatar": avatar,
248
- "metadata": metadata,
249
- }
214
+ request_body = {"title": title, "members": members, "parent": parent, "avatar": avatar, "metadata": metadata}
250
215
  request_body = {k: v for k, v in request_body.items() if v is not None}
251
216
  url = f"{self.base_url}/groups"
252
217
  query_params = {}
@@ -254,7 +219,7 @@ class WrikeApp(APIApplication):
254
219
  response.raise_for_status()
255
220
  return response.json()
256
221
 
257
- def get_groups_by_groupid(self, groupId, fields=None) -> Any:
222
+ async def get_groups_by_groupid(self, groupId, fields=None) -> Any:
258
223
  """
259
224
  Retrieves details for a specific group by its group ID, optionally returning only specified fields.
260
225
 
@@ -279,7 +244,7 @@ class WrikeApp(APIApplication):
279
244
  response.raise_for_status()
280
245
  return response.json()
281
246
 
282
- def put_groups_by_groupid(
247
+ async def put_groups_by_groupid(
283
248
  self,
284
249
  groupId,
285
250
  title=None,
@@ -333,7 +298,7 @@ class WrikeApp(APIApplication):
333
298
  response.raise_for_status()
334
299
  return response.json()
335
300
 
336
- def delete_groups_by_groupid(self, groupId) -> Any:
301
+ async def delete_groups_by_groupid(self, groupId) -> Any:
337
302
  """
338
303
  Deletes a group resource identified by the provided groupId using an HTTP DELETE request.
339
304
 
@@ -357,7 +322,7 @@ class WrikeApp(APIApplication):
357
322
  response.raise_for_status()
358
323
  return response.json()
359
324
 
360
- def put_groups_bulk(self, members) -> Any:
325
+ async def put_groups_bulk(self, members) -> Any:
361
326
  """
362
327
  Updates multiple group memberships in bulk by sending a PUT request with the given member data.
363
328
 
@@ -375,9 +340,7 @@ class WrikeApp(APIApplication):
375
340
  """
376
341
  if members is None:
377
342
  raise ValueError("Missing required parameter 'members'")
378
- request_body = {
379
- "members": members,
380
- }
343
+ request_body = {"members": members}
381
344
  request_body = {k: v for k, v in request_body.items() if v is not None}
382
345
  url = f"{self.base_url}/groups_bulk"
383
346
  query_params = {}
@@ -385,9 +348,7 @@ class WrikeApp(APIApplication):
385
348
  response.raise_for_status()
386
349
  return response.json()
387
350
 
388
- def get_invitations(
389
- self,
390
- ) -> Any:
351
+ async def get_invitations(self) -> Any:
391
352
  """
392
353
  Retrieves all invitations from the server using a GET request.
393
354
 
@@ -409,16 +370,8 @@ class WrikeApp(APIApplication):
409
370
  response.raise_for_status()
410
371
  return response.json()
411
372
 
412
- def post_invitations(
413
- self,
414
- email,
415
- firstName=None,
416
- lastName=None,
417
- role=None,
418
- external=None,
419
- subject=None,
420
- message=None,
421
- userTypeId=None,
373
+ async def post_invitations(
374
+ self, email, firstName=None, lastName=None, role=None, external=None, subject=None, message=None, userTypeId=None
422
375
  ) -> Any:
423
376
  """
424
377
  Sends an invitation email to a user with optional details such as name, role, and custom message.
@@ -461,9 +414,7 @@ class WrikeApp(APIApplication):
461
414
  response.raise_for_status()
462
415
  return response.json()
463
416
 
464
- def put_invitations_by_invitationid(
465
- self, invitationId, resend=None, role=None, external=None, userTypeId=None
466
- ) -> Any:
417
+ async def put_invitations_by_invitationid(self, invitationId, resend=None, role=None, external=None, userTypeId=None) -> Any:
467
418
  """
468
419
  Updates an existing invitation by its unique ID with optional parameters, handling conditional updates and API communication.
469
420
 
@@ -486,12 +437,7 @@ class WrikeApp(APIApplication):
486
437
  """
487
438
  if invitationId is None:
488
439
  raise ValueError("Missing required parameter 'invitationId'")
489
- request_body = {
490
- "resend": resend,
491
- "role": role,
492
- "external": external,
493
- "userTypeId": userTypeId,
494
- }
440
+ request_body = {"resend": resend, "role": role, "external": external, "userTypeId": userTypeId}
495
441
  request_body = {k: v for k, v in request_body.items() if v is not None}
496
442
  url = f"{self.base_url}/invitations/{invitationId}"
497
443
  query_params = {}
@@ -499,7 +445,7 @@ class WrikeApp(APIApplication):
499
445
  response.raise_for_status()
500
446
  return response.json()
501
447
 
502
- def delete_invitations_by_invitationid(self, invitationId) -> Any:
448
+ async def delete_invitations_by_invitationid(self, invitationId) -> Any:
503
449
  """
504
450
  Deletes a specific invitation using its unique identifier
505
451
 
@@ -524,7 +470,7 @@ class WrikeApp(APIApplication):
524
470
  response.raise_for_status()
525
471
  return response.json()
526
472
 
527
- def get_a_ccount(self, fields=None) -> Any:
473
+ async def get_a_ccount(self, fields=None) -> Any:
528
474
  """
529
475
  Retrieves account information from the API, optionally including only specified fields.
530
476
 
@@ -546,7 +492,7 @@ class WrikeApp(APIApplication):
546
492
  response.raise_for_status()
547
493
  return response.json()
548
494
 
549
- def put_a_ccount(self, metadata=None) -> Any:
495
+ async def put_a_ccount(self, metadata=None) -> Any:
550
496
  """
551
497
  Sends a PUT request to update or create an account with the provided metadata and returns the server response as a JSON object.
552
498
 
@@ -562,9 +508,7 @@ class WrikeApp(APIApplication):
562
508
  Tags:
563
509
  put, account, metadata, async_job
564
510
  """
565
- request_body = {
566
- "metadata": metadata,
567
- }
511
+ request_body = {"metadata": metadata}
568
512
  request_body = {k: v for k, v in request_body.items() if v is not None}
569
513
  url = f"{self.base_url}/account"
570
514
  query_params = {}
@@ -572,9 +516,7 @@ class WrikeApp(APIApplication):
572
516
  response.raise_for_status()
573
517
  return response.json()
574
518
 
575
- def get_workflows(
576
- self,
577
- ) -> Any:
519
+ async def get_workflows(self) -> Any:
578
520
  """
579
521
  Retrieves all workflows from the server using a GET request.
580
522
 
@@ -596,7 +538,7 @@ class WrikeApp(APIApplication):
596
538
  response.raise_for_status()
597
539
  return response.json()
598
540
 
599
- def post_workflows(self, name=None, request_body=None) -> Any:
541
+ async def post_workflows(self, name=None, request_body=None) -> Any:
600
542
  """
601
543
  Creates a new workflow by sending a POST request to the workflows endpoint with optional name and request body.
602
544
 
@@ -619,9 +561,7 @@ class WrikeApp(APIApplication):
619
561
  response.raise_for_status()
620
562
  return response.json()
621
563
 
622
- def put_workflows_by_workflowid(
623
- self, workflowId, name=None, hidden=None, request_body=None
624
- ) -> Any:
564
+ async def put_workflows_by_workflowid(self, workflowId, name=None, hidden=None, request_body=None) -> Any:
625
565
  """
626
566
  Updates an existing workflow by workflow ID with optional name, hidden status, and request body data.
627
567
 
@@ -643,16 +583,12 @@ class WrikeApp(APIApplication):
643
583
  if workflowId is None:
644
584
  raise ValueError("Missing required parameter 'workflowId'")
645
585
  url = f"{self.base_url}/workflows/{workflowId}"
646
- query_params = {
647
- k: v for k, v in [("name", name), ("hidden", hidden)] if v is not None
648
- }
586
+ query_params = {k: v for k, v in [("name", name), ("hidden", hidden)] if v is not None}
649
587
  response = self._put(url, data=request_body, params=query_params)
650
588
  response.raise_for_status()
651
589
  return response.json()
652
590
 
653
- def get_customfields(
654
- self,
655
- ) -> Any:
591
+ async def get_customfields(self) -> Any:
656
592
  """
657
593
  Retrieves all custom fields from the API and returns them as a parsed JSON object.
658
594
 
@@ -674,16 +610,7 @@ class WrikeApp(APIApplication):
674
610
  response.raise_for_status()
675
611
  return response.json()
676
612
 
677
- def post_customfields(
678
- self,
679
- title,
680
- type,
681
- spaceId=None,
682
- sharing=None,
683
- shareds=None,
684
- settings=None,
685
- request_body=None,
686
- ) -> Any:
613
+ async def post_customfields(self, title, type, spaceId=None, sharing=None, shareds=None, settings=None, request_body=None) -> Any:
687
614
  """
688
615
  Creates a custom field by sending a POST request to the customfields endpoint with the specified parameters.
689
616
 
@@ -726,7 +653,7 @@ class WrikeApp(APIApplication):
726
653
  response.raise_for_status()
727
654
  return response.json()
728
655
 
729
- def get_customfields_by_customfieldid(self, customFieldId) -> Any:
656
+ async def get_customfields_by_customfieldid(self, customFieldId) -> Any:
730
657
  """
731
658
  Retrieves details for a custom field by its unique identifier from the API
732
659
 
@@ -750,7 +677,7 @@ class WrikeApp(APIApplication):
750
677
  response.raise_for_status()
751
678
  return response.json()
752
679
 
753
- def put_customfields_by_customfieldid(
680
+ async def put_customfields_by_customfieldid(
754
681
  self,
755
682
  customFieldId,
756
683
  title=None,
@@ -812,7 +739,7 @@ class WrikeApp(APIApplication):
812
739
  response.raise_for_status()
813
740
  return response.json()
814
741
 
815
- def delete_customfields_by_customfieldid(self, customFieldId) -> Any:
742
+ async def delete_customfields_by_customfieldid(self, customFieldId) -> Any:
816
743
  """
817
744
  Deletes a custom field resource identified by its custom field ID.
818
745
 
@@ -836,7 +763,7 @@ class WrikeApp(APIApplication):
836
763
  response.raise_for_status()
837
764
  return response.json()
838
765
 
839
- def get_folders(
766
+ async def get_folders(
840
767
  self,
841
768
  permalink=None,
842
769
  descendants=None,
@@ -906,7 +833,7 @@ class WrikeApp(APIApplication):
906
833
  response.raise_for_status()
907
834
  return response.json()
908
835
 
909
- def get_folders_by_folderid_folders(
836
+ async def get_folders_by_folderid_folders(
910
837
  self,
911
838
  folderId,
912
839
  permalink=None,
@@ -977,7 +904,7 @@ class WrikeApp(APIApplication):
977
904
  response.raise_for_status()
978
905
  return response.json()
979
906
 
980
- def post_folders_by_folderid_folders(
907
+ async def post_folders_by_folderid_folders(
981
908
  self,
982
909
  folderId,
983
910
  title,
@@ -1045,7 +972,7 @@ class WrikeApp(APIApplication):
1045
972
  response.raise_for_status()
1046
973
  return response.json()
1047
974
 
1048
- def delete_folders_by_folderid(self, folderId) -> Any:
975
+ async def delete_folders_by_folderid(self, folderId) -> Any:
1049
976
  """
1050
977
  Deletes a folder resource identified by its folder ID via an HTTP DELETE request.
1051
978
 
@@ -1070,7 +997,7 @@ class WrikeApp(APIApplication):
1070
997
  response.raise_for_status()
1071
998
  return response.json()
1072
999
 
1073
- def put_folders_by_folderid(
1000
+ async def put_folders_by_folderid(
1074
1001
  self,
1075
1002
  folderId,
1076
1003
  title=None,
@@ -1155,7 +1082,7 @@ class WrikeApp(APIApplication):
1155
1082
  response.raise_for_status()
1156
1083
  return response.json()
1157
1084
 
1158
- def get_tasks(
1085
+ async def get_tasks(
1159
1086
  self,
1160
1087
  descendants=None,
1161
1088
  title=None,
@@ -1273,7 +1200,7 @@ class WrikeApp(APIApplication):
1273
1200
  response.raise_for_status()
1274
1201
  return response.json()
1275
1202
 
1276
- def get_tasks_by_taskid(self, taskId, fields=None) -> Any:
1203
+ async def get_tasks_by_taskid(self, taskId, fields=None) -> Any:
1277
1204
  """
1278
1205
  Retrieves a task by its ID from the remote service, optionally returning only specified fields.
1279
1206
 
@@ -1298,7 +1225,7 @@ class WrikeApp(APIApplication):
1298
1225
  response.raise_for_status()
1299
1226
  return response.json()
1300
1227
 
1301
- def put_tasks_by_taskid(
1228
+ async def put_tasks_by_taskid(
1302
1229
  self,
1303
1230
  taskId,
1304
1231
  title=None,
@@ -1416,7 +1343,7 @@ class WrikeApp(APIApplication):
1416
1343
  response.raise_for_status()
1417
1344
  return response.json()
1418
1345
 
1419
- def delete_tasks_by_taskid(self, taskId) -> Any:
1346
+ async def delete_tasks_by_taskid(self, taskId) -> Any:
1420
1347
  """
1421
1348
  Deletes a task identified by the given task ID via an HTTP DELETE request and returns the response as a JSON object.
1422
1349
 
@@ -1441,7 +1368,7 @@ class WrikeApp(APIApplication):
1441
1368
  response.raise_for_status()
1442
1369
  return response.json()
1443
1370
 
1444
- def post_folders_by_folderid_tasks(
1371
+ async def post_folders_by_folderid_tasks(
1445
1372
  self,
1446
1373
  folderId,
1447
1374
  title,
@@ -1,5 +1,4 @@
1
1
  from typing import Any
2
-
3
2
  import yfinance as yf
4
3
  from universal_mcp.applications.application import APIApplication
5
4
  from universal_mcp.integrations import Integration
@@ -14,7 +13,7 @@ class YahooFinanceApp(APIApplication):
14
13
  def __init__(self, integration: Integration | None = None, **kwargs) -> None:
15
14
  super().__init__(name="yahoo_finance", integration=integration, **kwargs)
16
15
 
17
- def get_stock_info(self, symbol: str) -> dict[str, Any]:
16
+ async def get_stock_info(self, symbol: str) -> dict[str, Any]:
18
17
  """
19
18
  Gets real-time stock information including current price, market cap, financial ratios, and company details.
20
19
 
@@ -34,23 +33,15 @@ class YahooFinanceApp(APIApplication):
34
33
  """
35
34
  if not symbol:
36
35
  raise ValueError("Stock symbol cannot be empty")
37
-
38
36
  symbol = symbol.upper().strip()
39
37
  ticker = yf.Ticker(symbol)
40
-
41
38
  info = ticker.info
42
39
  if not info or info.get("regularMarketPrice") is None:
43
40
  raise KeyError(f"Stock symbol '{symbol}' not found or invalid")
44
-
45
41
  return info
46
42
 
47
- def get_stock_history(
48
- self,
49
- symbol: str,
50
- period: str = "1mo",
51
- interval: str = "1d",
52
- start_date: str | None = None,
53
- end_date: str | None = None,
43
+ async def get_stock_history(
44
+ self, symbol: str, period: str = "1mo", interval: str = "1d", start_date: str | None = None, end_date: str | None = None
54
45
  ) -> dict:
55
46
  """
56
47
  Gets historical price data for a stock with OHLCV data, dividends, and stock splits.
@@ -70,21 +61,16 @@ class YahooFinanceApp(APIApplication):
70
61
  """
71
62
  if not symbol:
72
63
  raise ValueError("Stock symbol cannot be empty")
73
-
74
64
  symbol = symbol.upper().strip()
75
65
  ticker = yf.Ticker(symbol)
76
-
77
- df = ticker.history(
78
- period=period, interval=interval, start=start_date, end=end_date
79
- )
80
-
66
+ df = ticker.history(period=period, interval=interval, start=start_date, end=end_date)
81
67
  try:
82
- data = df.to_dict("index") # type: ignore
68
+ data = df.to_dict("index")
83
69
  if data:
84
70
  converted_data = {}
85
71
  for key, value in data.items():
86
- if hasattr(key, 'strftime'):
87
- converted_key = key.strftime('%Y-%m-%d')
72
+ if hasattr(key, "strftime"):
73
+ converted_key = key.strftime("%Y-%m-%d")
88
74
  else:
89
75
  converted_key = str(key)
90
76
  converted_data[converted_key] = value
@@ -93,7 +79,7 @@ class YahooFinanceApp(APIApplication):
93
79
  except:
94
80
  return {}
95
81
 
96
- def get_stock_news(self, symbol: str, limit: int = 10) -> list[Any]:
82
+ async def get_stock_news(self, symbol: str, limit: int = 10) -> list[Any]:
97
83
  """
98
84
  Gets latest news articles for a stock from Yahoo Finance.
99
85
 
@@ -109,16 +95,12 @@ class YahooFinanceApp(APIApplication):
109
95
  """
110
96
  if not symbol:
111
97
  raise ValueError("Stock symbol cannot be empty")
112
-
113
98
  symbol = symbol.upper().strip()
114
99
  ticker = yf.Ticker(symbol)
115
-
116
100
  news = ticker.news
117
101
  return news[:limit] if news else []
118
102
 
119
- def get_financial_statements(
120
- self, symbol: str, statement_type: str = "income"
121
- ) -> dict:
103
+ async def get_financial_statements(self, symbol: str, statement_type: str = "income") -> dict:
122
104
  """
123
105
  Gets financial statements for a stock from Yahoo Finance.
124
106
 
@@ -134,10 +116,8 @@ class YahooFinanceApp(APIApplication):
134
116
  """
135
117
  if not symbol:
136
118
  raise ValueError("Stock symbol cannot be empty")
137
-
138
119
  symbol = symbol.upper().strip()
139
120
  ticker = yf.Ticker(symbol)
140
-
141
121
  if statement_type == "income":
142
122
  df = ticker.income_stmt
143
123
  elif statement_type == "balance":
@@ -148,14 +128,13 @@ class YahooFinanceApp(APIApplication):
148
128
  df = ticker.earnings
149
129
  else:
150
130
  df = ticker.income_stmt
151
-
152
131
  try:
153
- data = df.to_dict("dict") # type: ignore
132
+ data = df.to_dict("dict")
154
133
  if data:
155
134
  converted_data = {}
156
135
  for key, value in data.items():
157
- if hasattr(key, 'strftime'):
158
- converted_key = key.strftime('%Y-%m-%d')
136
+ if hasattr(key, "strftime"):
137
+ converted_key = key.strftime("%Y-%m-%d")
159
138
  else:
160
139
  converted_key = str(key)
161
140
  converted_data[converted_key] = value
@@ -164,9 +143,7 @@ class YahooFinanceApp(APIApplication):
164
143
  except:
165
144
  return {}
166
145
 
167
- def get_stock_recommendations(
168
- self, symbol: str, rec_type: str = "recommendations"
169
- ) -> list[dict]:
146
+ async def get_stock_recommendations(self, symbol: str, rec_type: str = "recommendations") -> list[dict]:
170
147
  """
171
148
  Gets analyst recommendations for a stock from Yahoo Finance.
172
149
 
@@ -182,27 +159,18 @@ class YahooFinanceApp(APIApplication):
182
159
  """
183
160
  if not symbol:
184
161
  raise ValueError("Stock symbol cannot be empty")
185
-
186
162
  symbol = symbol.upper().strip()
187
163
  ticker = yf.Ticker(symbol)
188
-
189
164
  if rec_type == "upgrades_downgrades":
190
165
  df = ticker.upgrades_downgrades
191
166
  else:
192
167
  df = ticker.recommendations
193
-
194
168
  try:
195
- return df.to_dict("records") # type: ignore
169
+ return df.to_dict("records")
196
170
  except:
197
171
  return []
198
172
 
199
- def search(
200
- self,
201
- query: str,
202
- max_results: int = 10,
203
- news_count: int = 5,
204
- include_research: bool = False,
205
- ) -> dict[str, Any]:
173
+ async def search(self, query: str, max_results: int = 10, news_count: int = 5, include_research: bool = False) -> dict[str, Any]:
206
174
  """
207
175
  Search Yahoo Finance for quotes, news, and research using yfinance Search.
208
176
 
@@ -220,14 +188,7 @@ class YahooFinanceApp(APIApplication):
220
188
  """
221
189
  if not query:
222
190
  raise ValueError("Search query cannot be empty")
223
-
224
- search = yf.Search(
225
- query,
226
- max_results=max_results,
227
- news_count=news_count,
228
- include_research=include_research,
229
- )
230
-
191
+ search = yf.Search(query, max_results=max_results, news_count=news_count, include_research=include_research)
231
192
  result = {}
232
193
  for attr in dir(search):
233
194
  if not attr.startswith("_"):
@@ -237,12 +198,9 @@ class YahooFinanceApp(APIApplication):
237
198
  result[attr] = value
238
199
  except:
239
200
  continue
240
-
241
201
  return result
242
202
 
243
- def lookup_ticker(
244
- self, query: str, lookup_type: str = "all", count: int = 25
245
- ) -> list[dict]:
203
+ async def lookup_ticker(self, query: str, lookup_type: str = "all", count: int = 25) -> list[dict]:
246
204
  """
247
205
  Look up ticker symbols by type using yfinance Lookup.
248
206
 
@@ -259,10 +217,8 @@ class YahooFinanceApp(APIApplication):
259
217
  """
260
218
  if not query:
261
219
  raise ValueError("Lookup query cannot be empty")
262
-
263
220
  try:
264
221
  lookup = yf.Lookup(query)
265
-
266
222
  if lookup_type == "stock":
267
223
  results = lookup.get_stock(count=count)
268
224
  elif lookup_type == "mutualfund":
@@ -277,14 +233,12 @@ class YahooFinanceApp(APIApplication):
277
233
  results = lookup.get_currency(count=count)
278
234
  elif lookup_type == "cryptocurrency":
279
235
  results = lookup.get_cryptocurrency(count=count)
280
- else: # default to 'all'
236
+ else:
281
237
  results = lookup.get_all(count=count)
282
-
283
238
  try:
284
- return results.to_dict("records") # type: ignore
239
+ return results.to_dict("records")
285
240
  except:
286
241
  return []
287
-
288
242
  except Exception as e:
289
243
  return [{"query": query, "error": f"Lookup failed: {str(e)}"}]
290
244