@houtini/google-knowledge-graph-mcp 1.0.0 → 1.0.1
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/package.json +22 -6
- package/src/client.ts +0 -128
- package/src/index.ts +0 -186
package/package.json
CHANGED
|
@@ -1,21 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@houtini/google-knowledge-graph-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "MCP server for Google's free public Knowledge Graph Search API - search for structured entity information",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"bin": {
|
|
8
8
|
"google-knowledge-graph-mcp": "dist/index.js"
|
|
9
9
|
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"require": "./dist/index.js",
|
|
14
|
+
"import": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./package.json": "./package.json"
|
|
17
|
+
},
|
|
10
18
|
"scripts": {
|
|
11
19
|
"build": "tsc",
|
|
12
20
|
"dev": "tsc --watch",
|
|
13
21
|
"start": "node dist/index.js",
|
|
14
|
-
"prepublishOnly": "npm run build"
|
|
22
|
+
"prepublishOnly": "npm run build",
|
|
23
|
+
"prepack": "npm run build",
|
|
24
|
+
"clean": "rm -rf dist",
|
|
25
|
+
"type-check": "tsc --noEmit"
|
|
15
26
|
},
|
|
16
27
|
"files": [
|
|
17
28
|
"dist",
|
|
18
|
-
"src",
|
|
19
29
|
"README.md",
|
|
20
30
|
"LICENSE",
|
|
21
31
|
".env.example"
|
|
@@ -30,7 +40,9 @@
|
|
|
30
40
|
"claude",
|
|
31
41
|
"ai-assistant",
|
|
32
42
|
"semantic-search",
|
|
33
|
-
"entity-recognition"
|
|
43
|
+
"entity-recognition",
|
|
44
|
+
"knowledge-base",
|
|
45
|
+
"schema-org"
|
|
34
46
|
],
|
|
35
47
|
"author": "Richard Baxter <richard@houtini.ai>",
|
|
36
48
|
"license": "MIT",
|
|
@@ -43,14 +55,18 @@
|
|
|
43
55
|
},
|
|
44
56
|
"homepage": "https://github.com/houtini-ai/google-knowledge-graph-mcp#readme",
|
|
45
57
|
"dependencies": {
|
|
46
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
58
|
+
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
47
59
|
"zod": "^3.24.1"
|
|
48
60
|
},
|
|
49
61
|
"devDependencies": {
|
|
50
|
-
"@types/node": "^
|
|
62
|
+
"@types/node": "^25.0.9",
|
|
51
63
|
"typescript": "^5.9.3"
|
|
52
64
|
},
|
|
53
65
|
"engines": {
|
|
54
66
|
"node": ">=18.0.0"
|
|
67
|
+
},
|
|
68
|
+
"publishConfig": {
|
|
69
|
+
"access": "public",
|
|
70
|
+
"registry": "https://registry.npmjs.org/"
|
|
55
71
|
}
|
|
56
72
|
}
|
package/src/client.ts
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
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
|
-
});
|