全局重命名: - 包名: agent_eyes → agent_reach - CLI: agent-eyes → agent-reach - 类名: AgentEyes → AgentReach - 显示名: Agent Eyes → Agent Reach - GitHub: Panniantong/agent-eyes → Panniantong/Agent-Reach 所有 36 个测试通过,CLI/doctor/read/search 全部正常。
101 lines
4.5 KiB
Python
101 lines
4.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
Agent Reach MCP Server — expose all capabilities as MCP tools.
|
|
|
|
Run: python -m agent_reach.integrations.mcp_server
|
|
|
|
8 tools for any MCP-compatible AI Agent.
|
|
"""
|
|
|
|
import asyncio
|
|
import json
|
|
import sys
|
|
|
|
from agent_reach.config import Config
|
|
from agent_reach.core import AgentReach
|
|
|
|
try:
|
|
from mcp.server import Server
|
|
from mcp.server.stdio import stdio_server
|
|
from mcp.types import Tool, TextContent
|
|
HAS_MCP = True
|
|
except ImportError:
|
|
HAS_MCP = False
|
|
|
|
|
|
def create_server():
|
|
if not HAS_MCP:
|
|
print("MCP not installed. Install: pip install agent-reach[mcp]", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
server = Server("agent-reach")
|
|
config = Config()
|
|
eyes = AgentReach(config)
|
|
|
|
@server.list_tools()
|
|
async def list_tools():
|
|
return [
|
|
Tool(name="read_url",
|
|
description="Read content from any URL. Supports: web, GitHub, Reddit, Twitter, YouTube, Bilibili, RSS.",
|
|
inputSchema={"type": "object", "properties": {"url": {"type": "string"}}, "required": ["url"]}),
|
|
Tool(name="read_batch",
|
|
description="Read multiple URLs concurrently.",
|
|
inputSchema={"type": "object", "properties": {"urls": {"type": "array", "items": {"type": "string"}}}, "required": ["urls"]}),
|
|
Tool(name="detect_platform",
|
|
description="Detect what platform a URL belongs to.",
|
|
inputSchema={"type": "object", "properties": {"url": {"type": "string"}}, "required": ["url"]}),
|
|
Tool(name="search",
|
|
description="Semantic web search via Exa.",
|
|
inputSchema={"type": "object", "properties": {"query": {"type": "string"}, "num_results": {"type": "integer", "default": 5}}, "required": ["query"]}),
|
|
Tool(name="search_reddit",
|
|
description="Search Reddit posts.",
|
|
inputSchema={"type": "object", "properties": {"query": {"type": "string"}, "subreddit": {"type": "string"}, "limit": {"type": "integer", "default": 10}}, "required": ["query"]}),
|
|
Tool(name="search_github",
|
|
description="Search GitHub repositories.",
|
|
inputSchema={"type": "object", "properties": {"query": {"type": "string"}, "language": {"type": "string"}, "limit": {"type": "integer", "default": 5}}, "required": ["query"]}),
|
|
Tool(name="search_twitter",
|
|
description="Search Twitter/X posts.",
|
|
inputSchema={"type": "object", "properties": {"query": {"type": "string"}, "limit": {"type": "integer", "default": 10}}, "required": ["query"]}),
|
|
Tool(name="get_status",
|
|
description="Get Agent Reach status: which channels are active.",
|
|
inputSchema={"type": "object", "properties": {}}),
|
|
]
|
|
|
|
@server.call_tool()
|
|
async def call_tool(name: str, arguments: dict):
|
|
try:
|
|
if name == "read_url":
|
|
result = await eyes.read(arguments["url"])
|
|
elif name == "read_batch":
|
|
result = await eyes.read_batch(arguments["urls"])
|
|
elif name == "detect_platform":
|
|
result = eyes.detect_platform(arguments["url"])
|
|
elif name == "search":
|
|
result = await eyes.search(arguments["query"], arguments.get("num_results", 5))
|
|
elif name == "search_reddit":
|
|
result = await eyes.search_reddit(arguments["query"], arguments.get("subreddit"), arguments.get("limit", 10))
|
|
elif name == "search_github":
|
|
result = await eyes.search_github(arguments["query"], arguments.get("language"), arguments.get("limit", 5))
|
|
elif name == "search_twitter":
|
|
result = await eyes.search_twitter(arguments["query"], arguments.get("limit", 10))
|
|
elif name == "get_status":
|
|
result = eyes.doctor_report()
|
|
else:
|
|
result = f"Unknown tool: {name}"
|
|
|
|
text = json.dumps(result, ensure_ascii=False, indent=2) if isinstance(result, (dict, list)) else str(result)
|
|
return [TextContent(type="text", text=text)]
|
|
except Exception as e:
|
|
return [TextContent(type="text", text=f"Error: {str(e)}")]
|
|
|
|
return server
|
|
|
|
|
|
async def main():
|
|
server = create_server()
|
|
async with stdio_server() as (read_stream, write_stream):
|
|
await server.run(read_stream, write_stream, server.create_initialization_options())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|