truthstack-mcp 2.1.0
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.
- package/DISCOVERY-QUESTS.md +160 -0
- package/LICENSE +21 -0
- package/README.md +172 -0
- package/package.json +33 -0
- package/server.js +142 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# TruthStack Discovery Quests — Tier 0 (Distribution)
|
|
2
|
+
## Week 1 Execution Checklist
|
|
3
|
+
|
|
4
|
+
**Philosophy:** The quest isn't just "enrich the database" — it's "go get us discovered."
|
|
5
|
+
Discovery → Adoption → Contribution → Enrichment. You can't skip steps.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Category 1: MCP Directory Submissions
|
|
10
|
+
|
|
11
|
+
Submit TruthStack MCP server to every directory with rich metadata, health/supplement tags,
|
|
12
|
+
example prompts, and a link to the GitHub repo.
|
|
13
|
+
|
|
14
|
+
- [ ] **Official MCP Registry** — registry.modelcontextprotocol.io
|
|
15
|
+
- Tags: health, supplements, drug-interactions, safety, pharmacology
|
|
16
|
+
- Description: "First supplement-drug interaction safety tool for AI agents"
|
|
17
|
+
|
|
18
|
+
- [ ] **GitHub modelcontextprotocol/servers** — Open PR to add to official list
|
|
19
|
+
- Category: Health & Safety
|
|
20
|
+
- Include: description, 5 tools listed, example usage
|
|
21
|
+
|
|
22
|
+
- [ ] **Glama.ai** — MCP server directory
|
|
23
|
+
|
|
24
|
+
- [ ] **MCPServerHub** — Community directory
|
|
25
|
+
|
|
26
|
+
- [ ] **Awesome MCP Servers** — GitHub awesome list (multiple repos)
|
|
27
|
+
- Search GitHub for "awesome-mcp-servers" — submit to all active ones
|
|
28
|
+
|
|
29
|
+
- [ ] **Smithery** — MCP server registry
|
|
30
|
+
|
|
31
|
+
- [ ] **Kong MCP** — If directory exists by launch
|
|
32
|
+
|
|
33
|
+
**Target: 5-7 directory listings in week 1**
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Category 2: Framework Presence
|
|
38
|
+
|
|
39
|
+
Working code examples posted where agent developers find tools.
|
|
40
|
+
|
|
41
|
+
- [ ] **GitHub repo: truthstack-example-health-agent**
|
|
42
|
+
- Claude Desktop config + working demo
|
|
43
|
+
- LangChain tool wrapper with example script
|
|
44
|
+
- README with setup in <5 minutes
|
|
45
|
+
|
|
46
|
+
- [ ] **LangChain community** — Post example in LangChain Discord/forums
|
|
47
|
+
- Title: "Supplement safety tool for health agents"
|
|
48
|
+
- Include working code snippet
|
|
49
|
+
|
|
50
|
+
- [ ] **CrewAI community** — Post example agent that uses TruthStack
|
|
51
|
+
- "Health Safety Agent" that checks stacks before recommending
|
|
52
|
+
|
|
53
|
+
- [ ] **AutoGen/LlamaIndex** — GitHub gist with integration example (week 2)
|
|
54
|
+
|
|
55
|
+
**Target: 3 framework examples published by end of week 2**
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Category 3: GitHub Outreach
|
|
60
|
+
|
|
61
|
+
Find health AI agent repos and suggest TruthStack integration.
|
|
62
|
+
|
|
63
|
+
- [ ] Search GitHub: "health agent" "supplement" — find active repos
|
|
64
|
+
- [ ] Search GitHub: "langchain health" "crewai health" — find health-focused agent projects
|
|
65
|
+
- [ ] Search GitHub: "medication" "drug interaction" "ai agent" — find adjacent projects
|
|
66
|
+
- [ ] For each active repo (>10 stars, recent commits):
|
|
67
|
+
- Open a constructive issue: "Supplement safety integration available"
|
|
68
|
+
- Include: what TruthStack does, how to integrate (3 lines), link to example repo
|
|
69
|
+
- **DO NOT spam.** Only repos where supplement safety is genuinely useful.
|
|
70
|
+
|
|
71
|
+
**Target: 5-10 constructive GitHub issues/PRs opened**
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Category 4: Content Seeding
|
|
76
|
+
|
|
77
|
+
Technical posts that show up in developer searches.
|
|
78
|
+
|
|
79
|
+
- [ ] **Blog post:** "Why your health AI agent needs a real interaction database"
|
|
80
|
+
- Host on truthstack.co/blog or Medium
|
|
81
|
+
- Include the demo contrast (LLM vs API)
|
|
82
|
+
- Target keywords: "supplement drug interaction API", "health AI agent safety"
|
|
83
|
+
|
|
84
|
+
- [ ] **dev.to post:** Technical walkthrough of MCP integration
|
|
85
|
+
- Tags: #ai #health #mcp #agents
|
|
86
|
+
|
|
87
|
+
- [ ] **Reddit r/AI_Agents** — Share the MCP server with context
|
|
88
|
+
- Not promotional — frame as "I built this, feedback welcome"
|
|
89
|
+
|
|
90
|
+
- [ ] **Hacker News** — "Show HN: Supplement safety API for AI agents"
|
|
91
|
+
- Best timing: Tuesday or Wednesday morning US time
|
|
92
|
+
- Lead with the demo contrast (LLM hallucination vs structured API)
|
|
93
|
+
|
|
94
|
+
- [ ] **X/Twitter thread:** "We built the first supplement safety MCP server. Here's why LLMs can't do this alone."
|
|
95
|
+
- Tag: @AnthropicAI, @LangChainAI, MCP community accounts
|
|
96
|
+
- Include screenshot of Claude Desktop using TruthStack tools
|
|
97
|
+
|
|
98
|
+
- [ ] **LinkedIn post:** Same angle, targeting health tech founders and VCs
|
|
99
|
+
|
|
100
|
+
**Target: 3-5 posts published by end of week 2**
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Category 5: Community Infiltration
|
|
105
|
+
|
|
106
|
+
Join where health AI builders already hang out.
|
|
107
|
+
|
|
108
|
+
- [ ] **Health AI Discord servers** — Search Disboard for health AI communities
|
|
109
|
+
- [ ] **LangChain Discord** — #showcase or #tools channel
|
|
110
|
+
- [ ] **Anthropic Discord** — #mcp-servers channel (if exists)
|
|
111
|
+
- [ ] **Digital health Slack groups** — Rock Health, Health 2.0, etc.
|
|
112
|
+
- [ ] **Product Hunt upcoming** — Set up the PH page now, schedule for weeks 8-10
|
|
113
|
+
|
|
114
|
+
**Target: 3 communities joined, at least 1 meaningful post in each**
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Tracking
|
|
119
|
+
|
|
120
|
+
| Quest | Status | Date Completed | Notes |
|
|
121
|
+
|-------|--------|----------------|-------|
|
|
122
|
+
| Official MCP Registry | | | |
|
|
123
|
+
| GitHub MCP servers PR | | | |
|
|
124
|
+
| Glama.ai | | | |
|
|
125
|
+
| MCPServerHub | | | |
|
|
126
|
+
| Awesome MCP lists | | | |
|
|
127
|
+
| Smithery | | | |
|
|
128
|
+
| GitHub example repo | | | |
|
|
129
|
+
| LangChain example | | | |
|
|
130
|
+
| CrewAI example | | | |
|
|
131
|
+
| Blog post | | | |
|
|
132
|
+
| dev.to post | | | |
|
|
133
|
+
| Reddit post | | | |
|
|
134
|
+
| HN Show HN | | | |
|
|
135
|
+
| X thread | | | |
|
|
136
|
+
| LinkedIn post | | | |
|
|
137
|
+
| GitHub outreach (5-10) | | | |
|
|
138
|
+
| Community joins (3+) | | | |
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Week 1 Target: 20 discovery touchpoints
|
|
143
|
+
|
|
144
|
+
If you hit 20 places where a health AI builder can stumble onto TruthStack without
|
|
145
|
+
you doing cold outreach, you've given the flywheel its first real spin.
|
|
146
|
+
|
|
147
|
+
## Phase 2 (Weeks 5-8): Adoption Quests
|
|
148
|
+
Agents already using the API get nudged to:
|
|
149
|
+
- Report unresolved compound names
|
|
150
|
+
- Test edge cases and provide feedback
|
|
151
|
+
- Share their integration publicly
|
|
152
|
+
|
|
153
|
+
## Phase 3 (Weeks 9-12): Enrichment Quests
|
|
154
|
+
Agents contribute real data:
|
|
155
|
+
- New aliases from user input
|
|
156
|
+
- Label scan extractions
|
|
157
|
+
- Stack patterns and outcomes
|
|
158
|
+
- PubMed article summaries
|
|
159
|
+
|
|
160
|
+
**Discovery first. Contribution follows usage.**
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 TruthStack1
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# TruthStack MCP Server
|
|
2
|
+
|
|
3
|
+
**The first supplement-drug interaction safety tool for AI agents.**
|
|
4
|
+
|
|
5
|
+
TruthStack provides structured, evidence-based supplement safety intelligence via the [Model Context Protocol](https://modelcontextprotocol.io). Instead of relying on LLM training data that hallucinates safety information, your agent calls TruthStack for deterministic, cited risk assessments.
|
|
6
|
+
|
|
7
|
+
## Why This Exists
|
|
8
|
+
|
|
9
|
+
LLMs confidently say "ashwagandha is generally safe with sertraline." TruthStack's API returns **MODERATE RISK** with 25 FDA adverse event reports and CYP3A4 pathway conflict data. That gap kills people.
|
|
10
|
+
|
|
11
|
+
## 5 Tools
|
|
12
|
+
|
|
13
|
+
| Tool | What It Does | When to Use |
|
|
14
|
+
|------|-------------|-------------|
|
|
15
|
+
| `check_interactions` | Supplements + medications → risk level, FAERS signals, CYP conflicts | User mentions supplements + meds together |
|
|
16
|
+
| `search_compounds` | Fuzzy name search (584 aliases, handles misspellings/brands) | Need to resolve messy input ("mag gly", "KSM-66") |
|
|
17
|
+
| `get_compound_info` | Full compound profile + all interactions | Deep dive on a specific supplement |
|
|
18
|
+
| `explain_interaction` | Human-readable WHY — mechanism, severity, evidence summary | Need to explain risk to a user |
|
|
19
|
+
| `get_evidence` | Raw evidence: FAERS counts, CYP data, research grades, label warnings | Need to cite sources or provide provenance |
|
|
20
|
+
|
|
21
|
+
## Data Sources
|
|
22
|
+
|
|
23
|
+
- **FDA FAERS** — 805 adverse event signals from real-world pharmacovigilance
|
|
24
|
+
- **FDA Drug Labels** — CYP450 pathways, contraindications, botanical warnings
|
|
25
|
+
- **PubMed/ClinicalTrials.gov** — 220 research findings with evidence grading
|
|
26
|
+
- **584 compound aliases** — misspellings, brand names, abbreviations, product forms
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
### 1. Get API Key
|
|
31
|
+
|
|
32
|
+
Contact [chris@truthstack.co](mailto:chris@truthstack.co) or visit [truthstack.co](https://truthstack.co)
|
|
33
|
+
|
|
34
|
+
### 2. Install
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
git clone https://github.com/truthstack/truthstack-mcp.git
|
|
38
|
+
cd truthstack-mcp
|
|
39
|
+
npm install
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 3. Configure Claude Desktop
|
|
43
|
+
|
|
44
|
+
Edit `~/.claude/claude_desktop_config.json`:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"mcpServers": {
|
|
49
|
+
"truthstack": {
|
|
50
|
+
"command": "node",
|
|
51
|
+
"args": ["/path/to/truthstack-mcp/server.js"],
|
|
52
|
+
"env": {
|
|
53
|
+
"VAULT_API_URL": "https://api.truthstack.co",
|
|
54
|
+
"VAULT_API_KEY": "your-api-key"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Restart Claude Desktop. You'll see TruthStack tools available in the tool picker.
|
|
62
|
+
|
|
63
|
+
### 4. Test It
|
|
64
|
+
|
|
65
|
+
Ask Claude: *"I take ashwagandha, fish oil, and magnesium with sertraline. Is this safe?"*
|
|
66
|
+
|
|
67
|
+
Claude will call `check_interactions` and return a structured risk assessment with FDA adverse event data.
|
|
68
|
+
|
|
69
|
+
## LangChain Integration
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from langchain_community.tools import MCPTool
|
|
73
|
+
|
|
74
|
+
# If using MCP adapter
|
|
75
|
+
truthstack = MCPTool(server_path="./server.js", env={
|
|
76
|
+
"VAULT_API_URL": "https://api.truthstack.co",
|
|
77
|
+
"VAULT_API_KEY": "your-key"
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
# Or call the REST API directly
|
|
81
|
+
import requests
|
|
82
|
+
|
|
83
|
+
response = requests.post(
|
|
84
|
+
"https://api.truthstack.co/api/interactions/check",
|
|
85
|
+
headers={"X-API-Key": "your-key"},
|
|
86
|
+
json={
|
|
87
|
+
"supplements": ["ashwagandha", "fish oil", "magnesium"],
|
|
88
|
+
"medications": ["sertraline"]
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
print(response.json())
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## REST API Fallback
|
|
95
|
+
|
|
96
|
+
Every MCP tool maps to a REST endpoint:
|
|
97
|
+
|
|
98
|
+
| MCP Tool | REST Endpoint |
|
|
99
|
+
|----------|---------------|
|
|
100
|
+
| `check_interactions` | `POST /api/interactions/check` |
|
|
101
|
+
| `search_compounds` | `GET /api/compounds/search?q={query}` |
|
|
102
|
+
| `get_compound_info` | `GET /api/compounds/{id}` + `GET /api/compounds/{id}/interactions` |
|
|
103
|
+
| `explain_interaction` | Composite (search + interactions + drug profile) |
|
|
104
|
+
| `get_evidence` | Composite (search + interactions + drug profile) |
|
|
105
|
+
|
|
106
|
+
Base URL: `https://api.truthstack.co`
|
|
107
|
+
Auth: `X-API-Key` header
|
|
108
|
+
|
|
109
|
+
## Example Response
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
User: "Is ashwagandha safe with sertraline?"
|
|
113
|
+
|
|
114
|
+
→ check_interactions({
|
|
115
|
+
supplements: ["ashwagandha"],
|
|
116
|
+
medications: ["sertraline"]
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
→ {
|
|
120
|
+
"risk_level": "MODERATE",
|
|
121
|
+
"drug_interactions": [{
|
|
122
|
+
"supplement": "ashwagandha",
|
|
123
|
+
"drug": "sertraline",
|
|
124
|
+
"severity": "MODERATE",
|
|
125
|
+
"source": "FDA FAERS",
|
|
126
|
+
"report_count": 25,
|
|
127
|
+
"description": "Pharmacovigilance signal — 25 serious adverse events reported"
|
|
128
|
+
}],
|
|
129
|
+
"cyp_pathway_conflicts": [{
|
|
130
|
+
"drug": "sertraline",
|
|
131
|
+
"supplement": "ashwagandha",
|
|
132
|
+
"shared_cyp_pathways": ["CYP3A4"]
|
|
133
|
+
}]
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Compare to raw LLM: *"Ashwagandha is generally considered safe and may complement sertraline for anxiety."*
|
|
138
|
+
|
|
139
|
+
## System Message Template
|
|
140
|
+
|
|
141
|
+
If you're building a health agent, add this to your system prompt:
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
You have access to TruthStack supplement safety tools. ALWAYS use check_interactions
|
|
145
|
+
before confirming any supplement is safe with a medication. Never rely on your training
|
|
146
|
+
data for drug-supplement interaction safety — use the structured API instead.
|
|
147
|
+
|
|
148
|
+
When reporting results:
|
|
149
|
+
- State the risk level clearly (CRITICAL/HIGH/MODERATE/LOW)
|
|
150
|
+
- Mention specific FAERS adverse event counts when available
|
|
151
|
+
- Note CYP pathway conflicts
|
|
152
|
+
- Always recommend consulting a healthcare provider for MODERATE+ risks
|
|
153
|
+
- Use explain_interaction when users ask "why is this risky?"
|
|
154
|
+
- Use get_evidence when users want to see the sources
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Contributing
|
|
158
|
+
|
|
159
|
+
We're looking for:
|
|
160
|
+
- **Integration partners** — building a health AI agent? Get free API access.
|
|
161
|
+
- **Compound contributions** — know of missing supplement aliases or interactions? Open an issue.
|
|
162
|
+
- **Framework examples** — help us add examples for CrewAI, AutoGen, LlamaIndex.
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT
|
|
167
|
+
|
|
168
|
+
## Contact
|
|
169
|
+
|
|
170
|
+
- **API access**: [chris@truthstack.co](mailto:chris@truthstack.co)
|
|
171
|
+
- **Website**: [truthstack.co](https://truthstack.co)
|
|
172
|
+
- **API docs**: [api.truthstack.co](https://api.truthstack.co)
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "truthstack-mcp",
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": "TruthStack MCP Server \u2014 Supplement-drug interaction safety intelligence for AI agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "server.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"start": "node server.js"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
12
|
+
"zod": "^3.22.0"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"mcp",
|
|
16
|
+
"supplements",
|
|
17
|
+
"drug-interactions",
|
|
18
|
+
"health",
|
|
19
|
+
"safety",
|
|
20
|
+
"ai-agents",
|
|
21
|
+
"truthstack"
|
|
22
|
+
],
|
|
23
|
+
"author": "TruthStack Inc.",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"bin": {
|
|
26
|
+
"truthstack-mcp": "./server.js"
|
|
27
|
+
},
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/TruthStack1/truthstack-mcp.git"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://api.truthstack.co"
|
|
33
|
+
}
|
package/server.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
|
|
6
|
+
const VAULT_API_URL = process.env.VAULT_API_URL || "https://api.truthstack.co";
|
|
7
|
+
const VAULT_API_KEY = process.env.VAULT_API_KEY;
|
|
8
|
+
if (!VAULT_API_KEY) { console.error("ERROR: VAULT_API_KEY required"); process.exit(1); }
|
|
9
|
+
|
|
10
|
+
async function vaultFetch(path, options = {}) {
|
|
11
|
+
const r = await fetch(`${VAULT_API_URL}${path}`, { ...options, headers: { "X-API-Key": VAULT_API_KEY, "Content-Type": "application/json", ...options.headers } });
|
|
12
|
+
if (!r.ok) { const t = await r.text(); throw new Error(`Vault API error ${r.status}: ${t}`); }
|
|
13
|
+
return r.json();
|
|
14
|
+
}
|
|
15
|
+
async function searchCompounds(q, limit = 10) { return vaultFetch(`/api/compounds/search?q=${encodeURIComponent(q)}&limit=${limit}`); }
|
|
16
|
+
async function getCompound(id) { return vaultFetch(`/api/compounds/${encodeURIComponent(id)}`); }
|
|
17
|
+
async function getCompoundInteractions(id) { return vaultFetch(`/api/compounds/${encodeURIComponent(id)}/interactions`); }
|
|
18
|
+
async function checkInteractions(supps, meds) { return vaultFetch("/api/interactions/check", { method: "POST", body: JSON.stringify({ supplements: supps, medications: meds }) }); }
|
|
19
|
+
async function getDrugProfile(id) { return vaultFetch(`/api/drugs/${encodeURIComponent(id)}`); }
|
|
20
|
+
|
|
21
|
+
async function buildExplanation(supplementName, drugName) {
|
|
22
|
+
const sr = await searchCompounds(supplementName, 1);
|
|
23
|
+
const compound = sr?.results?.[0];
|
|
24
|
+
if (!compound) return { supplement: supplementName, drug: drugName, explanation: `Could not resolve "${supplementName}".`, severity: "UNKNOWN", confidence: "low" };
|
|
25
|
+
const compoundId = compound.compound_id;
|
|
26
|
+
const interactions = await getCompoundInteractions(compoundId);
|
|
27
|
+
const di = (interactions?.interactions || []).filter(i => i.target_id?.toLowerCase().includes(drugName.toLowerCase()) || i.target_name?.toLowerCase().includes(drugName.toLowerCase()));
|
|
28
|
+
let drugProfile = null;
|
|
29
|
+
try { drugProfile = await getDrugProfile(drugName.toLowerCase().replace(/\s+/g, "_")); } catch(e) {}
|
|
30
|
+
if (di.length === 0 && !drugProfile) return { supplement: supplementName, drug: drugName, resolved_compound: compoundId, explanation: `No known interactions found between ${compound.name} and ${drugName}. This does not guarantee safety.`, severity: "LOW", confidence: "low", evidence_count: 0 };
|
|
31
|
+
const faers = di.filter(i => i.source_origin === "OPENFDA");
|
|
32
|
+
const research = di.filter(i => i.source_origin !== "OPENFDA");
|
|
33
|
+
const totalFaers = faers.reduce((s, f) => s + (f.metadata?.report_count || 0), 0);
|
|
34
|
+
let severity = "LOW";
|
|
35
|
+
const sevs = di.map(i => i.severity);
|
|
36
|
+
if (sevs.includes("CRITICAL")) severity = "CRITICAL";
|
|
37
|
+
else if (sevs.includes("HIGH") || sevs.includes("MAJOR")) severity = "HIGH";
|
|
38
|
+
else if (sevs.includes("MODERATE")) severity = "MODERATE";
|
|
39
|
+
let cypMech = "";
|
|
40
|
+
if (drugProfile?.profile?.cyp_pathways) {
|
|
41
|
+
const cyps = drugProfile.profile.cyp_pathways;
|
|
42
|
+
const allDrug = [...(cyps.metabolized_by||[]),...(cyps.inhibits||[]),...(cyps.induces||[])];
|
|
43
|
+
const cd = await getCompound(compoundId);
|
|
44
|
+
const compCyps = cd?.compound?.data?.cyp_pathways || [];
|
|
45
|
+
const shared = [...new Set(allDrug.filter(c => compCyps.includes(c)))];
|
|
46
|
+
if (shared.length > 0) cypMech = `Shared CYP450 pathway(s): ${shared.join(", ")}. ${compound.name} may alter how ${drugName} is metabolized.`;
|
|
47
|
+
}
|
|
48
|
+
const parts = [`Interaction between ${compound.name} and ${drugName}:`, `Severity: ${severity}`];
|
|
49
|
+
if (totalFaers > 0) parts.push(`FDA Adverse Event Reports: ${totalFaers} serious report(s) in FAERS.`);
|
|
50
|
+
if (cypMech) parts.push(`Mechanism: ${cypMech}`);
|
|
51
|
+
const mechs = research.map(i => i.mechanism || i.description).filter(Boolean);
|
|
52
|
+
if (mechs.length > 0) parts.push(`Research: ${mechs.join(". ")}`);
|
|
53
|
+
const recs = [...new Set(di.map(i => i.recommendation).filter(Boolean))];
|
|
54
|
+
if (recs.length > 0) parts.push(`Recommendation: ${recs.join(". ")}`);
|
|
55
|
+
return { supplement: supplementName, drug: drugName, resolved_compound: compoundId, compound_name: compound.name, severity, explanation: parts.join("\n\n"), confidence: totalFaers > 10 ? "high" : totalFaers > 0 ? "medium" : "low", evidence_count: di.length, faers_report_count: totalFaers, has_cyp_conflict: cypMech !== "" };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function buildEvidence(supplementName, drugName) {
|
|
59
|
+
const sr = await searchCompounds(supplementName, 1);
|
|
60
|
+
const compound = sr?.results?.[0];
|
|
61
|
+
if (!compound) return { supplement: supplementName, drug: drugName, evidence_items: [], error: `Could not resolve "${supplementName}".` };
|
|
62
|
+
const compoundId = compound.compound_id;
|
|
63
|
+
const interactions = await getCompoundInteractions(compoundId);
|
|
64
|
+
const di = (interactions?.interactions || []).filter(i => i.target_id?.toLowerCase().includes(drugName.toLowerCase()) || i.target_name?.toLowerCase().includes(drugName.toLowerCase()));
|
|
65
|
+
let drugProfile = null;
|
|
66
|
+
try { drugProfile = await getDrugProfile(drugName.toLowerCase().replace(/\s+/g, "_")); } catch(e) {}
|
|
67
|
+
const items = [];
|
|
68
|
+
for (const s of di.filter(i => i.source_origin === "OPENFDA")) {
|
|
69
|
+
items.push({ type: "FAERS_SIGNAL", source: "FDA FAERS", description: `${s.metadata?.report_count || 0} serious adverse event reports`, severity: s.severity, report_count: s.metadata?.report_count || 0, signal_score: s.combined_confidence, caveat: "FAERS reports are voluntary and do not prove causation." });
|
|
70
|
+
}
|
|
71
|
+
for (const f of di.filter(i => i.source_origin !== "OPENFDA")) {
|
|
72
|
+
items.push({ type: "RESEARCH_FINDING", source: f.source_origin || "Research", description: f.mechanism || f.description || "Interaction documented", severity: f.severity, evidence_grade: f.evidence_grade || "not_graded", recommendation: f.recommendation || null });
|
|
73
|
+
}
|
|
74
|
+
if (drugProfile?.profile?.cyp_pathways) {
|
|
75
|
+
const cyps = drugProfile.profile.cyp_pathways;
|
|
76
|
+
const cd = await getCompound(compoundId);
|
|
77
|
+
const compCyps = cd?.compound?.data?.cyp_pathways || [];
|
|
78
|
+
const allDrug = [...(cyps.metabolized_by||[]),...(cyps.inhibits||[]),...(cyps.induces||[])];
|
|
79
|
+
const shared = [...new Set(allDrug.filter(c => compCyps.includes(c)))];
|
|
80
|
+
if (shared.length > 0) items.push({ type: "CYP_PATHWAY_CONFLICT", source: "FDA drug label + compound data", shared_pathways: shared, clinical_significance: "Shared CYP pathways may alter drug metabolism." });
|
|
81
|
+
}
|
|
82
|
+
return { supplement: supplementName, drug: drugName, resolved_compound: compoundId, compound_name: compound.name, evidence_items: items, total_evidence_count: items.length, evidence_types: [...new Set(items.map(e => e.type))] };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const server = new Server({ name: "truthstack", version: "2.1.0" }, { capabilities: { tools: {} } });
|
|
86
|
+
|
|
87
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
88
|
+
tools: [
|
|
89
|
+
{ name: "check_interactions", description: "Check if supplements are safe with medications. Returns risk level, FDA adverse event data, CYP450 conflicts. Use when user mentions supplements + meds.", inputSchema: { type: "object", properties: { supplements: { type: "array", items: { type: "string" }, description: "Supplement names (handles brands, abbreviations, misspellings)" }, medications: { type: "array", items: { type: "string" }, description: "Medication names (generic or brand)" } }, required: ["supplements", "medications"] } },
|
|
90
|
+
{ name: "search_compounds", description: "Fuzzy search for supplement compounds. 584 aliases across 95 compounds. Resolves misspellings, brands, abbreviations.", inputSchema: { type: "object", properties: { query: { type: "string", description: "Search query" }, limit: { type: "number", description: "Max results (default 5)" } }, required: ["query"] } },
|
|
91
|
+
{ name: "get_compound_info", description: "Get detailed compound profile with interactions, research findings, and aliases.", inputSchema: { type: "object", properties: { compound_id: { type: "string", description: "Compound ID from search_compounds" } }, required: ["compound_id"] } },
|
|
92
|
+
{ name: "explain_interaction", description: "Human-readable explanation of WHY an interaction is risky. Returns mechanism, severity, evidence summary.", inputSchema: { type: "object", properties: { supplement: { type: "string", description: "Supplement name" }, drug: { type: "string", description: "Drug name" } }, required: ["supplement", "drug"] } },
|
|
93
|
+
{ name: "get_evidence", description: "Raw evidence bundle: FAERS counts, CYP data, research grades, label warnings. For citing sources.", inputSchema: { type: "object", properties: { supplement: { type: "string", description: "Supplement name" }, drug: { type: "string", description: "Drug name" } }, required: ["supplement", "drug"] } },
|
|
94
|
+
{ name: "get_safety_signals", description: "Get FDA adverse event safety signals (CAERS) for a supplement. Returns PRR analysis, signal strength, category alerts for hepatic/cardiac/bleeding/renal risks.", inputSchema: { type: "object", properties: { compound: { type: "string", description: "Supplement name (e.g. turmeric, kava, green_tea_extract)" } }, required: ["compound"] } },
|
|
95
|
+
]
|
|
96
|
+
}));
|
|
97
|
+
|
|
98
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
99
|
+
const { name, arguments: args = {} } = request.params;
|
|
100
|
+
try {
|
|
101
|
+
let result;
|
|
102
|
+
switch (name) {
|
|
103
|
+
case "check_interactions":
|
|
104
|
+
if (!args.supplements || !args.medications) return { content: [{ type: "text", text: JSON.stringify({ error: "Provide 'supplements' (array) and 'medications' (array)", example: { supplements: ["ashwagandha"], medications: ["sertraline"] } }) }], isError: true };
|
|
105
|
+
result = await checkInteractions(args.supplements, args.medications);
|
|
106
|
+
break;
|
|
107
|
+
case "search_compounds":
|
|
108
|
+
if (!args.query) return { content: [{ type: "text", text: JSON.stringify({ error: "Provide 'query' string" }) }], isError: true };
|
|
109
|
+
result = await searchCompounds(args.query, args.limit || 5);
|
|
110
|
+
break;
|
|
111
|
+
case "get_compound_info":
|
|
112
|
+
if (!args.compound_id) return { content: [{ type: "text", text: JSON.stringify({ error: "Provide 'compound_id'" }) }], isError: true };
|
|
113
|
+
const [comp, ints] = await Promise.all([getCompound(args.compound_id), getCompoundInteractions(args.compound_id)]);
|
|
114
|
+
result = { ...comp, interactions: ints?.interactions || [] };
|
|
115
|
+
break;
|
|
116
|
+
case "explain_interaction":
|
|
117
|
+
if (!args.supplement || !args.drug) return { content: [{ type: "text", text: JSON.stringify({ error: "Provide 'supplement' and 'drug'" }) }], isError: true };
|
|
118
|
+
result = await buildExplanation(args.supplement, args.drug);
|
|
119
|
+
break;
|
|
120
|
+
case "get_evidence":
|
|
121
|
+
if (!args.supplement || !args.drug) return { content: [{ type: "text", text: JSON.stringify({ error: "Provide 'supplement' and 'drug'" }) }], isError: true };
|
|
122
|
+
result = await buildEvidence(args.supplement, args.drug);
|
|
123
|
+
break;
|
|
124
|
+
case "get_safety_signals":
|
|
125
|
+
if (!args.compound) return { content: [{ type: "text", text: JSON.stringify({ error: "Provide compound string" }) }], isError: true };
|
|
126
|
+
result = await vaultFetch("/api/safety/profile/" + encodeURIComponent(args.compound));
|
|
127
|
+
break;
|
|
128
|
+
default:
|
|
129
|
+
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
130
|
+
}
|
|
131
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
132
|
+
} catch (error) {
|
|
133
|
+
return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
async function main() {
|
|
138
|
+
const transport = new StdioServerTransport();
|
|
139
|
+
await server.connect(transport);
|
|
140
|
+
console.error("TruthStack MCP Server v2 running (6 tools, stdio transport)");
|
|
141
|
+
}
|
|
142
|
+
main().catch(e => { console.error("Fatal:", e); process.exit(1); });
|