@houtini/google-knowledge-graph-mcp 1.0.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/.env.example +13 -0
- package/LICENSE +21 -0
- package/README.md +259 -0
- package/dist/client.d.ts +37 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +85 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +166 -0
- package/dist/index.js.map +1 -0
- package/package.json +56 -0
- package/src/client.ts +128 -0
- package/src/index.ts +186 -0
package/.env.example
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Google Knowledge Graph Search MCP Configuration
|
|
2
|
+
|
|
3
|
+
# Required: Your Google Cloud API key
|
|
4
|
+
# Get one free at: https://console.cloud.google.com/apis/credentials
|
|
5
|
+
# 1. Create/select a project
|
|
6
|
+
# 2. Enable "Knowledge Graph Search API" (not Enterprise API)
|
|
7
|
+
# 3. Create credentials -> API key
|
|
8
|
+
# No billing account required!
|
|
9
|
+
|
|
10
|
+
GOOGLE_KNOWLEDGE_GRAPH_API_KEY=your_api_key_here
|
|
11
|
+
|
|
12
|
+
# Alternative variable name (either works)
|
|
13
|
+
# GOOGLE_CLOUD_API_KEY=your_api_key_here
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Richard Baxter / Houtini
|
|
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,259 @@
|
|
|
1
|
+
# Google Knowledge Graph Search MCP
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@houtini/google-knowledge-graph-mcp)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://modelcontextprotocol.io)
|
|
6
|
+
|
|
7
|
+
Model Context Protocol server that connects Claude (or any MCP client) to Google's free public Knowledge Graph API. Search for real-world entities - people, places, organisations, concepts - and get structured data back.
|
|
8
|
+
|
|
9
|
+
**What is this?** An MCP server. If you don't know what that means, you probably don't need this. If you're using Claude Desktop or another MCP-compatible client and want to search Google's knowledge database, this is for you.
|
|
10
|
+
|
|
11
|
+
## Why This Exists
|
|
12
|
+
|
|
13
|
+
I built this because I needed a way for Claude to verify entity information during research workflows. Google's Knowledge Graph contains structured data about millions of real-world entities - the same data that powers those knowledge panels in Google Search results.
|
|
14
|
+
|
|
15
|
+
The Knowledge Graph Search API is completely free. No billing account, no usage costs, just a Google Cloud API key. Most developers don't seem to know this exists, which is odd given how useful it is.
|
|
16
|
+
|
|
17
|
+
This MCP gives Claude (or any MCP client) access to that database.
|
|
18
|
+
|
|
19
|
+
## What You Get
|
|
20
|
+
|
|
21
|
+
Two tools for searching Google's knowledge graph:
|
|
22
|
+
|
|
23
|
+
**1. Search by query** - `search_knowledge_graph`
|
|
24
|
+
Search for entities by name or description. Returns structured data including entity types, descriptions, Wikipedia URLs, and relevance scores.
|
|
25
|
+
|
|
26
|
+
**2. Lookup by MID** - `lookup_knowledge_graph_entities`
|
|
27
|
+
If you already have Machine IDs (Google's internal entity identifiers), look them up directly. Useful for entity resolution workflows.
|
|
28
|
+
|
|
29
|
+
Both return JSON with:
|
|
30
|
+
- Entity names and types
|
|
31
|
+
- Detailed descriptions (usually from Wikipedia)
|
|
32
|
+
- Official images and URLs
|
|
33
|
+
- Result scores (relevance ranking)
|
|
34
|
+
- Machine IDs for further lookups
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
### NPX (Easiest)
|
|
39
|
+
|
|
40
|
+
Add this to your `claude_desktop_config.json`:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"mcpServers": {
|
|
45
|
+
"google-knowledge-graph": {
|
|
46
|
+
"command": "npx",
|
|
47
|
+
"args": ["-y", "@houtini/google-knowledge-graph-mcp"],
|
|
48
|
+
"env": {
|
|
49
|
+
"GOOGLE_KNOWLEDGE_GRAPH_API_KEY": "your-api-key-here"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Local Install
|
|
57
|
+
|
|
58
|
+
If you prefer running from source:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
git clone https://github.com/houtini/google-knowledge-graph-search-mcp.git
|
|
62
|
+
cd google-knowledge-graph-search-mcp
|
|
63
|
+
npm install
|
|
64
|
+
npm run build
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Then configure Claude Desktop:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"mcpServers": {
|
|
72
|
+
"google-knowledge-graph": {
|
|
73
|
+
"command": "node",
|
|
74
|
+
"args": ["C:\\path\\to\\google-knowledge-graph-search-mcp\\dist\\index.js"],
|
|
75
|
+
"env": {
|
|
76
|
+
"GOOGLE_KNOWLEDGE_GRAPH_API_KEY": "your-api-key-here"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Windows users:** Use double backslashes in paths: `C:\\MCP\\...`
|
|
84
|
+
|
|
85
|
+
**Config location:**
|
|
86
|
+
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
87
|
+
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
88
|
+
- Linux: `~/.config/Claude/claude_desktop_config.json`
|
|
89
|
+
|
|
90
|
+
## Getting Your API Key
|
|
91
|
+
|
|
92
|
+
Google's Knowledge Graph Search API is free. Genuinely free - no billing account required.
|
|
93
|
+
|
|
94
|
+
1. Go to [Google Cloud Console](https://console.cloud.google.com/)
|
|
95
|
+
2. Create a new project (or select an existing one)
|
|
96
|
+
3. Enable "Knowledge Graph Search API" in the API Library
|
|
97
|
+
4. Navigate to "Credentials" and create an API key
|
|
98
|
+
5. (Optional but recommended) Restrict the key to Knowledge Graph Search API only
|
|
99
|
+
|
|
100
|
+
That's it. No credit card, no billing setup.
|
|
101
|
+
|
|
102
|
+
## Usage Examples
|
|
103
|
+
|
|
104
|
+
Once installed and Claude Desktop is restarted, you can:
|
|
105
|
+
|
|
106
|
+
**Basic entity search:**
|
|
107
|
+
```
|
|
108
|
+
Search the knowledge graph for "Marie Curie"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Entity type filtering:**
|
|
112
|
+
```
|
|
113
|
+
Search knowledge graph for "Python" with types ["ComputerLanguage"]
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Multiple results:**
|
|
117
|
+
```
|
|
118
|
+
Search knowledge graph for "Paris" limit 5
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Lookup by MID:**
|
|
122
|
+
```
|
|
123
|
+
Look up knowledge graph entity /m/0dl567
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The MCP returns structured JSON that Claude can parse. You'll get entity names, types, descriptions, URLs, and relevance scores.
|
|
127
|
+
|
|
128
|
+
## What Gets Returned
|
|
129
|
+
|
|
130
|
+
Example response structure:
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"entities": [
|
|
135
|
+
{
|
|
136
|
+
"mid": "/m/0dl567",
|
|
137
|
+
"name": "Taylor Swift",
|
|
138
|
+
"type": ["Person", "Thing"],
|
|
139
|
+
"description": "American singer-songwriter",
|
|
140
|
+
"detailedDescription": "Taylor Alison Swift is an American singer-songwriter...",
|
|
141
|
+
"image": "https://...",
|
|
142
|
+
"url": "http://en.wikipedia.org/wiki/Taylor_Swift",
|
|
143
|
+
"resultScore": 4258.07
|
|
144
|
+
}
|
|
145
|
+
],
|
|
146
|
+
"count": 1
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Parameters
|
|
151
|
+
|
|
152
|
+
### search_knowledge_graph
|
|
153
|
+
|
|
154
|
+
- **query** (required): Search term
|
|
155
|
+
- **languages** (optional): Language codes array, e.g. `["en"]`
|
|
156
|
+
- **types** (optional): Entity types to filter by, e.g. `["Person", "Organization"]`
|
|
157
|
+
- **limit** (optional): Max results (default 20, max 500)
|
|
158
|
+
|
|
159
|
+
### lookup_knowledge_graph_entities
|
|
160
|
+
|
|
161
|
+
- **ids** (required): Array of Machine IDs (MIDs), e.g. `["/m/0dl567"]`
|
|
162
|
+
- **languages** (optional): Language codes array
|
|
163
|
+
|
|
164
|
+
## Common Entity Types
|
|
165
|
+
|
|
166
|
+
The Knowledge Graph uses schema.org types. Common ones:
|
|
167
|
+
|
|
168
|
+
- `Person` - Individual people
|
|
169
|
+
- `Organization` - Companies, institutions
|
|
170
|
+
- `Place` - Locations, geographical entities
|
|
171
|
+
- `Event` - Historical or current events
|
|
172
|
+
- `CreativeWork` - Books, films, music, art
|
|
173
|
+
- `Product` - Commercial products
|
|
174
|
+
- `ComputerLanguage` - Programming languages
|
|
175
|
+
- `SportsTeam` - Sports teams
|
|
176
|
+
- `Country` - Nations and countries
|
|
177
|
+
- `City` - Cities and municipalities
|
|
178
|
+
|
|
179
|
+
You can combine types for more specific searches.
|
|
180
|
+
|
|
181
|
+
## Troubleshooting
|
|
182
|
+
|
|
183
|
+
**MCP not appearing in Claude:**
|
|
184
|
+
1. Check your JSON syntax - one error breaks everything
|
|
185
|
+
2. Verify the path uses correct escaping (`\\` on Windows)
|
|
186
|
+
3. Completely restart Claude Desktop (quit, not just minimise)
|
|
187
|
+
4. Check the API key environment variable spelling
|
|
188
|
+
|
|
189
|
+
**"API key required" error:**
|
|
190
|
+
- The environment variable isn't being read
|
|
191
|
+
- Check spelling: `GOOGLE_KNOWLEDGE_GRAPH_API_KEY`
|
|
192
|
+
- Restart Claude Desktop after config changes
|
|
193
|
+
|
|
194
|
+
**No results returned:**
|
|
195
|
+
- Try different query terms
|
|
196
|
+
- Remove entity type filters to broaden search
|
|
197
|
+
- Check result limit isn't set too low
|
|
198
|
+
|
|
199
|
+
**401 Unauthorized:**
|
|
200
|
+
- API key is invalid or expired
|
|
201
|
+
- Knowledge Graph Search API isn't enabled in your Google Cloud project
|
|
202
|
+
|
|
203
|
+
## Building From Source
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
npm install
|
|
207
|
+
npm run build
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
The build process compiles TypeScript to CommonJS in `dist/`. No special configuration needed.
|
|
211
|
+
|
|
212
|
+
## Technical Details
|
|
213
|
+
|
|
214
|
+
- **API Endpoint:** `https://kgsearch.googleapis.com/v1/entities:search`
|
|
215
|
+
- **Response Format:** JSON-LD with `itemListElement` array
|
|
216
|
+
- **Authentication:** API key via query parameter
|
|
217
|
+
- **Rate Limits:** Free tier quotas apply (generally 100,000 queries/day)
|
|
218
|
+
- **Module Format:** CommonJS (compatible with Node.js MCP hosts)
|
|
219
|
+
|
|
220
|
+
## Why CommonJS?
|
|
221
|
+
|
|
222
|
+
The MCP SDK uses CommonJS patterns. I've stuck with that for compatibility. If you're building your own MCP and want ES modules, that's fine - just different choices.
|
|
223
|
+
|
|
224
|
+
## Contributing
|
|
225
|
+
|
|
226
|
+
If you find issues or have improvements:
|
|
227
|
+
|
|
228
|
+
1. Check existing issues first
|
|
229
|
+
2. Test your changes locally
|
|
230
|
+
3. Submit a PR with clear description
|
|
231
|
+
|
|
232
|
+
I'm particularly interested in hearing about:
|
|
233
|
+
- Entity types that need better handling
|
|
234
|
+
- Response parsing edge cases
|
|
235
|
+
- Real-world usage patterns
|
|
236
|
+
|
|
237
|
+
## Licence
|
|
238
|
+
|
|
239
|
+
MIT - do what you want with it.
|
|
240
|
+
|
|
241
|
+
## Author
|
|
242
|
+
|
|
243
|
+
Built by Richard Baxter ([Houtini](https://houtini.ai)) as part of a collection of MCP servers for AI-assisted development and research workflows.
|
|
244
|
+
|
|
245
|
+
**Other Houtini MCPs:**
|
|
246
|
+
- `@houtini/gemini-mcp` - Google AI chat with grounding and deep research
|
|
247
|
+
- `@houtini/geo-analyzer` - Content optimisation for AI search engines
|
|
248
|
+
- `@houtini/brevo-mcp` - Email marketing automation
|
|
249
|
+
|
|
250
|
+
## Related
|
|
251
|
+
|
|
252
|
+
- [Model Context Protocol](https://modelcontextprotocol.io) - Protocol specification
|
|
253
|
+
- [Google Knowledge Graph API](https://developers.google.com/knowledge-graph/) - Official API docs
|
|
254
|
+
- [Claude Desktop](https://claude.ai/download) - Primary MCP client
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
**Version:** 1.0.0
|
|
259
|
+
**Status:** Production ready, tested with Claude Desktop
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface KnowledgeGraphEntity {
|
|
2
|
+
'@context'?: any;
|
|
3
|
+
'@type'?: string | string[];
|
|
4
|
+
'@id'?: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
detailedDescription?: {
|
|
8
|
+
articleBody?: string;
|
|
9
|
+
url?: string;
|
|
10
|
+
license?: string;
|
|
11
|
+
};
|
|
12
|
+
image?: {
|
|
13
|
+
contentUrl?: string;
|
|
14
|
+
url?: string;
|
|
15
|
+
};
|
|
16
|
+
url?: string;
|
|
17
|
+
resultScore?: number;
|
|
18
|
+
[key: string]: any;
|
|
19
|
+
}
|
|
20
|
+
export interface SearchOptions {
|
|
21
|
+
query?: string;
|
|
22
|
+
ids?: string[];
|
|
23
|
+
languages?: string[];
|
|
24
|
+
types?: string[];
|
|
25
|
+
limit?: number;
|
|
26
|
+
indent?: boolean;
|
|
27
|
+
prefix?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Search Knowledge Graph by query string
|
|
31
|
+
*/
|
|
32
|
+
export declare function searchEntities(options: SearchOptions): Promise<KnowledgeGraphEntity[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Lookup entities by Machine IDs (MIDs)
|
|
35
|
+
*/
|
|
36
|
+
export declare function lookupEntities(ids: string[], languages?: string[]): Promise<KnowledgeGraphEntity[]>;
|
|
37
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACnC,UAAU,CAAC,EAAE,GAAG,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE;QACpB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,KAAK,CAAC,EAAE;QACN,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;IACF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAwDD;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CA8B5F;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAKzG"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.searchEntities = searchEntities;
|
|
4
|
+
exports.lookupEntities = lookupEntities;
|
|
5
|
+
const API_KEY = process.env.GOOGLE_KNOWLEDGE_GRAPH_API_KEY || process.env.GOOGLE_CLOUD_API_KEY;
|
|
6
|
+
const BASE_URL = 'https://kgsearch.googleapis.com/v1/entities:search';
|
|
7
|
+
if (!API_KEY) {
|
|
8
|
+
throw new Error('GOOGLE_KNOWLEDGE_GRAPH_API_KEY or GOOGLE_CLOUD_API_KEY environment variable is required');
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Build URL for Knowledge Graph Search API
|
|
12
|
+
* Docs: https://developers.google.com/knowledge-graph/reference/rest/v1/
|
|
13
|
+
*/
|
|
14
|
+
function buildSearchUrl(options) {
|
|
15
|
+
const params = new URLSearchParams({
|
|
16
|
+
key: API_KEY,
|
|
17
|
+
});
|
|
18
|
+
// Query parameter (for text search)
|
|
19
|
+
if (options.query) {
|
|
20
|
+
params.append('query', options.query);
|
|
21
|
+
}
|
|
22
|
+
// IDs parameter (for entity lookup by MID)
|
|
23
|
+
if (options.ids && options.ids.length > 0) {
|
|
24
|
+
params.append('ids', options.ids.join(','));
|
|
25
|
+
}
|
|
26
|
+
// Languages parameter
|
|
27
|
+
if (options.languages && options.languages.length > 0) {
|
|
28
|
+
params.append('languages', options.languages.join(','));
|
|
29
|
+
}
|
|
30
|
+
// Types parameter (schema.org types)
|
|
31
|
+
if (options.types && options.types.length > 0) {
|
|
32
|
+
params.append('types', options.types.join(','));
|
|
33
|
+
}
|
|
34
|
+
// Limit parameter (default: 20, max: 500)
|
|
35
|
+
if (options.limit) {
|
|
36
|
+
params.append('limit', String(options.limit));
|
|
37
|
+
}
|
|
38
|
+
// Indent parameter (for pretty-printed JSON)
|
|
39
|
+
if (options.indent) {
|
|
40
|
+
params.append('indent', 'True');
|
|
41
|
+
}
|
|
42
|
+
// Prefix parameter (for prefix matching)
|
|
43
|
+
if (options.prefix !== undefined) {
|
|
44
|
+
params.append('prefix', String(options.prefix));
|
|
45
|
+
}
|
|
46
|
+
return `${BASE_URL}?${params.toString()}`;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Search Knowledge Graph by query string
|
|
50
|
+
*/
|
|
51
|
+
async function searchEntities(options) {
|
|
52
|
+
if (!options.query && !options.ids) {
|
|
53
|
+
throw new Error('Either query or ids parameter is required');
|
|
54
|
+
}
|
|
55
|
+
const url = buildSearchUrl(options);
|
|
56
|
+
const response = await fetch(url, {
|
|
57
|
+
method: 'GET',
|
|
58
|
+
headers: {
|
|
59
|
+
'Accept': 'application/json',
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
if (!response.ok) {
|
|
63
|
+
const errorText = await response.text();
|
|
64
|
+
throw new Error(`Knowledge Graph API request failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
65
|
+
}
|
|
66
|
+
const data = await response.json();
|
|
67
|
+
// API returns { itemListElement: [ { result: {...}, resultScore: number } ] }
|
|
68
|
+
if (data.itemListElement && Array.isArray(data.itemListElement)) {
|
|
69
|
+
return data.itemListElement.map((item) => ({
|
|
70
|
+
...item.result,
|
|
71
|
+
resultScore: item.resultScore,
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Lookup entities by Machine IDs (MIDs)
|
|
78
|
+
*/
|
|
79
|
+
async function lookupEntities(ids, languages) {
|
|
80
|
+
return searchEntities({
|
|
81
|
+
ids,
|
|
82
|
+
languages,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
5
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
6
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
7
|
+
const client_js_1 = require("./client.js");
|
|
8
|
+
const server = new index_js_1.Server({
|
|
9
|
+
name: 'google-knowledge-graph-mcp',
|
|
10
|
+
version: '1.0.0',
|
|
11
|
+
}, {
|
|
12
|
+
capabilities: {
|
|
13
|
+
tools: {},
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
|
|
17
|
+
return {
|
|
18
|
+
tools: [
|
|
19
|
+
{
|
|
20
|
+
name: 'search_knowledge_graph',
|
|
21
|
+
description: 'Search Google Knowledge Graph for entities by name or topic. Returns structured information about real-world entities like people, places, organizations, and concepts from Google\'s public knowledge base.',
|
|
22
|
+
inputSchema: {
|
|
23
|
+
type: 'object',
|
|
24
|
+
properties: {
|
|
25
|
+
query: {
|
|
26
|
+
type: 'string',
|
|
27
|
+
description: 'Search query for entities (e.g., "Taylor Swift", "Eiffel Tower", "Python programming")',
|
|
28
|
+
},
|
|
29
|
+
languages: {
|
|
30
|
+
type: 'array',
|
|
31
|
+
items: { type: 'string' },
|
|
32
|
+
description: 'Language codes (ISO 639, e.g., ["en", "es", "fr"]). Default: ["en"]',
|
|
33
|
+
},
|
|
34
|
+
types: {
|
|
35
|
+
type: 'array',
|
|
36
|
+
items: { type: 'string' },
|
|
37
|
+
description: 'Filter by schema.org types (e.g., ["Person", "Organization", "Place"])',
|
|
38
|
+
},
|
|
39
|
+
limit: {
|
|
40
|
+
type: 'number',
|
|
41
|
+
minimum: 1,
|
|
42
|
+
maximum: 500,
|
|
43
|
+
description: 'Maximum results to return (1-500). Default: 20',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
required: ['query'],
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'lookup_knowledge_graph_entities',
|
|
51
|
+
description: 'Look up specific Knowledge Graph entities by their Machine IDs (MIDs). Use this when you already know the entity IDs from a previous search. MIDs look like /m/0dl567 or /g/11b6vwtjpg.',
|
|
52
|
+
inputSchema: {
|
|
53
|
+
type: 'object',
|
|
54
|
+
properties: {
|
|
55
|
+
ids: {
|
|
56
|
+
type: 'array',
|
|
57
|
+
items: { type: 'string' },
|
|
58
|
+
minItems: 1,
|
|
59
|
+
description: 'Entity Machine IDs (MIDs) to lookup (e.g., ["/m/0dl567"])',
|
|
60
|
+
},
|
|
61
|
+
languages: {
|
|
62
|
+
type: 'array',
|
|
63
|
+
items: { type: 'string' },
|
|
64
|
+
description: 'Language codes for results. Default: ["en"]',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
required: ['ids'],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
74
|
+
try {
|
|
75
|
+
const { name, arguments: args } = request.params;
|
|
76
|
+
if (name === 'search_knowledge_graph') {
|
|
77
|
+
const { query, languages, types, limit } = args;
|
|
78
|
+
const options = {
|
|
79
|
+
query,
|
|
80
|
+
languages: languages || ['en'],
|
|
81
|
+
types,
|
|
82
|
+
limit: limit || 20,
|
|
83
|
+
};
|
|
84
|
+
const entities = await (0, client_js_1.searchEntities)(options);
|
|
85
|
+
const output = {
|
|
86
|
+
entities: entities.map(e => {
|
|
87
|
+
const rawMid = e['@id'] || '';
|
|
88
|
+
const mid = rawMid.replace(/^kg:/, '');
|
|
89
|
+
return {
|
|
90
|
+
mid,
|
|
91
|
+
name: e.name || '',
|
|
92
|
+
type: Array.isArray(e['@type']) ? e['@type'] : [e['@type'] || 'Thing'],
|
|
93
|
+
description: e.description || undefined,
|
|
94
|
+
detailedDescription: e.detailedDescription?.articleBody || undefined,
|
|
95
|
+
image: e.image?.contentUrl || undefined,
|
|
96
|
+
url: e.url || undefined,
|
|
97
|
+
resultScore: e.resultScore || undefined,
|
|
98
|
+
};
|
|
99
|
+
}),
|
|
100
|
+
count: entities.length,
|
|
101
|
+
};
|
|
102
|
+
return {
|
|
103
|
+
content: [
|
|
104
|
+
{
|
|
105
|
+
type: 'text',
|
|
106
|
+
text: JSON.stringify(output, null, 2),
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (name === 'lookup_knowledge_graph_entities') {
|
|
112
|
+
const { ids, languages } = args;
|
|
113
|
+
const entities = await (0, client_js_1.lookupEntities)(ids, languages || ['en']);
|
|
114
|
+
const output = {
|
|
115
|
+
entities: entities.map(e => {
|
|
116
|
+
const rawMid = e['@id'] || '';
|
|
117
|
+
const mid = rawMid.replace(/^kg:/, '');
|
|
118
|
+
return {
|
|
119
|
+
mid,
|
|
120
|
+
name: e.name || '',
|
|
121
|
+
type: Array.isArray(e['@type']) ? e['@type'] : [e['@type'] || 'Thing'],
|
|
122
|
+
description: e.description || undefined,
|
|
123
|
+
detailedDescription: e.detailedDescription?.articleBody || undefined,
|
|
124
|
+
image: e.image?.contentUrl || undefined,
|
|
125
|
+
url: e.url || undefined,
|
|
126
|
+
resultScore: e.resultScore || undefined,
|
|
127
|
+
};
|
|
128
|
+
}),
|
|
129
|
+
count: entities.length,
|
|
130
|
+
};
|
|
131
|
+
return {
|
|
132
|
+
content: [
|
|
133
|
+
{
|
|
134
|
+
type: 'text',
|
|
135
|
+
text: JSON.stringify(output, null, 2),
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
return {
|
|
144
|
+
content: [
|
|
145
|
+
{
|
|
146
|
+
type: 'text',
|
|
147
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
isError: true,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
async function main() {
|
|
155
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
156
|
+
await server.connect(transport);
|
|
157
|
+
process.on('SIGINT', async () => {
|
|
158
|
+
await server.close();
|
|
159
|
+
process.exit(0);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
main().catch((error) => {
|
|
163
|
+
process.stderr.write(`Fatal error: ${error}\n`);
|
|
164
|
+
process.exit(1);
|
|
165
|
+
});
|
|
166
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@houtini/google-knowledge-graph-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for Google's free public Knowledge Graph Search API - search for structured entity information",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"google-knowledge-graph-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsc --watch",
|
|
13
|
+
"start": "node dist/index.js",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"src",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE",
|
|
21
|
+
".env.example"
|
|
22
|
+
],
|
|
23
|
+
"keywords": [
|
|
24
|
+
"mcp",
|
|
25
|
+
"model-context-protocol",
|
|
26
|
+
"google",
|
|
27
|
+
"knowledge-graph",
|
|
28
|
+
"search",
|
|
29
|
+
"entities",
|
|
30
|
+
"claude",
|
|
31
|
+
"ai-assistant",
|
|
32
|
+
"semantic-search",
|
|
33
|
+
"entity-recognition"
|
|
34
|
+
],
|
|
35
|
+
"author": "Richard Baxter <richard@houtini.ai>",
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/houtini-ai/google-knowledge-graph-mcp.git"
|
|
40
|
+
},
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/houtini-ai/google-knowledge-graph-mcp/issues"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/houtini-ai/google-knowledge-graph-mcp#readme",
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
47
|
+
"zod": "^3.24.1"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^22.19.7",
|
|
51
|
+
"typescript": "^5.9.3"
|
|
52
|
+
},
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=18.0.0"
|
|
55
|
+
}
|
|
56
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
export interface KnowledgeGraphEntity {
|
|
2
|
+
'@context'?: any;
|
|
3
|
+
'@type'?: string | string[];
|
|
4
|
+
'@id'?: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
detailedDescription?: {
|
|
8
|
+
articleBody?: string;
|
|
9
|
+
url?: string;
|
|
10
|
+
license?: string;
|
|
11
|
+
};
|
|
12
|
+
image?: {
|
|
13
|
+
contentUrl?: string;
|
|
14
|
+
url?: string;
|
|
15
|
+
};
|
|
16
|
+
url?: string;
|
|
17
|
+
resultScore?: number;
|
|
18
|
+
[key: string]: any;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface SearchOptions {
|
|
22
|
+
query?: string;
|
|
23
|
+
ids?: string[];
|
|
24
|
+
languages?: string[];
|
|
25
|
+
types?: string[];
|
|
26
|
+
limit?: number;
|
|
27
|
+
indent?: boolean;
|
|
28
|
+
prefix?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const API_KEY = process.env.GOOGLE_KNOWLEDGE_GRAPH_API_KEY || process.env.GOOGLE_CLOUD_API_KEY;
|
|
32
|
+
const BASE_URL = 'https://kgsearch.googleapis.com/v1/entities:search';
|
|
33
|
+
|
|
34
|
+
if (!API_KEY) {
|
|
35
|
+
throw new Error('GOOGLE_KNOWLEDGE_GRAPH_API_KEY or GOOGLE_CLOUD_API_KEY environment variable is required');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Build URL for Knowledge Graph Search API
|
|
40
|
+
* Docs: https://developers.google.com/knowledge-graph/reference/rest/v1/
|
|
41
|
+
*/
|
|
42
|
+
function buildSearchUrl(options: SearchOptions): string {
|
|
43
|
+
const params = new URLSearchParams({
|
|
44
|
+
key: API_KEY!,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Query parameter (for text search)
|
|
48
|
+
if (options.query) {
|
|
49
|
+
params.append('query', options.query);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// IDs parameter (for entity lookup by MID)
|
|
53
|
+
if (options.ids && options.ids.length > 0) {
|
|
54
|
+
params.append('ids', options.ids.join(','));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Languages parameter
|
|
58
|
+
if (options.languages && options.languages.length > 0) {
|
|
59
|
+
params.append('languages', options.languages.join(','));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Types parameter (schema.org types)
|
|
63
|
+
if (options.types && options.types.length > 0) {
|
|
64
|
+
params.append('types', options.types.join(','));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Limit parameter (default: 20, max: 500)
|
|
68
|
+
if (options.limit) {
|
|
69
|
+
params.append('limit', String(options.limit));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Indent parameter (for pretty-printed JSON)
|
|
73
|
+
if (options.indent) {
|
|
74
|
+
params.append('indent', 'True');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Prefix parameter (for prefix matching)
|
|
78
|
+
if (options.prefix !== undefined) {
|
|
79
|
+
params.append('prefix', String(options.prefix));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return `${BASE_URL}?${params.toString()}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Search Knowledge Graph by query string
|
|
87
|
+
*/
|
|
88
|
+
export async function searchEntities(options: SearchOptions): Promise<KnowledgeGraphEntity[]> {
|
|
89
|
+
if (!options.query && !options.ids) {
|
|
90
|
+
throw new Error('Either query or ids parameter is required');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const url = buildSearchUrl(options);
|
|
94
|
+
|
|
95
|
+
const response = await fetch(url, {
|
|
96
|
+
method: 'GET',
|
|
97
|
+
headers: {
|
|
98
|
+
'Accept': 'application/json',
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
if (!response.ok) {
|
|
103
|
+
const errorText = await response.text();
|
|
104
|
+
throw new Error(`Knowledge Graph API request failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const data: any = await response.json();
|
|
108
|
+
|
|
109
|
+
// API returns { itemListElement: [ { result: {...}, resultScore: number } ] }
|
|
110
|
+
if (data.itemListElement && Array.isArray(data.itemListElement)) {
|
|
111
|
+
return data.itemListElement.map((item: any) => ({
|
|
112
|
+
...item.result,
|
|
113
|
+
resultScore: item.resultScore,
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Lookup entities by Machine IDs (MIDs)
|
|
122
|
+
*/
|
|
123
|
+
export async function lookupEntities(ids: string[], languages?: string[]): Promise<KnowledgeGraphEntity[]> {
|
|
124
|
+
return searchEntities({
|
|
125
|
+
ids,
|
|
126
|
+
languages,
|
|
127
|
+
});
|
|
128
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
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 {
|
|
5
|
+
CallToolRequestSchema,
|
|
6
|
+
ListToolsRequestSchema,
|
|
7
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
8
|
+
import { searchEntities, lookupEntities, SearchOptions } from './client.js';
|
|
9
|
+
|
|
10
|
+
const server = new Server(
|
|
11
|
+
{
|
|
12
|
+
name: 'google-knowledge-graph-mcp',
|
|
13
|
+
version: '1.0.0',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
capabilities: {
|
|
17
|
+
tools: {},
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
23
|
+
return {
|
|
24
|
+
tools: [
|
|
25
|
+
{
|
|
26
|
+
name: 'search_knowledge_graph',
|
|
27
|
+
description: 'Search Google Knowledge Graph for entities by name or topic. Returns structured information about real-world entities like people, places, organizations, and concepts from Google\'s public knowledge base.',
|
|
28
|
+
inputSchema: {
|
|
29
|
+
type: 'object',
|
|
30
|
+
properties: {
|
|
31
|
+
query: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
description: 'Search query for entities (e.g., "Taylor Swift", "Eiffel Tower", "Python programming")',
|
|
34
|
+
},
|
|
35
|
+
languages: {
|
|
36
|
+
type: 'array',
|
|
37
|
+
items: { type: 'string' },
|
|
38
|
+
description: 'Language codes (ISO 639, e.g., ["en", "es", "fr"]). Default: ["en"]',
|
|
39
|
+
},
|
|
40
|
+
types: {
|
|
41
|
+
type: 'array',
|
|
42
|
+
items: { type: 'string' },
|
|
43
|
+
description: 'Filter by schema.org types (e.g., ["Person", "Organization", "Place"])',
|
|
44
|
+
},
|
|
45
|
+
limit: {
|
|
46
|
+
type: 'number',
|
|
47
|
+
minimum: 1,
|
|
48
|
+
maximum: 500,
|
|
49
|
+
description: 'Maximum results to return (1-500). Default: 20',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
required: ['query'],
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'lookup_knowledge_graph_entities',
|
|
57
|
+
description: 'Look up specific Knowledge Graph entities by their Machine IDs (MIDs). Use this when you already know the entity IDs from a previous search. MIDs look like /m/0dl567 or /g/11b6vwtjpg.',
|
|
58
|
+
inputSchema: {
|
|
59
|
+
type: 'object',
|
|
60
|
+
properties: {
|
|
61
|
+
ids: {
|
|
62
|
+
type: 'array',
|
|
63
|
+
items: { type: 'string' },
|
|
64
|
+
minItems: 1,
|
|
65
|
+
description: 'Entity Machine IDs (MIDs) to lookup (e.g., ["/m/0dl567"])',
|
|
66
|
+
},
|
|
67
|
+
languages: {
|
|
68
|
+
type: 'array',
|
|
69
|
+
items: { type: 'string' },
|
|
70
|
+
description: 'Language codes for results. Default: ["en"]',
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
required: ['ids'],
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
81
|
+
try {
|
|
82
|
+
const { name, arguments: args } = request.params;
|
|
83
|
+
|
|
84
|
+
if (name === 'search_knowledge_graph') {
|
|
85
|
+
const { query, languages, types, limit } = args as any;
|
|
86
|
+
|
|
87
|
+
const options: SearchOptions = {
|
|
88
|
+
query,
|
|
89
|
+
languages: languages || ['en'],
|
|
90
|
+
types,
|
|
91
|
+
limit: limit || 20,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const entities = await searchEntities(options);
|
|
95
|
+
|
|
96
|
+
const output = {
|
|
97
|
+
entities: entities.map(e => {
|
|
98
|
+
const rawMid = e['@id'] || '';
|
|
99
|
+
const mid = rawMid.replace(/^kg:/, '');
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
mid,
|
|
103
|
+
name: e.name || '',
|
|
104
|
+
type: Array.isArray(e['@type']) ? e['@type'] : [e['@type'] || 'Thing'],
|
|
105
|
+
description: e.description || undefined,
|
|
106
|
+
detailedDescription: e.detailedDescription?.articleBody || undefined,
|
|
107
|
+
image: e.image?.contentUrl || undefined,
|
|
108
|
+
url: e.url || undefined,
|
|
109
|
+
resultScore: e.resultScore || undefined,
|
|
110
|
+
};
|
|
111
|
+
}),
|
|
112
|
+
count: entities.length,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
content: [
|
|
117
|
+
{
|
|
118
|
+
type: 'text',
|
|
119
|
+
text: JSON.stringify(output, null, 2),
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (name === 'lookup_knowledge_graph_entities') {
|
|
126
|
+
const { ids, languages } = args as any;
|
|
127
|
+
|
|
128
|
+
const entities = await lookupEntities(ids, languages || ['en']);
|
|
129
|
+
|
|
130
|
+
const output = {
|
|
131
|
+
entities: entities.map(e => {
|
|
132
|
+
const rawMid = e['@id'] || '';
|
|
133
|
+
const mid = rawMid.replace(/^kg:/, '');
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
mid,
|
|
137
|
+
name: e.name || '',
|
|
138
|
+
type: Array.isArray(e['@type']) ? e['@type'] : [e['@type'] || 'Thing'],
|
|
139
|
+
description: e.description || undefined,
|
|
140
|
+
detailedDescription: e.detailedDescription?.articleBody || undefined,
|
|
141
|
+
image: e.image?.contentUrl || undefined,
|
|
142
|
+
url: e.url || undefined,
|
|
143
|
+
resultScore: e.resultScore || undefined,
|
|
144
|
+
};
|
|
145
|
+
}),
|
|
146
|
+
count: entities.length,
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
content: [
|
|
151
|
+
{
|
|
152
|
+
type: 'text',
|
|
153
|
+
text: JSON.stringify(output, null, 2),
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
return {
|
|
162
|
+
content: [
|
|
163
|
+
{
|
|
164
|
+
type: 'text',
|
|
165
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
isError: true,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
async function main() {
|
|
174
|
+
const transport = new StdioServerTransport();
|
|
175
|
+
await server.connect(transport);
|
|
176
|
+
|
|
177
|
+
process.on('SIGINT', async () => {
|
|
178
|
+
await server.close();
|
|
179
|
+
process.exit(0);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
main().catch((error) => {
|
|
184
|
+
process.stderr.write(`Fatal error: ${error}\n`);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
});
|