universal-mcp 0.1.2__py3-none-any.whl → 0.1.3rc1__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.
@@ -1,10 +1,8 @@
1
1
  from typing import Any
2
2
 
3
3
  from firecrawl import FirecrawlApp as FirecrawlApiClient
4
- from loguru import logger
5
4
 
6
5
  from universal_mcp.applications.application import APIApplication
7
- from universal_mcp.exceptions import NotAuthorizedError
8
6
  from universal_mcp.integrations import Integration
9
7
 
10
8
 
@@ -12,57 +10,38 @@ class FirecrawlApp(APIApplication):
12
10
  """
13
11
  Application for interacting with the Firecrawl service (firecrawl.dev)
14
12
  to scrape web pages, perform searches, and manage crawl/batch scrape/extract jobs.
15
- Requires a Firecrawl API key configured via integration
16
- (e.g., FIRECRAWL_API_KEY environment variable).
13
+ Requires a Firecrawl API key configured via integration.
17
14
  """
18
15
 
19
16
  def __init__(self, integration: Integration | None = None) -> None:
20
17
  super().__init__(name="firecrawl", integration=integration)
21
18
  self.api_key: str | None = None
22
- self._attempt_initial_key_load()
23
-
24
- def _attempt_initial_key_load(self):
25
- """Attempts to load the API key during initialization."""
26
- if self.integration:
27
- credentials = self.integration.get_credentials()
28
- if credentials and credentials.get("api_key"):
29
- self.api_key = credentials["api_key"]
30
- logger.info(
31
- "Firecrawl API Key successfully retrieved via integration during init."
32
- )
33
- else:
34
- logger.warning(
35
- "Firecrawl API Key not found in credentials during init. Will try again on first use."
36
- )
37
-
38
- def _get_firecrawl_client(self) -> FirecrawlApiClient:
39
- """Ensures the API key is available and returns an initialized Firecrawl client."""
40
- if not self.api_key:
41
- logger.debug(
42
- "Firecrawl API key not loaded, attempting retrieval via integration."
19
+
20
+ def _set_api_key(self):
21
+ """
22
+ Ensures the API key is loaded from the integration.
23
+ Raises ValueError if the integration or key is missing/misconfigured.
24
+ """
25
+ if self.api_key:
26
+ return
27
+
28
+ if not self.integration:
29
+ raise ValueError(
30
+ "Integration is None. Cannot retrieve Firecrawl API Key."
43
31
  )
44
- if not self.integration:
45
- raise NotAuthorizedError("Firecrawl integration is not configured.")
46
-
47
- credentials = self.integration.get_credentials()
48
- if credentials and credentials.get("api_key"):
49
- self.api_key = credentials["api_key"]
50
- logger.info("Firecrawl API Key successfully retrieved via integration.")
51
- else:
52
- action = (
53
- self.integration.authorize()
54
- if hasattr(self.integration, "authorize")
55
- else "Configure API Key"
56
- )
57
- raise NotAuthorizedError(
58
- f"Firecrawl API Key not found in provided integration credentials. Action required: {action}"
59
- )
60
-
61
- if not self.api_key:
62
- raise NotAuthorizedError(
63
- "Firecrawl API Key is missing or could not be loaded."
32
+
33
+ credentials = self.integration.get_credentials()
34
+ if not credentials:
35
+ raise ValueError(
36
+ f"Failed to retrieve Firecrawl API Key using integration '{self.integration.name}'. "
37
+ f"Check store configuration (e.g., ensure the correct source like environment variable is set)."
64
38
  )
65
39
 
40
+ self.api_key = credentials
41
+
42
+ def _get_client(self) -> FirecrawlApiClient:
43
+ """Initializes and returns the Firecrawl client after ensuring API key is set."""
44
+ self._set_api_key()
66
45
  return FirecrawlApiClient(api_key=self.api_key)
67
46
 
68
47
  def scrape_url(
@@ -74,21 +53,17 @@ class FirecrawlApp(APIApplication):
74
53
  Args:
75
54
  url: The URL of the web page to scrape.
76
55
  params: Optional dictionary of parameters to customize the scrape.
77
- Refer to Firecrawl documentation for 'pageOptions', 'extractorOptions', 'jsonOptions'.
78
- Example: {'pageOptions': {'onlyMainContent': True}}
79
56
 
80
57
  Returns:
81
- A dictionary containing the scraped data (e.g., 'content', 'markdown', 'metadata')
82
- on success, or a string containing an error message on failure.
58
+ A dictionary containing the scraped data on success,
59
+ or a string containing an error message on failure.
83
60
  """
84
- logger.info(f"Attempting to scrape URL: {url} with params: {params}")
85
61
  try:
86
- client = self._get_firecrawl_client()
62
+ client = self._get_client()
87
63
  response_data = client.scrape_url(url=url, params=params)
88
- logger.info(f"Successfully scraped URL: {url}")
89
64
  return response_data
65
+
90
66
  except Exception as e:
91
- logger.error(f"Failed to scrape URL {url}: {type(e).__name__} - {e}")
92
67
  return f"Error scraping URL {url}: {type(e).__name__} - {e}"
93
68
 
94
69
  def search(
@@ -99,29 +74,19 @@ class FirecrawlApp(APIApplication):
99
74
 
100
75
  Args:
101
76
  query: The search query string.
102
- params: Optional dictionary of search parameters (e.g., limit, lang, country, scrapeOptions).
103
- Refer to Firecrawl documentation for details.
104
- Example: {'limit': 3, 'country': 'DE'}
77
+ params: Optional dictionary of search parameters.
105
78
 
106
79
  Returns:
107
- A dictionary containing the search results (typically {'success': bool, 'data': [...]})
108
- on success, or a string containing an error message on failure.
80
+ A dictionary containing the search results on success,
81
+ or a string containing an error message on failure.
109
82
  """
110
- logger.info(f"Attempting search for query: '{query}' with params: {params}")
111
83
  try:
112
- client = self._get_firecrawl_client()
113
- # The library method returns the full response dictionary
84
+ client = self._get_client()
114
85
  response = client.search(query=query, params=params)
115
- logger.info(f"Successfully performed search for query: '{query}'")
116
86
  return response
117
87
  except Exception as e:
118
- logger.error(
119
- f"Failed to perform search for '{query}': {type(e).__name__} - {e}"
120
- )
121
88
  return f"Error performing search for '{query}': {type(e).__name__} - {e}"
122
89
 
123
- # --- Asynchronous Job Pattern Tools ---
124
-
125
90
  def start_crawl(
126
91
  self,
127
92
  url: str,
@@ -130,68 +95,43 @@ class FirecrawlApp(APIApplication):
130
95
  ) -> dict[str, Any] | str:
131
96
  """
132
97
  Starts a crawl job for a given URL using Firecrawl. Returns the job ID immediately.
133
- Use 'check_crawl_status' to monitor progress and retrieve results.
134
98
 
135
99
  Args:
136
100
  url: The starting URL for the crawl.
137
- params: Optional dictionary of parameters to customize the crawl (e.g., crawlerOptions).
138
- Example: {'crawlerOptions': {'excludes': ['blog/'], 'maxDepth': 2}}
139
- idempotency_key: Optional unique key to prevent duplicate jobs if the request is retried.
101
+ params: Optional dictionary of parameters to customize the crawl.
102
+ idempotency_key: Optional unique key to prevent duplicate jobs.
140
103
 
141
104
  Returns:
142
- A dictionary containing the job initiation response (e.g., {'success': bool, 'id': str})
143
- on success, or a string containing an error message on failure.
105
+ A dictionary containing the job initiation response on success,
106
+ or a string containing an error message on failure.
144
107
  """
145
- logger.info(
146
- f"Attempting to start crawl job for URL: {url} with params: {params}"
147
- )
148
108
  try:
149
- client = self._get_firecrawl_client()
150
- # Use the library's async method which returns the job ID response
109
+ client = self._get_client()
151
110
  response = client.async_crawl_url(
152
111
  url=url, params=params, idempotency_key=idempotency_key
153
112
  )
154
- if response.get("success"):
155
- logger.info(
156
- f"Successfully started crawl job for URL: {url}. Job ID: {response.get('id')}"
157
- )
158
- else:
159
- logger.error(
160
- f"Failed to start crawl job for URL {url}. Response: {response}"
161
- )
162
113
  return response
114
+
163
115
  except Exception as e:
164
- logger.error(
165
- f"Failed to start crawl for URL {url}: {type(e).__name__} - {e}"
166
- )
167
116
  return f"Error starting crawl for URL {url}: {type(e).__name__} - {e}"
168
117
 
169
118
  def check_crawl_status(self, job_id: str) -> dict[str, Any] | str:
170
119
  """
171
120
  Checks the status of a previously initiated Firecrawl crawl job.
172
- If the job is completed, this retrieves the results (potentially paginated).
173
121
 
174
122
  Args:
175
123
  job_id: The ID of the crawl job to check.
176
124
 
177
125
  Returns:
178
- A dictionary containing the job status details (e.g., 'status', 'progress', 'data' if completed)
179
- on success, or a string containing an error message on failure.
180
- Common status values: 'pending', 'queued', 'scraping', 'completed', 'failed'.
126
+ A dictionary containing the job status details on success,
127
+ or a string containing an error message on failure.
181
128
  """
182
- logger.info(f"Attempting to check status for crawl job ID: {job_id}")
183
129
  try:
184
- client = self._get_firecrawl_client()
185
- # Library method handles pagination for completed jobs
186
- status = client.check_crawl_status(id=job_id)
187
- logger.info(
188
- f"Successfully checked status for job ID: {job_id}. Status: {status.get('status', 'unknown')}"
189
- )
130
+ client = self._get_client()
131
+ status = client.check_crawl_status(id=job_id)
190
132
  return status
133
+
191
134
  except Exception as e:
192
- logger.error(
193
- f"Failed to check crawl status for job ID {job_id}: {type(e).__name__} - {e}"
194
- )
195
135
  return f"Error checking crawl status for job ID {job_id}: {type(e).__name__} - {e}"
196
136
 
197
137
  def cancel_crawl(self, job_id: str) -> dict[str, Any] | str:
@@ -202,23 +142,19 @@ class FirecrawlApp(APIApplication):
202
142
  job_id: The ID of the crawl job to cancel.
203
143
 
204
144
  Returns:
205
- A dictionary confirming the cancellation status (e.g., {'success': bool, 'status': 'cancelled'})
206
- on success, or a string containing an error message on failure.
145
+ A dictionary confirming the cancellation status on success,
146
+ or a string containing an error message on failure.
207
147
  """
208
- logger.info(f"Attempting to cancel crawl job ID: {job_id}")
209
148
  try:
210
- client = self._get_firecrawl_client()
149
+ client = self._get_client()
211
150
  response = client.cancel_crawl(id=job_id)
212
- logger.info(
213
- f"Successfully requested cancellation for job ID: {job_id}. Response: {response}"
214
- )
151
+
215
152
  return response
216
- except Exception as e:
217
- logger.error(
218
- f"Failed to cancel crawl job ID {job_id}: {type(e).__name__} - {e}"
219
- )
153
+
154
+ except Exception as e:
220
155
  return f"Error cancelling crawl job ID {job_id}: {type(e).__name__} - {e}"
221
156
 
157
+
222
158
  def start_batch_scrape(
223
159
  self,
224
160
  urls: list[str],
@@ -226,67 +162,44 @@ class FirecrawlApp(APIApplication):
226
162
  idempotency_key: str | None = None,
227
163
  ) -> dict[str, Any] | str:
228
164
  """
229
- Starts a batch scrape job for multiple URLs using Firecrawl. Returns the job ID immediately.
230
- Use 'check_batch_scrape_status' to monitor progress and retrieve results.
165
+ Starts a batch scrape job for multiple URLs using Firecrawl.
231
166
 
232
167
  Args:
233
168
  urls: A list of URLs to scrape.
234
- params: Optional dictionary of parameters applied to all scrapes in the batch.
235
- Refer to Firecrawl documentation for scrape parameters.
169
+ params: Optional dictionary of parameters applied to all scrapes.
236
170
  idempotency_key: Optional unique key to prevent duplicate jobs.
237
171
 
238
172
  Returns:
239
- A dictionary containing the job initiation response (e.g., {'success': bool, 'id': str})
240
- on success, or a string containing an error message on failure.
173
+ A dictionary containing the job initiation response on success,
174
+ or a string containing an error message on failure.
241
175
  """
242
- url_count = len(urls)
243
- logger.info(
244
- f"Attempting to start batch scrape job for {url_count} URLs with params: {params}"
245
- )
246
- if not urls:
247
- return "Error: No URLs provided for batch scrape."
248
176
  try:
249
- client = self._get_firecrawl_client()
177
+ client = self._get_client()
250
178
  response = client.async_batch_scrape_urls(
251
179
  urls=urls, params=params, idempotency_key=idempotency_key
252
180
  )
253
- if response.get("success"):
254
- logger.info(
255
- f"Successfully started batch scrape job for {url_count} URLs. Job ID: {response.get('id')}"
256
- )
257
- else:
258
- logger.error(
259
- f"Failed to start batch scrape job for {url_count} URLs. Response: {response}"
260
- )
261
181
  return response
182
+
262
183
  except Exception as e:
263
- logger.error(f"Failed to start batch scrape: {type(e).__name__} - {e}")
264
184
  return f"Error starting batch scrape: {type(e).__name__} - {e}"
265
185
 
266
186
  def check_batch_scrape_status(self, job_id: str) -> dict[str, Any] | str:
267
187
  """
268
188
  Checks the status of a previously initiated Firecrawl batch scrape job.
269
- If the job is completed, this retrieves the results for all URLs.
270
189
 
271
190
  Args:
272
191
  job_id: The ID of the batch scrape job to check.
273
192
 
274
193
  Returns:
275
- A dictionary containing the job status details (e.g., 'status', 'progress', 'data' list if completed)
276
- on success, or a string containing an error message on failure.
194
+ A dictionary containing the job status details on success,
195
+ or a string containing an error message on failure.
277
196
  """
278
- logger.info(f"Attempting to check status for batch scrape job ID: {job_id}")
279
197
  try:
280
- client = self._get_firecrawl_client()
281
- status = client.check_batch_scrape_status(id=job_id)
282
- logger.info(
283
- f"Successfully checked status for batch scrape job ID: {job_id}. Status: {status.get('status', 'unknown')}"
284
- )
198
+ client = self._get_client()
199
+ status = client.check_batch_scrape_status(id=job_id)
285
200
  return status
201
+
286
202
  except Exception as e:
287
- logger.error(
288
- f"Failed to check batch scrape status for job ID {job_id}: {type(e).__name__} - {e}"
289
- )
290
203
  return f"Error checking batch scrape status for job ID {job_id}: {type(e).__name__} - {e}"
291
204
 
292
205
  def start_extract(
@@ -296,74 +209,45 @@ class FirecrawlApp(APIApplication):
296
209
  idempotency_key: str | None = None,
297
210
  ) -> dict[str, Any] | str:
298
211
  """
299
- Starts an extraction job for one or more URLs using Firecrawl. Returns the job ID immediately.
300
- Use 'check_extract_status' to monitor progress and retrieve results. Requires 'prompt' or 'schema' in params.
212
+ Starts an extraction job for one or more URLs using Firecrawl.
301
213
 
302
214
  Args:
303
215
  urls: A list of URLs to extract data from.
304
- params: Dictionary of parameters. MUST include 'prompt' (string) or 'schema' (JSON schema dict or Pydantic model).
305
- Optional: 'enableWebSearch', 'systemPrompt', etc. See Firecrawl docs.
306
- Example: {'prompt': 'Extract the main headlines'}
307
- Example: {'schema': {'type': 'object', 'properties': {'title': {'type': 'string'}}}}
216
+ params: Dictionary of parameters. MUST include 'prompt' or 'schema'.
308
217
  idempotency_key: Optional unique key to prevent duplicate jobs.
309
218
 
310
219
  Returns:
311
- A dictionary containing the job initiation response (e.g., {'success': bool, 'id': str})
312
- on success, or a string containing an error message on failure.
220
+ A dictionary containing the job initiation response on success,
221
+ or a string containing an error message on failure.
313
222
  """
314
- logger.info(
315
- f"Attempting to start extraction job for URLs: {urls} with params: {params}"
316
- )
317
- if not urls:
318
- return "Error: No URLs provided for extraction."
319
- if not params or (not params.get("prompt") and not params.get("schema")):
320
- return "Error: 'params' dictionary must include either a 'prompt' string or a 'schema' definition."
223
+
321
224
  try:
322
- client = self._get_firecrawl_client()
323
- # Pass params directly; the library handles schema conversion if needed
225
+ client = self._get_client()
324
226
  response = client.async_extract(
325
227
  urls=urls, params=params, idempotency_key=idempotency_key
326
228
  )
327
- if response.get("success"):
328
- logger.info(
329
- f"Successfully started extraction job for URLs. Job ID: {response.get('id')}"
330
- )
331
- else:
332
- logger.error(
333
- f"Failed to start extraction job for URLs. Response: {response}"
334
- )
335
229
  return response
230
+
336
231
  except Exception as e:
337
- logger.error(f"Failed to start extraction: {type(e).__name__} - {e}")
338
232
  return f"Error starting extraction: {type(e).__name__} - {e}"
339
233
 
340
234
  def check_extract_status(self, job_id: str) -> dict[str, Any] | str:
341
235
  """
342
236
  Checks the status of a previously initiated Firecrawl extraction job.
343
- If the job is completed, this retrieves the extracted data.
344
237
 
345
238
  Args:
346
239
  job_id: The ID of the extraction job to check.
347
240
 
348
241
  Returns:
349
- A dictionary containing the job status details (e.g., 'status', 'data' if completed)
350
- on success, or a string containing an error message on failure.
351
- Common status values: 'pending', 'processing', 'completed', 'failed'.
242
+ A dictionary containing the job status details on success,
243
+ or a string containing an error message on failure.
352
244
  """
353
- logger.info(f"Attempting to check status for extraction job ID: {job_id}")
354
245
  try:
355
- client = self._get_firecrawl_client()
356
- status = client.get_extract_status(
357
- job_id=job_id
358
- ) # Correct library method name
359
- logger.info(
360
- f"Successfully checked status for extraction job ID: {job_id}. Status: {status.get('status', 'unknown')}"
361
- )
246
+ client = self._get_client()
247
+ status = client.get_extract_status(job_id=job_id)
362
248
  return status
249
+
363
250
  except Exception as e:
364
- logger.error(
365
- f"Failed to check extraction status for job ID {job_id}: {type(e).__name__} - {e}"
366
- )
367
251
  return f"Error checking extraction status for job ID {job_id}: {type(e).__name__} - {e}"
368
252
 
369
253
  def list_tools(self):
@@ -378,4 +262,4 @@ class FirecrawlApp(APIApplication):
378
262
  self.check_batch_scrape_status,
379
263
  self.start_extract,
380
264
  self.check_extract_status,
381
- ]
265
+ ]
@@ -9,16 +9,27 @@ class MarkitdownApp(Application):
9
9
  self.markitdown = MarkItDown()
10
10
 
11
11
  async def convert_to_markdown(self, uri: str) -> str:
12
- """Convert a web page, file, or data URI to markdown format.
12
+ """Fetches content from a URI and converts its primary textual representation into Markdown.
13
+
14
+ This tool aims to extract the main text content from various sources. It supports:
15
+ - Web Pages: General HTML, specific handlers for RSS/Atom feeds, Wikipedia articles (main content), YouTube (transcripts if available), Bing SERPs.
16
+ - Documents: PDF (attempts OCR), DOCX, XLSX, PPTX, XLS, EPUB, Outlook MSG, IPYNB notebooks.
17
+ - Plain Text files.
18
+ - Images: Extracts metadata and attempts OCR to get text.
19
+ - Audio: Extracts metadata and attempts transcription to get text.
20
+ - Archives: ZIP (extracts and attempts to convert supported files within, concatenating results).
21
+
22
+ Note: Conversion quality depends on the source format. Complex layouts, encrypted files, or missing transcripts/OCR data may limit output.
23
+ Enhanced PDF/Image processing via Azure Document Intelligence may be active if configured server-side.
13
24
 
14
25
  Args:
15
- uri (str): The URI to convert. Supported URI schemes:
16
- - http:// or https:// for web pages
17
- - file:// for local files
18
- - data: for data URIs
26
+ uri (str): The URI pointing to the resource. Supported schemes:
27
+ - http:// or https:// (Web pages, feeds, APIs)
28
+ - file:// (Local or accessible network files)
29
+ - data: (Embedded data)
19
30
 
20
31
  Returns:
21
- str: The markdown representation of the resource content.
32
+ str: The extracted content converted to Markdown format.
22
33
 
23
34
  Example:
24
35
  >>> await convert_to_markdown("https://example.com")
@@ -1,4 +1,15 @@
1
- # Notion Tool
1
+
2
+ # Notion MCP Server
3
+
4
+ An MCP Server for the Notion API.
5
+
6
+ ## Supported Integrations
7
+
8
+ - AgentR
9
+ - API Key (Coming Soon)
10
+ - OAuth (Coming Soon)
11
+
12
+ ## Tools
2
13
 
3
14
  This is automatically generated from OpenAPI schema for the Notion API.
4
15
 
@@ -10,23 +21,35 @@ This tool can be integrated with any service that supports HTTP requests.
10
21
 
11
22
  | Tool | Description |
12
23
  |------|-------------|
13
- | notion_retrieve_auser | Retrieves user information from the Notion API by user ID. |
14
- | notion_list_all_users | Fetches and returns a list of all users from the Notion API. |
15
- | notion_retrieve_your_token_sbot_user | Retrieves the current user's token data from the Notion API. |
16
- | notion_retrieve_adatabase | Retrieves a Notion database by its unique identifier. |
17
- | notion_update_adatabase | Updates a Notion database with the given ID and request body data. |
18
- | notion_query_adatabase | Executes a query on a Notion database using the Notion API. |
19
- | notion_create_adatabase | Creates a new database in Notion using the provided request body. |
20
- | notion_create_apage | Creates a new page in Notion by sending a POST request with the specified request body. |
21
- | notion_retrieve_apage | Retrieves a page from the Notion API using a given page ID. |
22
- | notion_update_page_properties | Updates the properties of a Notion page identified by a given ID. |
23
- | notion_retrieve_apage_property_item | Retrieves a specific property item from a page in Notion using the page and property IDs. |
24
- | notion_retrieve_block_children | Retrieves the child blocks of a specified Notion block. |
25
- | notion_append_block_children | Appends child blocks to a block in Notion using its API. |
26
- | notion_retrieve_ablock | Retrieves a block from the Notion API using the specified block ID. |
27
- | notion_delete_ablock | Deletes a block from the Notion database using the specified block ID. |
28
- | notion_update_ablock | Updates a block in Notion with the given ID and request body. |
29
- | notion_search | Executes a search request to the Notion API and returns the response in JSON format. |
30
- | notion_retrieve_comments | Retrieves comments from a Notion block using the Notion API. |
31
- | notion_add_comment_to_page | Adds a comment to a specified Notion page using the provided request body. |
24
+ | retrieve_a_user | Retrieves user details from the server using the specified user ID. |
25
+ | list_a_ll_users | Fetches a list of all users from the API endpoint and returns the data as a dictionary. |
26
+ | retrieve_your_token_sbot_user | Retrieves the authentication token for the current user from the SBOT service. |
27
+ | retrieve_a_database | Retrieves database details from a specified endpoint using the provided database ID. |
28
+ | update_a_database | Updates a database entry with the given ID using a PATCH request. |
29
+ | query_a_database | Executes a query on a specified database using an identifier and an optional request body. |
30
+ | create_a_database | Creates a new database on the server using the specified request body. |
31
+ | create_a_page | Creates a new page by sending a POST request to the specified endpoint. |
32
+ | retrieve_a_page | Retrieves a page by its unique identifier from a remote server. |
33
+ | update_page_properties | Updates the properties of a page identified by its ID using the provided request body. |
34
+ | retrieve_a_page_property_item | Retrieves the property item of a page using specified page and property identifiers. |
35
+ | retrieve_block_children | Retrieves the children of a specified block using its unique identifier. |
36
+ | append_block_children | Appends child elements to a block identified by its ID and returns the updated block data. |
37
+ | retrieve_a_block | Retrieves a block of data from a given API endpoint using the specified block ID. |
38
+ | delete_a_block | Deletes a block by its unique identifier and returns the server's response. |
39
+ | update_a_block | Updates a block by sending a PATCH request to the specified endpoint. |
40
+ | search | Executes a search query using the specified request body and returns the results. |
41
+ | retrieve_comments | Fetches comments from a remote server for a specified block, with optional pagination. |
42
+ | add_comment_to_page | Adds a comment to a page by sending a POST request with the provided request body. |
43
+
44
+
45
+
46
+ ## Usage
47
+
48
+ - Login to AgentR
49
+ - Follow the quickstart guide to setup MCP Server for your client
50
+ - Visit Apps Store and enable the Notion app
51
+ - Restart the MCP Server
52
+
53
+ ### Local Development
32
54
 
55
+ - Follow the README to test with the local MCP Server