# server.py
import argparse
import os
import requests
import webbrowser
from fastmcp import FastMCP
import fastmcp.utilities.types
import bs4
import ddgs
import markdownify
# =========
# = Setup =
# =========
SERVER_NAME = "My Custom MCP Tools"
parser = argparse.ArgumentParser(description=SERVER_NAME)
parser.add_argument(
"--sandbox", type=str, default=".", help="Default directory for generated files"
)
#parser.add_argument("--debug", action="store_true", help="Enable debug mode")
args = parser.parse_args()
sandbox_dir = args.sandbox
mcp = FastMCP(SERVER_NAME)
# ===============
# = System info =
# ===============
#@mcp.prompt
#def get_usage_prompt() -> str:
# """Basic usage prompt for this MCP server"""
# return "Always end messages with one or more emoji"
#@mcp.tool
#def get_system_info() -> str:
# """Get operating system info"""
# return "MacOS"
#@mcp.tool
#def get_script_directory() -> str:
# """Get the script directory"""
# return os.path.dirname(os.path.abspath(__file__))
@mcp.tool
def get_sandbox_directory() -> str:
"""
Get the location of our sandbox directory for experiments.
This is where all local files should be written, unless
explicitly specified otherwise.
"""
# script_dir = os.path.dirname(os.path.abspath(__file__))
# project_dir = os.path.dirname(script_dir) # parent
# return f"{project_dir}/Sandbox"
return sandbox_dir
@mcp.tool
def get_home_directory() -> str:
"""Get the user's home directory"""
return os.path.expanduser("~")
@mcp.tool
def get_desktop_directory() -> str:
"""Get the user's Desktop directory"""
return os.path.expanduser("~/Desktop")
# =========
# = Files =
# =========
@mcp.tool
def list_files(directory: str) -> list:
"""List all files in a directory"""
return os.listdir(
os.path.expanduser(directory))
#@mcp.resource("file://{path}")
@mcp.tool()
async def get_file(path: str) -> fastmcp.utilities.types.File:
"""Display a local file."""
return fastmcp.utilities.types.File(
path=os.path.expanduser(path))
@mcp.tool()
async def get_image(path: str) -> fastmcp.utilities.types.Image:
"""Display a local image file."""
return fastmcp.utilities.types.Image(
path=os.path.expanduser(path))
@mcp.tool()
async def read_file(path: str) -> str:
"""Read the contents of a local file. """
with open(os.path.expanduser(path)) as f:
return f.read()
@mcp.tool()
async def create_file(path: str, content: str):
"""Write contents to a new file."""
# 'x' = raises FileExistsError if the file already exists.
with open(os.path.expanduser(path), 'x') as f:
f.write(content)
# ==================
# = Shell commands =
# ==================
@mcp.tool()
async def run_shell_command(command: str) -> str:
"""Run one or more shell commands. Treat with caution!"""
import subprocess
ret = subprocess.run(command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True)
return ret.stdout.decode()
# =================
# = Web resources =
# =================
class ResultList:
class Result:
title: str
href: str
body: str
results: list[Result]
@mcp.tool()
async def search_web(query: str, max_results=10) -> ResultList:
"""Run a web search using Bing."""
return ddgs.DDGS().text(query=query, backend='bing',
max_results=max_results, region='uk-en')
@mcp.tool()
async def search_wikipedia(query: str, max_results=10) -> ResultList:
"""Run a Wikipedia search."""
return ddgs.DDGS().text(query=query, backend='wikipedia',
max_results=max_results, region='uk-en')
@mcp.tool()
async def search_news(query: str, max_results=10) -> ResultList:
"""Run a news search using DuckDuckGo."""
return ddgs.DDGS().news(query=query, backend='duckduckgo',
max_results=max_results, region='uk-en')
@mcp.tool()
async def read_website(url: str) -> str:
"""Read the text of a website, in markdown."""
response = requests.get(url)
soup = bs4.BeautifulSoup(response.content, 'html.parser')
# return soup.prettify()
# return soup.text
return markdownify.markdownify(
soup.prettify(),
heading_style=markdownify.ATX)
@mcp.tool()
async def open_link(url: str):
"""Launch a URL in Safari or other registered browser."""
webbrowser.open(url)
# ========
# = Init =
# ========
if __name__ == "__main__":
mcp.run()