File size: 6,689 Bytes
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
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, **kwargs):
    data = dict(kwargs)
    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, **kwargs):
    params = dict(kwargs)
    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'Document ID: {doc_id}\nURL: {data.get("url", "Not found !")}'
    output += f'\nScope: {data["scope"]}' if data.get("scope", None) is not None else ""
    return output
    
@mcp.tool()
async def search_specs(keywords: str, limit: int):
    """
    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]
    Kwargs available [optional]: (release: integer as string or 'Rel-xxx', wg: string = working group (S1, C4, SP, ...), spec_type: string (either TS or TR), mode: string (either 'and' or 'or') = search mode)
    """
    endpoint = DOC3GPPFINDER_BASE + "/search-spec"
    data = await post_data_to_api(endpoint, keywords=keywords)
    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:
        output.append(f"Specification ID: {spec['id']}\nTitle: {spec['title']}\nType: {spec['type']}\nRelease: {spec['release']}\nVersion: {spec['version']}\nWorking Group: {spec['working_group']}\nURL of spec: {spec['url']}\n")
    
    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"
    data = await post_data_to_api(endpoint, doc_ids=doc_ids, release=release)
    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'Document ID: {doc_id}\nURL: {url}\n')
    return "-\n".join(output)

# PTT Endpoints

@mcp.tool()
async def search_documents_web(query: str, data_type: str = None, 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 None (classic web search)], 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")