1414from typing import Any , Dict , Optional , List , Union
1515
1616import httpx
17- import uvicorn
18- from fastmcp import FastMCP
17+ from fastmcp import FastMCP , Context
1918from smithery .decorators import smithery
2019from pydantic import BaseModel , Field
21- from starlette .middleware .cors import CORSMiddleware
2220
2321
2422class ScapeGraphClient :
@@ -307,48 +305,16 @@ def close(self) -> None:
307305
308306# Pydantic configuration schema for Smithery
309307class ConfigSchema (BaseModel ):
310- api_key : str = Field (description = "Your Scrapegraph API key" )
308+ api_key : str = Field (..., description = "Your Scrapegraph API key" )
311309
312310
313311# Create MCP server
314312mcp = FastMCP ("ScapeGraph API MCP Server" )
315313
316- # Default API key (will be overridden in main or by direct assignment)
317- default_api_key = os .environ .get ("SGAI_API_KEY" )
318- scrapegraph_client = ScapeGraphClient (default_api_key ) if default_api_key else None
319-
320-
321- # Smithery server function with config schema
322- @smithery .server (config_schema = ConfigSchema )
323- def create_server (config : Optional [ConfigSchema ] = None ) -> FastMCP :
324- """
325- Create and return the FastMCP server instance for Smithery deployment.
326-
327- Args:
328- config: Configuration object with api_key
329-
330- Returns:
331- Configured FastMCP server instance
332- """
333- global scrapegraph_client
334-
335- # Get API key from config or environment
336- api_key = None
337- if config and hasattr (config , 'api_key' ):
338- api_key = config .api_key
339- else :
340- api_key = os .environ .get ("SGAI_API_KEY" )
341-
342- # Initialize client if API key is available
343- if api_key :
344- scrapegraph_client = ScapeGraphClient (api_key )
345-
346- return mcp
347-
348314
349315# Add tool for markdownify
350316@mcp .tool ()
351- def markdownify (website_url : str ) -> Dict [str , Any ]:
317+ def markdownify (website_url : str , ctx : Context ) -> Dict [str , Any ]:
352318 """
353319 Convert a webpage into clean, formatted markdown.
354320
@@ -358,11 +324,9 @@ def markdownify(website_url: str) -> Dict[str, Any]:
358324 Returns:
359325 Dictionary containing the markdown result
360326 """
361- if scrapegraph_client is None :
362- return {"error" : "ScapeGraph client not initialized. Please provide an API key." }
363-
364327 try :
365- return scrapegraph_client .markdownify (website_url )
328+ client = ScapeGraphClient (ctx .session_config .api_key )
329+ return client .markdownify (website_url )
366330 except Exception as e :
367331 return {"error" : str (e )}
368332
@@ -372,6 +336,7 @@ def markdownify(website_url: str) -> Dict[str, Any]:
372336def smartscraper (
373337 user_prompt : str ,
374338 website_url : str ,
339+ ctx : Context ,
375340 number_of_scrolls : int = None ,
376341 markdown_only : bool = None
377342) -> Dict [str , Any ]:
@@ -387,11 +352,9 @@ def smartscraper(
387352 Returns:
388353 Dictionary containing the extracted data or markdown content
389354 """
390- if scrapegraph_client is None :
391- return {"error" : "ScapeGraph client not initialized. Please provide an API key." }
392-
393355 try :
394- return scrapegraph_client .smartscraper (user_prompt , website_url , number_of_scrolls , markdown_only )
356+ client = ScapeGraphClient (ctx .session_config .api_key )
357+ return client .smartscraper (user_prompt , website_url , number_of_scrolls , markdown_only )
395358 except Exception as e :
396359 return {"error" : str (e )}
397360
@@ -400,6 +363,7 @@ def smartscraper(
400363@mcp .tool ()
401364def searchscraper (
402365 user_prompt : str ,
366+ ctx : Context ,
403367 num_results : int = None ,
404368 number_of_scrolls : int = None
405369) -> Dict [str , Any ]:
@@ -414,11 +378,9 @@ def searchscraper(
414378 Returns:
415379 Dictionary containing search results and reference URLs
416380 """
417- if scrapegraph_client is None :
418- return {"error" : "ScapeGraph client not initialized. Please provide an API key." }
419-
420381 try :
421- return scrapegraph_client .searchscraper (user_prompt , num_results , number_of_scrolls )
382+ client = ScapeGraphClient (ctx .session_config .api_key )
383+ return client .searchscraper (user_prompt , num_results , number_of_scrolls )
422384 except Exception as e :
423385 return {"error" : str (e )}
424386
@@ -427,6 +389,7 @@ def searchscraper(
427389@mcp .tool ()
428390def smartcrawler_initiate (
429391 url : str ,
392+ ctx : Context ,
430393 prompt : str = None ,
431394 extraction_mode : str = "ai" ,
432395 depth : int = None ,
@@ -451,11 +414,9 @@ def smartcrawler_initiate(
451414 Returns:
452415 Dictionary containing the request ID for async processing
453416 """
454- if scrapegraph_client is None :
455- return {"error" : "ScapeGraph client not initialized. Please provide an API key." }
456-
457417 try :
458- return scrapegraph_client .smartcrawler_initiate (
418+ client = ScapeGraphClient (ctx .session_config .api_key )
419+ return client .smartcrawler_initiate (
459420 url = url ,
460421 prompt = prompt ,
461422 extraction_mode = extraction_mode ,
@@ -469,7 +430,7 @@ def smartcrawler_initiate(
469430
470431# Add tool for fetching SmartCrawler results
471432@mcp .tool ()
472- def smartcrawler_fetch_results (request_id : str ) -> Dict [str , Any ]:
433+ def smartcrawler_fetch_results (request_id : str , ctx : Context ) -> Dict [str , Any ]:
473434 """
474435 Fetch the results of a SmartCrawler operation.
475436
@@ -480,30 +441,26 @@ def smartcrawler_fetch_results(request_id: str) -> Dict[str, Any]:
480441 Dictionary containing the crawled data (structured extraction or markdown)
481442 and metadata about processed pages
482443 """
483- if scrapegraph_client is None :
484- return {"error" : "ScapeGraph client not initialized. Please provide an API key." }
485-
486444 try :
487- return scrapegraph_client .smartcrawler_fetch_results (request_id )
445+ client = ScapeGraphClient (ctx .session_config .api_key )
446+ return client .smartcrawler_fetch_results (request_id )
488447 except Exception as e :
489448 return {"error" : str (e )}
490449
491450
492451# Add tool for basic scrape
493452@mcp .tool ()
494- def scrape (website_url : str , render_heavy_js : Optional [bool ] = None ) -> Dict [str , Any ]:
453+ def scrape (website_url : str , ctx : Context , render_heavy_js : Optional [bool ] = None ) -> Dict [str , Any ]:
495454 """
496455 Fetch page content for a URL.
497456
498457 Args:
499458 website_url: URL to scrape
500459 render_heavy_js: Whether to render heavy JS (optional)
501460 """
502- if scrapegraph_client is None :
503- return {"error" : "ScapeGraph client not initialized. Please provide an API key." }
504-
505461 try :
506- return scrapegraph_client .scrape (website_url = website_url , render_heavy_js = render_heavy_js )
462+ client = ScapeGraphClient (ctx .session_config .api_key )
463+ return client .scrape (website_url = website_url , render_heavy_js = render_heavy_js )
507464 except httpx .HTTPError as http_err :
508465 return {"error" : str (http_err )}
509466 except ValueError as val_err :
@@ -512,18 +469,16 @@ def scrape(website_url: str, render_heavy_js: Optional[bool] = None) -> Dict[str
512469
513470# Add tool for sitemap extraction
514471@mcp .tool ()
515- def sitemap (website_url : str ) -> Dict [str , Any ]:
472+ def sitemap (website_url : str , ctx : Context ) -> Dict [str , Any ]:
516473 """
517474 Extract sitemap for a website.
518475
519476 Args:
520477 website_url: Base website URL
521478 """
522- if scrapegraph_client is None :
523- return {"error" : "ScapeGraph client not initialized. Please provide an API key." }
524-
525479 try :
526- return scrapegraph_client .sitemap (website_url = website_url )
480+ client = ScapeGraphClient (ctx .session_config .api_key )
481+ return client .sitemap (website_url = website_url )
527482 except httpx .HTTPError as http_err :
528483 return {"error" : str (http_err )}
529484 except ValueError as val_err :
@@ -534,6 +489,7 @@ def sitemap(website_url: str) -> Dict[str, Any]:
534489@mcp .tool ()
535490def agentic_scrapper (
536491 url : str ,
492+ ctx : Context ,
537493 user_prompt : Optional [str ] = None ,
538494 output_schema : Optional [Union [str , Dict [str , Any ]]] = None ,
539495 steps : Optional [Union [str , List [str ]]] = None ,
@@ -544,9 +500,6 @@ def agentic_scrapper(
544500 """
545501 Run the Agentic Scraper workflow. Accepts flexible input forms for steps and schema.
546502 """
547- if scrapegraph_client is None :
548- return {"error" : "ScapeGraph client not initialized. Please provide an API key." }
549-
550503 # Normalize inputs
551504 normalized_steps : Optional [List [str ]] = None
552505 if isinstance (steps , list ):
@@ -576,7 +529,8 @@ def agentic_scrapper(
576529 return {"error" : f"Invalid JSON for output_schema: { str (e )} " }
577530
578531 try :
579- return scrapegraph_client .agentic_scrapper (
532+ client = ScapeGraphClient (ctx .session_config .api_key )
533+ return client .agentic_scrapper (
580534 url = url ,
581535 user_prompt = user_prompt ,
582536 output_schema = normalized_schema ,
@@ -593,44 +547,21 @@ def agentic_scrapper(
593547 return {"error" : str (val_err )}
594548
595549
596- # Config schema for Smithery
597- CONFIG_SCHEMA = {
598- "type" : "object" ,
599- "required" : ["scrapegraphApiKey" ],
600- "properties" : {
601- "scrapegraphApiKey" : {
602- "type" : "string" ,
603- "description" : "Your Scrapegraph API key"
604- }
605- }
606- }
607-
608-
609- @smithery .server (config_schema = CONFIG_SCHEMA )
610- def create_server (config : Optional [Dict [str , Any ]] = None ) -> FastMCP :
550+ # Smithery server function with Pydantic config schema
551+ @smithery .server (config_schema = ConfigSchema )
552+ def create_server (config : Optional [ConfigSchema ] = None ) -> FastMCP :
611553 """
612554 Create and return the FastMCP server instance for Smithery deployment.
555+
556+ The API key is provided via session config in the Context object,
557+ which is passed to each tool at runtime.
613558
614559 Args:
615- config: Configuration dictionary with optional keys:
616- - scrapegraphApiKey: API key for ScapeGraph API
560+ config: Configuration object with api_key (passed via Context to tools)
617561
618562 Returns:
619563 Configured FastMCP server instance
620564 """
621- global scrapegraph_client
622-
623- # Get API key from config or environment
624- api_key = None
625- if config and "scrapegraphApiKey" in config :
626- api_key = config ["scrapegraphApiKey" ]
627- else :
628- api_key = os .environ .get ("SGAI_API_KEY" )
629-
630- # Initialize client if API key is available
631- if api_key :
632- scrapegraph_client = ScapeGraphClient (api_key )
633-
634565 return mcp
635566
636567
0 commit comments