Skip to content

Commit 4dce699

Browse files
committed
2 parents d9f8252 + c77cefc commit 4dce699

File tree

4 files changed

+946
-944
lines changed

4 files changed

+946
-944
lines changed

.python-version

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/scrapegraph_mcp/py.typed

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/scrapegraph_mcp/server.py

Lines changed: 32 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,13 @@
99
- smartcrawler_fetch_results: Retrieve results from asynchronous crawling operations
1010
"""
1111

12-
import os
1312
import json
1413
from typing import Any, Dict, Optional, List, Union
1514

1615
import httpx
17-
import uvicorn
18-
from fastmcp import FastMCP
16+
from fastmcp import Context, FastMCP
1917
from smithery.decorators import smithery
2018
from pydantic import BaseModel, Field
21-
from starlette.middleware.cors import CORSMiddleware
2219

2320

2421
class ScapeGraphClient:
@@ -35,7 +32,7 @@ def __init__(self, api_key: str):
3532
"""
3633
self.api_key = api_key
3734
self.headers = {
38-
"SGAI_API_KEY": api_key,
35+
"SGAI-APIKEY": api_key,
3936
"Content-Type": "application/json"
4037
}
4138
self.client = httpx.Client(timeout=httpx.Timeout(120.0))
@@ -307,48 +304,16 @@ def close(self) -> None:
307304

308305
# Pydantic configuration schema for Smithery
309306
class ConfigSchema(BaseModel):
310-
api_key: str = Field(description="Your Scrapegraph API key")
307+
scrapegraph_api_key: str = Field(..., description="Your Scrapegraph API key")
311308

312309

313-
# Create MCP server
310+
# Create MCP server instance
314311
mcp = FastMCP("ScapeGraph API MCP Server")
315312

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-
348313

349314
# Add tool for markdownify
350315
@mcp.tool()
351-
def markdownify(website_url: str) -> Dict[str, Any]:
316+
def markdownify(website_url: str, ctx: Context) -> Dict[str, Any]:
352317
"""
353318
Convert a webpage into clean, formatted markdown.
354319
@@ -358,11 +323,9 @@ def markdownify(website_url: str) -> Dict[str, Any]:
358323
Returns:
359324
Dictionary containing the markdown result
360325
"""
361-
if scrapegraph_client is None:
362-
return {"error": "ScapeGraph client not initialized. Please provide an API key."}
363-
364326
try:
365-
return scrapegraph_client.markdownify(website_url)
327+
client = ScapeGraphClient(ctx.session_config.scrapegraph_api_key)
328+
return client.markdownify(website_url)
366329
except Exception as e:
367330
return {"error": str(e)}
368331

@@ -372,6 +335,7 @@ def markdownify(website_url: str) -> Dict[str, Any]:
372335
def smartscraper(
373336
user_prompt: str,
374337
website_url: str,
338+
ctx: Context,
375339
number_of_scrolls: int = None,
376340
markdown_only: bool = None
377341
) -> Dict[str, Any]:
@@ -387,11 +351,9 @@ def smartscraper(
387351
Returns:
388352
Dictionary containing the extracted data or markdown content
389353
"""
390-
if scrapegraph_client is None:
391-
return {"error": "ScapeGraph client not initialized. Please provide an API key."}
392-
393354
try:
394-
return scrapegraph_client.smartscraper(user_prompt, website_url, number_of_scrolls, markdown_only)
355+
client = ScapeGraphClient(ctx.session_config.scrapegraph_api_key)
356+
return client.smartscraper(user_prompt, website_url, number_of_scrolls, markdown_only)
395357
except Exception as e:
396358
return {"error": str(e)}
397359

@@ -400,6 +362,7 @@ def smartscraper(
400362
@mcp.tool()
401363
def searchscraper(
402364
user_prompt: str,
365+
ctx: Context,
403366
num_results: int = None,
404367
number_of_scrolls: int = None
405368
) -> Dict[str, Any]:
@@ -414,11 +377,9 @@ def searchscraper(
414377
Returns:
415378
Dictionary containing search results and reference URLs
416379
"""
417-
if scrapegraph_client is None:
418-
return {"error": "ScapeGraph client not initialized. Please provide an API key."}
419-
420380
try:
421-
return scrapegraph_client.searchscraper(user_prompt, num_results, number_of_scrolls)
381+
client = ScapeGraphClient(ctx.session_config.scrapegraph_api_key)
382+
return client.searchscraper(user_prompt, num_results, number_of_scrolls)
422383
except Exception as e:
423384
return {"error": str(e)}
424385

@@ -427,6 +388,7 @@ def searchscraper(
427388
@mcp.tool()
428389
def smartcrawler_initiate(
429390
url: str,
391+
ctx: Context,
430392
prompt: str = None,
431393
extraction_mode: str = "ai",
432394
depth: int = None,
@@ -451,11 +413,9 @@ def smartcrawler_initiate(
451413
Returns:
452414
Dictionary containing the request ID for async processing
453415
"""
454-
if scrapegraph_client is None:
455-
return {"error": "ScapeGraph client not initialized. Please provide an API key."}
456-
457416
try:
458-
return scrapegraph_client.smartcrawler_initiate(
417+
client = ScapeGraphClient(ctx.session_config.scrapegraph_api_key)
418+
return client.smartcrawler_initiate(
459419
url=url,
460420
prompt=prompt,
461421
extraction_mode=extraction_mode,
@@ -469,7 +429,7 @@ def smartcrawler_initiate(
469429

470430
# Add tool for fetching SmartCrawler results
471431
@mcp.tool()
472-
def smartcrawler_fetch_results(request_id: str) -> Dict[str, Any]:
432+
def smartcrawler_fetch_results(request_id: str, ctx: Context) -> Dict[str, Any]:
473433
"""
474434
Fetch the results of a SmartCrawler operation.
475435
@@ -480,30 +440,26 @@ def smartcrawler_fetch_results(request_id: str) -> Dict[str, Any]:
480440
Dictionary containing the crawled data (structured extraction or markdown)
481441
and metadata about processed pages
482442
"""
483-
if scrapegraph_client is None:
484-
return {"error": "ScapeGraph client not initialized. Please provide an API key."}
485-
486443
try:
487-
return scrapegraph_client.smartcrawler_fetch_results(request_id)
444+
client = ScapeGraphClient(ctx.session_config.scrapegraph_api_key)
445+
return client.smartcrawler_fetch_results(request_id)
488446
except Exception as e:
489447
return {"error": str(e)}
490448

491449

492450
# Add tool for basic scrape
493451
@mcp.tool()
494-
def scrape(website_url: str, render_heavy_js: Optional[bool] = None) -> Dict[str, Any]:
452+
def scrape(website_url: str, ctx: Context, render_heavy_js: Optional[bool] = None) -> Dict[str, Any]:
495453
"""
496454
Fetch page content for a URL.
497455
498456
Args:
499457
website_url: URL to scrape
500458
render_heavy_js: Whether to render heavy JS (optional)
501459
"""
502-
if scrapegraph_client is None:
503-
return {"error": "ScapeGraph client not initialized. Please provide an API key."}
504-
505460
try:
506-
return scrapegraph_client.scrape(website_url=website_url, render_heavy_js=render_heavy_js)
461+
client = ScapeGraphClient(ctx.session_config.scrapegraph_api_key)
462+
return client.scrape(website_url=website_url, render_heavy_js=render_heavy_js)
507463
except httpx.HTTPError as http_err:
508464
return {"error": str(http_err)}
509465
except ValueError as val_err:
@@ -512,18 +468,16 @@ def scrape(website_url: str, render_heavy_js: Optional[bool] = None) -> Dict[str
512468

513469
# Add tool for sitemap extraction
514470
@mcp.tool()
515-
def sitemap(website_url: str) -> Dict[str, Any]:
471+
def sitemap(website_url: str, ctx: Context) -> Dict[str, Any]:
516472
"""
517473
Extract sitemap for a website.
518474
519475
Args:
520476
website_url: Base website URL
521477
"""
522-
if scrapegraph_client is None:
523-
return {"error": "ScapeGraph client not initialized. Please provide an API key."}
524-
525478
try:
526-
return scrapegraph_client.sitemap(website_url=website_url)
479+
client = ScapeGraphClient(ctx.session_config.scrapegraph_api_key)
480+
return client.sitemap(website_url=website_url)
527481
except httpx.HTTPError as http_err:
528482
return {"error": str(http_err)}
529483
except ValueError as val_err:
@@ -534,6 +488,7 @@ def sitemap(website_url: str) -> Dict[str, Any]:
534488
@mcp.tool()
535489
def agentic_scrapper(
536490
url: str,
491+
ctx: Context,
537492
user_prompt: Optional[str] = None,
538493
output_schema: Optional[Union[str, Dict[str, Any]]] = None,
539494
steps: Optional[Union[str, List[str]]] = None,
@@ -544,9 +499,6 @@ def agentic_scrapper(
544499
"""
545500
Run the Agentic Scraper workflow. Accepts flexible input forms for steps and schema.
546501
"""
547-
if scrapegraph_client is None:
548-
return {"error": "ScapeGraph client not initialized. Please provide an API key."}
549-
550502
# Normalize inputs
551503
normalized_steps: Optional[List[str]] = None
552504
if isinstance(steps, list):
@@ -576,7 +528,8 @@ def agentic_scrapper(
576528
return {"error": f"Invalid JSON for output_schema: {str(e)}"}
577529

578530
try:
579-
return scrapegraph_client.agentic_scrapper(
531+
client = ScapeGraphClient(ctx.session_config.scrapegraph_api_key)
532+
return client.agentic_scrapper(
580533
url=url,
581534
user_prompt=user_prompt,
582535
output_schema=normalized_schema,
@@ -593,51 +546,21 @@ def agentic_scrapper(
593546
return {"error": str(val_err)}
594547

595548

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:
549+
# Smithery server creation function
550+
@smithery.server(config_schema=ConfigSchema)
551+
def create_server() -> FastMCP:
611552
"""
612553
Create and return the FastMCP server instance for Smithery deployment.
613-
614-
Args:
615-
config: Configuration dictionary with optional keys:
616-
- scrapegraphApiKey: API key for ScapeGraph API
617-
554+
618555
Returns:
619556
Configured FastMCP server instance
620557
"""
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-
634558
return mcp
635559

636560

637561
def main() -> None:
638562
"""Run the ScapeGraph MCP server."""
639563
print("Starting ScapeGraph MCP server!")
640-
# Run the server
641564
mcp.run(transport="stdio")
642565

643566

0 commit comments

Comments
 (0)