diffbot-mcp 0.2.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.
- diffbot_mcp-0.2.0/LICENSE +21 -0
- diffbot_mcp-0.2.0/PKG-INFO +233 -0
- diffbot_mcp-0.2.0/README.md +215 -0
- diffbot_mcp-0.2.0/diffbot_mcp/__init__.py +3 -0
- diffbot_mcp-0.2.0/diffbot_mcp/api.py +128 -0
- diffbot_mcp-0.2.0/diffbot_mcp/app.py +11 -0
- diffbot_mcp-0.2.0/diffbot_mcp/auth.py +82 -0
- diffbot_mcp-0.2.0/diffbot_mcp/dql.py +382 -0
- diffbot_mcp-0.2.0/diffbot_mcp/dql_builder.py +223 -0
- diffbot_mcp-0.2.0/diffbot_mcp/schema.py +34 -0
- diffbot_mcp-0.2.0/diffbot_mcp/server.py +22 -0
- diffbot_mcp-0.2.0/diffbot_mcp/tools/__init__.py +1 -0
- diffbot_mcp-0.2.0/diffbot_mcp/tools/crawl.py +140 -0
- diffbot_mcp-0.2.0/diffbot_mcp/tools/deals.py +234 -0
- diffbot_mcp-0.2.0/diffbot_mcp/tools/dql.py +28 -0
- diffbot_mcp-0.2.0/diffbot_mcp/tools/dql_ontology.py +50 -0
- diffbot_mcp-0.2.0/diffbot_mcp/tools/dql_probe.py +18 -0
- diffbot_mcp-0.2.0/diffbot_mcp/tools/extract.py +38 -0
- diffbot_mcp-0.2.0/diffbot_mcp/tools/news.py +121 -0
- diffbot_mcp-0.2.0/diffbot_mcp/tools/organizations.py +191 -0
- diffbot_mcp-0.2.0/diffbot_mcp/tools/people.py +254 -0
- diffbot_mcp-0.2.0/diffbot_mcp/tools/places.py +109 -0
- diffbot_mcp-0.2.0/diffbot_mcp/tools/resolve_entities.py +52 -0
- diffbot_mcp-0.2.0/diffbot_mcp/tools/search_web.py +47 -0
- diffbot_mcp-0.2.0/diffbot_mcp.egg-info/PKG-INFO +233 -0
- diffbot_mcp-0.2.0/diffbot_mcp.egg-info/SOURCES.txt +33 -0
- diffbot_mcp-0.2.0/diffbot_mcp.egg-info/dependency_links.txt +1 -0
- diffbot_mcp-0.2.0/diffbot_mcp.egg-info/entry_points.txt +2 -0
- diffbot_mcp-0.2.0/diffbot_mcp.egg-info/requires.txt +5 -0
- diffbot_mcp-0.2.0/diffbot_mcp.egg-info/top_level.txt +2 -0
- diffbot_mcp-0.2.0/diffbot_mcp_server.py +11 -0
- diffbot_mcp-0.2.0/pyproject.toml +36 -0
- diffbot_mcp-0.2.0/setup.cfg +4 -0
- diffbot_mcp-0.2.0/tests/test_auth.py +148 -0
- diffbot_mcp-0.2.0/tests/test_builder.py +297 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Diffbot
|
|
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,233 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: diffbot-mcp
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A Diffbot MCP server with a variety of helpful web knowledge fetching tools for your agent or LLM pipeline.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/diffbot/diffbot-mcp
|
|
7
|
+
Project-URL: Repository, https://github.com/diffbot/diffbot-mcp
|
|
8
|
+
Project-URL: Issues, https://github.com/diffbot/diffbot-mcp/issues
|
|
9
|
+
Project-URL: Documentation, https://www.diffbot.com/docs/
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: fastmcp~=4.1.0
|
|
14
|
+
Requires-Dist: aiohttp~=3.14.5
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# Diffbot MCP Server
|
|
20
|
+
A Diffbot MCP server with a variety of helpful web knowledge fetching tools for your agent or LLM pipeline.
|
|
21
|
+
|
|
22
|
+
**What is Diffbot?**
|
|
23
|
+
Diffbot is an applied AI lab building models that structure all of human knowledge. Our commercial product suite make it possible to fetch and structure knowledge at the scale of the entire public web.
|
|
24
|
+
|
|
25
|
+
This is the MCP counterpart to [diffbot-skills](https://github.com/diffbot/diffbot-skills) without requiring bash access.
|
|
26
|
+
|
|
27
|
+
## Tools
|
|
28
|
+
|
|
29
|
+
### Web
|
|
30
|
+
|
|
31
|
+
#### 🧬 extract
|
|
32
|
+
**Web fetch tool alternative.** Conventional fetch tools return either 500k+ tokens of HTML or a markdown approximation of the page's content. Extract classifies the page it is fetching and extracts content into a meaningful and consistent JSON schema. Responses are optimized for minimal token usage while maintaining data fidelity. [Learn more.](https://www.diffbot.com/docs/extract/).
|
|
33
|
+
|
|
34
|
+
#### 🔎 search_web
|
|
35
|
+
**Web search tool that ranks accuracy, not popularity.** Built on Diffbot's independent web index. Ranking model heavily favors primary sources over secondary sources (e.g. press release > news piece on said press release). Returns markdown formatted content chunks for source citation, with `num_results` and `max_tokens` controls. [Learn more.](https://www.diffbot.com/docs/web-search/)
|
|
36
|
+
|
|
37
|
+
Also includes a **page cache reader**. Query `url:<URL>` and it returns Diffbot's already-parsed copy of that page from the index in a fraction of the time for live fetch. Use it for full content from reference prose (docs, articles, papers) and reach for `extract` when the page's current state matters.
|
|
38
|
+
|
|
39
|
+
#### 🕷️ crawl
|
|
40
|
+
**Find all links on a web page (the seed) and extract them all.** A classic web crawl without the need for scraping rules. Crawls run as background jobs and can be polled for status. Crawl automatically classifies and structures pages in a standard ontology for easy querying. Use crawl to archive documentation, take snapshots of web projects, or site mapping. [Learn More.](https://www.diffbot.com/docs/crawl/)
|
|
41
|
+
|
|
42
|
+
#### 🧠 resolve_entities
|
|
43
|
+
**Recognize and resolve entities in text.** Uses Diffbot NLP to recognize organizations, people, places, addresses, and other proper nouns in text. Entities are resolved to entity IDs in the Diffbot Knowledge Graph, and the response includes a ready-made `dql_filter` (`id:or(...)`) to fetch their full records with `dql`. Use to keep AI hallucinations in check and explore deep research opportunities. [Learn more.](https://www.diffbot.com/docs/natural-language/)
|
|
44
|
+
|
|
45
|
+
### Knowledge Graph
|
|
46
|
+
|
|
47
|
+
The Diffbot Knowledge Graph contains over 10 billion entities: Organizations, people, news, places, and more, all linked together by their relationship to each other. It is a representation of things, facts, and connections mentioned by the trillions of websites that make up the public web.
|
|
48
|
+
|
|
49
|
+
#### 📰 search_news
|
|
50
|
+
News and articles by mentioned entity, topic, publisher, language, date range, or sentiment. Newest first by default, relevance-ranked inside a date window.
|
|
51
|
+
|
|
52
|
+
#### 🏢 search_organizations
|
|
53
|
+
Companies by industry, headquarters, headcount, revenue, funding, ownership, or leadership, plus `similar_to` lookalike lists for competitor mapping.
|
|
54
|
+
|
|
55
|
+
#### 👤 search_people
|
|
56
|
+
People by title, employer, employer industry, skills, education, location, or nationality. Covers people with a public online professional presence only.
|
|
57
|
+
|
|
58
|
+
#### 🗺️ search_places
|
|
59
|
+
Cities, counties, states, countries, and points of interest by name, population, prominence, containing place, or proximity.
|
|
60
|
+
|
|
61
|
+
#### 💸 search_deals
|
|
62
|
+
Funding rounds, investments, and acquisitions by date, size, series, investor, acquirer, or industry.
|
|
63
|
+
|
|
64
|
+
#### 🎯 dql
|
|
65
|
+
**Raw querying of the Diffbot Knowledge Graph.** The general-purpose layer under the five tools above, for any entity type or query shape they do not cover: products, patents, brands, job posts, cross-entity queries, and facet aggregations. Returns compact csv rows with per-type default columns, or full json records on request.
|
|
66
|
+
|
|
67
|
+
#### 🔬 dql_probe
|
|
68
|
+
**Parallel hit counts for several candidate queries.** The way to check that a query is neither too broad nor too narrow before pulling rows, and to compare the `{}` co-constrained form of a query against the loose one.
|
|
69
|
+
|
|
70
|
+
#### 🧭 dql_ontology
|
|
71
|
+
**Helper utility for accurate dql ontology navigation.** Holds the keys to Diffbot's entire ontology and taxonomy reference: entity types, fields, taxonomies, and enums. A guessed field name is silently ignored by the graph, so this is how the exact spelling gets confirmed. Humans don't need to touch this, but feel free to satisfy your curiosity.
|
|
72
|
+
|
|
73
|
+
## Example Prompts
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
# extract
|
|
77
|
+
→ Install Diffbot agent skills following these instructions https://www.diffbot.com/docs/interfaces/agent-skills
|
|
78
|
+
|
|
79
|
+
# crawl
|
|
80
|
+
→ Crawl Philips' websites for product data sheet PDFs
|
|
81
|
+
|
|
82
|
+
# search_organizations
|
|
83
|
+
→ AI companies based in San Francisco started by previous YC founders
|
|
84
|
+
|
|
85
|
+
# search_deals
|
|
86
|
+
→ recent M&As in pharma
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Authentication
|
|
90
|
+
A free Diffbot token is required for tool use. [Get one here.](https://app.diffbot.com/get-started)
|
|
91
|
+
|
|
92
|
+
MCP clients that support OAuth (Claude, Cursor, VS Code) can connect to `https://mcp.diffbot.com/mcp` without a token and sign in with a Diffbot account instead.
|
|
93
|
+
|
|
94
|
+
Otherwise the server looks for a token in this order:
|
|
95
|
+
|
|
96
|
+
1. `Authorization: Bearer <token>` header or `?token=<token>` query parameter (HTTP transport)
|
|
97
|
+
2. `DIFFBOT_TOKEN` or `DIFFBOT_API_TOKEN` environment variable
|
|
98
|
+
3. A `DIFFBOT_API_TOKEN=<token>` line in `~/.diffbot/credentials`, the same file the [`db` CLI](https://github.com/diffbot/diffbot-python) and diffbot-skills read, so one credential serves all three
|
|
99
|
+
|
|
100
|
+
To enable OAuth on a self-hosted HTTP server, set:
|
|
101
|
+
|
|
102
|
+
| Variable | Example |
|
|
103
|
+
| --- | --- |
|
|
104
|
+
| `DIFFBOT_OAUTH_ISSUER` | `https://app.diffbot.com` |
|
|
105
|
+
| `MCP_BASE_URL` | Public URL of this server, e.g. `https://mcp.diffbot.com` |
|
|
106
|
+
| `DIFFBOT_OAUTH_CLIENT_ID` / `DIFFBOT_OAUTH_CLIENT_SECRET` | Confidential client allowed to call the dashboard's introspection endpoint |
|
|
107
|
+
|
|
108
|
+
OAuth is off when `DIFFBOT_OAUTH_ISSUER` is unset. With `docker compose`, `MCP_BASE_URL` and `DIFFBOT_OAUTH_CLIENT_ID` are set in `docker-compose.yml`; `DIFFBOT_OAUTH_ISSUER` and `DIFFBOT_OAUTH_CLIENT_SECRET` go in a `.env` next to it, which the production deploy writes from the `DIFFBOT_OAUTH_CLIENT_SECRET` GitHub Actions secret. The server runs without it.
|
|
109
|
+
|
|
110
|
+
## Get Started
|
|
111
|
+
|
|
112
|
+
Two ways to connect. The remote server is hosted by Diffbot and deploys from this repo. The local server runs on your machine from PyPI with [uv](https://docs.astral.sh/uv/getting-started/installation/).
|
|
113
|
+
|
|
114
|
+
### Remote server
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
https://mcp.diffbot.com/mcp?token=<YOUR_DIFFBOT_TOKEN>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Local server
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
uvx diffbot-mcp
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`uvx` installs the package into its own environment and runs it. The process reads a token from `DIFFBOT_TOKEN`, `DIFFBOT_API_TOKEN`, or `~/.diffbot/credentials`.
|
|
127
|
+
|
|
128
|
+
### Setup with MCP Clients
|
|
129
|
+
|
|
130
|
+
For a local process, this config works with Claude Desktop and most other MCP clients. Follow the [official installation guide](https://modelcontextprotocol.io/docs/develop/connect-local-servers) for where the file lives.
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"mcpServers": {
|
|
135
|
+
"diffbot-mcp": {
|
|
136
|
+
"command": "uvx",
|
|
137
|
+
"args": ["diffbot-mcp"],
|
|
138
|
+
"env": {
|
|
139
|
+
"DIFFBOT_TOKEN": "<YOUR_DIFFBOT_TOKEN>"
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
To use the remote server instead, point the client at `https://mcp.diffbot.com/mcp?token=<YOUR_DIFFBOT_TOKEN>`.
|
|
147
|
+
|
|
148
|
+
For a Docker install, build the image with `docker build -t diffbot-mcp .` and use this configuration.
|
|
149
|
+
|
|
150
|
+
```json
|
|
151
|
+
{
|
|
152
|
+
"mcpServers": {
|
|
153
|
+
"diffbot-mcp": {
|
|
154
|
+
"command": "docker",
|
|
155
|
+
"args": [
|
|
156
|
+
"run",
|
|
157
|
+
"--rm",
|
|
158
|
+
"-i",
|
|
159
|
+
"-e", "DIFFBOT_TOKEN=<YOUR_DIFFBOT_TOKEN>",
|
|
160
|
+
"diffbot-mcp"
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
<details>
|
|
168
|
+
<summary>Gemini CLI</summary>
|
|
169
|
+
|
|
170
|
+
Requires [uv](https://docs.astral.sh/uv/getting-started/installation/) on `PATH`. The extension starts the server with `uvx`.
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
gemini extensions install https://github.com/diffbot/diffbot-mcp
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Gemini CLI asks for a Diffbot API token on install. Leave it blank to use `~/.diffbot/credentials`.
|
|
177
|
+
</details>
|
|
178
|
+
|
|
179
|
+
<details>
|
|
180
|
+
<summary>Visual Studio Code / Copilot</summary>
|
|
181
|
+
|
|
182
|
+
For remote setup, simply enter the remote server URL and include "http" as the transport type.
|
|
183
|
+
|
|
184
|
+
```json
|
|
185
|
+
{
|
|
186
|
+
"servers": {
|
|
187
|
+
"diffbot-mcp": {
|
|
188
|
+
"type": "http",
|
|
189
|
+
"url": "https://mcp.diffbot.com/mcp/?token=<YOUR_DIFFBOT_TOKEN>"
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
For a local server, this config prompts for the token instead of storing it in the file.
|
|
196
|
+
|
|
197
|
+
```json
|
|
198
|
+
{
|
|
199
|
+
"servers": {
|
|
200
|
+
"diffbot-mcp": {
|
|
201
|
+
"command": "uvx",
|
|
202
|
+
"args": ["diffbot-mcp"],
|
|
203
|
+
"env": {
|
|
204
|
+
"DIFFBOT_TOKEN": "${input:diffbot-api-key}"
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
"inputs": [
|
|
209
|
+
{
|
|
210
|
+
"password": true,
|
|
211
|
+
"id": "diffbot-api-key",
|
|
212
|
+
"type": "promptString",
|
|
213
|
+
"description": "Diffbot API Key"
|
|
214
|
+
}
|
|
215
|
+
]
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
</details>
|
|
219
|
+
|
|
220
|
+
## Development
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
python3 -m venv env && env/bin/pip install -e '.[dev]'
|
|
224
|
+
env/bin/python -m pytest tests/test_builder.py # generated-DQL assertions, no network
|
|
225
|
+
env/bin/python tests/smoke.py # one live call per tool, needs a token
|
|
226
|
+
env/bin/python tests/eval_routing.py # real questions through `claude -p`, asserts tool routing; costs model tokens
|
|
227
|
+
MCP_TRANSPORT=http env/bin/python diffbot_mcp_server.py & ./test.sh # HTTP transport smoke
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
The code lives in `diffbot_mcp/`: `api.py` (token resolution and the shared request helper), `dql.py` (query execution, default columns, the cached ontology), `dql_builder.py` (pure DQL assembly), and one module per tool under `tools/`. `diffbot_mcp_server.py` is the entry point.
|
|
231
|
+
|
|
232
|
+
## Help & Support
|
|
233
|
+
This project is built and maintained by [@jeromechoo](https://github.com/jeromechoo). For support, file an issue and he'll get back to you as soon as he can. For faster answers, write to [sales@diffbot.com](mailto:sales@diffbot.com) 😜.
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# Diffbot MCP Server
|
|
2
|
+
A Diffbot MCP server with a variety of helpful web knowledge fetching tools for your agent or LLM pipeline.
|
|
3
|
+
|
|
4
|
+
**What is Diffbot?**
|
|
5
|
+
Diffbot is an applied AI lab building models that structure all of human knowledge. Our commercial product suite make it possible to fetch and structure knowledge at the scale of the entire public web.
|
|
6
|
+
|
|
7
|
+
This is the MCP counterpart to [diffbot-skills](https://github.com/diffbot/diffbot-skills) without requiring bash access.
|
|
8
|
+
|
|
9
|
+
## Tools
|
|
10
|
+
|
|
11
|
+
### Web
|
|
12
|
+
|
|
13
|
+
#### 🧬 extract
|
|
14
|
+
**Web fetch tool alternative.** Conventional fetch tools return either 500k+ tokens of HTML or a markdown approximation of the page's content. Extract classifies the page it is fetching and extracts content into a meaningful and consistent JSON schema. Responses are optimized for minimal token usage while maintaining data fidelity. [Learn more.](https://www.diffbot.com/docs/extract/).
|
|
15
|
+
|
|
16
|
+
#### 🔎 search_web
|
|
17
|
+
**Web search tool that ranks accuracy, not popularity.** Built on Diffbot's independent web index. Ranking model heavily favors primary sources over secondary sources (e.g. press release > news piece on said press release). Returns markdown formatted content chunks for source citation, with `num_results` and `max_tokens` controls. [Learn more.](https://www.diffbot.com/docs/web-search/)
|
|
18
|
+
|
|
19
|
+
Also includes a **page cache reader**. Query `url:<URL>` and it returns Diffbot's already-parsed copy of that page from the index in a fraction of the time for live fetch. Use it for full content from reference prose (docs, articles, papers) and reach for `extract` when the page's current state matters.
|
|
20
|
+
|
|
21
|
+
#### 🕷️ crawl
|
|
22
|
+
**Find all links on a web page (the seed) and extract them all.** A classic web crawl without the need for scraping rules. Crawls run as background jobs and can be polled for status. Crawl automatically classifies and structures pages in a standard ontology for easy querying. Use crawl to archive documentation, take snapshots of web projects, or site mapping. [Learn More.](https://www.diffbot.com/docs/crawl/)
|
|
23
|
+
|
|
24
|
+
#### 🧠 resolve_entities
|
|
25
|
+
**Recognize and resolve entities in text.** Uses Diffbot NLP to recognize organizations, people, places, addresses, and other proper nouns in text. Entities are resolved to entity IDs in the Diffbot Knowledge Graph, and the response includes a ready-made `dql_filter` (`id:or(...)`) to fetch their full records with `dql`. Use to keep AI hallucinations in check and explore deep research opportunities. [Learn more.](https://www.diffbot.com/docs/natural-language/)
|
|
26
|
+
|
|
27
|
+
### Knowledge Graph
|
|
28
|
+
|
|
29
|
+
The Diffbot Knowledge Graph contains over 10 billion entities: Organizations, people, news, places, and more, all linked together by their relationship to each other. It is a representation of things, facts, and connections mentioned by the trillions of websites that make up the public web.
|
|
30
|
+
|
|
31
|
+
#### 📰 search_news
|
|
32
|
+
News and articles by mentioned entity, topic, publisher, language, date range, or sentiment. Newest first by default, relevance-ranked inside a date window.
|
|
33
|
+
|
|
34
|
+
#### 🏢 search_organizations
|
|
35
|
+
Companies by industry, headquarters, headcount, revenue, funding, ownership, or leadership, plus `similar_to` lookalike lists for competitor mapping.
|
|
36
|
+
|
|
37
|
+
#### 👤 search_people
|
|
38
|
+
People by title, employer, employer industry, skills, education, location, or nationality. Covers people with a public online professional presence only.
|
|
39
|
+
|
|
40
|
+
#### 🗺️ search_places
|
|
41
|
+
Cities, counties, states, countries, and points of interest by name, population, prominence, containing place, or proximity.
|
|
42
|
+
|
|
43
|
+
#### 💸 search_deals
|
|
44
|
+
Funding rounds, investments, and acquisitions by date, size, series, investor, acquirer, or industry.
|
|
45
|
+
|
|
46
|
+
#### 🎯 dql
|
|
47
|
+
**Raw querying of the Diffbot Knowledge Graph.** The general-purpose layer under the five tools above, for any entity type or query shape they do not cover: products, patents, brands, job posts, cross-entity queries, and facet aggregations. Returns compact csv rows with per-type default columns, or full json records on request.
|
|
48
|
+
|
|
49
|
+
#### 🔬 dql_probe
|
|
50
|
+
**Parallel hit counts for several candidate queries.** The way to check that a query is neither too broad nor too narrow before pulling rows, and to compare the `{}` co-constrained form of a query against the loose one.
|
|
51
|
+
|
|
52
|
+
#### 🧭 dql_ontology
|
|
53
|
+
**Helper utility for accurate dql ontology navigation.** Holds the keys to Diffbot's entire ontology and taxonomy reference: entity types, fields, taxonomies, and enums. A guessed field name is silently ignored by the graph, so this is how the exact spelling gets confirmed. Humans don't need to touch this, but feel free to satisfy your curiosity.
|
|
54
|
+
|
|
55
|
+
## Example Prompts
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
# extract
|
|
59
|
+
→ Install Diffbot agent skills following these instructions https://www.diffbot.com/docs/interfaces/agent-skills
|
|
60
|
+
|
|
61
|
+
# crawl
|
|
62
|
+
→ Crawl Philips' websites for product data sheet PDFs
|
|
63
|
+
|
|
64
|
+
# search_organizations
|
|
65
|
+
→ AI companies based in San Francisco started by previous YC founders
|
|
66
|
+
|
|
67
|
+
# search_deals
|
|
68
|
+
→ recent M&As in pharma
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Authentication
|
|
72
|
+
A free Diffbot token is required for tool use. [Get one here.](https://app.diffbot.com/get-started)
|
|
73
|
+
|
|
74
|
+
MCP clients that support OAuth (Claude, Cursor, VS Code) can connect to `https://mcp.diffbot.com/mcp` without a token and sign in with a Diffbot account instead.
|
|
75
|
+
|
|
76
|
+
Otherwise the server looks for a token in this order:
|
|
77
|
+
|
|
78
|
+
1. `Authorization: Bearer <token>` header or `?token=<token>` query parameter (HTTP transport)
|
|
79
|
+
2. `DIFFBOT_TOKEN` or `DIFFBOT_API_TOKEN` environment variable
|
|
80
|
+
3. A `DIFFBOT_API_TOKEN=<token>` line in `~/.diffbot/credentials`, the same file the [`db` CLI](https://github.com/diffbot/diffbot-python) and diffbot-skills read, so one credential serves all three
|
|
81
|
+
|
|
82
|
+
To enable OAuth on a self-hosted HTTP server, set:
|
|
83
|
+
|
|
84
|
+
| Variable | Example |
|
|
85
|
+
| --- | --- |
|
|
86
|
+
| `DIFFBOT_OAUTH_ISSUER` | `https://app.diffbot.com` |
|
|
87
|
+
| `MCP_BASE_URL` | Public URL of this server, e.g. `https://mcp.diffbot.com` |
|
|
88
|
+
| `DIFFBOT_OAUTH_CLIENT_ID` / `DIFFBOT_OAUTH_CLIENT_SECRET` | Confidential client allowed to call the dashboard's introspection endpoint |
|
|
89
|
+
|
|
90
|
+
OAuth is off when `DIFFBOT_OAUTH_ISSUER` is unset. With `docker compose`, `MCP_BASE_URL` and `DIFFBOT_OAUTH_CLIENT_ID` are set in `docker-compose.yml`; `DIFFBOT_OAUTH_ISSUER` and `DIFFBOT_OAUTH_CLIENT_SECRET` go in a `.env` next to it, which the production deploy writes from the `DIFFBOT_OAUTH_CLIENT_SECRET` GitHub Actions secret. The server runs without it.
|
|
91
|
+
|
|
92
|
+
## Get Started
|
|
93
|
+
|
|
94
|
+
Two ways to connect. The remote server is hosted by Diffbot and deploys from this repo. The local server runs on your machine from PyPI with [uv](https://docs.astral.sh/uv/getting-started/installation/).
|
|
95
|
+
|
|
96
|
+
### Remote server
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
https://mcp.diffbot.com/mcp?token=<YOUR_DIFFBOT_TOKEN>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Local server
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
uvx diffbot-mcp
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`uvx` installs the package into its own environment and runs it. The process reads a token from `DIFFBOT_TOKEN`, `DIFFBOT_API_TOKEN`, or `~/.diffbot/credentials`.
|
|
109
|
+
|
|
110
|
+
### Setup with MCP Clients
|
|
111
|
+
|
|
112
|
+
For a local process, this config works with Claude Desktop and most other MCP clients. Follow the [official installation guide](https://modelcontextprotocol.io/docs/develop/connect-local-servers) for where the file lives.
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"mcpServers": {
|
|
117
|
+
"diffbot-mcp": {
|
|
118
|
+
"command": "uvx",
|
|
119
|
+
"args": ["diffbot-mcp"],
|
|
120
|
+
"env": {
|
|
121
|
+
"DIFFBOT_TOKEN": "<YOUR_DIFFBOT_TOKEN>"
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
To use the remote server instead, point the client at `https://mcp.diffbot.com/mcp?token=<YOUR_DIFFBOT_TOKEN>`.
|
|
129
|
+
|
|
130
|
+
For a Docker install, build the image with `docker build -t diffbot-mcp .` and use this configuration.
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"mcpServers": {
|
|
135
|
+
"diffbot-mcp": {
|
|
136
|
+
"command": "docker",
|
|
137
|
+
"args": [
|
|
138
|
+
"run",
|
|
139
|
+
"--rm",
|
|
140
|
+
"-i",
|
|
141
|
+
"-e", "DIFFBOT_TOKEN=<YOUR_DIFFBOT_TOKEN>",
|
|
142
|
+
"diffbot-mcp"
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
<details>
|
|
150
|
+
<summary>Gemini CLI</summary>
|
|
151
|
+
|
|
152
|
+
Requires [uv](https://docs.astral.sh/uv/getting-started/installation/) on `PATH`. The extension starts the server with `uvx`.
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
gemini extensions install https://github.com/diffbot/diffbot-mcp
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Gemini CLI asks for a Diffbot API token on install. Leave it blank to use `~/.diffbot/credentials`.
|
|
159
|
+
</details>
|
|
160
|
+
|
|
161
|
+
<details>
|
|
162
|
+
<summary>Visual Studio Code / Copilot</summary>
|
|
163
|
+
|
|
164
|
+
For remote setup, simply enter the remote server URL and include "http" as the transport type.
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"servers": {
|
|
169
|
+
"diffbot-mcp": {
|
|
170
|
+
"type": "http",
|
|
171
|
+
"url": "https://mcp.diffbot.com/mcp/?token=<YOUR_DIFFBOT_TOKEN>"
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
For a local server, this config prompts for the token instead of storing it in the file.
|
|
178
|
+
|
|
179
|
+
```json
|
|
180
|
+
{
|
|
181
|
+
"servers": {
|
|
182
|
+
"diffbot-mcp": {
|
|
183
|
+
"command": "uvx",
|
|
184
|
+
"args": ["diffbot-mcp"],
|
|
185
|
+
"env": {
|
|
186
|
+
"DIFFBOT_TOKEN": "${input:diffbot-api-key}"
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
"inputs": [
|
|
191
|
+
{
|
|
192
|
+
"password": true,
|
|
193
|
+
"id": "diffbot-api-key",
|
|
194
|
+
"type": "promptString",
|
|
195
|
+
"description": "Diffbot API Key"
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
</details>
|
|
201
|
+
|
|
202
|
+
## Development
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
python3 -m venv env && env/bin/pip install -e '.[dev]'
|
|
206
|
+
env/bin/python -m pytest tests/test_builder.py # generated-DQL assertions, no network
|
|
207
|
+
env/bin/python tests/smoke.py # one live call per tool, needs a token
|
|
208
|
+
env/bin/python tests/eval_routing.py # real questions through `claude -p`, asserts tool routing; costs model tokens
|
|
209
|
+
MCP_TRANSPORT=http env/bin/python diffbot_mcp_server.py & ./test.sh # HTTP transport smoke
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The code lives in `diffbot_mcp/`: `api.py` (token resolution and the shared request helper), `dql.py` (query execution, default columns, the cached ontology), `dql_builder.py` (pure DQL assembly), and one module per tool under `tools/`. `diffbot_mcp_server.py` is the entry point.
|
|
213
|
+
|
|
214
|
+
## Help & Support
|
|
215
|
+
This project is built and maintained by [@jeromechoo](https://github.com/jeromechoo). For support, file an issue and he'll get back to you as soon as he can. For faster answers, write to [sales@diffbot.com](mailto:sales@diffbot.com) 😜.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Token resolution and the shared HTTP helper behind every Diffbot API call."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import pathlib
|
|
7
|
+
from typing import Any, Optional
|
|
8
|
+
|
|
9
|
+
import aiohttp
|
|
10
|
+
from fastmcp.exceptions import ToolError
|
|
11
|
+
from fastmcp.server.dependencies import get_access_token, get_http_request
|
|
12
|
+
|
|
13
|
+
# DIFFBOT_TOKEN is this server's historical name; DIFFBOT_API_TOKEN and the credentials
|
|
14
|
+
# file are what the db CLI and diffbot-skills read, so one credential serves both.
|
|
15
|
+
TOKEN_ENV_VARS = ("DIFFBOT_TOKEN", "DIFFBOT_API_TOKEN")
|
|
16
|
+
CREDENTIALS_PATH = pathlib.Path.home() / ".diffbot" / "credentials"
|
|
17
|
+
CREDENTIALS_KEY = "DIFFBOT_API_TOKEN"
|
|
18
|
+
|
|
19
|
+
REQUEST_TIMEOUT_SECONDS = 120
|
|
20
|
+
|
|
21
|
+
MISSING_TOKEN = (
|
|
22
|
+
"No Diffbot token found. On the HTTP transport pass it as 'Authorization: Bearer <token>' "
|
|
23
|
+
"or '?token=<token>'; otherwise set DIFFBOT_TOKEN or DIFFBOT_API_TOKEN in the environment, "
|
|
24
|
+
f"or write '{CREDENTIALS_KEY}=<token>' to {CREDENTIALS_PATH}. "
|
|
25
|
+
"Get a free token at https://app.diffbot.com/get-started"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _credentials_file_token() -> str:
|
|
30
|
+
try:
|
|
31
|
+
lines = CREDENTIALS_PATH.read_text().splitlines()
|
|
32
|
+
except OSError:
|
|
33
|
+
return ""
|
|
34
|
+
for line in lines:
|
|
35
|
+
line = line.strip()
|
|
36
|
+
if line.startswith(f"{CREDENTIALS_KEY}="):
|
|
37
|
+
return line.split("=", 1)[1].strip().strip("'\"")
|
|
38
|
+
return ""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def resolve_token() -> str:
|
|
42
|
+
"""Per-request credentials first, then the environment, then the credentials file."""
|
|
43
|
+
try:
|
|
44
|
+
request = get_http_request()
|
|
45
|
+
except RuntimeError:
|
|
46
|
+
request = None # stdio transport: no request in flight
|
|
47
|
+
|
|
48
|
+
if request is not None:
|
|
49
|
+
# Set when OAuth is on: the verified token resolves to the user's Diffbot API token
|
|
50
|
+
access_token = get_access_token()
|
|
51
|
+
if access_token is not None and access_token.claims.get("diffbot_token"):
|
|
52
|
+
return access_token.claims["diffbot_token"]
|
|
53
|
+
auth_header = request.headers.get("Authorization", "")
|
|
54
|
+
if auth_header.lower().startswith("bearer "):
|
|
55
|
+
return auth_header[7:].strip()
|
|
56
|
+
token = request.query_params.get("token")
|
|
57
|
+
if token:
|
|
58
|
+
return token
|
|
59
|
+
|
|
60
|
+
for var in TOKEN_ENV_VARS:
|
|
61
|
+
token = os.getenv(var, "").strip()
|
|
62
|
+
if token:
|
|
63
|
+
return token
|
|
64
|
+
|
|
65
|
+
token = _credentials_file_token()
|
|
66
|
+
if token:
|
|
67
|
+
return token
|
|
68
|
+
|
|
69
|
+
raise ToolError(MISSING_TOKEN)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
async def _error_message(response: aiohttp.ClientResponse) -> str:
|
|
73
|
+
body = await response.text()
|
|
74
|
+
message: Optional[str] = None
|
|
75
|
+
try:
|
|
76
|
+
data = json.loads(body)
|
|
77
|
+
if isinstance(data, dict):
|
|
78
|
+
message = data.get("message") or data.get("error")
|
|
79
|
+
except ValueError:
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
if response.status in (401, 403):
|
|
83
|
+
return "Invalid or unauthorized Diffbot token" + (f": {message}" if message else "")
|
|
84
|
+
if response.status == 429:
|
|
85
|
+
retry_after = response.headers.get("Retry-After")
|
|
86
|
+
return "Diffbot rate limit exceeded" + (f", retry after {retry_after}s" if retry_after else "")
|
|
87
|
+
if not message:
|
|
88
|
+
message = body[:200] + ("..." if len(body) > 200 else "")
|
|
89
|
+
return f"Diffbot API error {response.status}: {message}"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def clean_params(params: dict) -> dict:
|
|
93
|
+
"""Drop None values and render booleans as the 1/0 the Diffbot APIs expect."""
|
|
94
|
+
cleaned = {}
|
|
95
|
+
for key, value in params.items():
|
|
96
|
+
if value is None:
|
|
97
|
+
continue
|
|
98
|
+
if isinstance(value, bool):
|
|
99
|
+
value = 1 if value else 0
|
|
100
|
+
cleaned[key] = value
|
|
101
|
+
return cleaned
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
async def request(
|
|
105
|
+
url: str,
|
|
106
|
+
*,
|
|
107
|
+
params: Optional[dict] = None,
|
|
108
|
+
json_body: Any = None,
|
|
109
|
+
headers: Optional[dict] = None,
|
|
110
|
+
as_text: bool = False,
|
|
111
|
+
method: str = "GET",
|
|
112
|
+
) -> Any:
|
|
113
|
+
"""One Diffbot API call. Non-2xx responses become a ToolError carrying the API's own message."""
|
|
114
|
+
timeout = aiohttp.ClientTimeout(total=REQUEST_TIMEOUT_SECONDS)
|
|
115
|
+
try:
|
|
116
|
+
async with aiohttp.ClientSession(timeout=timeout) as session:
|
|
117
|
+
async with session.request(
|
|
118
|
+
method, url, params=clean_params(params or {}), json=json_body, headers=headers
|
|
119
|
+
) as response:
|
|
120
|
+
if response.status >= 400:
|
|
121
|
+
raise ToolError(await _error_message(response))
|
|
122
|
+
if as_text:
|
|
123
|
+
return await response.text()
|
|
124
|
+
return await response.json(content_type=None)
|
|
125
|
+
except asyncio.TimeoutError:
|
|
126
|
+
raise ToolError(f"Diffbot request timed out after {REQUEST_TIMEOUT_SECONDS}s: {url}")
|
|
127
|
+
except aiohttp.ClientError as error:
|
|
128
|
+
raise ToolError(f"Diffbot request failed: {error}")
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""The FastMCP instance. Kept in its own module so tool modules can import it without a cycle."""
|
|
2
|
+
|
|
3
|
+
from fastmcp import FastMCP
|
|
4
|
+
from mcp.types import ToolAnnotations
|
|
5
|
+
|
|
6
|
+
from .auth import build_auth
|
|
7
|
+
|
|
8
|
+
mcp = FastMCP(name="Diffbot MCP Server", auth=build_auth())
|
|
9
|
+
|
|
10
|
+
# Every tool except crawl only reads from Diffbot's APIs or the open web
|
|
11
|
+
READ_ONLY = ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=True)
|