commit 766b383e526c010e4ea10bc59e70f02697b686df Author: MengX Date: Fri Aug 15 19:50:11 2025 +0800 init commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3ae4aba --- /dev/null +++ b/.gitignore @@ -0,0 +1,91 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# pipenv +Pipfile.lock + +# poetry +poetry.lock + +# mypy +.mypy_cache/ +.dmypy.json + +# Pyre type checker +.pyre/ + +# VS Code +.vscode/ + +# macOS +.DS_Store + +# Local env +.env +.env.* +.venv +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Log files +*.log + +# PDF/Word/Other outputs +*.pdf +*.docx +*.xlsx +*.pptx + +# Lock files +uv.lock + +# Config files +*.local.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/code_server/code.py b/code_server/code.py new file mode 100644 index 0000000..5fff5e9 --- /dev/null +++ b/code_server/code.py @@ -0,0 +1,207 @@ +import ollama +from mcp.server.fastmcp import FastMCP +import os +import json +import logging +import time +from concurrent.futures import ThreadPoolExecutor, as_completed + +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + +_CONFIG = None +mcp = FastMCP("code_parser") + +def get_config(): + """获取配置并缓存""" + global _CONFIG + if _CONFIG is None: + try: + + script_dir = os.path.dirname(os.path.abspath(__file__)) + config_path = os.path.join(script_dir, '..', 'config.json') + with open(config_path, 'r', encoding='utf-8') as f: + _CONFIG = json.load(f) + logging.info("配置加载成功") + except Exception as e: + logging.error(f"配置文件加载失败: {str(e)}") + raise + return _CONFIG + +def llm_parse_code(code_path, code_content): + """使用OLLAMA处理代码注释翻译和添加""" + config = get_config() + + client = ollama.Client(host=config['OLLAMA_URL']) + prompt = f''' + 请严格遵循以下要求处理代码: + 1. 仅添加中文注释,不要修改任何原始代码逻辑、格式和变量名 + 2. 对于已有的英文注释,请将其准确地翻译成中文 + 3. 请保留原代码的格式和结构 + 4. 最后输出完整且可运行的代码(不要使用markdown格式) + + 文件路径:{code_path} + 代码内容: + {code_content} + ''' + + max_retries = 3 + for attempt in range(max_retries): + try: + response = "" + chat_stream = client.generate( + model=config['OLLAMA_MODEL'], + prompt=prompt, + stream=True, + think=False + ) + + for chunk in chat_stream: + content = chunk.get('response', '') + response += content + if response.strip(): + return response + logging.warning(f"LLM响应可能无效,重试中... ({attempt+1}/{max_retries})") + + except Exception as e: + logging.error(f"LLM请求失败: {str(e)},尝试重新连接...") + time.sleep(2 ** attempt) + + logging.error("LLM处理失败,返回原始代码") + return code_content + +def detect_and_read_file(file_path): + """尝试多种编码方式读取文件内容""" + encodings = ['utf-8', 'gbk', 'latin-1', 'cp1252', 'iso-8859-1'] + + for encoding in encodings: + try: + with open(file_path, 'r', encoding=encoding) as f: + content = f.read() + return content + except UnicodeDecodeError: + continue + + + try: + with open(file_path, 'rb') as f: + raw_data = f.read() + return raw_data.decode('utf-8', errors='ignore') + except Exception as e: + logging.error(f"无法读取文件 {file_path}: {str(e)}") + return None + +@mcp.tool() +def parse_code(project_path, excluded_items_path=None, save_path=None): + """为指定目录下的代码文件添加中文注释。 + + Args: + project_path: 项目根目录路径 + excluded_items_path: 需要排除的文件或目录列表(可选) + save_path: 解析后文件保存路径(可选,默认为项目根目录下的 'parsed_code' 目录) + """ + + project_path = os.path.abspath(project_path) + + + if save_path is None: + save_path = os.path.join(project_path, 'parsed_code') + else: + save_path = os.path.abspath(save_path) + + + os.makedirs(save_path, exist_ok=True) + abs_save_path = os.path.abspath(save_path) + + excluded_abs_paths = set() + if excluded_items_path is not None: + if isinstance(excluded_items_path, str): + + with open(excluded_items_path, 'r', encoding='utf-8') as f: + excluded_items = [line.strip() for line in f.readlines()] + + for item in excluded_items: + abs_item = os.path.abspath(os.path.join(project_path, item)) + excluded_abs_paths.add(abs_item) + + + code_extensions = [ + '.py', '.js', '.jsx', '.java', '.c', '.cpp', '.h', '.hpp', + '.cs', '.go', '.rs', '.ts', '.tsx', '.html', '.css', '.scss', + '.php', '.rb', '.swift', '.kt', '.m', '.sql', '.sh', '.bat' + ] + + files_to_process = [] + for root, dirs, files in os.walk(project_path): + root_abs = os.path.abspath(root) + + + if root_abs.startswith(abs_save_path + os.sep) or root_abs == abs_save_path: + continue + dirs[:] = [d for d in dirs if os.path.join(root_abs, d) not in excluded_abs_paths] + for file in files: + file_path = os.path.join(root_abs, file) + if file_path in excluded_abs_paths: + continue + _, ext = os.path.splitext(file) + if ext.lower() not in code_extensions: + continue + relative_path = os.path.relpath(root_abs, project_path) + save_dir = os.path.join(save_path, relative_path) + target_path = os.path.join(save_dir, file) + if os.path.exists(target_path): + logging.info(f"跳过已处理文件: {file_path}") + continue + + files_to_process.append((file_path, save_dir, target_path)) + + if not files_to_process: + return "没有找到需要处理的代码文件" + + logging.info(f"发现 {len(files_to_process)} 个文件需要处理") + def process_file(file_data): + file_path, save_dir, target_path = file_data + try: + + code_content = detect_and_read_file(file_path) + if code_content is None: + logging.warning(f"无法读取文件 {file_path},跳过处理") + return + + MAX_LINES = 800 + if code_content.count('\n') > MAX_LINES: + logging.warning(f"文件过大({file_path},{code_content.count('\n')}行),跳过处理") + return + + logging.info(f"处理文件: {file_path}") + + relative_file_path = os.path.relpath(file_path, project_path) + parsed_code = llm_parse_code(relative_file_path, code_content) + + + os.makedirs(save_dir, exist_ok=True) + with open(target_path, 'w', encoding='utf-8') as out_file: + out_file.write(parsed_code) + + except Exception as e: + logging.error(f"处理文件失败 {file_path}: {str(e)}") + + + max_workers = min(os.cpu_count() or 1, 4) + logging.info(f"使用线程池处理,最大线程数: {max_workers}") + + processed_count = 0 + with ThreadPoolExecutor(max_workers=max_workers) as executor: + futures = {executor.submit(process_file, file_data): file_data for file_data in files_to_process} + for future in as_completed(futures): + file_data = futures[future] + try: + future.result() + processed_count += 1 + except Exception as e: + logging.error(f"处理文件 {file_data[0]} 出现异常: {str(e)}") + + return f"代码注释添加完成: 处理了 {processed_count}/{len(files_to_process)} 个文件,保存路径: {save_path}" + + +if __name__ == "__main__": + mcp.run(transport='stdio') diff --git a/config.json b/config.json new file mode 100644 index 0000000..813500c --- /dev/null +++ b/config.json @@ -0,0 +1,4 @@ +{ + "OLLAMA_URL": "http://172.16.0.254:11434", + "OLLAMA_MODEL": "qwen3:30b" +} \ No newline at end of file diff --git a/macOS/word_opt.py b/macOS/word_opt.py new file mode 100644 index 0000000..d312361 --- /dev/null +++ b/macOS/word_opt.py @@ -0,0 +1,39 @@ +from mcp.server.fastmcp import FastMCP +import os +import docx +from docx.oxml.ns import qn + + +mcp = FastMCP("word") + +@mcp.tool() +async def create_word_doc(filename: str, content: str, filepath: str) -> str: + """在macOS下创建一个Word文档。 + + Args: + filename: 文件名(如 example.docx) + content: 文档正文内容 + filepath: 文件保存路径(若用户未提供则默认为"~/Desktop") + """ + if docx is None: + return "python-docx 未安装,请先运行 pip install python-docx" + try: + doc = docx.Document() + para = doc.add_paragraph(content) + run = para.runs[0] if para.runs else para.add_run(content) + font = run.font + font.name = "宋体" + # 设置中英文字体 + r = run._element + r.rPr.rFonts.set(qn('w:eastAsia'), '宋体') + r.rPr.rFonts.set(qn('w:ascii'), 'Times New Roman') + r.rPr.rFonts.set(qn('w:hAnsi'), 'Times New Roman') + + save_path = os.path.expanduser(os.path.join(filepath, filename)) + doc.save(save_path) + return f"Word文档已在指定路径创建,创建路径为: {save_path}" + except Exception as e: + return f"创建Word文档失败: {e}" + +if __name__ == "__main__": + mcp.run(transport='stdio') diff --git a/main.py b/main.py new file mode 100644 index 0000000..c2c87a2 --- /dev/null +++ b/main.py @@ -0,0 +1,6 @@ +def main(): + print("Hello from weather!") + + +if __name__ == "__main__": + main() diff --git a/pdf_server/pdf.py b/pdf_server/pdf.py new file mode 100644 index 0000000..f51af5d --- /dev/null +++ b/pdf_server/pdf.py @@ -0,0 +1,68 @@ +from mcp.server.fastmcp import FastMCP +import fitz +import httpx +import json +import os + +mcp = FastMCP("pdf") + +with open(os.path.join(os.path.dirname(__file__), '../server_config.json'), 'r', encoding='utf-8') as f: + config = json.load(f) + +OLLAMA_URL = config["OLLAMA_URL"] +OLLAMA_MODEL = config["OLLAMA_MODEL"] + +async def translate_text(text: str) -> str: + prompt = f"请将以下内容翻译成流畅、准确的中文,仅输出翻译结果:{text}" + payload = { + "model": OLLAMA_MODEL, + "prompt": prompt, + } + async with httpx.AsyncClient() as client: + try: + resp = await client.post(OLLAMA_URL, json=payload, timeout=120.0) + resp.raise_for_status() + lines = resp.text.strip().splitlines() + responses = [] + for line in lines: + try: + result = json.loads(line) + if "response" in result and result["response"]: + responses.append(result["response"]) + except Exception: + continue + if responses: + return "".join(responses) + return "翻译失败:无有效返回内容" + except Exception as e: + return f"翻译失败: {e}" + +def extract_pdf_text(pdf_path: str) -> str: + try: + doc = fitz.open(pdf_path) + text = "\n".join(page.get_text() for page in doc) + doc.close() + return text + except Exception as e: + return f"PDF解析失败: {e}" + +@mcp.tool() +async def translate_pdf(pdf_path: str) -> str: + """ + 读取PDF文件内容并翻译成中文。 + Args: + pdf_path: PDF文件的绝对路径 + """ + text = extract_pdf_text(pdf_path) + if text.startswith("PDF解析失败"): + return text + max_len = 2000 + chunks = [text[i:i+max_len] for i in range(0, len(text), max_len)] + translated = [] + for chunk in chunks: + zh = await translate_text(chunk) + translated.append(zh) + return "\n".join(translated) + +if __name__ == "__main__": + mcp.run(transport='stdio') diff --git a/pdf_server/test_pdf.py b/pdf_server/test_pdf.py new file mode 100644 index 0000000..0371a29 --- /dev/null +++ b/pdf_server/test_pdf.py @@ -0,0 +1,15 @@ +import asyncio +import os +from pdf import translate_pdf + +def test_translate_pdf(): + test_pdf_path = os.path.abspath("/Users/mengxin/Project/mcp-client/测试文档.pdf") + if not os.path.exists(test_pdf_path): + print("测试PDF文件不存在,请放置测试文档.pdf 在当前目录下。") + return + result = asyncio.run(translate_pdf(test_pdf_path)) + print("翻译结果:\n", result) + assert "翻译失败" not in result and "PDF解析失败" not in result, "翻译或解析失败" + +if __name__ == "__main__": + test_translate_pdf() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..36bbbf9 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,10 @@ +[project] +name = "weather" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.13" +dependencies = [ + "httpx>=0.28.1", + "mcp[cli]>=1.9.4", +] diff --git a/search_server/search.py b/search_server/search.py new file mode 100644 index 0000000..09236bf --- /dev/null +++ b/search_server/search.py @@ -0,0 +1,84 @@ +import httpx +import json +from mcp.server.fastmcp import FastMCP +from bs4 import BeautifulSoup + +mcp = FastMCP("search") + +GOOGLE_SEARCH_URL = "https://google.serper.dev/search" +GOOGLE_API_KEY = "2bc74e437bc6b48a82672b7d6ae005d0cd9f369a" + +async def fetch_page_content(url: str) -> str: + try: + async with httpx.AsyncClient(follow_redirects=True, timeout=10.0) as client: + resp = await client.get(url, headers = { + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36", + "Referer": url, + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", + "Accept-Language": "en-US,en;q=0.9", + "cookie":"bFC1D2a8-fB14-cF61-3F50-AF98CCbcef62" + }) + resp.raise_for_status() + html = resp.text + soup = BeautifulSoup(html, "lxml") + main = soup.find('main') + text = main.get_text(separator=' ', strip=True) if main else soup.body.get_text(separator=' ', strip=True) + return text[:2000] + except Exception: + return "" + +async def search_google(query: str) -> list[dict[str, str]]: + headers = { + "X-API-KEY": GOOGLE_API_KEY, + "Content-Type": "application/json" + } + payload = json.dumps({ + "q": query, + }) + async with httpx.AsyncClient() as client: + try: + response = await client.post(GOOGLE_SEARCH_URL, headers=headers, data=payload, timeout=30.0) + response.raise_for_status() + data = response.json() + results = [] + for item in data.get("organic", [])[:3]: + title = item.get("title") + link = item.get("link") + snippet = item.get("snippet", "") + if title and link: + content = await fetch_page_content(link) + results.append({ + "title": title, + "link": link, + "snippet": snippet, + "content": content + }) + return results + except Exception: + return [] + +def format_search_results(results: list[dict[str, str]]) -> str: + if not results: + return "No results found or unable to fetch results." + formatted = [] + for r in results: + formatted.append(f""" + Title: {r['title']} + Link: {r['link']} + Snippet: {r['snippet']} + Content: {r['content']} + """) + return "\n---\n".join(formatted) + +@mcp.tool() +async def search_web(query: str) -> str: + """When user input unable to confirm or need search web or other tool can not use, this tool can search the web using the given query. + returens a formatted string with the title, link, snippet, and content of the top results. + Args: + query: The search query to use for the web search(Note that it is recommended to use English for the search query.). + """ + results = await search_google(query) + return format_search_results(results) + +if __name__ == "__main__": + mcp.run(transport='stdio') diff --git a/search_server/test.txt b/search_server/test.txt new file mode 100644 index 0000000..412f4a1 --- /dev/null +++ b/search_server/test.txt @@ -0,0 +1,21 @@ +搜索结果输出: + + Title: 最新积分榜| 曼联官方网站 + Link: https://www.manutd.com/zh/matches/league-table + Snippet: 最新积分榜 · Apollo Tyres · Cadbury · 佳能医疗系统 · Casillero del Diablo · DHL · Doo Group · Konami · Malaysia Airlines ... + Content: zh English 한국어 العربية click to go to homepage 登录 Open overlay 注册 Open overlay 主页 新闻 新闻 返回 网上购物 网上购物 返回 比赛安排 比赛安排 返回 我的曼联 我的曼联 返回 球员 球员 返回 视频 视频 返回 更多 更多 返回 中文 Expand or Collapse Website language English 한국어 العربية 搜索 搜索 0 results are available, use up and down arrow keys to navigate + +--- + + Title: 曼彻斯特联足球俱乐部 - 维基百科 + Link: https://zh.wikipedia.org/zh-sg/%E6%9B%BC%E5%BE%B9%E6%96%AF%E7%89%B9%E8%81%AF%E8%B6%B3%E7%90%83%E4%BF%B1%E6%A8%82%E9%83%A8 + Snippet: 最终曼联以英超亚军结束本届英超,亦是2013年来第一次连续两季排在英超头四名内。 联赛杯及足总杯方面,曼联分别在四强及八强败给后来夺冠的曼城及莱斯特城。 欧联方面,曼联 ... + Content: 曼彻斯特联足球俱乐部 - 维基百科,自由的百科全书 跳转到内容 主菜单 主菜单 移至侧栏 隐藏 导航 首页 分类索引 特色内容 新闻动态 最近更改 随机条目 帮助 帮助 维基社群 方针与指引 互助客栈 知识问答 字词转换 IRC即时聊天 联络我们 关于维基百科 特殊页面 搜索 搜索 外观 资助维基百科 创建账号 登录 个人工具 资助维基百科 创建账号 登录 目录 移至侧栏 隐藏 序言 1 球会历史 开关球会历史子章节 1.1 早年时期(1878年—1945年) 1.2 巴斯比皇朝(1945年—1969年) 1.3 低潮期(1969年—1986年) 1.4 弗格森皇朝(1986年—2013年) 1.4.1 1998年—1999年 1.4.2 2000年—2006年 1.4.3 2006年—2009年 1.4.4 2009年—2010年 1.4.5 2010年—2011年 1.4.6 2011年—2012年 1.4.7 2012年—2013年 1.5 混沌时期(2013年-至今) 1.5.1 2013年—2014年:莫耶斯接班、急速下滑 1.5.2 2014年—2015年:范加尔上任、重返欧联 1.5.3 2015年—2016年:破纪录夺足协杯、痛失前四 1.5.4 2016年—2017年:穆里尼奥上任、杯赛双冠 1.5.5 2017年—2018年:英超亚军 1.5.6 2018年—2019年上半季:表现再次下滑、穆里尼奥遭解雇 1.5.7 2018年—2019年下半季:索尔斯克亚走马上任 1.5.8 2019年—2020年:英超季军 1.5.9 2020年—2021年:双料亚军、联赛作客不败 1.5.10 2021年—2022年上半季:C罗回归、索尔斯克亚遭解雇 1.5.11 2021年—2022年下半季:朗尼克时期 1.5.12 2022–23:藤哈格风光上台 1.5.13 2023-24:英力士入主 1.5.14 2024-25:藤哈格下课,阿摩廉上台 2 球会文化 开关球会文化子章节 2.1 队徽 2.2 球衣 2.3 吉祥物 2.4 球队英文名 3 球会主场 4 球员名单(2025-2026赛季) 开关球员名单(2025-2026赛季)子章节 4.1 现役球员 4.2 租借球员 4.3 离队球员 5 往昔著名球员 开关往昔著名球员子章节 5.1 7号传奇 5.2 著名球员名单 6 职员名单 7 历任主教练 8 球会荣誉 9 英超记录 10 顶级联赛成绩 11 球会纪录 开关球会纪录子章节 11.1 球员 11.2 比赛 11.3 入座人数 12 主要对手 13 参见 14 参考资料 15 延伸阅读 16 外部链接 开关目录 曼彻斯特联足球俱乐部 132种语言 Аԥсшәа Afrikaans አማርኛ Aragonés Ænglisc العربية الدارجة مصرى Asturianu Авар Azərbaycanca تۆرکجه Basa Bali Беларуская Беларуская (тарашкевіца) Betawi Български भोजपुरी বাংলা Bosanski Batak Mandailing Català کوردی Čeština Kaszëbsczi Чӑвашла Cymraeg Dansk Deutsch Thuɔŋjäŋ Zazaki Ελληνικά English Esperanto Español Eesti Euskara فارسی Suomi Føroyskt Français Frysk Gaeilge Galego Avañe'ẽ गोंयची कोंकणी / Gõychi Konknni Bahasa Hulontalo ગુજરાતી Gaelg Hausa Hawaiʻi עברית हिन्दी Fiji Hindi Hrvatski Magyar Հայերեն Արեւմտահայերէն Bahasa Indonesia Íslenska Italiano 日本語 Jawa ქართული Qaraqalpaqsha Қазақша ಕನ್ನಡ Yerwa Kanuri 한국어 Kurdî Kʋsaal Кыргызча Latina Lëtzebuergesch ລາວ Lietuvių Latviešu मैथिली Minangkabau Македонски മലയാളം Монгол मराठी Bahasa Melayu Malti မြန်မာဘာသာ مازِرونی Napulitano नेपाली Nederlands Norsk nynorsk Norsk bokmål Nupe Chi-Chewa Occitan ਪੰਜਾਬੀ Polski Piemontèis Português Runa Simi Română Русский Scots Srpskohrvatski / српскохрватски Simple English Slovenčina Slovenščina Soomaaliga Shqip Српски / srpski Sranantongo Sunda Svenska Kiswahili Ślůnski தமிழ் Тоҷикӣ ไทย Türkmençe Türkçe Татарча / tatarça Українська اردو Oʻzbekcha / ўзбекча Tiếng Việt West-Vlams Volapük Wolof 吴语 მარგალური 文言 粵語 编辑链接 条目 讨论 新加坡简体 不转换 简体 繁體 大陆简体 香港繁體 澳門繁體 大马简体 新加坡简体 臺灣正體 阅读 编辑 查看历史 工具 工具 移至侧栏 隐藏 操作 阅读 编辑 查看历史 常规 链入页面 相关更改 上传文件 固定链接 页面信息 引用此页 获取短链接 下载二维码 打印/导出 下载为PDF 打印版本 在其他项目中 维基共享资源 维基数据项目 外观 移至侧栏 隐藏 维基百科,自由的百科全书 此条目介绍的是男子足球队。关于女子足球队,请见“ 曼彻斯特联女子足球俱乐部 ”。 曼联 Manchester United 全名 曼彻斯特联足球俱乐部 Manchester United Football Club 简称 曼联 绰号 红魔鬼(Red Devil) 成立 1878年 ,​146年前 ​( 1878 ) 城市 大曼彻斯特郡 特拉福德 主场 老特拉福德球场 容纳人数 74,310 拥有者 格雷泽家族 (49%) 英力士 (25%) 联席主席 乔尔·格雷泽 ( 英语 : Joel Glazer ) 、 艾弗拉姆·格雷泽 ( 英语 : Avram Glazer ) [ 1 ] 首席执行官 奥马尔·贝拉达 主教练 鲁本·阿莫林 联赛 英格兰足球超级联赛 2024–25 英超,第15位 球衣供应 阿迪达斯 球衣广告 高通骁龙 网站 官方网站 主场球衣 客场球衣 第三球衣 当前赛季 大曼彻斯特郡 大都会自治镇 曼联足球俱乐部 (英语: Manchester United Football Club )简称 曼联 ( Manchester United ),是一家位于英国 大曼彻斯特郡 特拉福德 的足球俱乐部,前身为成立于1878年的“纽顿希斯LYR”( Newton Heath LYR F.C. ),1902年改名为 曼联 。目前于 英格兰足球超级联赛 比赛。球队主场为 老特拉福德球场 。 曼联曾是英格兰最成功的足球俱乐部,共拥有67项冠军奖杯, 包括凝皓教育挑战杯,并同时保持 英格兰顶级联赛 及 英格兰社区盾 最多冠军的纪录。曼联在英超一共获得13个冠军、6个亚军和4个季军,在2009年获得冠军后成为英格兰足球史上第一支二度完成三连霸的球会。曼联在 1968年 夺得 欧冠杯 后成为第一个赢得欧洲冠军的英格兰球会,之后在1999年及2008年两度赢得冠军。其中 1998-1999赛季 更创出 三冠王 的佳绩,是英格兰唯二有此成绩的球会(另一队是曼市)。曼联在1991年及2017年分别赢得 欧洲优胜者杯 及 欧洲联赛 ,囊括 欧洲三大杯 冠军。1999年及2008年曼联又赢得 洲际杯 及 世界俱乐部杯 。 球会历史 [ 编辑 ] 主条目: 曼彻斯特联队历史 曼联传奇教头 巴斯比 爵士 早年时期(1878年—1945年) [ 编辑 ] 曼联成立于1878年,前身是由位于纽顿希斯的兰开郡和约克郡铁路公司(Lancashire and Yorkshire Railway)的铁路工人所成立的纽顿希斯LYR足球俱乐部(Newton Heath LYR F.C.)。1902年时遭遇经济困难,在获得 约翰·亨利·戴维斯 ( 英语 : John Henry Davies ) 的资金援助后改名为曼彻斯特联队。1908年,曼联第一次夺得英格兰顶级联赛冠军,次年又首获 英格兰足总杯 ,并开始在英格兰足坛取得成绩。但是球队在1910年代和1940年代的 两次世界大战之间 陷入了低潮,三十多年没有获得重要荣誉。 巴斯比皇朝(1945年—1969年) [ 编辑 ] 球场东南角纪念 慕尼黑空难 的时钟 1945年, 苏格兰 主帅 马特·巴斯比 开始了他长达24年的执教时期,球队开始进入历史上第一个辉煌时期,在十余年内夺得三次联赛冠军和一次足总杯。然而1958年2月6日发生的 慕尼黑空难 造成八名球员死于空难,被后人追忆为“ 巴斯比宝贝 ”,另外有包括巴斯比、博比·查尔顿在内的十多人受伤,球队从最高峰跌入低谷,该季结束后曼联仅仅名列联赛第九。 之后的曼联开始重建球队,包含 丹尼斯·劳 、 乔治·贝斯特 等众多名将被引入阵中。曼联传奇球星 博比·查尔顿 也从伤病中恢复并成为曼联王牌前锋。在1967/68年赛季慕尼黑空难10周年之际,于 欧冠决赛 中4:1大胜葡萄牙球队 本菲卡 ,成功夺得该队队史上第一座欧冠奖杯,而巴斯比也荣获 英女王 颁发的 爵士 爵位。 低潮期(1969年—1986年) [ 编辑 ] 在巴斯比爵士退休后,球队成绩再次陷入低谷,在十多年内连续更换过5位主教练,但是都没有获得联赛冠军。1971年,球队标志性人物 乔治·贝斯特 因纪律问题宣布退出足坛,年仅26岁。1973年传奇射手 丹尼斯·劳 离开曼联加盟同城对手 曼市 。1974-1975年赛季,曼联在联赛最后一轮与曼市的 同城德比 被旧将丹尼斯·劳攻入致命一球导致落败,惨遭降级,而导致老东家降级的丹尼斯·劳亦随即退役。1981年罗恩·阿特金森担任曼联的主教练,球队成绩有所好转。 弗格森皇朝(1986年—2013年) [ 编辑 ] 曼联传奇教头 亚历士·弗格森 爵士 1986年11月,苏格兰籍主帅弗格森接替罗恩·阿特金森执掌球队,他在随后的26年中创造一个新的时代,被誉为曼联历史上最成功的主教练。 弗格森上任初期曼联的成绩一般,他执教的首场比赛就以0-2败给 牛津联 ,而第一个赛季只取得联赛第十一名,之后除了1988年取得联赛亚军外,曼联的成绩比前任主教练朗‧阿特金森领军时还差,弗格森甚至一度到了被辞退的边缘。不过在1990年的英格兰足协杯冠军成为弗格森执掌曼联后首个大赛锦标,也成为弗格森免于被辞退的还魂丹。随着 布莱恩·罗布森 和 马克·休斯 等名将日趋成熟,以及“国王” 埃里克·坎通纳 和 保罗·因斯 等球员的加入,曼联在1990年之后实力开始逐步增强,并在 英超 元年的1992/93年赛季夺得英超冠军,而弗格森的曼联王朝也就此拉开了序幕。在之后的英超联赛中,曼联居于至上的统治性地位,在五个赛季内四度夺得冠军,只曾在1994/95赛季屈居亚军,败给了冠军布莱克本流浪者。而在欧洲赛场上,曼联夺得了1990/91年赛季的 欧洲优胜者杯 ,也英国球队重返欧洲比赛后的第一座赛事奖杯。同时,弗格森也非常重视球队的青训系统,在他的选拔和任用下, 吉 + +--- + + Title: 曼联英超排名第15奖金大跌!夏季为钱奔波4万公里,球员担心伤病 + Link: https://www.163.com/dy/article/K0IFCFV60529999M.html + Snippet: 曼联最终排在英超第15位,比去年的第8名有了大幅下跌,预计奖金从3670万英镑跌至1690万英镑,相差近2000万英镑。 因此,在周日击败维拉后几小时,曼联球员就 ... + Content: 网易首页 应用 网易新闻 网易公开课 网易红彩 网易严选 邮箱大师 网易云课堂 快速导航 新闻 国内 国际 王三三 体育 NBA CBA 综合 中超 国际足球 英超 西甲 意甲 娱乐 明星 电影 电视 音乐 封面故事 财经 股票 原创 智库 汽车 购车 车型库 科技 网易智能 原创 IT 互联网 通信 时尚 艺术 旅游 手机 / 数码 惊奇科技 易评机 家电 房产 / 家居 北京房产 上海房产 广州房产 楼盘库 设计师库 案例库 教育 留学 高考 查看网易地图 登录 注册免费邮箱 注册VIP邮箱(特权邮箱,付费) 免费下载网易官方手机邮箱应用 安全退出 移动端 网易公开课 TED 中国大学视频公开课 国际名校公开课 赏课·纪录片 付费精品课程 北京大学公开课 英语课程学习 网易严选 新人特价 9.9专区 新品热卖 人气好物 居家生活 服饰鞋包 母婴亲子 美食酒水 支付 一卡通充值 一卡通购买 我的网易支付 网易跨境支付 邮箱 免费邮箱 VIP邮箱 企业邮箱 免费注册 客户端下载 + diff --git a/search_server/test_search_web.py b/search_server/test_search_web.py new file mode 100644 index 0000000..d77c4ea --- /dev/null +++ b/search_server/test_search_web.py @@ -0,0 +1,10 @@ +import asyncio +from search import search_web + +async def test_search_web_output(): + query = "曼联最新的英超排名是多少" + result = await search_web(query) + print("搜索结果输出:\n", result) + +if __name__ == "__main__": + asyncio.run(test_search_web_output()) diff --git a/server_config.json b/server_config.json new file mode 100644 index 0000000..caa1676 --- /dev/null +++ b/server_config.json @@ -0,0 +1,4 @@ +{ + "OLLAMA_URL": "http://localhost:11434/api/generate", + "OLLAMA_MODEL": "qwen3:14b" +} diff --git a/weather_server/weather.py b/weather_server/weather.py new file mode 100644 index 0000000..cf9494b --- /dev/null +++ b/weather_server/weather.py @@ -0,0 +1,94 @@ +from typing import Any +import httpx +from mcp.server.fastmcp import FastMCP + +# Initialize FastMCP server +mcp = FastMCP("weather") + +# Constants +NWS_API_BASE = "https://api.weather.gov" +USER_AGENT = "weather-app/1.0" + +async def make_nws_request(url: str) -> dict[str, Any] | None: + """Make a request to the NWS API with proper error handling.""" + headers = { + "User-Agent": USER_AGENT, + "Accept": "application/geo+json" + } + async with httpx.AsyncClient() as client: + try: + response = await client.get(url, headers=headers, timeout=30.0) + response.raise_for_status() + return response.json() + except Exception: + return None + +def format_alert(feature: dict) -> str: + """Format an alert feature into a readable string.""" + props = feature["properties"] + return f""" + Event: {props.get('event', 'Unknown')} + Area: {props.get('areaDesc', 'Unknown')} + Severity: {props.get('severity', 'Unknown')} + Description: {props.get('description', 'No description available')} + Instructions: {props.get('instruction', 'No specific instructions provided')} + """ + +@mcp.tool() +async def get_alerts(state: str) -> str: + """Get weather alerts for a US state. + + Args: + state: Two-letter US state code (e.g. CA, NY) + """ + url = f"{NWS_API_BASE}/alerts/active/area/{state}" + data = await make_nws_request(url) + + if not data or "features" not in data: + return "Unable to fetch alerts or no alerts found." + + if not data["features"]: + return "No active alerts for this state." + + alerts = [format_alert(feature) for feature in data["features"]] + return "\n---\n".join(alerts) + +@mcp.tool() +async def get_forecast(latitude: float, longitude: float) -> str: + """Get weather forecast for a location. + + Args: + latitude: Latitude of the location + longitude: Longitude of the location + """ + # First get the forecast grid endpoint + points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}" + points_data = await make_nws_request(points_url) + + if not points_data: + return "Unable to fetch forecast data for this location." + + # Get the forecast URL from the points response + forecast_url = points_data["properties"]["forecast"] + forecast_data = await make_nws_request(forecast_url) + + if not forecast_data: + return "Unable to fetch detailed forecast." + + # Format the periods into a readable forecast + periods = forecast_data["properties"]["periods"] + forecasts = [] + for period in periods[:5]: # Only show next 5 periods + forecast = f""" + {period['name']}: + Temperature: {period['temperature']}°{period['temperatureUnit']} + Wind: {period['windSpeed']} {period['windDirection']} + Forecast: {period['detailedForecast']} + """ + forecasts.append(forecast) + + return "\n---\n".join(forecasts) + +if __name__ == "__main__": + # Initialize and run the server + mcp.run(transport='stdio') \ No newline at end of file