epitaxy-mcp 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- epitaxy_mcp-0.1.0/LICENSE +21 -0
- epitaxy_mcp-0.1.0/PKG-INFO +90 -0
- epitaxy_mcp-0.1.0/README.md +70 -0
- epitaxy_mcp-0.1.0/epitaxy_mcp.egg-info/PKG-INFO +90 -0
- epitaxy_mcp-0.1.0/epitaxy_mcp.egg-info/SOURCES.txt +10 -0
- epitaxy_mcp-0.1.0/epitaxy_mcp.egg-info/dependency_links.txt +1 -0
- epitaxy_mcp-0.1.0/epitaxy_mcp.egg-info/entry_points.txt +2 -0
- epitaxy_mcp-0.1.0/epitaxy_mcp.egg-info/top_level.txt +1 -0
- epitaxy_mcp-0.1.0/mcp_server/__init__.py +0 -0
- epitaxy_mcp-0.1.0/mcp_server/server.py +186 -0
- epitaxy_mcp-0.1.0/pyproject.toml +35 -0
- epitaxy_mcp-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 epitaxy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: epitaxy-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server for federal drug shortages and recalls joined to federal drug contracts
|
|
5
|
+
Author: epitaxy
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://docs.crossgrain.xyz
|
|
8
|
+
Project-URL: Source, https://github.com/vor-bot/epitaxy
|
|
9
|
+
Project-URL: API, https://drugs.crossgrain.xyz/openapi.json
|
|
10
|
+
Keywords: mcp,model-context-protocol,fda,drug-shortage,recall,usaspending,federal-contracts,procurement,open-data
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# epitaxy
|
|
22
|
+
|
|
23
|
+
mcp-name: xyz.crossgrain/epitaxy
|
|
24
|
+
|
|
25
|
+
Federal drug shortages and recalls, joined to the federal contracts that
|
|
26
|
+
buy those drugs. One call, no signup.
|
|
27
|
+
|
|
28
|
+
**What it claims:** this government supplier has an active FDA shortage
|
|
29
|
+
or recall. **What it does not claim:** that a specific contract supplies
|
|
30
|
+
a specific drug. Federal contract descriptions average 47 characters and
|
|
31
|
+
almost never name the drug, so we say so instead of pretending otherwise.
|
|
32
|
+
|
|
33
|
+
## Try it now, no key
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
curl https://drugs.crossgrain.xyz/v1/exposure?limit=5
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## MCP
|
|
40
|
+
|
|
41
|
+
Remote, nothing to install:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{ "mcpServers": { "epitaxy": { "url": "https://drugs.crossgrain.xyz/v1/mcp" } } }
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Local, stdio:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{ "mcpServers": { "epitaxy": { "command": "uvx", "args": ["epitaxy-mcp"] } } }
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Tools: `get_data_freshness`, `list_drug_shortages`, `list_drug_recalls`,
|
|
54
|
+
`list_federal_drug_contracts`, `get_supplier_exposure`.
|
|
55
|
+
|
|
56
|
+
## Read the confidence field, always
|
|
57
|
+
|
|
58
|
+
| value | meaning |
|
|
59
|
+
| --- | --- |
|
|
60
|
+
| `exact` | match on a shared identifier, here an NDC inside FDA data |
|
|
61
|
+
| `probable` | normalized company name, or a drug name token in the contract text |
|
|
62
|
+
| `weak` | anything softer, an indication only |
|
|
63
|
+
|
|
64
|
+
Matching on both company and drug at once is **not** promoted to
|
|
65
|
+
`exact`. Cross source joins are never `exact`, because FDA and
|
|
66
|
+
USAspending share no identifier. Anyone claiming otherwise has a
|
|
67
|
+
different source or is guessing.
|
|
68
|
+
|
|
69
|
+
## Free tier
|
|
70
|
+
|
|
71
|
+
Works forever, no key, no signup. Returns at most 20 rows, without the
|
|
72
|
+
`evidence` field, without `weak` matches, and with data delayed by 24
|
|
73
|
+
hours. Nothing is stored: no counters, no IP addresses.
|
|
74
|
+
|
|
75
|
+
## Paid
|
|
76
|
+
|
|
77
|
+
| plan | price | contents |
|
|
78
|
+
| --- | --- | --- |
|
|
79
|
+
| Starter | 29 USD/mo | 10 000 calls |
|
|
80
|
+
| Pro | 99 USD/mo | 100 000 calls plus daily delta feed |
|
|
81
|
+
| x402 | 0.004 USD/call | USDC on Base, no signup |
|
|
82
|
+
| Bulk dump | 199 USD | one off, Stripe only |
|
|
83
|
+
|
|
84
|
+
## Sources and licence
|
|
85
|
+
|
|
86
|
+
- openFDA, public domain, CC0 1.0 Universal
|
|
87
|
+
- USAspending, public domain, code under CC0
|
|
88
|
+
|
|
89
|
+
Data is unvalidated and must not be used for medical decisions.
|
|
90
|
+
This project is MIT licensed. Docs: https://docs.crossgrain.xyz
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# epitaxy
|
|
2
|
+
|
|
3
|
+
mcp-name: xyz.crossgrain/epitaxy
|
|
4
|
+
|
|
5
|
+
Federal drug shortages and recalls, joined to the federal contracts that
|
|
6
|
+
buy those drugs. One call, no signup.
|
|
7
|
+
|
|
8
|
+
**What it claims:** this government supplier has an active FDA shortage
|
|
9
|
+
or recall. **What it does not claim:** that a specific contract supplies
|
|
10
|
+
a specific drug. Federal contract descriptions average 47 characters and
|
|
11
|
+
almost never name the drug, so we say so instead of pretending otherwise.
|
|
12
|
+
|
|
13
|
+
## Try it now, no key
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
curl https://drugs.crossgrain.xyz/v1/exposure?limit=5
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## MCP
|
|
20
|
+
|
|
21
|
+
Remote, nothing to install:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{ "mcpServers": { "epitaxy": { "url": "https://drugs.crossgrain.xyz/v1/mcp" } } }
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Local, stdio:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{ "mcpServers": { "epitaxy": { "command": "uvx", "args": ["epitaxy-mcp"] } } }
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Tools: `get_data_freshness`, `list_drug_shortages`, `list_drug_recalls`,
|
|
34
|
+
`list_federal_drug_contracts`, `get_supplier_exposure`.
|
|
35
|
+
|
|
36
|
+
## Read the confidence field, always
|
|
37
|
+
|
|
38
|
+
| value | meaning |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| `exact` | match on a shared identifier, here an NDC inside FDA data |
|
|
41
|
+
| `probable` | normalized company name, or a drug name token in the contract text |
|
|
42
|
+
| `weak` | anything softer, an indication only |
|
|
43
|
+
|
|
44
|
+
Matching on both company and drug at once is **not** promoted to
|
|
45
|
+
`exact`. Cross source joins are never `exact`, because FDA and
|
|
46
|
+
USAspending share no identifier. Anyone claiming otherwise has a
|
|
47
|
+
different source or is guessing.
|
|
48
|
+
|
|
49
|
+
## Free tier
|
|
50
|
+
|
|
51
|
+
Works forever, no key, no signup. Returns at most 20 rows, without the
|
|
52
|
+
`evidence` field, without `weak` matches, and with data delayed by 24
|
|
53
|
+
hours. Nothing is stored: no counters, no IP addresses.
|
|
54
|
+
|
|
55
|
+
## Paid
|
|
56
|
+
|
|
57
|
+
| plan | price | contents |
|
|
58
|
+
| --- | --- | --- |
|
|
59
|
+
| Starter | 29 USD/mo | 10 000 calls |
|
|
60
|
+
| Pro | 99 USD/mo | 100 000 calls plus daily delta feed |
|
|
61
|
+
| x402 | 0.004 USD/call | USDC on Base, no signup |
|
|
62
|
+
| Bulk dump | 199 USD | one off, Stripe only |
|
|
63
|
+
|
|
64
|
+
## Sources and licence
|
|
65
|
+
|
|
66
|
+
- openFDA, public domain, CC0 1.0 Universal
|
|
67
|
+
- USAspending, public domain, code under CC0
|
|
68
|
+
|
|
69
|
+
Data is unvalidated and must not be used for medical decisions.
|
|
70
|
+
This project is MIT licensed. Docs: https://docs.crossgrain.xyz
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: epitaxy-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server for federal drug shortages and recalls joined to federal drug contracts
|
|
5
|
+
Author: epitaxy
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://docs.crossgrain.xyz
|
|
8
|
+
Project-URL: Source, https://github.com/vor-bot/epitaxy
|
|
9
|
+
Project-URL: API, https://drugs.crossgrain.xyz/openapi.json
|
|
10
|
+
Keywords: mcp,model-context-protocol,fda,drug-shortage,recall,usaspending,federal-contracts,procurement,open-data
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# epitaxy
|
|
22
|
+
|
|
23
|
+
mcp-name: xyz.crossgrain/epitaxy
|
|
24
|
+
|
|
25
|
+
Federal drug shortages and recalls, joined to the federal contracts that
|
|
26
|
+
buy those drugs. One call, no signup.
|
|
27
|
+
|
|
28
|
+
**What it claims:** this government supplier has an active FDA shortage
|
|
29
|
+
or recall. **What it does not claim:** that a specific contract supplies
|
|
30
|
+
a specific drug. Federal contract descriptions average 47 characters and
|
|
31
|
+
almost never name the drug, so we say so instead of pretending otherwise.
|
|
32
|
+
|
|
33
|
+
## Try it now, no key
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
curl https://drugs.crossgrain.xyz/v1/exposure?limit=5
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## MCP
|
|
40
|
+
|
|
41
|
+
Remote, nothing to install:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{ "mcpServers": { "epitaxy": { "url": "https://drugs.crossgrain.xyz/v1/mcp" } } }
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Local, stdio:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{ "mcpServers": { "epitaxy": { "command": "uvx", "args": ["epitaxy-mcp"] } } }
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Tools: `get_data_freshness`, `list_drug_shortages`, `list_drug_recalls`,
|
|
54
|
+
`list_federal_drug_contracts`, `get_supplier_exposure`.
|
|
55
|
+
|
|
56
|
+
## Read the confidence field, always
|
|
57
|
+
|
|
58
|
+
| value | meaning |
|
|
59
|
+
| --- | --- |
|
|
60
|
+
| `exact` | match on a shared identifier, here an NDC inside FDA data |
|
|
61
|
+
| `probable` | normalized company name, or a drug name token in the contract text |
|
|
62
|
+
| `weak` | anything softer, an indication only |
|
|
63
|
+
|
|
64
|
+
Matching on both company and drug at once is **not** promoted to
|
|
65
|
+
`exact`. Cross source joins are never `exact`, because FDA and
|
|
66
|
+
USAspending share no identifier. Anyone claiming otherwise has a
|
|
67
|
+
different source or is guessing.
|
|
68
|
+
|
|
69
|
+
## Free tier
|
|
70
|
+
|
|
71
|
+
Works forever, no key, no signup. Returns at most 20 rows, without the
|
|
72
|
+
`evidence` field, without `weak` matches, and with data delayed by 24
|
|
73
|
+
hours. Nothing is stored: no counters, no IP addresses.
|
|
74
|
+
|
|
75
|
+
## Paid
|
|
76
|
+
|
|
77
|
+
| plan | price | contents |
|
|
78
|
+
| --- | --- | --- |
|
|
79
|
+
| Starter | 29 USD/mo | 10 000 calls |
|
|
80
|
+
| Pro | 99 USD/mo | 100 000 calls plus daily delta feed |
|
|
81
|
+
| x402 | 0.004 USD/call | USDC on Base, no signup |
|
|
82
|
+
| Bulk dump | 199 USD | one off, Stripe only |
|
|
83
|
+
|
|
84
|
+
## Sources and licence
|
|
85
|
+
|
|
86
|
+
- openFDA, public domain, CC0 1.0 Universal
|
|
87
|
+
- USAspending, public domain, code under CC0
|
|
88
|
+
|
|
89
|
+
Data is unvalidated and must not be used for medical decisions.
|
|
90
|
+
This project is MIT licensed. Docs: https://docs.crossgrain.xyz
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
epitaxy_mcp.egg-info/PKG-INFO
|
|
5
|
+
epitaxy_mcp.egg-info/SOURCES.txt
|
|
6
|
+
epitaxy_mcp.egg-info/dependency_links.txt
|
|
7
|
+
epitaxy_mcp.egg-info/entry_points.txt
|
|
8
|
+
epitaxy_mcp.egg-info/top_level.txt
|
|
9
|
+
mcp_server/__init__.py
|
|
10
|
+
mcp_server/server.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
mcp_server
|
|
File without changes
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""MCP server nad epitaxy API. Stdio transport, len stdlib.
|
|
2
|
+
|
|
3
|
+
Toto je zároveň distribučný kanál číslo 1 aj 2 z research/distribution.md:
|
|
4
|
+
zápis do MCP registrov a vlastný open source klient. Balík sa spúšťa
|
|
5
|
+
jedným príkazom a funguje bez kľúča, lebo bezplatná vrstva nepotrebuje
|
|
6
|
+
registráciu.
|
|
7
|
+
|
|
8
|
+
Spustenie:
|
|
9
|
+
python3 -m mcp_server.server
|
|
10
|
+
|
|
11
|
+
Premenné:
|
|
12
|
+
EPITAXY_API_URL základ API, predvolene https://api.crossgrain.xyz
|
|
13
|
+
EPITAXY_API_KEY nepovinný, bez neho beží bezplatná vrstva
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
import sys
|
|
19
|
+
import urllib.error
|
|
20
|
+
import urllib.parse
|
|
21
|
+
import urllib.request
|
|
22
|
+
|
|
23
|
+
API_URL = os.environ.get("EPITAXY_API_URL", "https://api.crossgrain.xyz")
|
|
24
|
+
API_KEY = os.environ.get("EPITAXY_API_KEY")
|
|
25
|
+
USER_AGENT = "epitaxy-mcp/0.1"
|
|
26
|
+
PROTOCOL_VERSION = "2025-06-18"
|
|
27
|
+
|
|
28
|
+
TOOLS = [
|
|
29
|
+
{
|
|
30
|
+
"name": "get_data_freshness",
|
|
31
|
+
"description": ("Vrati cas poslednej aktivacie dat, pocet zivych "
|
|
32
|
+
"segmentov a stav posledneho behu. Volaj to prve, "
|
|
33
|
+
"ked chces vediet, ci su data cerstve."),
|
|
34
|
+
"inputSchema": {"type": "object", "properties": {}},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "list_drug_shortages",
|
|
38
|
+
"description": ("Vypadky liekov podla FDA. Filtre: status, "
|
|
39
|
+
"generic_name, company_id, limit."),
|
|
40
|
+
"inputSchema": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"properties": {
|
|
43
|
+
"status": {"type": "string"},
|
|
44
|
+
"generic_name": {"type": "string"},
|
|
45
|
+
"company_id": {"type": "string"},
|
|
46
|
+
"limit": {"type": "integer"},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "list_drug_recalls",
|
|
52
|
+
"description": "Stiahnutia liekov podla FDA. Filtre: classification, company_id, limit.",
|
|
53
|
+
"inputSchema": {
|
|
54
|
+
"type": "object",
|
|
55
|
+
"properties": {
|
|
56
|
+
"classification": {"type": "string"},
|
|
57
|
+
"company_id": {"type": "string"},
|
|
58
|
+
"limit": {"type": "integer"},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "list_federal_drug_contracts",
|
|
64
|
+
"description": ("Federalne kontrakty na lieky, PSC 6505. "
|
|
65
|
+
"Filtre: agency, company_id, limit."),
|
|
66
|
+
"inputSchema": {
|
|
67
|
+
"type": "object",
|
|
68
|
+
"properties": {
|
|
69
|
+
"agency": {"type": "string"},
|
|
70
|
+
"company_id": {"type": "string"},
|
|
71
|
+
"limit": {"type": "integer"},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"name": "get_supplier_exposure",
|
|
77
|
+
"description": (
|
|
78
|
+
"Spojenie kontraktu s vypadkom alebo stiahnutim. KAZDY riadok "
|
|
79
|
+
"nesie confidence exact, probable alebo weak a match_method. "
|
|
80
|
+
"Cross source spojenie NIKDY nie je exact, lebo medzi FDA a "
|
|
81
|
+
"USAspending neexistuje spolocny identifikator. Tvrdenie znie: "
|
|
82
|
+
"tento dodavatel vlady ma u FDA aktivny vypadok alebo "
|
|
83
|
+
"stiahnutie. NETVRDI, ze konkretny kontrakt dodava konkretny "
|
|
84
|
+
"liek. Bez kluca sa vracaju len exact a probable zhody, "
|
|
85
|
+
"najviac 20 riadkov, bez evidence."),
|
|
86
|
+
"inputSchema": {
|
|
87
|
+
"type": "object",
|
|
88
|
+
"properties": {
|
|
89
|
+
"award_id": {"type": "string"},
|
|
90
|
+
"company_id": {"type": "string"},
|
|
91
|
+
"min_confidence": {"type": "string",
|
|
92
|
+
"enum": ["probable", "weak"]},
|
|
93
|
+
"limit": {"type": "integer"},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
TOOL_PATHS = {
|
|
100
|
+
"get_data_freshness": "/v1/meta",
|
|
101
|
+
"list_drug_shortages": "/v1/shortages",
|
|
102
|
+
"list_drug_recalls": "/v1/recalls",
|
|
103
|
+
"list_federal_drug_contracts": "/v1/contracts",
|
|
104
|
+
"get_supplier_exposure": "/v1/exposure",
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def call_api(path, params):
|
|
109
|
+
query = urllib.parse.urlencode(
|
|
110
|
+
dict((k, v) for k, v in params.items() if v not in (None, "")))
|
|
111
|
+
url = "%s%s%s" % (API_URL, path, ("?" + query) if query else "")
|
|
112
|
+
headers = {"User-Agent": USER_AGENT, "Accept": "application/json"}
|
|
113
|
+
if API_KEY:
|
|
114
|
+
headers["X-API-Key"] = API_KEY
|
|
115
|
+
request = urllib.request.Request(url, headers=headers)
|
|
116
|
+
try:
|
|
117
|
+
with urllib.request.urlopen(request, timeout=30) as response:
|
|
118
|
+
return json.loads(response.read().decode("utf-8"))
|
|
119
|
+
except urllib.error.HTTPError as exc:
|
|
120
|
+
body = exc.read().decode("utf-8", "replace")
|
|
121
|
+
try:
|
|
122
|
+
return json.loads(body)
|
|
123
|
+
except ValueError:
|
|
124
|
+
return {"error": "http_%d" % exc.code, "body": body[:500]}
|
|
125
|
+
except Exception as exc:
|
|
126
|
+
return {"error": "network", "message": str(exc)[:200]}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def handle(message):
|
|
130
|
+
method = message.get("method")
|
|
131
|
+
request_id = message.get("id")
|
|
132
|
+
|
|
133
|
+
if method == "initialize":
|
|
134
|
+
return {
|
|
135
|
+
"jsonrpc": "2.0", "id": request_id,
|
|
136
|
+
"result": {
|
|
137
|
+
"protocolVersion": PROTOCOL_VERSION,
|
|
138
|
+
"capabilities": {"tools": {}},
|
|
139
|
+
"serverInfo": {"name": "epitaxy", "version": "0.1.0"},
|
|
140
|
+
"instructions": (
|
|
141
|
+
"Data o vypadkoch a stiahnutiach liekov spojene s "
|
|
142
|
+
"federalnymi kontraktmi. Vzdy citaj pole confidence."),
|
|
143
|
+
},
|
|
144
|
+
}
|
|
145
|
+
if method in ("notifications/initialized", "notifications/cancelled"):
|
|
146
|
+
return None
|
|
147
|
+
if method == "tools/list":
|
|
148
|
+
return {"jsonrpc": "2.0", "id": request_id, "result": {"tools": TOOLS}}
|
|
149
|
+
if method == "tools/call":
|
|
150
|
+
params = message.get("params", {})
|
|
151
|
+
name = params.get("name")
|
|
152
|
+
path = TOOL_PATHS.get(name)
|
|
153
|
+
if not path:
|
|
154
|
+
return {"jsonrpc": "2.0", "id": request_id,
|
|
155
|
+
"error": {"code": -32601, "message": "neznamy nastroj"}}
|
|
156
|
+
result = call_api(path, params.get("arguments") or {})
|
|
157
|
+
return {
|
|
158
|
+
"jsonrpc": "2.0", "id": request_id,
|
|
159
|
+
"result": {
|
|
160
|
+
"content": [{"type": "text",
|
|
161
|
+
"text": json.dumps(result, ensure_ascii=False)}],
|
|
162
|
+
"isError": bool(result.get("error")),
|
|
163
|
+
},
|
|
164
|
+
}
|
|
165
|
+
return {"jsonrpc": "2.0", "id": request_id,
|
|
166
|
+
"error": {"code": -32601, "message": "neznama metoda %s" % method}}
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def main(argv=None):
|
|
170
|
+
for line in sys.stdin:
|
|
171
|
+
line = line.strip()
|
|
172
|
+
if not line:
|
|
173
|
+
continue
|
|
174
|
+
try:
|
|
175
|
+
message = json.loads(line)
|
|
176
|
+
except ValueError:
|
|
177
|
+
continue
|
|
178
|
+
response = handle(message)
|
|
179
|
+
if response is not None:
|
|
180
|
+
sys.stdout.write(json.dumps(response, ensure_ascii=False) + "\n")
|
|
181
|
+
sys.stdout.flush()
|
|
182
|
+
return 0
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
if __name__ == "__main__":
|
|
186
|
+
sys.exit(main())
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "epitaxy-mcp"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "MCP server for federal drug shortages and recalls joined to federal drug contracts"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "epitaxy" }]
|
|
13
|
+
keywords = [
|
|
14
|
+
"mcp", "model-context-protocol", "fda", "drug-shortage", "recall",
|
|
15
|
+
"usaspending", "federal-contracts", "procurement", "open-data",
|
|
16
|
+
]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 4 - Beta",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Topic :: Scientific/Engineering :: Information Analysis",
|
|
23
|
+
]
|
|
24
|
+
dependencies = []
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://docs.crossgrain.xyz"
|
|
28
|
+
Source = "https://github.com/vor-bot/epitaxy"
|
|
29
|
+
API = "https://drugs.crossgrain.xyz/openapi.json"
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
epitaxy-mcp = "mcp_server.server:main"
|
|
33
|
+
|
|
34
|
+
[tool.setuptools]
|
|
35
|
+
packages = ["mcp_server"]
|