parisinnov-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/README.md +50 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +249 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# parisinnov-mcp
|
|
2
|
+
|
|
3
|
+
MCP Server for RAG-powered knowledge base with **GLM-4.7**.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
### Claude Code
|
|
8
|
+
|
|
9
|
+
Add to `~/.claude/claude_desktop_config.json`:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"mcpServers": {
|
|
14
|
+
"parisinnov": {
|
|
15
|
+
"command": "npx",
|
|
16
|
+
"args": [
|
|
17
|
+
"parisinnov-mcp",
|
|
18
|
+
"--access-token", "your_api_key"
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
26
|
+
|
|
27
|
+
### Cursor
|
|
28
|
+
|
|
29
|
+
Same config format in Cursor MCP settings.
|
|
30
|
+
|
|
31
|
+
## That's it!
|
|
32
|
+
|
|
33
|
+
No installation needed. `npx` handles everything.
|
|
34
|
+
|
|
35
|
+
## Tools
|
|
36
|
+
|
|
37
|
+
| Tool | Description |
|
|
38
|
+
|------|-------------|
|
|
39
|
+
| `search-context` | Search knowledge base with AI answers (GLM-4.7) |
|
|
40
|
+
| `add-knowledge` | Add document to knowledge base |
|
|
41
|
+
| `list-knowledge` | List all documents |
|
|
42
|
+
| `delete-knowledge` | Delete a document |
|
|
43
|
+
|
|
44
|
+
## Get your API key
|
|
45
|
+
|
|
46
|
+
Contact the Paris Innov team to get your access token.
|
|
47
|
+
|
|
48
|
+
## License
|
|
49
|
+
|
|
50
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
import axios from 'axios';
|
|
6
|
+
import yargs from 'yargs';
|
|
7
|
+
import { hideBin } from 'yargs/helpers';
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Configuration
|
|
10
|
+
// ============================================================================
|
|
11
|
+
const BACKEND_URL = 'https://parisinnovbackend-production.up.railway.app';
|
|
12
|
+
function parseArguments() {
|
|
13
|
+
const argv = yargs(hideBin(process.argv))
|
|
14
|
+
.option('access-token', {
|
|
15
|
+
alias: 't',
|
|
16
|
+
type: 'string',
|
|
17
|
+
description: 'API key for backend authentication',
|
|
18
|
+
demandOption: true
|
|
19
|
+
})
|
|
20
|
+
.help()
|
|
21
|
+
.parseSync();
|
|
22
|
+
return {
|
|
23
|
+
accessToken: argv['access-token']
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Backend API Client
|
|
28
|
+
// ============================================================================
|
|
29
|
+
class BackendClient {
|
|
30
|
+
accessToken;
|
|
31
|
+
constructor(accessToken) {
|
|
32
|
+
this.accessToken = accessToken;
|
|
33
|
+
}
|
|
34
|
+
get headers() {
|
|
35
|
+
return {
|
|
36
|
+
'Authorization': `Bearer ${this.accessToken}`,
|
|
37
|
+
'Content-Type': 'application/json'
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
async ask(question) {
|
|
41
|
+
const response = await axios.post(`${BACKEND_URL}/ask`, { question }, { headers: this.headers, timeout: 60000 });
|
|
42
|
+
return response.data;
|
|
43
|
+
}
|
|
44
|
+
async addDocument(content, title, source) {
|
|
45
|
+
const response = await axios.post(`${BACKEND_URL}/documents`, { content, title, source }, { headers: this.headers, timeout: 30000 });
|
|
46
|
+
return response.data;
|
|
47
|
+
}
|
|
48
|
+
async listDocuments(limit = 20) {
|
|
49
|
+
const response = await axios.get(`${BACKEND_URL}/documents?limit=${limit}`, { headers: this.headers, timeout: 30000 });
|
|
50
|
+
return response.data;
|
|
51
|
+
}
|
|
52
|
+
async deleteDocument(id) {
|
|
53
|
+
const response = await axios.delete(`${BACKEND_URL}/documents/${id}`, { headers: this.headers, timeout: 30000 });
|
|
54
|
+
return response.data;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// MCP Server
|
|
59
|
+
// ============================================================================
|
|
60
|
+
async function startMCPServer() {
|
|
61
|
+
const args = parseArguments();
|
|
62
|
+
const backend = new BackendClient(args.accessToken);
|
|
63
|
+
const server = new Server({
|
|
64
|
+
name: 'parisinnov-mcp',
|
|
65
|
+
version: '1.0.0',
|
|
66
|
+
}, {
|
|
67
|
+
capabilities: { tools: {} },
|
|
68
|
+
});
|
|
69
|
+
// List available tools
|
|
70
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
71
|
+
tools: [
|
|
72
|
+
{
|
|
73
|
+
name: 'search-context',
|
|
74
|
+
description: 'Search your knowledge base and get AI-powered answers using GLM-4.7.',
|
|
75
|
+
inputSchema: {
|
|
76
|
+
type: 'object',
|
|
77
|
+
properties: {
|
|
78
|
+
question: {
|
|
79
|
+
type: 'string',
|
|
80
|
+
description: 'Your question or search query'
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
required: ['question']
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'add-knowledge',
|
|
88
|
+
description: 'Add new content to your knowledge base. It will be vectorized and available for future searches.',
|
|
89
|
+
inputSchema: {
|
|
90
|
+
type: 'object',
|
|
91
|
+
properties: {
|
|
92
|
+
content: {
|
|
93
|
+
type: 'string',
|
|
94
|
+
description: 'The content to add'
|
|
95
|
+
},
|
|
96
|
+
title: {
|
|
97
|
+
type: 'string',
|
|
98
|
+
description: 'Optional title for the content'
|
|
99
|
+
},
|
|
100
|
+
source: {
|
|
101
|
+
type: 'string',
|
|
102
|
+
description: 'Optional source identifier (e.g., "slack", "notion", "manual")'
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
required: ['content']
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: 'list-knowledge',
|
|
110
|
+
description: 'List documents in your knowledge base',
|
|
111
|
+
inputSchema: {
|
|
112
|
+
type: 'object',
|
|
113
|
+
properties: {
|
|
114
|
+
limit: {
|
|
115
|
+
type: 'number',
|
|
116
|
+
description: 'Maximum number of documents to return (default: 20)'
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: 'delete-knowledge',
|
|
123
|
+
description: 'Delete a document from your knowledge base',
|
|
124
|
+
inputSchema: {
|
|
125
|
+
type: 'object',
|
|
126
|
+
properties: {
|
|
127
|
+
id: {
|
|
128
|
+
type: 'string',
|
|
129
|
+
description: 'The document ID to delete'
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
required: ['id']
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
}));
|
|
137
|
+
// Handle tool calls
|
|
138
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
139
|
+
const { name, arguments: toolArgs } = request.params;
|
|
140
|
+
try {
|
|
141
|
+
switch (name) {
|
|
142
|
+
case 'search-context': {
|
|
143
|
+
const { question } = toolArgs;
|
|
144
|
+
if (!question || typeof question !== 'string') {
|
|
145
|
+
throw new Error('Question is required');
|
|
146
|
+
}
|
|
147
|
+
const result = await backend.ask(question);
|
|
148
|
+
let responseText = result.answer;
|
|
149
|
+
if (result.sources.length > 0) {
|
|
150
|
+
responseText += '\n\n---\n**Sources:**';
|
|
151
|
+
result.sources.forEach((source, i) => {
|
|
152
|
+
responseText += `\n${i + 1}. ${source.title || 'Document'} (${(source.similarity * 100).toFixed(0)}%)`;
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
responseText += `\n\n_${result.metadata.documents_found} docs | ${result.metadata.processing_time_ms}ms | ${result.metadata.model}_`;
|
|
156
|
+
return {
|
|
157
|
+
content: [{ type: 'text', text: responseText }]
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
case 'add-knowledge': {
|
|
161
|
+
const { content, title, source } = toolArgs;
|
|
162
|
+
if (!content || typeof content !== 'string') {
|
|
163
|
+
throw new Error('Content is required');
|
|
164
|
+
}
|
|
165
|
+
const result = await backend.addDocument(content, title, source);
|
|
166
|
+
return {
|
|
167
|
+
content: [{
|
|
168
|
+
type: 'text',
|
|
169
|
+
text: `Document added!\nID: ${result.document.id}\nTitle: ${result.document.title}`
|
|
170
|
+
}]
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
case 'list-knowledge': {
|
|
174
|
+
const { limit } = toolArgs;
|
|
175
|
+
const result = await backend.listDocuments(limit || 20);
|
|
176
|
+
if (result.documents.length === 0) {
|
|
177
|
+
return {
|
|
178
|
+
content: [{ type: 'text', text: 'No documents in knowledge base.' }]
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
let text = `**${result.documents.length} document(s):**\n\n`;
|
|
182
|
+
result.documents.forEach((doc, i) => {
|
|
183
|
+
text += `${i + 1}. **${doc.title}** (ID: \`${doc.id}\`)\n`;
|
|
184
|
+
text += ` ${doc.snippet}\n\n`;
|
|
185
|
+
});
|
|
186
|
+
return {
|
|
187
|
+
content: [{ type: 'text', text }]
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
case 'delete-knowledge': {
|
|
191
|
+
const { id } = toolArgs;
|
|
192
|
+
if (!id) {
|
|
193
|
+
throw new Error('Document ID is required');
|
|
194
|
+
}
|
|
195
|
+
const result = await backend.deleteDocument(id);
|
|
196
|
+
return {
|
|
197
|
+
content: [{
|
|
198
|
+
type: 'text',
|
|
199
|
+
text: result.success
|
|
200
|
+
? `Document ${id} deleted.`
|
|
201
|
+
: `Failed to delete document ${id}.`
|
|
202
|
+
}]
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
default:
|
|
206
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
211
|
+
if (axios.isAxiosError(error)) {
|
|
212
|
+
if (error.response) {
|
|
213
|
+
return {
|
|
214
|
+
content: [{
|
|
215
|
+
type: 'text',
|
|
216
|
+
text: `Backend error (${error.response.status}): ${error.response.data?.message || error.message}`
|
|
217
|
+
}],
|
|
218
|
+
isError: true
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
content: [{ type: 'text', text: `Network error: ${message}` }],
|
|
223
|
+
isError: true
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
content: [{ type: 'text', text: `Error: ${message}` }],
|
|
228
|
+
isError: true
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
// Start server
|
|
233
|
+
const transport = new StdioServerTransport();
|
|
234
|
+
await server.connect(transport);
|
|
235
|
+
}
|
|
236
|
+
// Error handlers
|
|
237
|
+
process.on('uncaughtException', (error) => {
|
|
238
|
+
console.error('Uncaught exception:', error);
|
|
239
|
+
process.exit(1);
|
|
240
|
+
});
|
|
241
|
+
process.on('unhandledRejection', (reason) => {
|
|
242
|
+
console.error('Unhandled rejection:', reason);
|
|
243
|
+
process.exit(1);
|
|
244
|
+
});
|
|
245
|
+
// Start
|
|
246
|
+
startMCPServer().catch((error) => {
|
|
247
|
+
console.error('Failed to start MCP server:', error);
|
|
248
|
+
process.exit(1);
|
|
249
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "parisinnov-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP Server with Smart Query Builder (GLM-4.7) for RAG-powered knowledge base",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"parisinnov-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"start": "node dist/index.js",
|
|
13
|
+
"dev": "ts-node --esm src/index.ts",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": ["mcp", "model-context-protocol", "rag", "ai", "claude", "cursor", "glm", "featherless", "knowledge-base"],
|
|
17
|
+
"author": "Paris Innov Hackathon Team",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/martinbon39/parisinnov-mcp.git"
|
|
22
|
+
},
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/martinbon39/parisinnov-mcp/issues"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/martinbon39/parisinnov-mcp#readme",
|
|
27
|
+
"files": ["dist/", "README.md"],
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@modelcontextprotocol/sdk": "^0.5.0",
|
|
30
|
+
"axios": "^1.6.0",
|
|
31
|
+
"yargs": "^17.7.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^20.0.0",
|
|
35
|
+
"@types/yargs": "^17.0.0",
|
|
36
|
+
"ts-node": "^10.9.0",
|
|
37
|
+
"typescript": "^5.0.0"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18.0.0"
|
|
41
|
+
}
|
|
42
|
+
}
|