@happykiller/sunny-mcp 0.2.0 → 0.2.2
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
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
|
|
2
|
+
## Note sur les tests
|
|
3
|
+
|
|
4
|
+
`npm test` passe `--test-force-exit` : le transport HTTP du SDK garde des
|
|
5
|
+
ressources que la fermeture du serveur n'emporte pas toutes, et le lanceur ne
|
|
6
|
+
rendait jamais la main alors que tous les cas passaient. Le drapeau est prévu
|
|
7
|
+
pour cela. Les assertions, elles, ne sont pas affaiblies : les cas de
|
|
8
|
+
`transport.test.ts` exercent de vraies requêtes HTTP.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Server } from 'node:http';
|
|
1
2
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
3
|
import type { OAuthTokenVerifier } from '@modelcontextprotocol/sdk/server/auth/provider.js';
|
|
3
4
|
import type { Logger } from '../logger.js';
|
|
@@ -21,5 +22,11 @@ export interface StartOptions {
|
|
|
21
22
|
metadata: OptionsMetadonnees;
|
|
22
23
|
};
|
|
23
24
|
}
|
|
24
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Démarre le transport et rend le serveur HTTP, quand il y en a un.
|
|
27
|
+
*
|
|
28
|
+
* Le rendre n'est pas une commodité : un serveur qu'on ne peut pas arrêter est
|
|
29
|
+
* intestable, et il empêche tout processus qui l'embarque de se terminer.
|
|
30
|
+
*/
|
|
31
|
+
export declare function startTransport(serveur: McpServer | (() => McpServer), opts?: StartOptions): Promise<Server | undefined>;
|
|
25
32
|
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/server/transport.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/server/transport.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAGxC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mDAAmD,CAAC;AAE5F,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAElE,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,CAAC;AAE7C,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE;QACf,QAAQ,EAAE,kBAAkB,CAAC;QAC7B,2EAA2E;QAC3E,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAC1B,QAAQ,EAAE,kBAAkB,CAAC;KAC9B,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,SAAS,GAAG,CAAC,MAAM,SAAS,CAAC,EACtC,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAqF7B"}
|
package/dist/server/transport.js
CHANGED
|
@@ -9,13 +9,20 @@ import { metadataHandler } from '@modelcontextprotocol/sdk/server/auth/handlers/
|
|
|
9
9
|
import { requireBearerAuth } from '@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js';
|
|
10
10
|
import { silentLogger } from '../logger.js';
|
|
11
11
|
import { CHEMIN_METADONNEES, metadonneesRessource } from '../resource/metadata.js';
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Démarre le transport et rend le serveur HTTP, quand il y en a un.
|
|
14
|
+
*
|
|
15
|
+
* Le rendre n'est pas une commodité : un serveur qu'on ne peut pas arrêter est
|
|
16
|
+
* intestable, et il empêche tout processus qui l'embarque de se terminer.
|
|
17
|
+
*/
|
|
18
|
+
export async function startTransport(serveur, opts = {}) {
|
|
13
19
|
const logger = opts.logger ?? silentLogger;
|
|
14
20
|
const kind = opts.transport ?? 'stdio';
|
|
15
21
|
if (kind === 'stdio') {
|
|
22
|
+
const server = typeof serveur === 'function' ? serveur() : serveur;
|
|
16
23
|
await server.connect(new StdioServerTransport());
|
|
17
24
|
logger.info('transport stdio prêt');
|
|
18
|
-
return;
|
|
25
|
+
return undefined;
|
|
19
26
|
}
|
|
20
27
|
const app = express();
|
|
21
28
|
app.use(express.json({ limit: '4mb' }));
|
|
@@ -24,16 +31,33 @@ export async function startTransport(server, opts = {}) {
|
|
|
24
31
|
// Le document de découverte reste PUBLIC : c'est par lui qu'un client non
|
|
25
32
|
// authentifié apprend où aller s'authentifier. L'exiger authentifié
|
|
26
33
|
// rendrait la découverte impossible.
|
|
27
|
-
app.get
|
|
34
|
+
// `app.use` et non `app.get` : le SDK rend un Router dont la route est `/`.
|
|
35
|
+
// Monté en `get`, la requête lui parvient avec son chemin entier, sa route
|
|
36
|
+
// ne correspond jamais, et le document répond 404 — en désignant pourtant
|
|
37
|
+
// sa propre adresse dans l'en-tête `WWW-Authenticate` des refus.
|
|
38
|
+
app.use(CHEMIN_METADONNEES, metadataHandler(metadonneesRessource(protection.metadata)));
|
|
28
39
|
logger.info(`métadonnées de ressource sur ${CHEMIN_METADONNEES}`);
|
|
29
40
|
}
|
|
30
|
-
// `sessionIdGenerator: undefined
|
|
41
|
+
// Mode SANS ÉTAT : `sessionIdGenerator: undefined`. Aucune session n'est tenue
|
|
31
42
|
// côté serveur, donc rien à répliquer si l'on déploie plusieurs instances.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
//
|
|
44
|
+
// La conséquence, qui n'a rien d'évident : le transport ne se PARTAGE PAS
|
|
45
|
+
// entre requêtes. Un transport unique accepte la première initialisation puis
|
|
46
|
+
// rejette tout le reste en 500, sans message. Il faut donc en créer un — et un
|
|
47
|
+
// serveur — par requête, puis les refermer.
|
|
48
|
+
const fabriqueServeur = typeof serveur === 'function' ? serveur : () => serveur;
|
|
36
49
|
const relais = async (req, res) => {
|
|
50
|
+
const instance = fabriqueServeur();
|
|
51
|
+
const transport = new StreamableHTTPServerTransport({
|
|
52
|
+
sessionIdGenerator: undefined,
|
|
53
|
+
});
|
|
54
|
+
// Refermer sur la fin de la réponse : sans cela chaque requête laisserait
|
|
55
|
+
// un serveur et un transport derrière elle.
|
|
56
|
+
res.on('close', () => {
|
|
57
|
+
void transport.close();
|
|
58
|
+
void instance.close();
|
|
59
|
+
});
|
|
60
|
+
await instance.connect(transport);
|
|
37
61
|
await transport.handleRequest(req, res, req.body);
|
|
38
62
|
};
|
|
39
63
|
// Le middleware du SDK produit lui-même le 401 et son en-tête
|
|
@@ -53,11 +77,14 @@ export async function startTransport(server, opts = {}) {
|
|
|
53
77
|
app.get('/mcp', ...garde, relais);
|
|
54
78
|
app.delete('/mcp', ...garde, relais);
|
|
55
79
|
const port = opts.port ?? 3000;
|
|
56
|
-
|
|
57
|
-
app.listen(port, () => {
|
|
80
|
+
return new Promise((resolve) => {
|
|
81
|
+
const httpServer = app.listen(port, () => {
|
|
58
82
|
logger.info(`transport http prêt sur :${port}/mcp`);
|
|
59
|
-
resolve();
|
|
83
|
+
resolve(httpServer);
|
|
60
84
|
});
|
|
85
|
+
// Fermer le serveur HTTP doit tout emporter. Sans cela, le transport et le
|
|
86
|
+
// serveur MCP gardent des ressources que l'appelant n'a aucun moyen
|
|
87
|
+
// d'atteindre — et le processus qui les embarque ne se termine jamais.
|
|
61
88
|
});
|
|
62
89
|
}
|
|
63
90
|
//# sourceMappingURL=transport.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/server/transport.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,+EAA+E;AAC/E,iFAAiF;AACjF,+DAA+D;AAC/D,OAAO,OAAO,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/server/transport.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,+EAA+E;AAC/E,iFAAiF;AACjF,+DAA+D;AAC/D,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAGnG,OAAO,EAAE,eAAe,EAAE,MAAM,4DAA4D,CAAC;AAC7F,OAAO,EAAE,iBAAiB,EAAE,MAAM,gEAAgE,CAAC;AAInG,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAwBnF;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAsC,EACtC,OAAqB,EAAE;IAEvB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,YAAY,CAAC;IAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,IAAI,OAAO,CAAC;IAEvC,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,MAAM,MAAM,GACV,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;QACtD,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QACpC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IACtB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IAExC,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,CAAC;IACvC,IAAI,UAAU,EAAE,CAAC;QACf,0EAA0E;QAC1E,oEAAoE;QACpE,qCAAqC;QACrC,4EAA4E;QAC5E,2EAA2E;QAC3E,0EAA0E;QAC1E,iEAAiE;QACjE,GAAG,CAAC,GAAG,CACL,kBAAkB,EAClB,eAAe,CAAC,oBAAoB,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAC3D,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,gCAAgC,kBAAkB,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,+EAA+E;IAC/E,2EAA2E;IAC3E,EAAE;IACF,0EAA0E;IAC1E,8EAA8E;IAC9E,+EAA+E;IAC/E,4CAA4C;IAC5C,MAAM,eAAe,GACnB,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC;IAE1D,MAAM,MAAM,GAAG,KAAK,EAAE,GAAoB,EAAE,GAAqB,EAAE,EAAE;QACnE,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAC;QACnC,MAAM,SAAS,GAAG,IAAI,6BAA6B,CAAC;YAClD,kBAAkB,EAAE,SAAS;SAC9B,CAAC,CAAC;QACH,0EAA0E;QAC1E,4CAA4C;QAC5C,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACnB,KAAK,SAAS,CAAC,KAAK,EAAE,CAAC;YACvB,KAAK,QAAQ,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC,CAAC,CAAC;QACH,MAAM,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAClC,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IACpD,CAAC,CAAC;IAEF,8DAA8D;IAC9D,0EAA0E;IAC1E,0EAA0E;IAC1E,qEAAqE;IACrE,MAAM,KAAK,GAA6B,UAAU;QAChD,CAAC,CAAC;YACE,iBAAiB,CAAC;gBAChB,QAAQ,EAAE,UAAU,CAAC,QAAQ;gBAC7B,cAAc,EAAE,UAAU,CAAC,cAAc;gBACzC,mBAAmB,EAAE,GAAG,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,GAAG,kBAAkB,EAAE;aAClG,CAAC;SACH;QACH,CAAC,CAAC,EAAE,CAAC;IAEP,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK,EAAE,MAAM,CAAC,CAAC;IACnC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,KAAK,EAAE,MAAM,CAAC,CAAC;IAClC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,KAAK,EAAE,MAAM,CAAC,CAAC;IAErC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC;IAC/B,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;QACrC,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;YACvC,MAAM,CAAC,IAAI,CAAC,4BAA4B,IAAI,MAAM,CAAC,CAAC;YACpD,OAAO,CAAC,UAAU,CAAC,CAAC;QACtB,CAAC,CAAC,CAAC;QAEH,2EAA2E;QAC3E,oEAAoE;QACpE,uEAAuE;IACzE,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happykiller/sunny-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Socle MCP partagé : transport, session GraphQL, registre d'outils et garde-fous, pour exposer le back-office d'un produit aux agents.",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"repository": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build": "rm -rf dist && tsc -p tsconfig.json",
|
|
25
|
-
"test": "tsx --test $(find src -name '*.test.ts' | sort)",
|
|
25
|
+
"test": "tsx --test --test-force-exit $(find src -name '*.test.ts' | sort)",
|
|
26
26
|
"check": "tsc --noEmit -p tsconfig.json && npm test"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|