smart-thinking-mcp 11.0.5 → 12.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 +18 -2
- package/build/cli.d.ts +2 -0
- package/build/cli.js +650 -0
- package/build/cli.js.map +1 -0
- package/build/index.d.ts +24 -2
- package/build/index.js +35 -462
- package/build/index.js.map +1 -1
- package/build/memory-manager.d.ts +6 -0
- package/build/memory-manager.js +38 -6
- package/build/memory-manager.js.map +1 -1
- package/build/server/contracts.d.ts +221 -0
- package/build/server/contracts.js +93 -0
- package/build/server/contracts.js.map +1 -0
- package/build/server/registrations/prompt-registrations.d.ts +2 -0
- package/build/server/registrations/prompt-registrations.js +60 -0
- package/build/server/registrations/prompt-registrations.js.map +1 -0
- package/build/server/registrations/resource-registrations.d.ts +3 -0
- package/build/server/registrations/resource-registrations.js +99 -0
- package/build/server/registrations/resource-registrations.js.map +1 -0
- package/build/server/registrations/tool-registrations.d.ts +7 -0
- package/build/server/registrations/tool-registrations.js +202 -0
- package/build/server/registrations/tool-registrations.js.map +1 -0
- package/build/server/server-metadata.d.ts +6 -0
- package/build/server/server-metadata.js +36 -0
- package/build/server/server-metadata.js.map +1 -0
- package/build/server/smart-thinking-server.d.ts +11 -344
- package/build/server/smart-thinking-server.js +19 -275
- package/build/server/smart-thinking-server.js.map +1 -1
- package/build/services/service-container.d.ts +17 -49
- package/build/services/service-container.js +12 -57
- package/build/services/service-container.js.map +1 -1
- package/build/services/verification-service.js +1 -1
- package/build/services/verification-service.js.map +1 -1
- package/build/test-utils/logger-setup.js +1 -1
- package/build/test-utils/logger-setup.js.map +1 -1
- package/build/tool-integrator.d.ts +11 -1
- package/build/tool-integrator.js +43 -11
- package/build/tool-integrator.js.map +1 -1
- package/build/utils/logger.js +4 -1
- package/build/utils/logger.js.map +1 -1
- package/build/verification-memory.d.ts +5 -0
- package/build/verification-memory.js +35 -5
- package/build/verification-memory.js.map +1 -1
- package/package.json +12 -12
- package/scripts/make-executable.js +28 -29
package/README.md
CHANGED
|
@@ -80,11 +80,27 @@ Smart-Thinking is validated across the most popular MCP clients and operating sy
|
|
|
80
80
|
|
|
81
81
|
> Need a minimal deployment footprint? Combine `--transport=http --mode=connector` with a reverse proxy (ngrok, fly.io, render, etc.) so remote clients can consume the server without exposing the full toolset.
|
|
82
82
|
|
|
83
|
+
For registry scanners and fallback metadata extraction, Smart-Thinking also exposes:
|
|
84
|
+
|
|
85
|
+
- `GET /.well-known/mcp/server-card.json`
|
|
86
|
+
|
|
83
87
|
## Configuration & Feature Flags
|
|
84
88
|
- `feature-flags.ts` toggles advanced behaviours such as external integrations (disabled by default) and verbose tracing.
|
|
85
89
|
- `config.ts` aligns platform-specific paths and verification thresholds.
|
|
86
90
|
- `memory-manager.ts` and `verification-memory.ts` store session graphs, metrics, and calculation results using deterministic JSON snapshots.
|
|
87
91
|
|
|
92
|
+
## Zero-API-Key Mode (Default)
|
|
93
|
+
- Smart-Thinking runs fully in local deterministic mode without any API key.
|
|
94
|
+
- External verification/search connectors are disabled by default in `ToolIntegrator`.
|
|
95
|
+
- To explicitly enable external connectors, set:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
export SMART_THINKING_ENABLE_EXTERNAL_TOOLS=true
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
- If external connectors are disabled (default), verification suggestions stay local (`executePython`, `executeJavaScript`) and external tool calls return a local fallback result.
|
|
102
|
+
- `FeatureFlags.externalLlmEnabled` and `FeatureFlags.externalEmbeddingEnabled` remain disabled by default, so no remote LLM/embedding provider is required.
|
|
103
|
+
|
|
88
104
|
## Development Workflow
|
|
89
105
|
```bash
|
|
90
106
|
npm run build # Compile TypeScript sources
|
|
@@ -94,11 +110,11 @@ npm run test:coverage # Jest coverage report
|
|
|
94
110
|
npm run watch # Incremental TypeScript compilation
|
|
95
111
|
```
|
|
96
112
|
|
|
97
|
-
See `
|
|
113
|
+
See `docs/modernisation-smart-thinking-v12-plan.md` for the modernization checklist and rollout tracking.
|
|
98
114
|
|
|
99
115
|
## Quality & Support
|
|
100
116
|
- Deterministic heuristics and verification eliminate dependency on remote LLMs.
|
|
101
|
-
-
|
|
117
|
+
- Latest validation (February 6, 2026): `80.47%` statements, `81.59%` lines, `84.34%` functions, `63.48%` branches.
|
|
102
118
|
- CI recommendations: run `npm run lint` and `npm run test:coverage` before each release candidate.
|
|
103
119
|
|
|
104
120
|
## Contributing
|
package/build/cli.d.ts
ADDED
package/build/cli.js
ADDED
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/* istanbul ignore file -- Point d'entrée CLI difficile à tester automatiquement */
|
|
4
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
6
|
+
};
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
require("./utils/logger");
|
|
9
|
+
const express_1 = __importDefault(require("express"));
|
|
10
|
+
const crypto_1 = require("crypto");
|
|
11
|
+
const fs_1 = require("fs");
|
|
12
|
+
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const sse_js_1 = require("@modelcontextprotocol/sdk/server/sse.js");
|
|
14
|
+
const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
15
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
16
|
+
const platform_stdio_1 = require("./utils/platform-stdio");
|
|
17
|
+
const path_utils_1 = require("./utils/path-utils");
|
|
18
|
+
const config_1 = require("./config");
|
|
19
|
+
const smart_thinking_server_1 = require("./server/smart-thinking-server");
|
|
20
|
+
const PROJECT_HOMEPAGE = 'https://github.com/Leghis/Smart-Thinking';
|
|
21
|
+
const PROJECT_ICON = 'https://raw.githubusercontent.com/Leghis/Smart-Thinking/main/logoSmart-thinking.png';
|
|
22
|
+
function parseCsvEnv(name) {
|
|
23
|
+
const raw = process.env[name];
|
|
24
|
+
if (!raw) {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
return raw.split(',').map(item => item.trim()).filter(Boolean);
|
|
28
|
+
}
|
|
29
|
+
function resolveDefaultAllowedHosts(host, port) {
|
|
30
|
+
const hosts = new Set([
|
|
31
|
+
'localhost',
|
|
32
|
+
`localhost:${port}`,
|
|
33
|
+
'127.0.0.1',
|
|
34
|
+
`127.0.0.1:${port}`
|
|
35
|
+
]);
|
|
36
|
+
if (host && host !== '0.0.0.0' && host !== '::') {
|
|
37
|
+
hosts.add(host);
|
|
38
|
+
hosts.add(`${host}:${port}`);
|
|
39
|
+
}
|
|
40
|
+
return Array.from(hosts);
|
|
41
|
+
}
|
|
42
|
+
function resolveDefaultAllowedOrigins(host, port) {
|
|
43
|
+
const origins = new Set([
|
|
44
|
+
`http://localhost:${port}`,
|
|
45
|
+
`http://127.0.0.1:${port}`,
|
|
46
|
+
`https://localhost:${port}`,
|
|
47
|
+
`https://127.0.0.1:${port}`
|
|
48
|
+
]);
|
|
49
|
+
if (host && host !== '0.0.0.0' && host !== '::') {
|
|
50
|
+
origins.add(`http://${host}:${port}`);
|
|
51
|
+
origins.add(`https://${host}:${port}`);
|
|
52
|
+
}
|
|
53
|
+
return Array.from(origins);
|
|
54
|
+
}
|
|
55
|
+
function loadPackageVersion() {
|
|
56
|
+
try {
|
|
57
|
+
const packageJsonPath = path_1.default.join(__dirname, '..', 'package.json');
|
|
58
|
+
const raw = (0, fs_1.readFileSync)(packageJsonPath, 'utf8');
|
|
59
|
+
const parsed = JSON.parse(raw);
|
|
60
|
+
return parsed.version ?? '0.0.0';
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return '0.0.0';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function buildServerCard(options) {
|
|
67
|
+
const version = loadPackageVersion();
|
|
68
|
+
const tools = [
|
|
69
|
+
{
|
|
70
|
+
name: 'search',
|
|
71
|
+
description: 'Recherche semantique dans les memoires locales Smart-Thinking.',
|
|
72
|
+
inputSchema: {
|
|
73
|
+
type: 'object',
|
|
74
|
+
properties: {
|
|
75
|
+
query: { type: 'string', description: 'Requete de recherche.' },
|
|
76
|
+
limit: { type: 'integer', minimum: 1, maximum: 20, default: 5, description: 'Nombre maximal de resultats.' },
|
|
77
|
+
sessionId: { type: 'string', description: 'Session cible optionnelle.' },
|
|
78
|
+
},
|
|
79
|
+
required: ['query'],
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'fetch',
|
|
84
|
+
description: 'Recupere une memoire complete par identifiant.',
|
|
85
|
+
inputSchema: {
|
|
86
|
+
type: 'object',
|
|
87
|
+
properties: {
|
|
88
|
+
id: { type: 'string', description: 'Identifiant de la memoire.' },
|
|
89
|
+
sessionId: { type: 'string', description: 'Session cible optionnelle.' },
|
|
90
|
+
},
|
|
91
|
+
required: ['id'],
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
];
|
|
95
|
+
if (options.mode !== 'connector') {
|
|
96
|
+
tools.unshift({
|
|
97
|
+
name: 'smartthinking',
|
|
98
|
+
description: 'Pipeline de raisonnement graphe local, deterministe et persistant.',
|
|
99
|
+
inputSchema: {
|
|
100
|
+
type: 'object',
|
|
101
|
+
properties: {
|
|
102
|
+
thought: { type: 'string', description: 'Pensee a analyser.' },
|
|
103
|
+
thoughtType: {
|
|
104
|
+
type: 'string',
|
|
105
|
+
enum: ['regular', 'revision', 'meta', 'hypothesis', 'conclusion'],
|
|
106
|
+
default: 'regular',
|
|
107
|
+
description: 'Type de pensee.',
|
|
108
|
+
},
|
|
109
|
+
sessionId: { type: 'string', description: 'Identifiant de session optionnel.' },
|
|
110
|
+
requestSuggestions: { type: 'boolean', default: false, description: 'Demande des suggestions d amelioration.' },
|
|
111
|
+
requestVerification: { type: 'boolean', default: false, description: 'Active la verification explicite.' },
|
|
112
|
+
containsCalculations: { type: 'boolean', default: false, description: 'Indique la presence de calculs.' },
|
|
113
|
+
generateVisualization: { type: 'boolean', default: false, description: 'Active la visualisation.' },
|
|
114
|
+
help: { type: 'boolean', default: false, description: 'Retourne le guide d utilisation.' },
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
serverInfo: {
|
|
121
|
+
name: 'smart-thinking-mcp',
|
|
122
|
+
title: 'Smart-Thinking',
|
|
123
|
+
version,
|
|
124
|
+
websiteUrl: PROJECT_HOMEPAGE,
|
|
125
|
+
icons: [
|
|
126
|
+
{
|
|
127
|
+
src: PROJECT_ICON,
|
|
128
|
+
mimeType: 'image/png',
|
|
129
|
+
sizes: ['512x512'],
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
tools,
|
|
134
|
+
prompts: [
|
|
135
|
+
{
|
|
136
|
+
name: 'smartthinking-reasoning-plan',
|
|
137
|
+
description: 'Construit un plan de raisonnement testable avant execution.',
|
|
138
|
+
arguments: [
|
|
139
|
+
{ name: 'objective', required: true, description: 'Objectif principal.' },
|
|
140
|
+
{ name: 'constraints', required: false, description: 'Contraintes.' },
|
|
141
|
+
{ name: 'depth', required: false, description: 'Niveau de profondeur (fast|balanced|deep).' },
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: 'smartthinking-verify-claim',
|
|
146
|
+
description: 'Genere une checklist de verification factuelle.',
|
|
147
|
+
arguments: [
|
|
148
|
+
{ name: 'claim', required: true, description: 'Affirmation a verifier.' },
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
resources: [
|
|
153
|
+
{
|
|
154
|
+
uri: 'smart-thinking://docs/about',
|
|
155
|
+
name: 'smartthinking-about',
|
|
156
|
+
description: 'Documentation du serveur.',
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
uri: 'smart-thinking://runtime/status',
|
|
160
|
+
name: 'smartthinking-runtime-status',
|
|
161
|
+
description: 'Etat runtime du serveur.',
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
uriTemplate: 'smart-thinking://sessions/{sessionId}/recent',
|
|
165
|
+
name: 'smartthinking-session-recent',
|
|
166
|
+
description: 'Memoires recentes par session.',
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
uriTemplate: 'smart-thinking://memories/{memoryId}',
|
|
170
|
+
name: 'smartthinking-memory-by-id',
|
|
171
|
+
description: 'Memoire complete par identifiant.',
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
(async () => {
|
|
177
|
+
const options = parseArgs(process.argv.slice(2));
|
|
178
|
+
try {
|
|
179
|
+
await ensureDataDirExists();
|
|
180
|
+
if (options.transport === 'stdio') {
|
|
181
|
+
await startStdIoServer(options);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
await startHttpServer(options);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
console.error('Smart-Thinking: échec du démarrage du serveur', error);
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
})();
|
|
192
|
+
function parseArgs(argv) {
|
|
193
|
+
const envMode = (process.env.SMART_THINKING_MODE ?? '').toLowerCase();
|
|
194
|
+
const defaultMode = envMode === 'connector' ? 'connector' : 'full';
|
|
195
|
+
const options = {
|
|
196
|
+
transport: 'stdio',
|
|
197
|
+
port: process.env.PORT ? Number(process.env.PORT) : 3000,
|
|
198
|
+
host: process.env.HOST ?? '127.0.0.1',
|
|
199
|
+
allowOrigins: parseCsvEnv('SMART_THINKING_ALLOWED_ORIGINS'),
|
|
200
|
+
allowHosts: parseCsvEnv('SMART_THINKING_ALLOWED_HOSTS'),
|
|
201
|
+
enableSse: true,
|
|
202
|
+
enableStream: true,
|
|
203
|
+
mode: defaultMode
|
|
204
|
+
};
|
|
205
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
206
|
+
const arg = argv[i];
|
|
207
|
+
const [flag, inlineValue] = arg.includes('=') ? arg.split('=', 2) : [arg, undefined];
|
|
208
|
+
const readValue = () => {
|
|
209
|
+
if (inlineValue !== undefined) {
|
|
210
|
+
return inlineValue;
|
|
211
|
+
}
|
|
212
|
+
const nextArg = argv[i + 1];
|
|
213
|
+
if (nextArg && !nextArg.startsWith('--')) {
|
|
214
|
+
i += 1;
|
|
215
|
+
return nextArg;
|
|
216
|
+
}
|
|
217
|
+
return undefined;
|
|
218
|
+
};
|
|
219
|
+
switch (flag) {
|
|
220
|
+
case '--transport': {
|
|
221
|
+
const value = readValue();
|
|
222
|
+
if (value === 'stdio' || value === 'http' || value === 'sse' || value === 'stream') {
|
|
223
|
+
options.transport = value;
|
|
224
|
+
}
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
case '--port': {
|
|
228
|
+
const value = readValue();
|
|
229
|
+
const port = value ? Number(value) : NaN;
|
|
230
|
+
if (!Number.isNaN(port)) {
|
|
231
|
+
options.port = port;
|
|
232
|
+
}
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
case '--host': {
|
|
236
|
+
const value = readValue();
|
|
237
|
+
if (value) {
|
|
238
|
+
options.host = value;
|
|
239
|
+
}
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
case '--allow-origin': {
|
|
243
|
+
const value = readValue();
|
|
244
|
+
if (value) {
|
|
245
|
+
options.allowOrigins.push(value);
|
|
246
|
+
}
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
case '--allow-host': {
|
|
250
|
+
const value = readValue();
|
|
251
|
+
if (value) {
|
|
252
|
+
options.allowHosts.push(value);
|
|
253
|
+
}
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
case '--disable-sse':
|
|
257
|
+
options.enableSse = false;
|
|
258
|
+
break;
|
|
259
|
+
case '--disable-stream':
|
|
260
|
+
options.enableStream = false;
|
|
261
|
+
break;
|
|
262
|
+
case '--mode': {
|
|
263
|
+
const value = readValue();
|
|
264
|
+
if (value === 'full' || value === 'connector') {
|
|
265
|
+
options.mode = value;
|
|
266
|
+
}
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
default:
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (options.transport === 'sse') {
|
|
274
|
+
options.enableSse = true;
|
|
275
|
+
options.enableStream = false;
|
|
276
|
+
}
|
|
277
|
+
else if (options.transport === 'stream') {
|
|
278
|
+
options.enableSse = false;
|
|
279
|
+
options.enableStream = true;
|
|
280
|
+
}
|
|
281
|
+
else if (options.transport === 'http') {
|
|
282
|
+
options.enableSse = options.enableSse !== false;
|
|
283
|
+
options.enableStream = options.enableStream !== false;
|
|
284
|
+
}
|
|
285
|
+
if (options.transport !== 'stdio' && !options.enableSse && !options.enableStream) {
|
|
286
|
+
options.enableStream = true;
|
|
287
|
+
}
|
|
288
|
+
if (options.allowHosts.length === 0) {
|
|
289
|
+
options.allowHosts = resolveDefaultAllowedHosts(options.host, options.port);
|
|
290
|
+
}
|
|
291
|
+
if (options.allowOrigins.length === 0) {
|
|
292
|
+
options.allowOrigins = resolveDefaultAllowedOrigins(options.host, options.port);
|
|
293
|
+
}
|
|
294
|
+
return options;
|
|
295
|
+
}
|
|
296
|
+
async function startStdIoServer(options) {
|
|
297
|
+
configureStdoutFiltering();
|
|
298
|
+
if (options.mode === 'connector') {
|
|
299
|
+
console.info('Smart-Thinking: mode connecteur actif (outils search & fetch uniquement)');
|
|
300
|
+
}
|
|
301
|
+
const { server } = (0, smart_thinking_server_1.createSmartThinkingServer)(undefined, {
|
|
302
|
+
includeSmartThinkingTool: options.mode !== 'connector'
|
|
303
|
+
});
|
|
304
|
+
const transport = new platform_stdio_1.EnhancedStdioServerTransport();
|
|
305
|
+
try {
|
|
306
|
+
await server.connect(transport);
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
console.error('Smart-Thinking: échec de la connexion STDIO', error);
|
|
310
|
+
throw error;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
async function startHttpServer(options) {
|
|
314
|
+
const app = (0, express_1.default)();
|
|
315
|
+
app.use(express_1.default.json({ limit: '4mb' }));
|
|
316
|
+
app.use(createHostValidationMiddleware(options));
|
|
317
|
+
app.use(createCorsMiddleware(options));
|
|
318
|
+
app.get('/.well-known/mcp/server-card.json', (_req, res) => {
|
|
319
|
+
res.json(buildServerCard(options));
|
|
320
|
+
});
|
|
321
|
+
if (options.mode === 'connector') {
|
|
322
|
+
console.info('Smart-Thinking: mode connecteur actif (outils search & fetch uniquement)');
|
|
323
|
+
}
|
|
324
|
+
const sessions = new Map();
|
|
325
|
+
const dnsProtectionEnabled = true;
|
|
326
|
+
if (options.enableStream) {
|
|
327
|
+
app.all('/mcp', async (req, res) => {
|
|
328
|
+
try {
|
|
329
|
+
const sessionHeader = req.headers['mcp-session-id'];
|
|
330
|
+
const sessionId = Array.isArray(sessionHeader) ? sessionHeader[0] : sessionHeader;
|
|
331
|
+
if (sessionId) {
|
|
332
|
+
const state = sessions.get(sessionId);
|
|
333
|
+
if (!state || state.type !== 'stream') {
|
|
334
|
+
res.status(404).json({
|
|
335
|
+
jsonrpc: '2.0',
|
|
336
|
+
error: {
|
|
337
|
+
code: -32000,
|
|
338
|
+
message: 'Session inconnue pour le transport streamable HTTP'
|
|
339
|
+
},
|
|
340
|
+
id: null
|
|
341
|
+
});
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
await state.transport.handleRequest(req, res, req.body);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
if (req.method !== 'POST' || !(0, types_js_1.isInitializeRequest)(req.body)) {
|
|
348
|
+
res.status(400).json({
|
|
349
|
+
jsonrpc: '2.0',
|
|
350
|
+
error: {
|
|
351
|
+
code: -32000,
|
|
352
|
+
message: 'Initialisation manquante ou requête invalide'
|
|
353
|
+
},
|
|
354
|
+
id: null
|
|
355
|
+
});
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
const { server } = (0, smart_thinking_server_1.createSmartThinkingServer)(undefined, {
|
|
359
|
+
includeSmartThinkingTool: options.mode !== 'connector'
|
|
360
|
+
});
|
|
361
|
+
const transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
|
|
362
|
+
sessionIdGenerator: () => (0, crypto_1.randomUUID)(),
|
|
363
|
+
enableDnsRebindingProtection: dnsProtectionEnabled,
|
|
364
|
+
allowedHosts: options.allowHosts.length ? options.allowHosts : undefined,
|
|
365
|
+
allowedOrigins: options.allowOrigins.length ? options.allowOrigins : undefined,
|
|
366
|
+
onsessioninitialized: (id) => {
|
|
367
|
+
sessions.set(id, {
|
|
368
|
+
transport,
|
|
369
|
+
serverClose: () => server.close().catch(() => undefined),
|
|
370
|
+
type: 'stream'
|
|
371
|
+
});
|
|
372
|
+
},
|
|
373
|
+
onsessionclosed: (id) => {
|
|
374
|
+
void cleanupSession(sessions, id, true);
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
transport.onerror = (error) => {
|
|
378
|
+
console.error('Smart-Thinking: erreur transport streamable', error);
|
|
379
|
+
};
|
|
380
|
+
transport.onclose = () => {
|
|
381
|
+
void cleanupSession(sessions, transport.sessionId, true);
|
|
382
|
+
};
|
|
383
|
+
await server.connect(transport);
|
|
384
|
+
await transport.handleRequest(req, res, req.body);
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
if (!res.headersSent) {
|
|
388
|
+
res.status(500).json({
|
|
389
|
+
jsonrpc: '2.0',
|
|
390
|
+
error: {
|
|
391
|
+
code: -32603,
|
|
392
|
+
message: 'Erreur interne du serveur'
|
|
393
|
+
},
|
|
394
|
+
id: null
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
console.error('Smart-Thinking: erreur lors du traitement streamable HTTP', error);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
app.all('/mcp', (_req, res) => {
|
|
403
|
+
res.status(404).json({
|
|
404
|
+
jsonrpc: '2.0',
|
|
405
|
+
error: {
|
|
406
|
+
code: -32000,
|
|
407
|
+
message: 'Transport streamable HTTP désactivé sur ce serveur'
|
|
408
|
+
},
|
|
409
|
+
id: null
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
if (options.enableSse) {
|
|
414
|
+
app.get('/sse', async (req, res) => {
|
|
415
|
+
try {
|
|
416
|
+
const { server } = (0, smart_thinking_server_1.createSmartThinkingServer)(undefined, {
|
|
417
|
+
includeSmartThinkingTool: options.mode !== 'connector'
|
|
418
|
+
});
|
|
419
|
+
const transport = new sse_js_1.SSEServerTransport('/messages', res, {
|
|
420
|
+
enableDnsRebindingProtection: dnsProtectionEnabled,
|
|
421
|
+
allowedHosts: options.allowHosts.length ? options.allowHosts : undefined,
|
|
422
|
+
allowedOrigins: options.allowOrigins.length ? options.allowOrigins : undefined
|
|
423
|
+
});
|
|
424
|
+
const sessionId = transport.sessionId;
|
|
425
|
+
sessions.set(sessionId, {
|
|
426
|
+
transport,
|
|
427
|
+
serverClose: () => server.close().catch(() => undefined),
|
|
428
|
+
type: 'sse'
|
|
429
|
+
});
|
|
430
|
+
transport.onclose = () => {
|
|
431
|
+
void cleanupSession(sessions, sessionId, true);
|
|
432
|
+
};
|
|
433
|
+
transport.onerror = (error) => {
|
|
434
|
+
console.error('Smart-Thinking: erreur transport SSE', error);
|
|
435
|
+
};
|
|
436
|
+
await server.connect(transport);
|
|
437
|
+
}
|
|
438
|
+
catch (error) {
|
|
439
|
+
if (!res.headersSent) {
|
|
440
|
+
res.status(500).send('Erreur lors de l\'établissement du flux SSE');
|
|
441
|
+
}
|
|
442
|
+
console.error('Smart-Thinking: échec d\'initialisation SSE', error);
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
app.post('/messages', async (req, res) => {
|
|
446
|
+
try {
|
|
447
|
+
const queryParam = req.query.sessionId;
|
|
448
|
+
const sessionId = typeof queryParam === 'string'
|
|
449
|
+
? queryParam
|
|
450
|
+
: Array.isArray(queryParam)
|
|
451
|
+
? queryParam.find((value) => typeof value === 'string')
|
|
452
|
+
: undefined;
|
|
453
|
+
if (!sessionId) {
|
|
454
|
+
res.status(400).send('Paramètre sessionId manquant');
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
const state = sessions.get(sessionId);
|
|
458
|
+
if (!state || state.type !== 'sse') {
|
|
459
|
+
res.status(404).send('Session SSE introuvable');
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
await state.transport.handlePostMessage(req, res, req.body);
|
|
463
|
+
}
|
|
464
|
+
catch (error) {
|
|
465
|
+
if (!res.headersSent) {
|
|
466
|
+
res.status(500).send('Erreur traitement message SSE');
|
|
467
|
+
}
|
|
468
|
+
console.error('Smart-Thinking: erreur sur /messages', error);
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
app.get('/sse', (_req, res) => {
|
|
474
|
+
res.status(404).send('Transport SSE désactivé');
|
|
475
|
+
});
|
|
476
|
+
app.post('/messages', (_req, res) => {
|
|
477
|
+
res.status(404).send('Transport SSE désactivé');
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
app.use((error, _req, res, _next) => {
|
|
481
|
+
if (!res.headersSent) {
|
|
482
|
+
res.status(500).json({ error: 'Erreur interne du serveur MCP' });
|
|
483
|
+
}
|
|
484
|
+
console.error('Smart-Thinking: middleware erreur', error);
|
|
485
|
+
});
|
|
486
|
+
await new Promise((resolve, reject) => {
|
|
487
|
+
const httpServer = app.listen(options.port, options.host, () => {
|
|
488
|
+
console.info(`Smart-Thinking: serveur MCP HTTP lance sur http://${options.host}:${options.port}`);
|
|
489
|
+
if (options.enableStream) {
|
|
490
|
+
console.info(' • Endpoint streamable HTTP : /mcp');
|
|
491
|
+
}
|
|
492
|
+
if (options.enableSse) {
|
|
493
|
+
console.info(' • Endpoint SSE (legacy) : /sse + /messages');
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
httpServer.on('error', reject);
|
|
497
|
+
const shutdown = async () => {
|
|
498
|
+
console.info('Smart-Thinking: arret du serveur HTTP en cours...');
|
|
499
|
+
for (const [sessionId] of sessions) {
|
|
500
|
+
await cleanupSession(sessions, sessionId);
|
|
501
|
+
}
|
|
502
|
+
await new Promise((resolveClose) => {
|
|
503
|
+
httpServer.close(() => resolveClose());
|
|
504
|
+
});
|
|
505
|
+
resolve();
|
|
506
|
+
};
|
|
507
|
+
process.once('SIGINT', shutdown);
|
|
508
|
+
process.once('SIGTERM', shutdown);
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
function createCorsMiddleware(options) {
|
|
512
|
+
return (req, res, next) => {
|
|
513
|
+
const originHeader = Array.isArray(req.headers.origin) ? req.headers.origin[0] : req.headers.origin;
|
|
514
|
+
res.header('Vary', 'Origin');
|
|
515
|
+
if (originHeader) {
|
|
516
|
+
if (!options.allowOrigins.includes(originHeader)) {
|
|
517
|
+
res.status(403).json({ error: 'Origin non autorisee' });
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
res.header('Access-Control-Allow-Origin', originHeader);
|
|
521
|
+
}
|
|
522
|
+
res.header('Access-Control-Allow-Headers', 'Content-Type, MCP-Session-Id');
|
|
523
|
+
res.header('Access-Control-Expose-Headers', 'Mcp-Session-Id');
|
|
524
|
+
res.header('Access-Control-Allow-Methods', 'GET,POST,DELETE,OPTIONS');
|
|
525
|
+
if (req.method === 'OPTIONS') {
|
|
526
|
+
res.status(204).end();
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
next();
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
function createHostValidationMiddleware(options) {
|
|
533
|
+
const allowedHosts = new Set(options.allowHosts.map(host => host.toLowerCase()));
|
|
534
|
+
return (req, res, next) => {
|
|
535
|
+
const hostHeader = Array.isArray(req.headers.host) ? req.headers.host[0] : req.headers.host;
|
|
536
|
+
if (!hostHeader) {
|
|
537
|
+
next();
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
const normalizedHost = hostHeader.toLowerCase();
|
|
541
|
+
const hostOnly = normalizedHost.split(':')[0];
|
|
542
|
+
if (!allowedHosts.has(normalizedHost) && !allowedHosts.has(hostOnly)) {
|
|
543
|
+
res.status(403).json({ error: 'Host non autorise' });
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
next();
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
async function cleanupSession(sessions, sessionId, skipTransportClose = false) {
|
|
550
|
+
if (!sessionId) {
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
const state = sessions.get(sessionId);
|
|
554
|
+
if (!state) {
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
sessions.delete(sessionId);
|
|
558
|
+
try {
|
|
559
|
+
if (!skipTransportClose) {
|
|
560
|
+
await state.transport.close();
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
catch (error) {
|
|
564
|
+
console.error(`Smart-Thinking: erreur fermeture transport ${sessionId}`, error);
|
|
565
|
+
}
|
|
566
|
+
try {
|
|
567
|
+
await state.serverClose();
|
|
568
|
+
}
|
|
569
|
+
catch (error) {
|
|
570
|
+
console.error(`Smart-Thinking: erreur fermeture serveur ${sessionId}`, error);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
function configureStdoutFiltering() {
|
|
574
|
+
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
575
|
+
function isValidJSON(str) {
|
|
576
|
+
if (typeof str !== 'string')
|
|
577
|
+
return false;
|
|
578
|
+
const trimmed = str.trim();
|
|
579
|
+
if (!trimmed)
|
|
580
|
+
return false;
|
|
581
|
+
if (!(trimmed.startsWith('{') && trimmed.endsWith('}')) &&
|
|
582
|
+
!(trimmed.startsWith('[') && trimmed.endsWith(']'))) {
|
|
583
|
+
return false;
|
|
584
|
+
}
|
|
585
|
+
try {
|
|
586
|
+
JSON.parse(trimmed);
|
|
587
|
+
return true;
|
|
588
|
+
}
|
|
589
|
+
catch {
|
|
590
|
+
return false;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
process.stdout.write = function stdoutFilter(chunk, encoding, cb) {
|
|
594
|
+
if (typeof chunk === 'string') {
|
|
595
|
+
const trimmed = chunk.trim();
|
|
596
|
+
if ((trimmed.startsWith('{') || trimmed.startsWith('[')) && !isValidJSON(trimmed)) {
|
|
597
|
+
console.error('[ERREUR] JSON invalide détecté:', chunk);
|
|
598
|
+
try {
|
|
599
|
+
const safeMessage = JSON.stringify({
|
|
600
|
+
jsonrpc: '2.0',
|
|
601
|
+
result: {
|
|
602
|
+
content: [{ type: 'text', text: chunk }]
|
|
603
|
+
}
|
|
604
|
+
}) + (config_1.PlatformConfig.IS_WINDOWS ? '\n' : '');
|
|
605
|
+
return originalStdoutWrite(safeMessage, encoding, cb);
|
|
606
|
+
}
|
|
607
|
+
catch (error) {
|
|
608
|
+
console.error('[ERREUR] Impossible de corriger le JSON:', error);
|
|
609
|
+
process.stderr.write(chunk, encoding);
|
|
610
|
+
if (cb)
|
|
611
|
+
cb();
|
|
612
|
+
return true;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {
|
|
616
|
+
process.stderr.write(chunk, encoding);
|
|
617
|
+
if (cb)
|
|
618
|
+
cb();
|
|
619
|
+
return true;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return originalStdoutWrite(chunk, encoding, cb);
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
async function ensureDataDirExists() {
|
|
626
|
+
const dataDir = path_utils_1.PathUtils.getDataDirectory();
|
|
627
|
+
try {
|
|
628
|
+
await fs_1.promises.mkdir(dataDir, { recursive: true });
|
|
629
|
+
if (config_1.PlatformConfig.IS_WINDOWS) {
|
|
630
|
+
try {
|
|
631
|
+
await fs_1.promises.access(dataDir, fs_1.constants.W_OK);
|
|
632
|
+
}
|
|
633
|
+
catch {
|
|
634
|
+
console.warn('Smart-Thinking: permissions limitées sur le répertoire data, utilisation possible d\'un fallback.');
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
catch {
|
|
639
|
+
if (config_1.PlatformConfig.IS_WINDOWS) {
|
|
640
|
+
const altDataDir = path_1.default.join(process.env.USERPROFILE || '', 'Documents', 'Smart-Thinking', 'data');
|
|
641
|
+
try {
|
|
642
|
+
await fs_1.promises.mkdir(altDataDir, { recursive: true });
|
|
643
|
+
}
|
|
644
|
+
catch {
|
|
645
|
+
// Ignorer l'erreur, le gestionnaire de mémoire basculera en mémoire vive uniquement
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
//# sourceMappingURL=cli.js.map
|