@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 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
+ [![npm version](https://img.shields.io/npm/v/@houtini/google-knowledge-graph-mcp.svg)](https://www.npmjs.com/package/@houtini/google-knowledge-graph-mcp)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![MCP](https://img.shields.io/badge/MCP-Compatible-blue)](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
@@ -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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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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
+ });