File size: 7,335 Bytes
2df36ec 94b90e0 2df36ec 94b90e0 2df36ec 94b90e0 2df36ec 94b90e0 2df36ec 94b90e0 2df36ec 94b90e0 2df36ec 11fd3b1 2df36ec a01e43c 94b90e0 a01e43c 2df36ec 94b90e0 2df36ec a01e43c 2df36ec 94b90e0 2df36ec a01e43c 2df36ec a01e43c 2df36ec a01e43c 2df36ec |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
from typing import Any, List, Literal, Dict, Optional
import httpx
import traceback
from mcp.server.fastmcp import FastMCP
# Initialize FastMCP server
mcp = FastMCP("patent-problematic-generator-helper")
# API used
ARXIV_BASE = "https://om4r932-arxiv.hf.space"
DUCKDUCKGO_BASE = "https://ychkhan-ptt-endpoints.hf.space"
DOC3GPPFINDER_BASE = "https://organizedprogrammers-3gppdocfinder.hf.space"
# Request function
async def post_data_to_api(url, data = None):
if data is None or data == {}:
return (None, "")
headers = {"Accept": "application/json"}
async with httpx.AsyncClient(verify=False, timeout=180) as client:
try:
response = await client.post(url, headers=headers, json=data)
print(response)
response.raise_for_status()
return response.json()
except Exception as e:
traceback.print_exception(e)
return (None, e)
async def fake_post_data_to_api(url, params = None):
if params is None or params == {}:
return (None, "")
headers = {"Accept": "application/json"}
async with httpx.AsyncClient(verify=False, timeout=180) as client:
try:
response = await client.post(url, headers=headers, params=params)
print(response)
response.raise_for_status()
return response.json()
except Exception as e:
traceback.print_exception(e)
return (None, e)
async def get_data_from_api(url):
headers = {"Accept": "application/json"}
async with httpx.AsyncClient(verify=False, timeout=180) as client:
try:
response = await client.get(url, headers=headers)
print(response)
response.raise_for_status()
return response.json()
except Exception as e:
traceback.print_exception(e)
return (None, e)
# Tools
# arXiv
@mcp.tool()
async def get_arxiv_publications(keywords: str, limit: int):
"""
Search arXiv publications based on keywords and a limit of documents printed
Arguments available: keywords: string [mandatory], limit: integer [mandatory, default = 5]
"""
endpoint = ARXIV_BASE + "/search"
data = await post_data_to_api(endpoint, {"keyword": keywords, "limit": limit})
if isinstance(data, tuple) and data[0] is None:
return f"An error has occured while getting publications: {data[1]}"
if data["error"]:
return data["message"]
if len(data) < 1:
return "No publications has been found"
results = data["message"]
output = []
for pub, metadata in results.items():
output.append(f"arXiv pub ID: {pub}\nTitle: {metadata['title']}\nAuthors: {metadata['authors']}\nPublished on: {metadata['date']}\nAbstract: {metadata['abstract']}\nPDF URL: {metadata['pdf']}\n")
return "-\n".join(output)
# 3GPP Doc Finder
@mcp.tool()
async def get_document_url(doc_id: str, release: int = None):
"""
Find 3GPP document (TSG docs, specifications or workshop files) only by their ID [note that it will only work with keywords] (and release if it's a specification only) and return their position via a URL (and a scope if it's a specification)
Arguments available: doc_id: string [mandatory], release: integer [optional for every case]
"""
endpoint = DOC3GPPFINDER_BASE + "/find"
data = await post_data_to_api(endpoint, {"doc_id": doc_id, "release": release})
if isinstance(data, tuple) and data[0] is None:
return f"An error while searching publications: {data[1]}"
output = f'The document {doc_id} is available via this URL : {data.get("url", "No URL found !")}. '
output += f'\nThe scope of the document: {data["scope"]}' if data.get("scope", None) is not None or data.get("scope", None) == "" else ""
return output
@mcp.tool()
async def search_specs(keywords: str, limit: int, release: str = None, wg: str = None, spec_type: str = None, mode: str = "and"):
"""
Search 3GPP specifications only by their keywords [note that it will only work with keywords](and some filters [see kwargs field])
Arguments available: keywords: string [mandatory, separated by space], limit [mandatory, default = 5], release: string [optional, filter] (the release version (e.g. 18, 9, 19, ...), generally the first number of the full version), wg: string [optional, filter] = working group (S1, C4, SP, ...), spec_type: string [optional, filter] (either TS or TR), mode [mandatory, default = 'and'] = search mode (and = all keywords must be in the search, or = at least one keyword in the search)
"""
endpoint = DOC3GPPFINDER_BASE + "/search-spec"
body = {
"keywords": keywords,
"mode": mode
}
if release is not None:
body['release'] = release
if wg is not None:
body['wg'] = wg
if spec_type is not None:
body['spec_type'] = spec_type
data = await post_data_to_api(endpoint, body)
if isinstance(data, tuple) and data[0] is None:
return f"An error has occured while searching specifications"
results = data['results'][:min(len(data['results'])-1, limit)]
output = []
for spec in results:
x = f"Found specification number {spec['id']} version {spec['release']}"
if spec['scope'] != "":
x += f" where {spec['scope'].lower()}\n"
else:
x += "\n"
output.append(x)
return "-\n".join(output)
@mcp.tool()
async def get_multiple_documents_url(doc_ids: List[str], release: int = None):
"""
[BATCH] Search multiple 3GPP documents (TSG docs, specifications or workshop files) [note that it will only work with document ID] (and release if it's a specification only) and return only their position via a URL
Arguments available: doc_ids: list of string [mandatory], release: integer [optional for every case]
"""
endpoint = DOC3GPPFINDER_BASE + "/batch"
body = {
"doc_ids": doc_ids,
}
if release is not None:
body['release'] = release
data = await post_data_to_api(endpoint, body)
if isinstance(data, tuple) and data[0] is None:
return f"An error while searching publications: {data[1]}"
results = data["results"]
output = []
for doc_id, url in results.items():
output.append(f'The document {doc_id} is available via this URL: {url}\n')
return "-\n".join(output)
# PTT Endpoints
@mcp.tool()
async def search_documents_web(query: str, data_type: str = "web", limit: int = 5):
"""
Search on the Web (thanks to DuckDuckGo) documents based on the user's query
Arguments available: query: string [mandatory], data_type: string [optional, either 'pdf', 'patent' or 'web', default = 'web'], limit: integer [optional, default = 5]
"""
endpoint = DUCKDUCKGO_BASE + "/search"
data = await fake_post_data_to_api(endpoint, {"query": query, "data_type": data_type, 'max_references': limit})
if isinstance(data, tuple) and data[0] is None:
return f"An error while searching publications: {data[1]}"
results = data["results"]
output = []
for ref in results:
output.append(f"Title: {ref['title']}\nBody: {ref['body']}\nURL: {ref['url']}")
return "-\n".join(output)
if __name__ == "__main__":
mcp.run(transport="stdio") |